Compare commits
2 commits
main
...
ui-events-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c21f7219f6 | ||
|
|
b24f1f688a |
15 changed files with 372 additions and 22 deletions
|
|
@ -14,6 +14,7 @@ import Notifications from './Notifications';
|
|||
import Roles from './Roles';
|
||||
import SessionsListingSettings from 'Components/Client/SessionsListingSettings';
|
||||
import Modules from 'Components/Client/Modules';
|
||||
import CustomEventsList from 'Components/Client/CustomEvents/CustomEventsList';
|
||||
|
||||
@withRouter
|
||||
export default class Client extends React.PureComponent {
|
||||
|
|
@ -38,6 +39,7 @@ export default class Client extends React.PureComponent {
|
|||
<Route exact strict path={clientRoute(CLIENT_TABS.MANAGE_ROLES)} component={Roles} />
|
||||
<Route exact strict path={clientRoute(CLIENT_TABS.AUDIT)} component={AuditView} />
|
||||
<Route exact strict path={clientRoute(CLIENT_TABS.MODULES)} component={Modules} />
|
||||
<Route exact strict path={clientRoute(CLIENT_TABS.CUSTOM_EVENTS)} component={CustomEventsList} />
|
||||
<Redirect to={clientRoute(CLIENT_TABS.PROFILE)} />
|
||||
</Switch>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import React from 'react';
|
||||
import { Button, Form, Input } from 'antd';
|
||||
import { FormField } from 'semantic-ui-react';
|
||||
|
||||
interface Props {
|
||||
event?: any;
|
||||
}
|
||||
|
||||
function CustomEventForm(props: Props) {
|
||||
return (
|
||||
<div>
|
||||
<Form layout="vertical">
|
||||
<FormField>
|
||||
<label>Name</label>
|
||||
<Input />
|
||||
</FormField>
|
||||
|
||||
<Button type="primary">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomEventForm;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
event: any;
|
||||
}
|
||||
|
||||
function CustomEventItem(props: Props) {
|
||||
return (
|
||||
<div className="flex">
|
||||
<div>{props.event.name}</div>
|
||||
<div>{props.event.user?.name}</div>
|
||||
{/*<div>{props.event.created_at}</div>*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomEventItem;
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import cn from 'classnames';
|
||||
import styles from 'Components/Client/Webhooks/webhooks.module.css';
|
||||
import { Button, Divider, Icon, Loader, NoContent } from 'UI';
|
||||
import AnimatedSVG from 'Shared/AnimatedSVG';
|
||||
import { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
import CustomEventItem from 'Components/Client/CustomEvents/CustomEventItem';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { useModal } from 'Components/ModalContext';
|
||||
import CustomEventForm from 'Components/Client/CustomEvents/CustomEventForm';
|
||||
import { List } from 'antd';
|
||||
|
||||
function CustomEventsList() {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const { customEventStore: store } = useStore();
|
||||
const { openModal } = useModal();
|
||||
|
||||
// useEffect(() => {
|
||||
// setLoading(true);
|
||||
// store.fetchAll({}).finally(() => {
|
||||
// setLoading(false);
|
||||
// });
|
||||
// setTimeout(() => {
|
||||
// setLoading(false);
|
||||
// }, 2000);
|
||||
// }, []);
|
||||
|
||||
const init = () => {
|
||||
console.log('init');
|
||||
showEvent();
|
||||
};
|
||||
|
||||
const showEvent = (event?: any) => {
|
||||
openModal(<CustomEventForm event={event} />, {
|
||||
title: event ? 'Edit Event' : 'Add Event',
|
||||
width: 400
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-sm border p-5">
|
||||
<div className={cn(styles.tabHeader)}>
|
||||
<h3 className={cn(styles.tabTitle, 'text-2xl')}>{'Events'}</h3>
|
||||
<Button className="ml-auto" variant="primary" onClick={() => init()}>Add Event</Button>
|
||||
</div>
|
||||
|
||||
<div className="text-base text-disabled-text flex items-center my-3 px-5">
|
||||
<Icon name="info-circle-fill" className="mr-2" size={16} />
|
||||
Leverage webhook notifications on alerts to trigger custom callbacks.
|
||||
</div>
|
||||
|
||||
<Loader loading={loading}>
|
||||
<NoContent
|
||||
title={
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<AnimatedSVG name={ICONS.NO_WEBHOOKS} size={60} />
|
||||
<div className="text-center my-4">None added yet</div>
|
||||
</div>
|
||||
}
|
||||
size="small"
|
||||
show={store.list.length === 0}
|
||||
>
|
||||
<List dataSource={store.list} renderItem={(event) => (
|
||||
<List.Item onClick={showEvent}>
|
||||
<CustomEventItem event={event} />
|
||||
</List.Item>
|
||||
)} />
|
||||
</NoContent>
|
||||
</Loader>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => ({
|
||||
sites: state.getIn(['site', 'list'])
|
||||
});
|
||||
|
||||
export default CustomEventsList;
|
||||
|
|
@ -117,7 +117,7 @@ function WidgetChart(props: Props) {
|
|||
useEffect(() => {
|
||||
_metric.updateKey('page', 1);
|
||||
loadPage();
|
||||
}, [drillDownPeriod, period, depsString, metric.metricType, metric.metricOf, metric.viewType, metric.metricValue, metric.startType]);
|
||||
}, [drillDownPeriod, period, depsString, metric.metricType, metric.metricOf, metric.viewType, metric.metricValue, metric.startType, metric.metricFormat]);
|
||||
useEffect(loadPage, [_metric.page]);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
import React from 'react';
|
||||
import { HEATMAP, TABLE, USER_PATH } from 'App/constants/card';
|
||||
import { Select, Space, Switch } from 'antd';
|
||||
import { useStore } from 'App/mstore';
|
||||
import ClickMapRagePicker from 'Components/Dashboard/components/ClickMapRagePicker/ClickMapRagePicker';
|
||||
import { FilterKey } from 'Types/filter/filterType';
|
||||
|
||||
interface Props {
|
||||
|
||||
}
|
||||
|
||||
|
||||
function WidgetOptions(props: Props) {
|
||||
const { metricStore, dashboardStore } = useStore();
|
||||
const metric: any = metricStore.instance;
|
||||
|
||||
const handleChange = (value: any) => {
|
||||
console.log(`selected ${value}`);
|
||||
metric.update({ metricFormat: value });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{metric.metricType === USER_PATH && (
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
metric.update({ hideExcess: !metric.hideExcess });
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<Switch checked={metric.hideExcess} size="small" />
|
||||
<span className="mr-4 color-gray-medium">
|
||||
Hide Minor Paths
|
||||
</span>
|
||||
</Space>
|
||||
</a>
|
||||
)}
|
||||
|
||||
{metric.metricType === TABLE && metric.metricOf != FilterKey.USERID && (
|
||||
<Select
|
||||
defaultValue={metric.metricFormat}
|
||||
style={{ width: 120 }}
|
||||
onChange={handleChange}
|
||||
options={[
|
||||
{ value: 'sessionCount', label: 'Sessions' },
|
||||
{ value: 'userCount', label: 'Users' }
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
|
||||
{metric.metricType === HEATMAP ? (
|
||||
<ClickMapRagePicker />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WidgetOptions;
|
||||
|
|
@ -8,6 +8,7 @@ import { useStore } from 'App/mstore';
|
|||
import ClickMapRagePicker from 'Components/Dashboard/components/ClickMapRagePicker';
|
||||
|
||||
import WidgetWrapper from '../WidgetWrapper';
|
||||
import WidgetOptions from 'Components/Dashboard/components/WidgetOptions';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
|
|
@ -28,22 +29,24 @@ function WidgetPreview(props: Props) {
|
|||
<div className="flex items-center justify-between px-4 pt-2">
|
||||
<h2 className="text-xl">{props.name}</h2>
|
||||
<div className="flex items-center">
|
||||
{metric.metricType === USER_PATH && (
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
metric.update({ hideExcess: !metric.hideExcess });
|
||||
}}
|
||||
>
|
||||
<Space>
|
||||
<Switch checked={metric.hideExcess} size="small" />
|
||||
<span className="mr-4 color-gray-medium">
|
||||
Hide Minor Paths
|
||||
</span>
|
||||
</Space>
|
||||
</a>
|
||||
)}
|
||||
<WidgetOptions />
|
||||
{/*{metric.metricType === USER_PATH && (*/}
|
||||
{/* <a*/}
|
||||
{/* href="#"*/}
|
||||
{/* onClick={(e) => {*/}
|
||||
{/* e.preventDefault();*/}
|
||||
{/* metric.update({ hideExcess: !metric.hideExcess });*/}
|
||||
{/* }}*/}
|
||||
{/* >*/}
|
||||
{/* <Space>*/}
|
||||
{/* <Switch checked={metric.hideExcess} size="small" />*/}
|
||||
{/* <span className="mr-4 color-gray-medium">*/}
|
||||
{/* Hide Minor Paths*/}
|
||||
{/* </span>*/}
|
||||
{/* </Space>*/}
|
||||
{/* </a>*/}
|
||||
{/*)}*/}
|
||||
|
||||
|
||||
{/*{isTimeSeries && (*/}
|
||||
{/* <>*/}
|
||||
|
|
@ -101,10 +104,7 @@ function WidgetPreview(props: Props) {
|
|||
{/*</>*/}
|
||||
{/*)}*/}
|
||||
|
||||
<div className="mx-4" />
|
||||
{metric.metricType === HEATMAP ? (
|
||||
<ClickMapRagePicker />
|
||||
) : null}
|
||||
|
||||
|
||||
{/* add to dashboard */}
|
||||
{/*{metric.exists() && (*/}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,8 @@ function SideMenu(props: Props) {
|
|||
[PREFERENCES_MENU.TEAM]: () => client(CLIENT_TABS.MANAGE_USERS),
|
||||
[PREFERENCES_MENU.NOTIFICATIONS]: () => client(CLIENT_TABS.NOTIFICATIONS),
|
||||
[PREFERENCES_MENU.BILLING]: () => client(CLIENT_TABS.BILLING),
|
||||
[PREFERENCES_MENU.MODULES]: () => client(CLIENT_TABS.MODULES)
|
||||
[PREFERENCES_MENU.MODULES]: () => client(CLIENT_TABS.MODULES),
|
||||
[PREFERENCES_MENU.CUSTOM_EVENTS]: () => client(CLIENT_TABS.CUSTOM_EVENTS),
|
||||
};
|
||||
|
||||
const handleClick = (item: any) => {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export const enum PREFERENCES_MENU {
|
|||
INTEGRATIONS = 'integrations',
|
||||
METADATA = 'metadata',
|
||||
WEBHOOKS = 'webhooks',
|
||||
CUSTOM_EVENTS = 'custom-events',
|
||||
MODULES = 'modules',
|
||||
PROJECTS = 'projects',
|
||||
ROLES_ACCESS = 'roles-access',
|
||||
|
|
@ -134,6 +135,7 @@ export const preferences: Category[] = [
|
|||
{ label: 'Integrations', key: PREFERENCES_MENU.INTEGRATIONS, icon: 'plug' },
|
||||
{ label: 'Metadata', key: PREFERENCES_MENU.METADATA, icon: 'tags' },
|
||||
{ label: 'Webhooks', key: PREFERENCES_MENU.WEBHOOKS, icon: 'link-45deg' },
|
||||
{ label: 'Custom Events', key: PREFERENCES_MENU.CUSTOM_EVENTS, icon: 'calendar' },
|
||||
{ label: 'Modules', key: PREFERENCES_MENU.MODULES, icon: 'puzzle' },
|
||||
{ label: 'Projects', key: PREFERENCES_MENU.PROJECTS, icon: 'folder2' },
|
||||
{
|
||||
|
|
|
|||
80
frontend/app/mstore/customEventsStore.ts
Normal file
80
frontend/app/mstore/customEventsStore.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { makeAutoObservable, runInAction } from 'mobx';
|
||||
import { customEventService } from 'App/services';
|
||||
import Audit from 'MOBX/types/audit';
|
||||
import CustomEvent from 'App/mstore/types/customEvent';
|
||||
|
||||
export default class CustomEventsStore {
|
||||
list: CustomEvent[] = [
|
||||
CustomEvent.fromJson({
|
||||
id: '1',
|
||||
name: 'Event 1',
|
||||
description: 'Description 1',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
user: {
|
||||
id: '1',
|
||||
name: 'User 1'
|
||||
}
|
||||
}),
|
||||
CustomEvent.fromJson({
|
||||
id: '2',
|
||||
name: 'Event 2',
|
||||
description: 'Description 2',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
user: {
|
||||
id: '1',
|
||||
name: 'User 1'
|
||||
}
|
||||
}),
|
||||
CustomEvent.fromJson({
|
||||
id: '3',
|
||||
name: 'Event 3',
|
||||
description: 'Description 3',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
user: {
|
||||
id: '1',
|
||||
name: 'User 1'
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
fetchAll = async (params: any = {}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
customEventService.fetchAll(params).then((response: any) => {
|
||||
runInAction(() => {
|
||||
this.list = response.data.map((item: any) => CustomEvent.fromJson(item));
|
||||
});
|
||||
resolve(this.list);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
}).finally(() => {
|
||||
// this.isLoading = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
createCustomEvent = async (data: any) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
customEventService.create(data).then(response => {
|
||||
runInAction(() => {
|
||||
this.list.push(CustomEvent.fromJson(response.data));
|
||||
});
|
||||
resolve(response);
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
updateCustomEvent = async (data: any) => {
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import AiFiltersStore from "./aiFiltersStore";
|
|||
import SpotStore from "./spotStore";
|
||||
import LoginStore from "./loginStore";
|
||||
import FilterStore from "./filterStore";
|
||||
import CustomEventStore from "./customEventsStore"
|
||||
|
||||
export class RootStore {
|
||||
dashboardStore: DashboardStore;
|
||||
|
|
@ -49,6 +50,7 @@ export class RootStore {
|
|||
spotStore: SpotStore;
|
||||
loginStore: LoginStore;
|
||||
filterStore: FilterStore;
|
||||
customEventStore: CustomEventStore;
|
||||
|
||||
constructor() {
|
||||
this.dashboardStore = new DashboardStore();
|
||||
|
|
@ -74,6 +76,7 @@ export class RootStore {
|
|||
this.spotStore = new SpotStore();
|
||||
this.loginStore = new LoginStore();
|
||||
this.filterStore = new FilterStore();
|
||||
this.customEventStore = new CustomEventStore();
|
||||
}
|
||||
|
||||
initClient() {
|
||||
|
|
|
|||
49
frontend/app/mstore/types/customEvent.ts
Normal file
49
frontend/app/mstore/types/customEvent.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ICustomEvent {
|
||||
id: string;
|
||||
name: string;
|
||||
user: User;
|
||||
created_at: Date;
|
||||
}
|
||||
|
||||
export default class CustomEvent implements ICustomEvent {
|
||||
id: string;
|
||||
name: string;
|
||||
user: User;
|
||||
created_at: Date;
|
||||
|
||||
constructor(id: string, name: string, user: User, created_at: Date) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.user = user;
|
||||
this.created_at = created_at;
|
||||
}
|
||||
|
||||
static fromJson(json: any): CustomEvent {
|
||||
return new CustomEvent(
|
||||
json.id,
|
||||
json.name,
|
||||
{
|
||||
id: json.user?.id,
|
||||
name: json.user?.name
|
||||
},
|
||||
new Date(json.created_at)
|
||||
);
|
||||
}
|
||||
|
||||
toJson(): any {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
user: {
|
||||
id: this.user.id,
|
||||
name: this.user.name
|
||||
},
|
||||
created_at: this.created_at.toISOString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -67,6 +67,7 @@ export const CLIENT_TABS = {
|
|||
SITES: 'projects',
|
||||
CUSTOM_FIELDS: 'metadata',
|
||||
WEBHOOKS: 'webhooks',
|
||||
CUSTOM_EVENTS: 'custom-events',
|
||||
NOTIFICATIONS: 'notifications',
|
||||
AUDIT: 'audit',
|
||||
BILLING: 'billing',
|
||||
|
|
|
|||
28
frontend/app/services/CustomEventService.ts
Normal file
28
frontend/app/services/CustomEventService.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import BaseService from './BaseService';
|
||||
|
||||
export default class CustomEventService extends BaseService {
|
||||
async fetchAll(params: {}): Promise<[]> {
|
||||
return this.client.get('/custom-events', params)
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
|
||||
async create(data: {}): Promise<{}> {
|
||||
return this.client.post('/custom-events', data)
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
|
||||
async update(data: {}): Promise<{}> {
|
||||
return this.client.put(`/custom-events/${data.id}`, data)
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<{}> {
|
||||
return this.client.delete(`/custom-events/${id}`)
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
|
||||
async fetchOne(id: string): Promise<{}> {
|
||||
return this.client.get(`/custom-events/${id}`)
|
||||
.then(r => r.json()).then(j => j.data);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import WebhookService from './WebhookService';
|
|||
import SpotService from './spotService';
|
||||
import LoginService from "./loginService";
|
||||
import FilterService from "./FilterService";
|
||||
import CustomEventService from 'App/services/CustomEventService';
|
||||
|
||||
export const dashboardService = new DashboardService();
|
||||
export const metricService = new MetricService();
|
||||
|
|
@ -42,6 +43,7 @@ export const aiService = new AiService();
|
|||
export const spotService = new SpotService();
|
||||
export const loginService = new LoginService();
|
||||
export const filterService = new FilterService();
|
||||
export const customEventService = new CustomEventService();
|
||||
|
||||
export const services = [
|
||||
dashboardService,
|
||||
|
|
@ -65,4 +67,5 @@ export const services = [
|
|||
spotService,
|
||||
loginService,
|
||||
filterService,
|
||||
customEventService,
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue