import React, { useCallback, useEffect, useState, type JSX, } from 'react'; import { useTranslation } from 'react-i18next'; import { apiDelete } from '~/client/util/apiv1-client'; import { apiv3Get } from '~/client/util/apiv3-client'; import { toastError, toastSuccess } from '~/client/util/toastr'; import { useAdminSocket } from '~/features/admin/states/socket-io'; import LabeledProgressBar from './Common/LabeledProgressBar'; import ArchiveFilesTable from './ExportArchiveData/ArchiveFilesTable'; import SelectCollectionsModal from './ExportArchiveData/SelectCollectionsModal'; const IGNORED_COLLECTION_NAMES = [ 'sessions', 'rlflx', 'yjs-writings', 'transferkeys', ]; const ExportArchiveDataPage = (): JSX.Element => { const socket = useAdminSocket(); const { t } = useTranslation('admin'); const [collections, setCollections] = useState([]); const [zipFileStats, setZipFileStats] = useState([]); const [progressList, setProgressList] = useState([]); const [isExportModalOpen, setExportModalOpen] = useState(false); const [isExporting, setExporting] = useState(false); const [isZipping, setZipping] = useState(false); const [isExported, setExported] = useState(false); const fetchData = useCallback(async() => { const [{ data: collectionsData }, { data: statusData }] = await Promise.all([ apiv3Get<{collections: any[]}>('/mongo/collections', {}), apiv3Get<{status: { zipFileStats: any[], isExporting: boolean, progressList: any[] }}>('/export/status', {}), ]); // filter only not ignored collection names const filteredCollections = collectionsData.collections.filter((collectionName) => { return !IGNORED_COLLECTION_NAMES.includes(collectionName); }); const { zipFileStats, isExporting, progressList } = statusData.status; setCollections(filteredCollections); setZipFileStats(zipFileStats); setExporting(isExporting); setProgressList(progressList); }, []); const setupWebsocketEventHandler = useCallback(() => { if (socket == null) { return () => {}; } const onProgress = ({ currentCount, totalCount, progressList }) => { setExporting(true); setProgressList(progressList); }; const onStartZipping = () => { setZipping(true); }; const onTerminateForExport = ({ addedZipFileStat }) => { setExporting(false); setZipping(false); setExported(true); setZipFileStats(prev => prev.concat([addedZipFileStat])); toastSuccess(`New Archive Data '${addedZipFileStat.fileName}' is added`); }; // Add listeners socket.on('admin:onProgressForExport', onProgress); socket.on('admin:onStartZippingForExport', onStartZipping); socket.on('admin:onTerminateForExport', onTerminateForExport); // Cleanup listeners return () => { socket.off('admin:onProgressForExport', onProgress); socket.off('admin:onStartZippingForExport', onStartZipping); socket.off('admin:onTerminateForExport', onTerminateForExport); }; }, [socket]); const onZipFileStatRemove = useCallback(async(fileName) => { try { await apiDelete(`/v3/export/${fileName}`, {}); setZipFileStats(prev => prev.filter(stat => stat.fileName !== fileName)); toastSuccess(`Deleted ${fileName}`); } catch (err) { toastError(err); } }, []); const exportingRequestedHandler = useCallback(() => {}, []); const renderProgressBarsForCollections = useCallback(() => { const cols = progressList.map((progressData) => { const { collectionName, currentCount, totalCount } = progressData; return (
); }); return
{cols}
; }, [progressList]); const renderProgressBarForZipping = useCallback(() => { const showZippingBar = isZipping || isExported; if (!showZippingBar) { return <>; } return (
); }, [isExported, isZipping]); useEffect(() => { fetchData(); const cleanupWebsocket = setupWebsocketEventHandler(); return () => { if (cleanupWebsocket) cleanupWebsocket(); }; }, [fetchData, setupWebsocketEventHandler]); const showExportingData = (isExported || isExporting) && (progressList != null); return (

{t('export_management.export_archive_data')}

{ showExportingData && (

{t('export_management.exporting_collection_list')}

{ renderProgressBarsForCollections() } { renderProgressBarForZipping() }
) }

{t('export_management.exported_data_list')}

setExportModalOpen(false)} collections={collections} />
); }; export default ExportArchiveDataPage;