ui: cols modal, more fixes
This commit is contained in:
parent
dcebeb7b5b
commit
95f8002eb4
4 changed files with 164 additions and 56 deletions
|
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
import { Input, Checkbox, Button } from 'antd';
|
||||
|
||||
function ColumnsModal({
|
||||
columns,
|
||||
onSelect,
|
||||
hiddenCols,
|
||||
}: {
|
||||
columns: { title: string; key: string }[];
|
||||
onSelect: (col: string[]) => void;
|
||||
hiddenCols: string[];
|
||||
}) {
|
||||
const [query, setQuery] = React.useState('');
|
||||
const [selected, setSelected] = React.useState(
|
||||
columns.map((col) => col.key).filter((col) => !hiddenCols.includes(col))
|
||||
);
|
||||
|
||||
const onConfirm = () => {
|
||||
onSelect(selected);
|
||||
};
|
||||
const onToggle = (col: string, isSelected: boolean) => {
|
||||
const newList = isSelected
|
||||
? [...selected, col]
|
||||
: selected.filter((c) => c !== col);
|
||||
setSelected(newList);
|
||||
};
|
||||
|
||||
const searchRe = new RegExp(query, 'ig');
|
||||
const filteredList = columns.filter((col) => searchRe.test(col.title));
|
||||
return (
|
||||
<div className="flex flex-col gap-2 shadow border rounded-lg p-4 absolute top-28 right-0 z-50 bg-white">
|
||||
<div className="font-semibold text-lg">Show/Hide Columns</div>
|
||||
<div className="text-sm">
|
||||
Select columns to display. Rearrange them in the table view.
|
||||
</div>
|
||||
<Input.Search
|
||||
placeholder={'Search columns'}
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
{filteredList.map((col) => (
|
||||
<Checkbox
|
||||
onChange={(e) => onToggle(col.key, e.target.checked)}
|
||||
checked={selected.includes(col.key)}
|
||||
>
|
||||
{col.title}
|
||||
</Checkbox>
|
||||
))}
|
||||
<Button onClick={onConfirm} type={'primary'}>
|
||||
Show Selected
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ColumnsModal;
|
||||
|
|
@ -4,37 +4,49 @@ import { Table, Dropdown } from 'antd';
|
|||
import { MoreOutlined } from '@ant-design/icons';
|
||||
import { numberWithCommas } from 'App/utils';
|
||||
import { Pagination } from 'UI';
|
||||
import OutsideClickDetectingDiv from '../../shared/OutsideClickDetectingDiv';
|
||||
import ColumnsModal from './ColumnsModal';
|
||||
import Event from './data/Event';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import EventDetailsModal from "./EventDetailsModal";
|
||||
import EventDetailsModal from './EventDetailsModal';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
const limit = 100;
|
||||
|
||||
const fetcher = async (page: number): Promise<{ list: any[], total: number }> => {
|
||||
const fetcher = async (
|
||||
page: number
|
||||
): Promise<{ list: any[]; total: number }> => {
|
||||
const total = 3000;
|
||||
return new Promise((resolve) => {
|
||||
const testEv = new Event({
|
||||
name: 'test ev',
|
||||
name: 'test ev #' + page,
|
||||
time: Date.now(),
|
||||
defaultFields: { userId: '123', userCity: 'NY', userEnvironment: 'Mac OS' },
|
||||
defaultFields: {
|
||||
userId: '123',
|
||||
userCity: 'NY',
|
||||
userEnvironment: 'Mac OS',
|
||||
},
|
||||
customFields: {},
|
||||
isAutoCapture: false,
|
||||
sessionId: '123123'
|
||||
sessionId: '123123',
|
||||
});
|
||||
const testAutoEv = new Event({
|
||||
name: 'auto test ev',
|
||||
time: Date.now(),
|
||||
defaultFields: { userId: '123', userCity: 'NY', userEnvironment: 'Mac OS' },
|
||||
defaultFields: {
|
||||
userId: '123',
|
||||
userCity: 'NY',
|
||||
userEnvironment: 'Mac OS',
|
||||
},
|
||||
customFields: {},
|
||||
isAutoCapture: true,
|
||||
sessionId: '123123'
|
||||
sessionId: '123123',
|
||||
});
|
||||
const list = [testEv.toData(), testAutoEv.toData()];
|
||||
|
||||
resolve({ list, total });
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function ActivityPage() {
|
||||
const [page, setPage] = React.useState(1);
|
||||
|
|
@ -59,9 +71,9 @@ function ActivityPage() {
|
|||
{
|
||||
label: 'Show/Hide Columns',
|
||||
key: 'edit-columns',
|
||||
onClick: () => setEditCols(true),
|
||||
}
|
||||
]
|
||||
onClick: () => setTimeout(() => setEditCols(true), 1),
|
||||
},
|
||||
];
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
|
@ -92,7 +104,16 @@ function ActivityPage() {
|
|||
key: 'userId',
|
||||
showSorterTooltip: { target: 'full-header' },
|
||||
sorter: (a, b) => a.userId.localeCompare(b.userId),
|
||||
render: (text) => <div className={'link'}>{text}</div>,
|
||||
render: (text) => (
|
||||
<div
|
||||
className={'link'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'City',
|
||||
|
|
@ -110,11 +131,15 @@ function ActivityPage() {
|
|||
},
|
||||
{
|
||||
title: (
|
||||
<Dropdown menu={{ items: dropdownItems }} trigger={'click'}>
|
||||
<div className={'cursor-pointer'}>
|
||||
<MoreOutlined />
|
||||
</div>
|
||||
</Dropdown>
|
||||
<Dropdown
|
||||
menu={{ items: dropdownItems }}
|
||||
trigger={'click'}
|
||||
placement={'bottomRight'}
|
||||
>
|
||||
<div className={'cursor-pointer'}>
|
||||
<MoreOutlined />
|
||||
</div>
|
||||
</Dropdown>
|
||||
),
|
||||
dataIndex: '$__opts__$',
|
||||
key: '$__opts__$',
|
||||
|
|
@ -132,8 +157,22 @@ function ActivityPage() {
|
|||
};
|
||||
|
||||
const onItemClick = (ev: Event) => {
|
||||
showModal(<EventDetailsModal ev={ev} onClose={hideModal} />, { width: 420, right: true });
|
||||
}
|
||||
showModal(<EventDetailsModal ev={ev} onClose={hideModal} />, {
|
||||
width: 420,
|
||||
right: true,
|
||||
});
|
||||
};
|
||||
|
||||
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 gap-2'}
|
||||
|
|
@ -171,42 +210,54 @@ function ActivityPage() {
|
|||
onFilterMove={onFilterMove}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
'bg-white rounded-xl shadow border flex flex-col overflow-hidden'
|
||||
}
|
||||
>
|
||||
<div className={'px-4 py-2 font-semibold text-lg'}>
|
||||
All users activity
|
||||
</div>
|
||||
<Table
|
||||
loading={isPending}
|
||||
onRow={(record, index) => ({
|
||||
onClick: (event) => onItemClick(record)
|
||||
})}
|
||||
dataSource={list}
|
||||
pagination={false}
|
||||
columns={shownCols}
|
||||
/>
|
||||
<div className="flex items-center justify-between px-4 py-3 shadow-sm w-full bg-white rounded-lg mt-2">
|
||||
<div>
|
||||
{'Showing '}
|
||||
<span className="font-medium">{(page - 1) * limit + 1}</span>
|
||||
{' to '}
|
||||
<span className="font-medium">
|
||||
{(page - 1) * limit + list.length}
|
||||
</span>
|
||||
{' of '}
|
||||
<span className="font-medium">{numberWithCommas(total)}</span>
|
||||
{' events.'}
|
||||
<div className={'relative'}>
|
||||
{editCols ? (
|
||||
<OutsideClickDetectingDiv onClickOutside={() => setEditCols(false)}>
|
||||
<ColumnsModal
|
||||
columns={shownCols.filter((col) => col.key !== '$__opts__$')}
|
||||
onSelect={onUpdateVisibleCols}
|
||||
hiddenCols={hiddenCols}
|
||||
/>
|
||||
</OutsideClickDetectingDiv>
|
||||
) : null}
|
||||
|
||||
<div
|
||||
className={
|
||||
'bg-white rounded-xl shadow border flex flex-col overflow-hidden'
|
||||
}
|
||||
>
|
||||
<div className={'px-4 py-2 font-semibold text-lg'}>
|
||||
All users activity
|
||||
</div>
|
||||
<Pagination
|
||||
page={page}
|
||||
total={total}
|
||||
onPageChange={onPageChange}
|
||||
limit={limit}
|
||||
debounceRequest={500}
|
||||
<Table
|
||||
loading={isPending}
|
||||
onRow={(record) => ({
|
||||
onClick: () => onItemClick(record),
|
||||
})}
|
||||
dataSource={list}
|
||||
pagination={false}
|
||||
columns={shownCols}
|
||||
/>
|
||||
<div className="flex items-center justify-between px-4 py-3 shadow-sm w-full bg-white rounded-lg mt-2">
|
||||
<div>
|
||||
{'Showing '}
|
||||
<span className="font-medium">{(page - 1) * limit + 1}</span>
|
||||
{' to '}
|
||||
<span className="font-medium">
|
||||
{(page - 1) * limit + list.length}
|
||||
</span>
|
||||
{' of '}
|
||||
<span className="font-medium">{numberWithCommas(total)}</span>
|
||||
{' events.'}
|
||||
</div>
|
||||
<Pagination
|
||||
page={page}
|
||||
total={total}
|
||||
onPageChange={onPageChange}
|
||||
limit={limit}
|
||||
debounceRequest={500}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ export const setQueryParams = (location: Location, params: Record<string, any>):
|
|||
};
|
||||
|
||||
export const login = (): string => '/login';
|
||||
export const spotLogin = (): string => '/spot-login';
|
||||
export const signup = (): string => '/signup';
|
||||
|
||||
export const forgotPassword = (): string => '/reset-password';
|
||||
|
|
@ -196,6 +195,8 @@ const REQUIRED_SITE_ID_ROUTES = [
|
|||
usabilityTestingView(''),
|
||||
|
||||
highlights(),
|
||||
|
||||
dataManagement.activity(),
|
||||
];
|
||||
const routeNeedsSiteId = (path: string): boolean => REQUIRED_SITE_ID_ROUTES.some(r => path.startsWith(r));
|
||||
const siteIdToUrl = (siteId = ':siteId'): string => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitAny": false,
|
||||
"alwaysStrict": true,
|
||||
"strictNullChecks": true,
|
||||
"skipLibCheck": true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue