From 45cfaece6fad71edcdee7dd1323b3f2264b9a368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Wed, 1 Mar 2023 12:38:47 +0000 Subject: [PATCH] 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) --- .../SignOnLogDeleteAllDialog.tsx | 28 +++++++ .../SignOnLogTable/SignOnLogTable.tsx | 83 +++++++++++++++---- .../useSignOnLogApi/useSignOnLogApi.ts | 30 +++++++ 3 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 frontend/src/component/signOnLog/SignOnLogTable/SignOnLogDeleteAllDialog/SignOnLogDeleteAllDialog.tsx diff --git a/frontend/src/component/signOnLog/SignOnLogTable/SignOnLogDeleteAllDialog/SignOnLogDeleteAllDialog.tsx b/frontend/src/component/signOnLog/SignOnLogTable/SignOnLogDeleteAllDialog/SignOnLogDeleteAllDialog.tsx new file mode 100644 index 00000000000..56c2f070c0f --- /dev/null +++ b/frontend/src/component/signOnLog/SignOnLogTable/SignOnLogDeleteAllDialog/SignOnLogDeleteAllDialog.tsx @@ -0,0 +1,28 @@ +import { Dialogue } from 'component/common/Dialogue/Dialogue'; + +interface IServiceAccountDeleteAllDialogProps { + open: boolean; + setOpen: React.Dispatch>; + onConfirm: () => void; +} + +export const SignOnLogDeleteAllDialog = ({ + open, + setOpen, + onConfirm, +}: IServiceAccountDeleteAllDialogProps) => ( + { + setOpen(false); + }} + > + You are about to clear the sign-on log. +
+ This will delete all the sign-on events. +
+); diff --git a/frontend/src/component/signOnLog/SignOnLogTable/SignOnLogTable.tsx b/frontend/src/component/signOnLog/SignOnLogTable/SignOnLogTable.tsx index 0f2c5b9c246..f3ae723a268 100644 --- a/frontend/src/component/signOnLog/SignOnLogTable/SignOnLogTable.tsx +++ b/frontend/src/component/signOnLog/SignOnLogTable/SignOnLogTable.tsx @@ -5,7 +5,7 @@ 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 { IconButton, Tooltip, 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'; @@ -25,6 +25,8 @@ import { useSignOnLogApi } from 'hooks/api/actions/useSignOnLogApi/useSignOnLogA import { formatDateYMDHMS } from 'utils/formatDate'; import { useSearchParams } from 'react-router-dom'; import { createLocalStorage } from 'utils/createLocalStorage'; +import { Delete, Download } from '@mui/icons-material'; +import { SignOnLogDeleteAllDialog } from './SignOnLogDeleteAllDialog/SignOnLogDeleteAllDialog'; export type PageQueryType = Partial< Record<'sort' | 'order' | 'search', string> @@ -48,7 +50,7 @@ export const SignOnLogTable = () => { const { setToastData, setToastApiError } = useToast(); const { events, loading, refetch } = useSignOnLog(); - const { removeEvent } = useSignOnLogApi(); + const { removeEvent, removeAllEvents, downloadCSV } = useSignOnLogApi(); const [searchParams, setSearchParams] = useSearchParams(); const [initialState] = useState(() => ({ @@ -65,8 +67,9 @@ export const SignOnLogTable = () => { })); const [searchValue, setSearchValue] = useState(initialState.globalFilter); - const [deleteOpen, setDeleteOpen] = useState(false); const [selectedEvent, setSelectedEvent] = useState(); + const [deleteOpen, setDeleteOpen] = useState(false); + const [deleteAllOpen, setDeleteAllOpen] = useState(false); const onDeleteConfirm = async (event: ISignOnEvent) => { 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 isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); @@ -223,17 +240,50 @@ export const SignOnLogTable = () => { - } - /> + <> + + } + /> + 0} + show={ + <> + } + /> + + + + + + + + setDeleteAllOpen(true) + } + > + + + + + } + /> + } > { setOpen={setDeleteOpen} onConfirm={onDeleteConfirm} /> + ); }; diff --git a/frontend/src/hooks/api/actions/useSignOnLogApi/useSignOnLogApi.ts b/frontend/src/hooks/api/actions/useSignOnLogApi/useSignOnLogApi.ts index a8570539ad9..bd2078017d6 100644 --- a/frontend/src/hooks/api/actions/useSignOnLogApi/useSignOnLogApi.ts +++ b/frontend/src/hooks/api/actions/useSignOnLogApi/useSignOnLogApi.ts @@ -5,6 +5,23 @@ export const useSignOnLogApi = () => { 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 requestId = 'removeEvent'; const req = createRequest( @@ -16,8 +33,21 @@ export const useSignOnLogApi = () => { 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 { + downloadCSV, removeEvent, + removeAllEvents, errors, loading, };