import { useCallback, useEffect, useRef, useState } from 'react'; import { LoadingSpinner } from '@growi/ui/dist/components'; import { useAtomValue } from 'jotai'; import { useTranslation } from 'react-i18next'; import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import type { IClearable } from '~/client/interfaces/clearable'; import { apiv3Post } from '~/client/util/apiv3-client'; import { toastError, toastSuccess } from '~/client/util/toastr'; import type { SupportedActionType } from '~/interfaces/activity'; import { auditLogAvailableActionsAtom } from '~/states/server-configurations'; import { DateRangePicker } from './DateRangePicker'; import { SearchUsernameTypeahead } from './SearchUsernameTypeahead'; import { SelectActionDropdown } from './SelectActionDropdown'; type Props = { isOpen: boolean; onClose: () => void; }; export const AuditLogExportModal = ({ isOpen, onClose, }: Props): JSX.Element => { const { t } = useTranslation('admin'); const typeaheadRef = useRef(null); const auditLogAvailableActionsData = useAtomValue( auditLogAvailableActionsAtom, ); const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); const [selectedUsernames, setSelectedUsernames] = useState([]); const [actionMap, setActionMap] = useState( new Map(), ); const [isExporting, setIsExporting] = useState(false); useEffect(() => { if (isOpen) { setStartDate(null); setEndDate(null); setSelectedUsernames([]); setActionMap( new Map( auditLogAvailableActionsData?.map((action) => [action, true]) ?? [], ), ); setIsExporting(false); typeaheadRef.current?.clear(); } }, [isOpen, auditLogAvailableActionsData]); const datePickerChangedHandler = useCallback((dateList: Date[] | null[]) => { setStartDate(dateList[0]); setEndDate(dateList[1]); }, []); const actionCheckboxChangedHandler = useCallback( (action: SupportedActionType) => { actionMap.set(action, !actionMap.get(action)); setActionMap(new Map(actionMap.entries())); }, [actionMap], ); const multipleActionCheckboxChangedHandler = useCallback( (actions: SupportedActionType[], isChecked: boolean) => { actions.forEach((action) => { actionMap.set(action, isChecked); }); setActionMap(new Map(actionMap.entries())); }, [actionMap], ); const setUsernamesHandler = useCallback((usernames: string[]) => { setSelectedUsernames(usernames); }, []); const exportHandler = useCallback(async () => { setIsExporting(true); try { const selectedActionList = Array.from(actionMap.entries()) .filter((v) => v[1]) .map((v) => v[0]); const filters: { actions?: SupportedActionType[]; dateFrom?: Date; dateTo?: Date; // TODO: Add users filter after implementing username-to-userId conversion } = {}; if (selectedActionList.length > 0) { filters.actions = selectedActionList; } if (startDate != null) { filters.dateFrom = startDate; } if (endDate != null) { filters.dateTo = endDate; } await apiv3Post('/audit-log-bulk-export', { filters }); toastSuccess(t('audit_log_management.export_requested')); onClose(); } catch { toastError(t('audit_log_management.export_failed')); } finally { setIsExporting(false); } }, [actionMap, startDate, endDate, t, onClose]); return ( {t('audit_log_management.export_audit_log')}
{t('audit_log_management.username')}
{t('audit_log_management.date')}
{t('audit_log_management.action')}
); };