diff --git a/frontend/app/components/DataManagement/Activity/ColumnsModal.tsx b/frontend/app/components/DataManagement/Activity/ColumnsModal.tsx new file mode 100644 index 000000000..7048aed55 --- /dev/null +++ b/frontend/app/components/DataManagement/Activity/ColumnsModal.tsx @@ -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 ( +
+
Show/Hide Columns
+
+ Select columns to display. Rearrange them in the table view. +
+ setQuery(e.target.value)} + /> + {filteredList.map((col) => ( + onToggle(col.key, e.target.checked)} + checked={selected.includes(col.key)} + > + {col.title} + + ))} + +
+ ); +} + +export default ColumnsModal; diff --git a/frontend/app/components/DataManagement/Activity/Page.tsx b/frontend/app/components/DataManagement/Activity/Page.tsx index 5260fd43b..b96516edf 100644 --- a/frontend/app/components/DataManagement/Activity/Page.tsx +++ b/frontend/app/components/DataManagement/Activity/Page.tsx @@ -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) =>
{text}
, + render: (text) => ( +
{ + e.stopPropagation(); + }} + > + {text} +
+ ), }, { title: 'City', @@ -110,11 +131,15 @@ function ActivityPage() { }, { title: ( - -
- -
-
+ +
+ +
+
), dataIndex: '$__opts__$', key: '$__opts__$', @@ -132,8 +157,22 @@ function ActivityPage() { }; const onItemClick = (ev: Event) => { - showModal(, { width: 420, right: true }); - } + showModal(, { + 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 (
-
-
- All users activity -
- ({ - onClick: (event) => onItemClick(record) - })} - dataSource={list} - pagination={false} - columns={shownCols} - /> -
-
- {'Showing '} - {(page - 1) * limit + 1} - {' to '} - - {(page - 1) * limit + list.length} - - {' of '} - {numberWithCommas(total)} - {' events.'} +
+ {editCols ? ( + setEditCols(false)}> + col.key !== '$__opts__$')} + onSelect={onUpdateVisibleCols} + hiddenCols={hiddenCols} + /> + + ) : null} + +
+
+ All users activity
- ({ + onClick: () => onItemClick(record), + })} + dataSource={list} + pagination={false} + columns={shownCols} /> +
+
+ {'Showing '} + {(page - 1) * limit + 1} + {' to '} + + {(page - 1) * limit + list.length} + + {' of '} + {numberWithCommas(total)} + {' events.'} +
+ +
diff --git a/frontend/app/routes.ts b/frontend/app/routes.ts index a3b399e35..25eb70c14 100644 --- a/frontend/app/routes.ts +++ b/frontend/app/routes.ts @@ -53,7 +53,6 @@ export const setQueryParams = (location: Location, params: Record): }; 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 => { diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index aab188729..a4ffea2c7 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "experimentalDecorators": true, "allowSyntheticDefaultImports": true, - "noImplicitAny": true, + "noImplicitAny": false, "alwaysStrict": true, "strictNullChecks": true, "skipLibCheck": true,