import { useEffect, useMemo, useState } from 'react'; import { TablePlaceholder, VirtualizedTable } from 'component/common/Table'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; import { PageContent } from 'component/common/PageContent/PageContent'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { useMediaQuery } from '@mui/material'; import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table'; import { sortTypes } from 'utils/sortTypes'; import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import theme from 'themes/theme'; import { Search } from 'component/common/Search/Search'; import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; import { useSearch } from 'hooks/useSearch'; import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell'; import { useSignOnLog } from 'hooks/api/getters/useSignOnLog/useSignOnLog'; import { SignOnLogSuccessfulCell } from './SignOnLogSuccessfulCell/SignOnLogSuccessfulCell'; import { ISignOnEvent } from 'interfaces/signOnEvent'; import { SignOnLogActionsCell } from './SignOnLogActionsCell/SignOnLogActionsCell'; import { SignOnLogDeleteDialog } from './SignOnLogDeleteDialog/SignOnLogDeleteDialog'; import { useSignOnLogApi } from 'hooks/api/actions/useSignOnLogApi/useSignOnLogApi'; import { formatDateYMDHMS } from 'utils/formatDate'; import { useSearchParams } from 'react-router-dom'; import { createLocalStorage } from 'utils/createLocalStorage'; export type PageQueryType = Partial< Record<'sort' | 'order' | 'search', string> >; const defaultSort: SortingRule = { id: 'created_at' }; const { value: storedParams, setValue: setStoredParams } = createLocalStorage( 'SignOnLogTable:v1', defaultSort ); export const SignOnLogTable = () => { const { setToastData, setToastApiError } = useToast(); const { events, loading, refetch } = useSignOnLog(); const { removeEvent } = useSignOnLogApi(); const [searchParams, setSearchParams] = useSearchParams(); const [initialState] = useState(() => ({ sortBy: [ { id: searchParams.get('sort') || storedParams.id, desc: searchParams.has('order') ? searchParams.get('order') === 'desc' : storedParams.desc, }, ], hiddenColumns: ['failure_reason'], globalFilter: searchParams.get('search') || '', })); const [searchValue, setSearchValue] = useState(initialState.globalFilter); const [deleteOpen, setDeleteOpen] = useState(false); const [selectedEvent, setSelectedEvent] = useState(); const onDeleteConfirm = async (event: ISignOnEvent) => { try { await removeEvent(event.id); setToastData({ title: `Event has been deleted`, type: 'success', }); refetch(); setDeleteOpen(false); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); } }; const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); const columns = useMemo( () => [ { Header: 'Created', accessor: 'created_at', Cell: ({ value }: { value: Date }) => ( ), sortType: 'date', maxWidth: 150, }, { Header: 'Username', accessor: 'username', minWidth: 100, Cell: HighlightCell, searchable: true, }, { Header: 'Authentication', accessor: (event: ISignOnEvent) => event.auth_type === 'simple' ? 'Password' : event.auth_type.toUpperCase(), width: 150, maxWidth: 150, Cell: HighlightCell, searchable: true, filterName: 'auth', }, { Header: 'IP address', accessor: 'ip', Cell: HighlightCell, searchable: true, }, { Header: 'Success', accessor: 'successful', align: 'center', Cell: SignOnLogSuccessfulCell, filterName: 'success', filterParsing: (value: boolean) => value.toString(), }, { Header: 'Actions', id: 'Actions', align: 'center', Cell: ({ row: { original: event } }: any) => ( { setSelectedEvent(event); setDeleteOpen(true); }} /> ), width: 150, disableSortBy: true, }, // Always hidden -- for search { accessor: 'failure_reason', Header: 'Failure Reason', searchable: true, }, ], [] ); const { data, getSearchText, getSearchContext } = useSearch( columns, searchValue, events ); const { headerGroups, rows, prepareRow, state: { sortBy }, setHiddenColumns, } = useTable( { columns: columns as any, data, initialState, sortTypes, autoResetHiddenColumns: false, autoResetSortBy: false, disableSortRemove: true, disableMultiSort: true, defaultColumn: { Cell: TextCell, }, }, useSortBy, useFlexLayout ); useConditionallyHiddenColumns( [ { condition: isExtraSmallScreen, columns: ['role', 'seenAt'], }, { condition: isSmallScreen, columns: ['imageUrl', 'tokens', 'createdAt'], }, ], setHiddenColumns, columns ); useEffect(() => { const tableState: PageQueryType = {}; tableState.sort = sortBy[0].id; if (sortBy[0].desc) { tableState.order = 'desc'; } if (searchValue) { tableState.search = searchValue; } setSearchParams(tableState, { replace: true, }); setStoredParams({ id: sortBy[0].id, desc: sortBy[0].desc || false, }); }, [sortBy, searchValue, setSearchParams]); return ( } /> } > } /> } > 0} show={ No sign-on events found matching “ {searchValue} ” } elseShow={ No sign-on events available. } /> } /> ); };