import type { FC } from 'react'; import React, { useState, useCallback, useRef } from 'react'; import { format } from 'date-fns'; import { useTranslation } from 'react-i18next'; import type { IClearable } from '~/client/interfaces/clearable'; import { toastError } from '~/client/util/toastr'; import type { SupportedActionType } from '~/interfaces/activity'; import { useSWRxActivity } from '~/stores/activity'; import { useAuditLogEnabled, useAuditLogAvailableActions } from '~/stores/context'; import { LoadingSpinner } from '../LoadingSpinner'; import PaginationWrapper from '../PaginationWrapper'; import { ActivityTable } from './AuditLog/ActivityTable'; import { AuditLogDisableMode } from './AuditLog/AuditLogDisableMode'; import { AuditLogSettings } from './AuditLog/AuditLogSettings'; import { DateRangePicker } from './AuditLog/DateRangePicker'; import { SearchUsernameTypeahead } from './AuditLog/SearchUsernameTypeahead'; import { SelectActionDropdown } from './AuditLog/SelectActionDropdown'; const formatDate = (date: Date | null) => { if (date == null) { return ''; } return format(new Date(date), 'yyyy-MM-dd'); }; const PAGING_LIMIT = 10; export const AuditLogManagement: FC = () => { const { t } = useTranslation('admin'); const typeaheadRef = useRef(null); const { data: auditLogAvailableActionsData } = useAuditLogAvailableActions(); /* * State */ const [isSettingPage, setIsSettingPage] = useState(false); const [activePageNumber, setActivePageNumber] = useState(1); const [jumpPageNumber, setJumpPageNumber] = useState(1); const offset = (activePageNumber - 1) * PAGING_LIMIT; const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); const [selectedUsernames, setSelectedUsernames] = useState([]); const [actionMap, setActionMap] = useState( new Map(auditLogAvailableActionsData != null ? auditLogAvailableActionsData.map(action => [action, true]) : []), ); /* * Fetch */ const selectedDate = { startDate: formatDate(startDate), endDate: formatDate(endDate) }; const selectedActionList = Array.from(actionMap.entries()).filter(v => v[1]).map(v => v[0]); const searchFilter = { actions: selectedActionList, dates: selectedDate, usernames: selectedUsernames }; const { data: activityData, mutate: mutateActivity, error } = useSWRxActivity(PAGING_LIMIT, offset, searchFilter); const activityList = activityData?.docs != null ? activityData.docs : []; const totalActivityNum = activityData?.totalDocs != null ? activityData.totalDocs : 0; const totalPagingPages = activityData?.totalPages != null ? activityData.totalPages : 0; const isLoading = activityData === undefined && error == null; if (error != null) { toastError('Failed to get Audit Log'); } const { data: auditLogEnabled } = useAuditLogEnabled(); /* * Functions */ const setActivePageHandler = useCallback((selectedPageNum: number) => { setActivePageNumber(selectedPageNum); }, []); const datePickerChangedHandler = useCallback((dateList: Date[] | null[]) => { setActivePageNumber(1); setStartDate(dateList[0]); setEndDate(dateList[1]); }, []); const actionCheckboxChangedHandler = useCallback((action: SupportedActionType) => { setActivePageNumber(1); actionMap.set(action, !actionMap.get(action)); setActionMap(new Map(actionMap.entries())); }, [actionMap, setActionMap]); const multipleActionCheckboxChangedHandler = useCallback((actions: SupportedActionType[], isChecked) => { setActivePageNumber(1); actions.forEach(action => actionMap.set(action, isChecked)); setActionMap(new Map(actionMap.entries())); }, [actionMap, setActionMap]); const setUsernamesHandler = useCallback((usernames: string[]) => { setActivePageNumber(1); setSelectedUsernames(usernames); }, []); const clearButtonPushedHandler = useCallback(() => { setActivePageNumber(1); setStartDate(null); setEndDate(null); setSelectedUsernames([]); typeaheadRef.current?.clear(); if (auditLogAvailableActionsData != null) { setActionMap(new Map(auditLogAvailableActionsData.map(action => [action, true]))); } }, [setActivePageNumber, setStartDate, setEndDate, setSelectedUsernames, setActionMap, auditLogAvailableActionsData]); const reloadButtonPushedHandler = useCallback(() => { setActivePageNumber(1); mutateActivity(); }, [mutateActivity]); const jumpPageInputChangeHandler = useCallback((e: React.ChangeEvent) => { const inputNumber = Number(e.target.value); const isNan = Number.isNaN(inputNumber); if (!isNan) { // eslint-disable-next-line no-nested-ternary const jumpPageNumber = inputNumber > totalPagingPages ? totalPagingPages : inputNumber <= 0 ? activePageNumber : inputNumber; setJumpPageNumber(jumpPageNumber); } else { setJumpPageNumber(activePageNumber); } }, [totalPagingPages, activePageNumber, setJumpPageNumber]); const jumpPageInputKeyDownHandler = useCallback((e) => { if (e.key === 'Enter') { setActivePageNumber(jumpPageNumber); } }, [setActivePageNumber, jumpPageNumber]); const jumpPageButtonPushedHandler = useCallback(() => { setActivePageNumber(jumpPageNumber); }, [jumpPageNumber]); // eslint-disable-next-line max-len const activityCounter = `${activityList.length === 0 ? 0 : offset + 1} - ${(PAGING_LIMIT * activePageNumber) - (PAGING_LIMIT - activityList.length)} of ${totalActivityNum}`; if (!auditLogEnabled) { return ; } return (

{isSettingPage ? t('audit_log_management.audit_log_settings') : t('audit_log_management.audit_log')} { !isSettingPage && ( )}

{isSettingPage ? ( ) : ( <>

{ isLoading ? (

) : ( ) }
)}
); };