ui: cols modal, more fixes

This commit is contained in:
nick-delirium 2025-01-29 10:40:20 +01:00
parent dcebeb7b5b
commit 95f8002eb4
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
4 changed files with 164 additions and 56 deletions

View file

@ -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;

View file

@ -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>

View file

@ -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 => {

View file

@ -2,7 +2,7 @@
"compilerOptions": {
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"noImplicitAny": true,
"noImplicitAny": false,
"alwaysStrict": true,
"strictNullChecks": true,
"skipLibCheck": true,