1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-06 01:15:28 +02:00
unleash.unleash/frontend/src/component/signOnLog/SignOnLogTable/SignOnLogTable.tsx

284 lines
9.7 KiB
TypeScript

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<string> = { 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<ISignOnEvent>();
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 }) => (
<TimeAgoCell value={value} dateFormat={formatDateYMDHMS} />
),
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) => (
<SignOnLogActionsCell
onDelete={() => {
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 (
<PageContent
isLoading={loading}
header={
<PageHeader
title={`Sign-on log (${rows.length})`}
actions={
<ConditionallyRender
condition={!isSmallScreen}
show={
<Search
initialValue={searchValue}
onChange={setSearchValue}
hasFilters
getSearchContext={getSearchContext}
/>
}
/>
}
>
<ConditionallyRender
condition={isSmallScreen}
show={
<Search
initialValue={searchValue}
onChange={setSearchValue}
hasFilters
getSearchContext={getSearchContext}
/>
}
/>
</PageHeader>
}
>
<SearchHighlightProvider value={getSearchText(searchValue)}>
<VirtualizedTable
rows={rows}
headerGroups={headerGroups}
prepareRow={prepareRow}
/>
</SearchHighlightProvider>
<ConditionallyRender
condition={rows.length === 0}
show={
<ConditionallyRender
condition={searchValue?.length > 0}
show={
<TablePlaceholder>
No sign-on events found matching &ldquo;
{searchValue}
&rdquo;
</TablePlaceholder>
}
elseShow={
<TablePlaceholder>
No sign-on events available.
</TablePlaceholder>
}
/>
}
/>
<SignOnLogDeleteDialog
event={selectedEvent}
open={deleteOpen}
setOpen={setDeleteOpen}
onConfirm={onDeleteConfirm}
/>
</PageContent>
);
};