ui: reorganising side menu and prop/ev lists
This commit is contained in:
parent
3a07a20195
commit
fce6a562fd
7 changed files with 277 additions and 39 deletions
|
|
@ -41,6 +41,7 @@ const components: any = {
|
|||
UserPage: lazy(() => import('Components/DataManagement/UsersEvents/UserPage')),
|
||||
UsersEventsPage: lazy(() => import('Components/DataManagement/UsersEvents/ListPage')),
|
||||
EventPage: lazy(() => import('Components/DataManagement/UsersEvents/EventPage')),
|
||||
PropertiesList: lazy(() => import('Components/DataManagement/Properties/ListPage')),
|
||||
};
|
||||
|
||||
const enhancedComponents: any = {
|
||||
|
|
@ -68,6 +69,7 @@ const enhancedComponents: any = {
|
|||
UserPage: components.UserPage,
|
||||
UsersEventsPage: components.UsersEventsPage,
|
||||
EventPage: components.EventPage,
|
||||
PropertiesList: components.PropertiesList,
|
||||
};
|
||||
|
||||
const withSiteId = routes.withSiteId;
|
||||
|
|
@ -117,13 +119,6 @@ const SCOPE_SETUP = routes.scopeSetup();
|
|||
|
||||
const HIGHLIGHTS_PATH = routes.highlights();
|
||||
|
||||
const DATA_MANAGEMENT = {
|
||||
ACTIVITY: routes.dataManagement.activity(),
|
||||
USER_PAGE: routes.dataManagement.userPage(),
|
||||
USERS_EVENTS: routes.dataManagement.usersEvents(),
|
||||
EVENT_PAGE: routes.dataManagement.eventPage(),
|
||||
}
|
||||
|
||||
function PrivateRoutes() {
|
||||
const { projectsStore, userStore, integrationsStore } = useStore();
|
||||
const onboarding = userStore.onboarding;
|
||||
|
|
@ -308,26 +303,40 @@ function PrivateRoutes() {
|
|||
<Route
|
||||
exact
|
||||
strict
|
||||
path={withSiteId(DATA_MANAGEMENT.ACTIVITY, siteIdList)}
|
||||
path={withSiteId(routes.dataManagement.activity(), siteIdList)}
|
||||
component={enhancedComponents.Activity}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path={withSiteId(DATA_MANAGEMENT.USER_PAGE, siteIdList)}
|
||||
path={withSiteId(routes.dataManagement.userPage(), siteIdList)}
|
||||
component={enhancedComponents.UserPage}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path={withSiteId(DATA_MANAGEMENT.USERS_EVENTS, siteIdList)}
|
||||
component={enhancedComponents.UsersEventsPage}
|
||||
path={withSiteId(routes.dataManagement.users(), siteIdList)}
|
||||
>
|
||||
<enhancedComponents.UsersEventsPage view={'users'} />
|
||||
</Route>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path={withSiteId(routes.dataManagement.events(), siteIdList)}
|
||||
>
|
||||
<enhancedComponents.UsersEventsPage view={'events'} />
|
||||
</Route>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path={withSiteId(routes.dataManagement.eventPage(), siteIdList)}
|
||||
component={enhancedComponents.EventPage}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path={withSiteId(DATA_MANAGEMENT.EVENT_PAGE, siteIdList)}
|
||||
component={enhancedComponents.EventPage}
|
||||
path={withSiteId(routes.dataManagement.properties(), siteIdList)}
|
||||
component={enhancedComponents.PropertiesList}
|
||||
/>
|
||||
{Object.entries(routes.redirects).map(([fr, to]) => (
|
||||
<Redirect key={fr} exact strict from={fr} to={to} />
|
||||
|
|
|
|||
232
frontend/app/components/DataManagement/Properties/ListPage.tsx
Normal file
232
frontend/app/components/DataManagement/Properties/ListPage.tsx
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
import React from 'react';
|
||||
import { Input, Table, Button, Dropdown } from 'antd';
|
||||
import { MoreOutlined } from '@ant-design/icons';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { withSiteId, dataManagement } from 'App/routes';
|
||||
import { Filter, Album } from 'lucide-react';
|
||||
import { list } from '../Activity/Page';
|
||||
import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
|
||||
import ColumnsModal from 'Components/DataManagement/Activity/ColumnsModal';
|
||||
import FullPagination from 'Shared/FullPagination';
|
||||
import Tabs from 'Shared/Tabs'
|
||||
|
||||
function ListPage() {
|
||||
const [view, setView] = React.useState('users');
|
||||
const views = [
|
||||
{
|
||||
key: 'users',
|
||||
label: <div className={'text-lg font-medium'}>Users</div>,
|
||||
},
|
||||
{
|
||||
key: 'events',
|
||||
label: <div className={'text-lg font-medium'}>Events</div>,
|
||||
},
|
||||
];
|
||||
const { projectsStore } = useStore();
|
||||
const siteId = projectsStore.activeSiteId;
|
||||
const history = useHistory();
|
||||
const toUser = (id: string) =>
|
||||
history.push(withSiteId(dataManagement.userPage(id), siteId));
|
||||
const toEvent = (id: string) =>
|
||||
history.push(withSiteId(dataManagement.eventPage(id), siteId));
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col gap-4 rounded-lg border bg-white mx-auto"
|
||||
style={{ maxWidth: 1360 }}
|
||||
>
|
||||
<div className={'flex items-center justify-between border-b px-4 pt-2 '}>
|
||||
<Tabs
|
||||
activeKey={view}
|
||||
onChange={(key) => setView(key)}
|
||||
items={views}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button type={'text'} icon={<Album size={14} />}>
|
||||
Docs
|
||||
</Button>
|
||||
<Input.Search size={'small'} placeholder={'Name, email, ID'} />
|
||||
</div>
|
||||
</div>
|
||||
{view === 'users' ? <UserPropsList toUser={toUser} /> : <EventPropsList toEvent={toEvent} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function EventPropsList({ toEvent }: { toEvent: (id: string) => void }) {
|
||||
const columns = [
|
||||
{
|
||||
title: 'Property',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
},
|
||||
{
|
||||
title: 'Display Name',
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: 'Description',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.description.localeCompare(b.description),
|
||||
},
|
||||
{
|
||||
title: '30 Day Volume',
|
||||
dataIndex: 'monthVolume',
|
||||
key: 'monthVolume',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.monthVolume.localeCompare(b.monthVolume),
|
||||
},
|
||||
];
|
||||
const page = 1;
|
||||
const total = 100;
|
||||
const onPageChange = (page: number) => {};
|
||||
const limit = 10;
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={list}
|
||||
pagination={false}
|
||||
onRow={(record) => ({
|
||||
onClick: () => toEvent(record.eventId),
|
||||
})}
|
||||
/>
|
||||
<FullPagination
|
||||
page={page}
|
||||
limit={limit}
|
||||
total={total}
|
||||
listLen={list.length}
|
||||
onPageChange={onPageChange}
|
||||
entity={'events'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function UserPropsList({ toUser }: { toUser: (id: string) => void }) {
|
||||
const [editCols, setEditCols] = React.useState(false);
|
||||
const [hiddenCols, setHiddenCols] = React.useState([]);
|
||||
|
||||
const dropdownItems = [
|
||||
{
|
||||
label: 'Show/Hide Columns',
|
||||
key: 'edit-columns',
|
||||
onClick: () => setTimeout(() => setEditCols(true), 1),
|
||||
},
|
||||
];
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
},
|
||||
{
|
||||
title: 'Display Name',
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: 'Description',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.description.localeCompare(b.description),
|
||||
},
|
||||
{
|
||||
title: '# Users',
|
||||
dataIndex: 'users',
|
||||
key: 'users',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.users.localeCompare(b.users),
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<Dropdown
|
||||
menu={{ items: dropdownItems }}
|
||||
trigger={'click'}
|
||||
placement={'bottomRight'}
|
||||
>
|
||||
<div className={'cursor-pointer'}>
|
||||
<MoreOutlined />
|
||||
</div>
|
||||
</Dropdown>
|
||||
),
|
||||
dataIndex: '$__opts__$',
|
||||
key: '$__opts__$',
|
||||
width: 50,
|
||||
},
|
||||
];
|
||||
|
||||
const page = 1;
|
||||
const total = 10;
|
||||
const onPageChange = (page: number) => {};
|
||||
const limit = 10;
|
||||
const list = [];
|
||||
|
||||
const onAddFilter = () => console.log('add filter');
|
||||
const excludeFilterKeys = [];
|
||||
const excludeCategory = [];
|
||||
|
||||
const shownCols = columns.map((col) => ({
|
||||
...col,
|
||||
hidden: hiddenCols.includes(col.key),
|
||||
}));
|
||||
const onUpdateVisibleCols = (cols: string[]) => {
|
||||
setHiddenCols((_) => {
|
||||
return columns
|
||||
.map((col) =>
|
||||
cols.includes(col.key) || col.key === '$__opts__$' ? null : col.key
|
||||
)
|
||||
.filter(Boolean);
|
||||
});
|
||||
setEditCols(false);
|
||||
};
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className={'relative'}>
|
||||
{editCols ? (
|
||||
<OutsideClickDetectingDiv onClickOutside={() => setEditCols(false)}>
|
||||
<ColumnsModal
|
||||
columns={shownCols.filter((col) => col.key !== '$__opts__$')}
|
||||
onSelect={onUpdateVisibleCols}
|
||||
hiddenCols={hiddenCols}
|
||||
topOffset={'top-24 -mt-4'}
|
||||
/>
|
||||
</OutsideClickDetectingDiv>
|
||||
) : null}
|
||||
<Table
|
||||
onRow={(record) => ({
|
||||
onClick: () => toUser(record.userId),
|
||||
})}
|
||||
pagination={false}
|
||||
rowClassName={'cursor-pointer'}
|
||||
dataSource={[]}
|
||||
columns={shownCols}
|
||||
/>
|
||||
</div>
|
||||
<FullPagination
|
||||
page={page}
|
||||
limit={limit}
|
||||
total={total}
|
||||
listLen={list.length}
|
||||
onPageChange={onPageChange}
|
||||
entity={'users'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(ListPage);
|
||||
|
|
@ -12,9 +12,8 @@ import { list } from '../Activity/Page';
|
|||
import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
|
||||
import ColumnsModal from 'Components/DataManagement/Activity/ColumnsModal';
|
||||
import FullPagination from 'Shared/FullPagination';
|
||||
import Tabs from 'Shared/Tabs'
|
||||
|
||||
function ListPage() {
|
||||
function ListPage({ view }: { view: 'users' | 'events' }) {
|
||||
const { projectsStore } = useStore();
|
||||
const siteId = projectsStore.activeSiteId;
|
||||
const history = useHistory();
|
||||
|
|
@ -22,29 +21,14 @@ function ListPage() {
|
|||
history.push(withSiteId(dataManagement.userPage(id), siteId));
|
||||
const toEvent = (id: string) =>
|
||||
history.push(withSiteId(dataManagement.eventPage(id), siteId));
|
||||
const [view, setView] = React.useState('users');
|
||||
|
||||
const views = [
|
||||
{
|
||||
key: 'users',
|
||||
label: <div className={'text-lg font-medium'}>Users</div>,
|
||||
},
|
||||
{
|
||||
key: 'events',
|
||||
label: <div className={'text-lg font-medium'}>Events</div>,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col gap-4 rounded-lg border bg-white mx-auto"
|
||||
style={{ maxWidth: 1360 }}
|
||||
>
|
||||
<div className={'flex items-center justify-between border-b px-4 pt-2 '}>
|
||||
<Tabs
|
||||
activeKey={view}
|
||||
onChange={(key) => setView(key)}
|
||||
items={views}
|
||||
/>
|
||||
<div className={'font-semibold text-lg capitalize'}>{view}</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button type={'text'} icon={<Album size={14} />}>
|
||||
Docs
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ function UserInfo() {
|
|||
return (
|
||||
<>
|
||||
<Breadcrumb items={[
|
||||
{ label: 'Users', to: dataManagement.usersEvents(), withSiteId: true },
|
||||
{ label: 'Users', to: dataManagement.users(), withSiteId: true },
|
||||
{ label: testUser.name },
|
||||
]} />
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ function SideMenu(props: Props) {
|
|||
}
|
||||
if (item.hidden) return item;
|
||||
|
||||
const dataAnalytics = [MENU.ACTIVITY, MENU.USERS, MENU.EVENTS]
|
||||
const isHidden = [
|
||||
item.key === MENU.RECOMMENDATIONS &&
|
||||
modules.includes(MODULES.RECOMMENDATIONS),
|
||||
|
|
@ -109,7 +110,7 @@ function SideMenu(props: Props) {
|
|||
item.key === MENU.USABILITY_TESTS && modules.includes(MODULES.USABILITY_TESTS),
|
||||
item.isAdmin && !isAdmin,
|
||||
item.isEnterprise && !isEnterprise,
|
||||
(item.key === MENU.ACTIVITY || item.key === MENU.USERS_EVENTS) && isMobile
|
||||
dataAnalytics.includes(item.key) && isMobile
|
||||
].some((cond) => cond);
|
||||
|
||||
return { ...item, hidden: isHidden };
|
||||
|
|
@ -154,7 +155,9 @@ function SideMenu(props: Props) {
|
|||
[PREFERENCES_MENU.MODULES]: () => client(CLIENT_TABS.MODULES),
|
||||
[MENU.HIGHLIGHTS]: () => withSiteId(routes.highlights(''), siteId),
|
||||
[MENU.ACTIVITY]: () => withSiteId(routes.dataManagement.activity(), siteId),
|
||||
[MENU.USERS_EVENTS]: () => withSiteId(routes.dataManagement.usersEvents(), siteId),
|
||||
[MENU.USERS]: () => withSiteId(routes.dataManagement.users(), siteId),
|
||||
[MENU.EVENTS]: () => withSiteId(routes.dataManagement.events(), siteId),
|
||||
[MENU.PROPS]: () => withSiteId(routes.dataManagement.properties(), siteId),
|
||||
};
|
||||
|
||||
const handleClick = (item: any) => {
|
||||
|
|
|
|||
|
|
@ -54,8 +54,10 @@ export const enum MENU {
|
|||
EXIT = 'exit',
|
||||
SPOTS = 'spots',
|
||||
ACTIVITY = 'activity',
|
||||
USER_PAGE = 'user-page',
|
||||
USERS_EVENTS = 'users-events',
|
||||
USER = 'user-page',
|
||||
USERS = 'data-users',
|
||||
EVENTS = 'data-events',
|
||||
PROPS = 'data-properties',
|
||||
}
|
||||
|
||||
export const categories: Category[] = [
|
||||
|
|
@ -106,7 +108,12 @@ export const categories: Category[] = [
|
|||
{
|
||||
title: 'Data Management',
|
||||
key: 'data-management',
|
||||
items: [{ label: 'Activity', key: MENU.ACTIVITY, icon: 'square-mouse-pointer' }, { label: 'Users and Events', key: MENU.USERS_EVENTS, icon: 'square-mouse-pointer' }],
|
||||
items: [
|
||||
{ label: 'Activity', key: MENU.ACTIVITY, icon: 'square-mouse-pointer' },
|
||||
{ label: 'Users', key: MENU.USERS, icon: 'square-mouse-pointer' },
|
||||
{ label: 'Events', key: MENU.EVENTS, icon: 'square-mouse-pointer' },
|
||||
{ label: 'Properties', key: MENU.PROPS, icon: 'square-mouse-pointer' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Product Optimization',
|
||||
|
|
|
|||
|
|
@ -150,8 +150,10 @@ export const highlights = (): string => '/highlights';
|
|||
export const dataManagement = {
|
||||
activity: () => '/data-management/activity',
|
||||
userPage: (id = ':userId', hash?: string | number) => hashed(`/data-management/user/${id}`, hash),
|
||||
usersEvents: () => '/data-management/users-and-events',
|
||||
users: () => '/data-management/users',
|
||||
events: () => '/data-management/events',
|
||||
eventPage: (id = ':eventId', hash?: string | number) => hashed(`/data-management/event/${id}`, hash),
|
||||
properties: () => '/data-management/properties',
|
||||
}
|
||||
|
||||
const REQUIRED_SITE_ID_ROUTES = [
|
||||
|
|
@ -201,7 +203,8 @@ const REQUIRED_SITE_ID_ROUTES = [
|
|||
|
||||
dataManagement.activity(),
|
||||
dataManagement.userPage(''),
|
||||
dataManagement.usersEvents(),
|
||||
dataManagement.users(),
|
||||
dataManagement.events(),
|
||||
dataManagement.eventPage(''),
|
||||
];
|
||||
const routeNeedsSiteId = (path: string): boolean => REQUIRED_SITE_ID_ROUTES.some(r => path.startsWith(r));
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue