1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-21 13:47:39 +02:00

feat: add download and delete all buttons to sign-on log UI (#3231)

Adds `Download sign-on log` and `Clear sign-on log` buttons to the UI,
making it easy for admins to manage their sign-on log.


![image](https://user-images.githubusercontent.com/14320932/222138757-284c3f79-142f-4e25-847d-666938d4c12c.png)

![image](https://user-images.githubusercontent.com/14320932/222138863-bccf6343-41dd-4068-95b0-fac3ce37313f.png)
This commit is contained in:
Nuno Góis 2023-03-01 12:38:47 +00:00 committed by GitHub
parent 5e1886fe93
commit 45cfaece6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 127 additions and 14 deletions

View File

@ -0,0 +1,28 @@
import { Dialogue } from 'component/common/Dialogue/Dialogue';
interface IServiceAccountDeleteAllDialogProps {
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
onConfirm: () => void;
}
export const SignOnLogDeleteAllDialog = ({
open,
setOpen,
onConfirm,
}: IServiceAccountDeleteAllDialogProps) => (
<Dialogue
title="Clear sign-on log?"
open={open}
primaryButtonText="Clear sign-on log"
secondaryButtonText="Cancel"
onClick={onConfirm}
onClose={() => {
setOpen(false);
}}
>
You are about to clear the sign-on log.
<br />
This will delete all the sign-on events.
</Dialogue>
);

View File

@ -5,7 +5,7 @@ import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { useMediaQuery } from '@mui/material'; import { IconButton, Tooltip, useMediaQuery } from '@mui/material';
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table'; import { SortingRule, useFlexLayout, useSortBy, useTable } from 'react-table';
import { sortTypes } from 'utils/sortTypes'; import { sortTypes } from 'utils/sortTypes';
@ -25,6 +25,8 @@ import { useSignOnLogApi } from 'hooks/api/actions/useSignOnLogApi/useSignOnLogA
import { formatDateYMDHMS } from 'utils/formatDate'; import { formatDateYMDHMS } from 'utils/formatDate';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { createLocalStorage } from 'utils/createLocalStorage'; import { createLocalStorage } from 'utils/createLocalStorage';
import { Delete, Download } from '@mui/icons-material';
import { SignOnLogDeleteAllDialog } from './SignOnLogDeleteAllDialog/SignOnLogDeleteAllDialog';
export type PageQueryType = Partial< export type PageQueryType = Partial<
Record<'sort' | 'order' | 'search', string> Record<'sort' | 'order' | 'search', string>
@ -48,7 +50,7 @@ export const SignOnLogTable = () => {
const { setToastData, setToastApiError } = useToast(); const { setToastData, setToastApiError } = useToast();
const { events, loading, refetch } = useSignOnLog(); const { events, loading, refetch } = useSignOnLog();
const { removeEvent } = useSignOnLogApi(); const { removeEvent, removeAllEvents, downloadCSV } = useSignOnLogApi();
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const [initialState] = useState(() => ({ const [initialState] = useState(() => ({
@ -65,8 +67,9 @@ export const SignOnLogTable = () => {
})); }));
const [searchValue, setSearchValue] = useState(initialState.globalFilter); const [searchValue, setSearchValue] = useState(initialState.globalFilter);
const [deleteOpen, setDeleteOpen] = useState(false);
const [selectedEvent, setSelectedEvent] = useState<ISignOnEvent>(); const [selectedEvent, setSelectedEvent] = useState<ISignOnEvent>();
const [deleteOpen, setDeleteOpen] = useState(false);
const [deleteAllOpen, setDeleteAllOpen] = useState(false);
const onDeleteConfirm = async (event: ISignOnEvent) => { const onDeleteConfirm = async (event: ISignOnEvent) => {
try { try {
@ -82,6 +85,20 @@ export const SignOnLogTable = () => {
} }
}; };
const onDeleteAllConfirm = async () => {
try {
await removeAllEvents();
setToastData({
title: `Log has been cleared`,
type: 'success',
});
refetch();
setDeleteAllOpen(false);
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
@ -223,17 +240,50 @@ export const SignOnLogTable = () => {
<PageHeader <PageHeader
title={`Sign-on log (${rows.length})`} title={`Sign-on log (${rows.length})`}
actions={ actions={
<ConditionallyRender <>
condition={!isSmallScreen} <ConditionallyRender
show={ condition={!isSmallScreen}
<Search show={
initialValue={searchValue} <Search
onChange={setSearchValue} initialValue={searchValue}
hasFilters onChange={setSearchValue}
getSearchContext={getSearchContext} hasFilters
/> getSearchContext={getSearchContext}
} />
/> }
/>
<ConditionallyRender
condition={rows.length > 0}
show={
<>
<ConditionallyRender
condition={!isSmallScreen}
show={<PageHeader.Divider />}
/>
<Tooltip
title="Download sign-on log"
arrow
>
<IconButton onClick={downloadCSV}>
<Download />
</IconButton>
</Tooltip>
<Tooltip
title="Clear sign-on log"
arrow
>
<IconButton
onClick={() =>
setDeleteAllOpen(true)
}
>
<Delete />
</IconButton>
</Tooltip>
</>
}
/>
</>
} }
> >
<ConditionallyRender <ConditionallyRender
@ -283,6 +333,11 @@ export const SignOnLogTable = () => {
setOpen={setDeleteOpen} setOpen={setDeleteOpen}
onConfirm={onDeleteConfirm} onConfirm={onDeleteConfirm}
/> />
<SignOnLogDeleteAllDialog
open={deleteAllOpen}
setOpen={setDeleteAllOpen}
onConfirm={onDeleteAllConfirm}
/>
</PageContent> </PageContent>
); );
}; };

View File

@ -5,6 +5,23 @@ export const useSignOnLogApi = () => {
propagateErrors: true, propagateErrors: true,
}); });
const downloadCSV = async () => {
const requestId = 'downloadCSV';
const req = createRequest(
'api/admin/signons',
{
method: 'GET',
responseType: 'blob',
headers: { Accept: 'text/csv' },
},
requestId
);
const file = await (await makeRequest(req.caller, req.id)).blob();
const url = window.URL.createObjectURL(file);
window.location.assign(url);
};
const removeEvent = async (eventId: number) => { const removeEvent = async (eventId: number) => {
const requestId = 'removeEvent'; const requestId = 'removeEvent';
const req = createRequest( const req = createRequest(
@ -16,8 +33,21 @@ export const useSignOnLogApi = () => {
await makeRequest(req.caller, req.id); await makeRequest(req.caller, req.id);
}; };
const removeAllEvents = async () => {
const requestId = 'removeAllEvents';
const req = createRequest(
'api/admin/signons',
{ method: 'DELETE' },
requestId
);
await makeRequest(req.caller, req.id);
};
return { return {
downloadCSV,
removeEvent, removeEvent,
removeAllEvents,
errors, errors,
loading, loading,
}; };