import type React from 'react'; import type { FC } from 'react'; import { useCallback, useRef, useState } from 'react'; import { LoadingSpinner } from '@growi/ui/dist/components'; import { format } from 'date-fns/format'; import { useAtomValue } from 'jotai'; 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 { useGrowiAppIdForGrowiCloud, useGrowiCloudUri } from '~/states/global'; import { auditLogAvailableActionsAtom, auditLogEnabledAtom, } from '~/states/server-configurations'; import { useSWRxActivity } from '~/stores/activity'; import PaginationWrapper from '../PaginationWrapper'; import { ActivityTable } from './AuditLog/ActivityTable'; import { AuditLogDisableMode } from './AuditLog/AuditLogDisableMode'; import { AuditLogExportModal } from './AuditLog/AuditLogExportModal'; 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 growiCloudUri = useGrowiCloudUri(); const growiAppIdForGrowiCloud = useGrowiAppIdForGrowiCloud(); const isCloud = growiCloudUri != null && growiAppIdForGrowiCloud != null; const typeaheadRef = useRef(null); const auditLogAvailableActionsData = useAtomValue( auditLogAvailableActionsAtom, ); /* * 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 auditLogEnabled = useAtomValue(auditLogEnabledAtom); /* * 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], ); const multipleActionCheckboxChangedHandler = useCallback( (actions: SupportedActionType[], isChecked) => { setActivePageNumber(1); actions.forEach((action) => { actionMap.set(action, isChecked); }); setActionMap(new Map(actionMap.entries())); }, [actionMap], ); 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]), ), ); } }, [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) { const jumpPageNumber = inputNumber > totalPagingPages ? totalPagingPages : inputNumber <= 0 ? activePageNumber : inputNumber; setJumpPageNumber(jumpPageNumber); } else { setJumpPageNumber(activePageNumber); } }, [totalPagingPages, activePageNumber], ); const jumpPageInputKeyDownHandler = useCallback( (e) => { if (e.key === 'Enter') { setActivePageNumber(jumpPageNumber); } }, [jumpPageNumber], ); const jumpPageButtonPushedHandler = useCallback(() => { setActivePageNumber(jumpPageNumber); }, [jumpPageNumber]); const [isExportModalOpen, setIsExportModalOpen] = useState(false); const startIndex = activityList.length === 0 ? 0 : offset + 1; const endIndex = activityList.length === 0 ? 0 : offset + activityList.length; if (!auditLogEnabled) { return ; } return (
{isCloud && ( share {t('cloud_setting_management.to_cloud_settings')} )}

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

{isSettingPage ? ( ) : ( <>

{startIndex} - {endIndex} of{' '} {totalActivityNum}

{isLoading ? (
) : ( )}
setIsExportModalOpen(false)} initialStartDate={startDate} initialEndDate={endDate} initialSelectedUsernames={selectedUsernames} initialActionMap={actionMap} /> )}
); };