cleanup
This commit is contained in:
+67
-227
@@ -13,14 +13,11 @@ import AssetManager, {
|
||||
createAssetView,
|
||||
} from '../asset_manager';
|
||||
import useApiError from '../hooks/useApiError';
|
||||
import TagsPanel from '../tags/TagsPanel';
|
||||
import CorrespondentsPanel from '../correspondents/CorrespondentsPanel';
|
||||
import DocumentTypesPanel from '../documentTypes/DocumentTypesPanel';
|
||||
import TagManager from '../tag_manager';
|
||||
import SettingsModal from '../settings/SettingsModal';
|
||||
import usePasskeys from '../settings/usePasskeys';
|
||||
import { AppShellContext } from '../appShellContext';
|
||||
import DropOverlay from './DropOverlay';
|
||||
import { useManagementModals } from './useManagementModals';
|
||||
import { api, useAppDispatch, useAppState } from './appState';
|
||||
|
||||
const ASSET_PRESIGN_TTL_MS = 240 * 1000; // backend issues 5 min tokens; refresh slightly early
|
||||
@@ -182,10 +179,6 @@ const AppLayout = () => {
|
||||
[reportApiError],
|
||||
);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isTagsModalOpen, setTagsModalOpen] = useState(false);
|
||||
const [isCorrespondentsModalOpen, setCorrespondentsModalOpen] = useState(false);
|
||||
const [isDocumentTypesModalOpen, setDocumentTypesModalOpen] = useState(false);
|
||||
const [isSettingsModalOpen, setSettingsModalOpen] = useState(false);
|
||||
const [creatingFolder, setCreatingFolder] = useState(false);
|
||||
const [folderNodes, setFolderNodes] = useState(() => {
|
||||
const rootNode = createRootNode();
|
||||
@@ -1596,20 +1589,6 @@ const AppLayout = () => {
|
||||
token,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSettingsModalOpen) {
|
||||
return;
|
||||
}
|
||||
refreshWebdavTokens();
|
||||
refreshPasskeys();
|
||||
}, [isSettingsModalOpen, refreshPasskeys, refreshWebdavTokens]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSettingsModalOpen) {
|
||||
setWebdavTokenSecret(null);
|
||||
}
|
||||
}, [isSettingsModalOpen]);
|
||||
|
||||
const handleTagUpdate = useCallback(
|
||||
async (tagId, changes) => {
|
||||
if (!tagId) {
|
||||
@@ -4059,83 +4038,34 @@ const AppLayout = () => {
|
||||
}
|
||||
}, [creatingFolder, handleFolderCreate, setStatusMessage]);
|
||||
|
||||
const openTagsModal = useCallback(() => {
|
||||
setCorrespondentsModalOpen(false);
|
||||
setDocumentTypesModalOpen(false);
|
||||
setTagsModalOpen(true);
|
||||
}, []);
|
||||
const {
|
||||
managementModals,
|
||||
openTagsModal,
|
||||
openCorrespondentsModal,
|
||||
openDocumentTypesModal,
|
||||
} = useManagementModals({
|
||||
locationPathname: location.pathname,
|
||||
tags,
|
||||
refreshTags,
|
||||
onTagCreate: handleTagCreate,
|
||||
onTagUpdate: handleTagUpdate,
|
||||
onTagDelete: handleTagDelete,
|
||||
correspondents,
|
||||
refreshCorrespondents,
|
||||
onCorrespondentCreate: handleCorrespondentCreate,
|
||||
onCorrespondentUpdate: handleCorrespondentUpdate,
|
||||
onCorrespondentDelete: handleCorrespondentDelete,
|
||||
documentTypes,
|
||||
refreshDocumentTypes,
|
||||
onDocumentTypeCreate: handleDocumentTypeCreate,
|
||||
onDocumentTypeUpdate: handleDocumentTypeUpdate,
|
||||
onDocumentTypeDelete: handleDocumentTypeDelete,
|
||||
setStatusMessage,
|
||||
});
|
||||
|
||||
const closeTagsModal = useCallback(() => {
|
||||
setTagsModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const openCorrespondentsModal = useCallback(() => {
|
||||
setTagsModalOpen(false);
|
||||
setCorrespondentsModalOpen(true);
|
||||
setDocumentTypesModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const closeCorrespondentsModal = useCallback(() => {
|
||||
setCorrespondentsModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const openDocumentTypesModal = useCallback(() => {
|
||||
setTagsModalOpen(false);
|
||||
setCorrespondentsModalOpen(false);
|
||||
setDocumentTypesModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const closeDocumentTypesModal = useCallback(() => {
|
||||
setDocumentTypesModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const openSettingsModal = useCallback(() => {
|
||||
setSettingsModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const closeSettingsModal = useCallback(() => {
|
||||
setSettingsModalOpen(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setTagsModalOpen(false);
|
||||
setCorrespondentsModalOpen(false);
|
||||
setDocumentTypesModalOpen(false);
|
||||
setSettingsModalOpen(false);
|
||||
}, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTagsModalOpen && !isCorrespondentsModalOpen && !isDocumentTypesModalOpen && !isSettingsModalOpen) {
|
||||
return;
|
||||
}
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
if (isTagsModalOpen) {
|
||||
closeTagsModal();
|
||||
} else if (isCorrespondentsModalOpen) {
|
||||
closeCorrespondentsModal();
|
||||
} else if (isDocumentTypesModalOpen) {
|
||||
closeDocumentTypesModal();
|
||||
} else if (isSettingsModalOpen) {
|
||||
closeSettingsModal();
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [
|
||||
isTagsModalOpen,
|
||||
isCorrespondentsModalOpen,
|
||||
isDocumentTypesModalOpen,
|
||||
isSettingsModalOpen,
|
||||
closeTagsModal,
|
||||
closeCorrespondentsModal,
|
||||
closeDocumentTypesModal,
|
||||
closeSettingsModal,
|
||||
]);
|
||||
const openSettings = useCallback(() => {
|
||||
navigate('/settings');
|
||||
}, [navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) return undefined;
|
||||
@@ -5216,7 +5146,7 @@ const AppLayout = () => {
|
||||
tenants: tenantOptions,
|
||||
activeTenantId: currentTenantId,
|
||||
onSelectTenant: handleTenantSelect,
|
||||
onOpenSettings: openSettingsModal,
|
||||
onOpenSettings: openSettings,
|
||||
}),
|
||||
[
|
||||
activeCorrespondentFilters,
|
||||
@@ -5237,7 +5167,7 @@ const AppLayout = () => {
|
||||
handleSearchSubmit,
|
||||
handleTenantSelect,
|
||||
loading,
|
||||
openSettingsModal,
|
||||
openSettings,
|
||||
previewActive,
|
||||
searchQuery,
|
||||
draggedFolderId,
|
||||
@@ -5401,6 +5331,23 @@ const AppLayout = () => {
|
||||
handleDocumentTypeSet,
|
||||
handleBulkDocumentTypeSet,
|
||||
handleBulkDocumentTypeClear,
|
||||
webdavTokens,
|
||||
webdavTokensLoading,
|
||||
creatingWebdavToken,
|
||||
deletingWebdavTokenId,
|
||||
refreshWebdavTokens,
|
||||
createWebdavToken,
|
||||
deleteWebdavToken,
|
||||
webdavTokenSecret,
|
||||
dismissCreatedWebdavToken,
|
||||
passkeys,
|
||||
passkeysSupported,
|
||||
passkeysLoading,
|
||||
registeringPasskey,
|
||||
revokingPasskeyId,
|
||||
refreshPasskeys,
|
||||
registerPasskey,
|
||||
revokePasskey,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
@@ -5420,7 +5367,7 @@ const AppLayout = () => {
|
||||
openTagsModal,
|
||||
openCorrespondentsModal,
|
||||
openDocumentTypesModal,
|
||||
openSettingsModal,
|
||||
openSettings,
|
||||
detailPanelOpen,
|
||||
setDetailPanelOpen,
|
||||
}),
|
||||
@@ -5455,6 +5402,23 @@ const AppLayout = () => {
|
||||
handleDocumentTypeSet,
|
||||
handleBulkDocumentTypeSet,
|
||||
handleBulkDocumentTypeClear,
|
||||
webdavTokens,
|
||||
webdavTokensLoading,
|
||||
creatingWebdavToken,
|
||||
deletingWebdavTokenId,
|
||||
refreshWebdavTokens,
|
||||
createWebdavToken,
|
||||
deleteWebdavToken,
|
||||
webdavTokenSecret,
|
||||
dismissCreatedWebdavToken,
|
||||
passkeys,
|
||||
passkeysSupported,
|
||||
passkeysLoading,
|
||||
registeringPasskey,
|
||||
revokingPasskeyId,
|
||||
refreshPasskeys,
|
||||
registerPasskey,
|
||||
revokePasskey,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
@@ -5473,7 +5437,7 @@ const AppLayout = () => {
|
||||
openTagsModal,
|
||||
openCorrespondentsModal,
|
||||
openDocumentTypesModal,
|
||||
openSettingsModal,
|
||||
openSettings,
|
||||
detailPanelOpen,
|
||||
setDetailPanelOpen,
|
||||
],
|
||||
@@ -5502,131 +5466,7 @@ const AppLayout = () => {
|
||||
folderName={dropOverlayState.folderName}
|
||||
/>
|
||||
<Outlet />
|
||||
{isTagsModalOpen && (
|
||||
<div
|
||||
className="modal-backdrop"
|
||||
role="presentation"
|
||||
onClick={closeTagsModal}
|
||||
>
|
||||
<div
|
||||
className="modal modal--panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="tags-modal-title"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="panel-modal__header">
|
||||
<h3 id="tags-modal-title">Manage Tags</h3>
|
||||
<button type="button" className="secondary" onClick={closeTagsModal}>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-modal__body">
|
||||
<TagsPanel
|
||||
tags={tags}
|
||||
onRefresh={refreshTags}
|
||||
onCreateTag={handleTagCreate}
|
||||
onUpdateTag={handleTagUpdate}
|
||||
onDeleteTag={handleTagDelete}
|
||||
onNotify={setStatusMessage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isCorrespondentsModalOpen && (
|
||||
<div
|
||||
className="modal-backdrop"
|
||||
role="presentation"
|
||||
onClick={closeCorrespondentsModal}
|
||||
>
|
||||
<div
|
||||
className="modal modal--panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="correspondents-modal-title"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="panel-modal__header">
|
||||
<h3 id="correspondents-modal-title">Manage Correspondents</h3>
|
||||
<button
|
||||
type="button"
|
||||
className="secondary"
|
||||
onClick={closeCorrespondentsModal}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-modal__body">
|
||||
<CorrespondentsPanel
|
||||
correspondents={correspondents}
|
||||
onRefresh={refreshCorrespondents}
|
||||
onCreate={handleCorrespondentCreate}
|
||||
onUpdate={handleCorrespondentUpdate}
|
||||
onDelete={handleCorrespondentDelete}
|
||||
onNotify={setStatusMessage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isDocumentTypesModalOpen && (
|
||||
<div
|
||||
className="modal-backdrop"
|
||||
role="presentation"
|
||||
onClick={closeDocumentTypesModal}
|
||||
>
|
||||
<div
|
||||
className="modal modal--panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="document-types-modal-title"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="panel-modal__header">
|
||||
<h3 id="document-types-modal-title">Manage Document Types</h3>
|
||||
<button
|
||||
type="button"
|
||||
className="secondary"
|
||||
onClick={closeDocumentTypesModal}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-modal__body">
|
||||
<DocumentTypesPanel
|
||||
documentTypes={documentTypes}
|
||||
onRefresh={refreshDocumentTypes}
|
||||
onCreate={handleDocumentTypeCreate}
|
||||
onUpdate={handleDocumentTypeUpdate}
|
||||
onDelete={handleDocumentTypeDelete}
|
||||
onNotify={setStatusMessage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<SettingsModal
|
||||
open={isSettingsModalOpen}
|
||||
onClose={closeSettingsModal}
|
||||
tokens={webdavTokens}
|
||||
loading={webdavTokensLoading}
|
||||
creating={creatingWebdavToken}
|
||||
deletingId={deletingWebdavTokenId}
|
||||
onRefresh={refreshWebdavTokens}
|
||||
onCreate={createWebdavToken}
|
||||
onDelete={deleteWebdavToken}
|
||||
createdToken={webdavTokenSecret}
|
||||
onDismissCreatedToken={dismissCreatedWebdavToken}
|
||||
passkeys={passkeys}
|
||||
passkeysSupported={passkeysSupported}
|
||||
passkeysLoading={passkeysLoading}
|
||||
registeringPasskey={registeringPasskey}
|
||||
revokingPasskeyId={revokingPasskeyId}
|
||||
onRefreshPasskeys={refreshPasskeys}
|
||||
onRegisterPasskey={registerPasskey}
|
||||
onRevokePasskey={revokePasskey}
|
||||
/>
|
||||
{managementModals}
|
||||
</div>
|
||||
</AppShellContext.Provider>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import AppLayout from './AppLayout';
|
||||
import DocumentsRoute from './DocumentsRoute';
|
||||
import LoginRoute from './LoginRoute';
|
||||
import SettingsRoute from './SettingsRoute';
|
||||
|
||||
const AppRouter = () => (
|
||||
<Routes>
|
||||
@@ -12,6 +13,7 @@ const AppRouter = () => (
|
||||
<Route path="/documents" element={<DocumentsRoute />} />
|
||||
<Route path="/documents/folder/:folderId" element={<DocumentsRoute />} />
|
||||
<Route path="/documents/:documentId" element={<DocumentsRoute />} />
|
||||
<Route path="/settings" element={<SettingsRoute />} />
|
||||
<Route path="*" element={<Navigate to="/documents" replace />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ChevronsRightIcon } from '../ui/icons';
|
||||
import { createDocumentsSurface } from '../documents/DocumentsTable';
|
||||
import { createPreviewSurface } from '../preview/PreviewWorkspace';
|
||||
import { createDesktopSurface } from '../DesktopWorkspace';
|
||||
import { useAppShell } from '../appShellContext';
|
||||
import DocumentsLayout from './DocumentsLayout';
|
||||
import { useWorkspaceSurface } from './useWorkspaceSurface';
|
||||
|
||||
const DocumentsRoute = () => {
|
||||
const {
|
||||
@@ -63,66 +60,14 @@ const DocumentsRoute = () => {
|
||||
navigate(target);
|
||||
}, [navigate, parentBreadcrumb]);
|
||||
|
||||
const isWorkspace = workspaceMode === 'skeuo';
|
||||
const isPreviewWorkspace = Boolean(previewWorkspaceDocument);
|
||||
const showPreviewWorkspace = !isWorkspace && isPreviewWorkspace;
|
||||
|
||||
const renderSidebarToggle = useCallback(() => {
|
||||
if (!sidebarCollapsed) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={expandSidebar}
|
||||
aria-label="Expand sidebar"
|
||||
title="Expand sidebar"
|
||||
>
|
||||
<ChevronsRightIcon />
|
||||
</button>
|
||||
);
|
||||
}, [sidebarCollapsed, expandSidebar]);
|
||||
|
||||
const documentsSurface = useMemo(() => {
|
||||
if (!documentsTableProps) {
|
||||
return null;
|
||||
}
|
||||
return createDocumentsSurface({
|
||||
tableProps: documentsTableProps,
|
||||
parentBreadcrumb,
|
||||
onNavigateParent: parentBreadcrumb ? handleNavigateParent : null,
|
||||
renderSidebarToggle,
|
||||
detailProps: detailPanelProps,
|
||||
detailOpen: detailPanelOpen,
|
||||
});
|
||||
}, [
|
||||
const { surface } = useWorkspaceSurface({
|
||||
sidebarCollapsed,
|
||||
onExpandSidebar: expandSidebar,
|
||||
documentsTableProps,
|
||||
parentBreadcrumb,
|
||||
handleNavigateParent,
|
||||
renderSidebarToggle,
|
||||
detailPanelProps,
|
||||
detailPanelOpen,
|
||||
]);
|
||||
|
||||
const previewSurface = useMemo(() => {
|
||||
if (!showPreviewWorkspace || !previewWorkspaceDocument) {
|
||||
return null;
|
||||
}
|
||||
return createPreviewSurface({
|
||||
document: previewWorkspaceDocument,
|
||||
previewEntry: previewWorkspaceEntry,
|
||||
ensureAssetUrl,
|
||||
ensurePreviewData,
|
||||
getDocumentAsset,
|
||||
resolveApiPath,
|
||||
notifyApiError,
|
||||
onRegenerate: handleThumbnailRegeneration,
|
||||
onClose: closeDocumentPreview,
|
||||
renderSidebarToggle,
|
||||
});
|
||||
}, [
|
||||
showPreviewWorkspace,
|
||||
workspaceMode,
|
||||
skeuoWorkspaceProps,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
ensureAssetUrl,
|
||||
@@ -132,24 +77,9 @@ const DocumentsRoute = () => {
|
||||
notifyApiError,
|
||||
handleThumbnailRegeneration,
|
||||
closeDocumentPreview,
|
||||
renderSidebarToggle,
|
||||
]);
|
||||
|
||||
const workspaceSurface = useMemo(() => {
|
||||
if (!isWorkspace) {
|
||||
return null;
|
||||
}
|
||||
return createDesktopSurface({
|
||||
workspaceProps: skeuoWorkspaceProps,
|
||||
renderSidebarToggle,
|
||||
});
|
||||
}, [isWorkspace, skeuoWorkspaceProps, renderSidebarToggle]);
|
||||
|
||||
const surface = showPreviewWorkspace
|
||||
? previewSurface
|
||||
: isWorkspace
|
||||
? workspaceSurface
|
||||
: documentsSurface;
|
||||
parentBreadcrumb,
|
||||
onNavigateParent: handleNavigateParent,
|
||||
});
|
||||
|
||||
if (!surface) {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import SettingsModal from '../settings/SettingsModal';
|
||||
import { useAppShell } from '../appShellContext';
|
||||
|
||||
const SettingsRoute = () => {
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
webdavTokens,
|
||||
webdavTokensLoading,
|
||||
creatingWebdavToken,
|
||||
deletingWebdavTokenId,
|
||||
refreshWebdavTokens,
|
||||
createWebdavToken,
|
||||
deleteWebdavToken,
|
||||
webdavTokenSecret,
|
||||
dismissCreatedWebdavToken,
|
||||
passkeys,
|
||||
passkeysSupported,
|
||||
passkeysLoading,
|
||||
registeringPasskey,
|
||||
revokingPasskeyId,
|
||||
refreshPasskeys,
|
||||
registerPasskey,
|
||||
revokePasskey,
|
||||
} = useAppShell();
|
||||
|
||||
useEffect(() => {
|
||||
refreshWebdavTokens();
|
||||
refreshPasskeys();
|
||||
}, [refreshWebdavTokens, refreshPasskeys]);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
dismissCreatedWebdavToken();
|
||||
navigate(-1);
|
||||
}, [dismissCreatedWebdavToken, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
handleClose();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [handleClose]);
|
||||
|
||||
useEffect(() => () => {
|
||||
dismissCreatedWebdavToken();
|
||||
}, [dismissCreatedWebdavToken]);
|
||||
|
||||
return (
|
||||
<SettingsModal
|
||||
open
|
||||
onClose={handleClose}
|
||||
tokens={webdavTokens}
|
||||
loading={webdavTokensLoading}
|
||||
creating={creatingWebdavToken}
|
||||
deletingId={deletingWebdavTokenId}
|
||||
onRefresh={refreshWebdavTokens}
|
||||
onCreate={createWebdavToken}
|
||||
onDelete={deleteWebdavToken}
|
||||
createdToken={webdavTokenSecret}
|
||||
onDismissCreatedToken={dismissCreatedWebdavToken}
|
||||
passkeys={passkeys}
|
||||
passkeysSupported={passkeysSupported}
|
||||
passkeysLoading={passkeysLoading}
|
||||
registeringPasskey={registeringPasskey}
|
||||
revokingPasskeyId={revokingPasskeyId}
|
||||
onRefreshPasskeys={refreshPasskeys}
|
||||
onRegisterPasskey={registerPasskey}
|
||||
onRevokePasskey={revokePasskey}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsRoute;
|
||||
@@ -0,0 +1,219 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import TagsPanel from '../tags/TagsPanel';
|
||||
import CorrespondentsPanel from '../correspondents/CorrespondentsPanel';
|
||||
import DocumentTypesPanel from '../documentTypes/DocumentTypesPanel';
|
||||
|
||||
const TAGS_MODAL = 'tags';
|
||||
const CORRESPONDENTS_MODAL = 'correspondents';
|
||||
const DOCUMENT_TYPES_MODAL = 'document-types';
|
||||
|
||||
export const useManagementModals = ({
|
||||
locationPathname,
|
||||
tags,
|
||||
refreshTags,
|
||||
onTagCreate,
|
||||
onTagUpdate,
|
||||
onTagDelete,
|
||||
correspondents,
|
||||
refreshCorrespondents,
|
||||
onCorrespondentCreate,
|
||||
onCorrespondentUpdate,
|
||||
onCorrespondentDelete,
|
||||
documentTypes,
|
||||
refreshDocumentTypes,
|
||||
onDocumentTypeCreate,
|
||||
onDocumentTypeUpdate,
|
||||
onDocumentTypeDelete,
|
||||
setStatusMessage,
|
||||
}) => {
|
||||
const [activeModal, setActiveModal] = useState(null);
|
||||
|
||||
const openTagsModal = useCallback(() => setActiveModal(TAGS_MODAL), []);
|
||||
const openCorrespondentsModal = useCallback(
|
||||
() => setActiveModal(CORRESPONDENTS_MODAL),
|
||||
[],
|
||||
);
|
||||
const openDocumentTypesModal = useCallback(
|
||||
() => setActiveModal(DOCUMENT_TYPES_MODAL),
|
||||
[],
|
||||
);
|
||||
const closeActiveModal = useCallback(() => setActiveModal(null), []);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveModal(null);
|
||||
}, [locationPathname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeModal) {
|
||||
return undefined;
|
||||
}
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
setActiveModal(null);
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [activeModal]);
|
||||
|
||||
const tagModal = useMemo(() => {
|
||||
if (activeModal !== TAGS_MODAL) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="modal-backdrop"
|
||||
role="presentation"
|
||||
onClick={closeActiveModal}
|
||||
>
|
||||
<div
|
||||
className="modal modal--panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="tags-modal-title"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="panel-modal__header">
|
||||
<h3 id="tags-modal-title">Manage Tags</h3>
|
||||
<button type="button" className="secondary" onClick={closeActiveModal}>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-modal__body">
|
||||
<TagsPanel
|
||||
tags={tags}
|
||||
onRefresh={refreshTags}
|
||||
onCreateTag={onTagCreate}
|
||||
onUpdateTag={onTagUpdate}
|
||||
onDeleteTag={onTagDelete}
|
||||
onNotify={setStatusMessage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [
|
||||
activeModal,
|
||||
closeActiveModal,
|
||||
onTagCreate,
|
||||
onTagDelete,
|
||||
onTagUpdate,
|
||||
refreshTags,
|
||||
setStatusMessage,
|
||||
tags,
|
||||
]);
|
||||
|
||||
const correspondentsModal = useMemo(() => {
|
||||
if (activeModal !== CORRESPONDENTS_MODAL) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="modal-backdrop"
|
||||
role="presentation"
|
||||
onClick={closeActiveModal}
|
||||
>
|
||||
<div
|
||||
className="modal modal--panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="correspondents-modal-title"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="panel-modal__header">
|
||||
<h3 id="correspondents-modal-title">Manage Correspondents</h3>
|
||||
<button type="button" className="secondary" onClick={closeActiveModal}>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-modal__body">
|
||||
<CorrespondentsPanel
|
||||
correspondents={correspondents}
|
||||
onRefresh={refreshCorrespondents}
|
||||
onCreate={onCorrespondentCreate}
|
||||
onUpdate={onCorrespondentUpdate}
|
||||
onDelete={onCorrespondentDelete}
|
||||
onNotify={setStatusMessage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [
|
||||
activeModal,
|
||||
closeActiveModal,
|
||||
correspondents,
|
||||
onCorrespondentCreate,
|
||||
onCorrespondentDelete,
|
||||
onCorrespondentUpdate,
|
||||
refreshCorrespondents,
|
||||
setStatusMessage,
|
||||
]);
|
||||
|
||||
const documentTypesModal = useMemo(() => {
|
||||
if (activeModal !== DOCUMENT_TYPES_MODAL) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="modal-backdrop"
|
||||
role="presentation"
|
||||
onClick={closeActiveModal}
|
||||
>
|
||||
<div
|
||||
className="modal modal--panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="document-types-modal-title"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="panel-modal__header">
|
||||
<h3 id="document-types-modal-title">Manage Document Types</h3>
|
||||
<button type="button" className="secondary" onClick={closeActiveModal}>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-modal__body">
|
||||
<DocumentTypesPanel
|
||||
documentTypes={documentTypes}
|
||||
onRefresh={refreshDocumentTypes}
|
||||
onCreate={onDocumentTypeCreate}
|
||||
onUpdate={onDocumentTypeUpdate}
|
||||
onDelete={onDocumentTypeDelete}
|
||||
onNotify={setStatusMessage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [
|
||||
activeModal,
|
||||
closeActiveModal,
|
||||
documentTypes,
|
||||
onDocumentTypeCreate,
|
||||
onDocumentTypeDelete,
|
||||
onDocumentTypeUpdate,
|
||||
refreshDocumentTypes,
|
||||
setStatusMessage,
|
||||
]);
|
||||
|
||||
const managementModals = (
|
||||
<>
|
||||
{tagModal}
|
||||
{correspondentsModal}
|
||||
{documentTypesModal}
|
||||
</>
|
||||
);
|
||||
|
||||
return {
|
||||
managementModals,
|
||||
openTagsModal,
|
||||
openCorrespondentsModal,
|
||||
openDocumentTypesModal,
|
||||
closeActiveModal,
|
||||
activeModal,
|
||||
};
|
||||
};
|
||||
|
||||
export default useManagementModals;
|
||||
@@ -0,0 +1,118 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { ChevronsRightIcon } from '../ui/icons';
|
||||
import { createDocumentsSurface } from '../documents/DocumentsTable';
|
||||
import { createPreviewSurface } from '../preview/PreviewWorkspace';
|
||||
import { createDesktopSurface } from '../DesktopWorkspace';
|
||||
|
||||
export const useWorkspaceSurface = ({
|
||||
sidebarCollapsed,
|
||||
onExpandSidebar,
|
||||
documentsTableProps,
|
||||
detailPanelProps,
|
||||
detailPanelOpen,
|
||||
workspaceMode,
|
||||
skeuoWorkspaceProps,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
ensureAssetUrl,
|
||||
ensurePreviewData,
|
||||
getDocumentAsset,
|
||||
resolveApiPath,
|
||||
notifyApiError,
|
||||
handleThumbnailRegeneration,
|
||||
closeDocumentPreview,
|
||||
parentBreadcrumb,
|
||||
onNavigateParent,
|
||||
}) => {
|
||||
const renderSidebarToggle = useCallback(() => {
|
||||
if (!sidebarCollapsed) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={onExpandSidebar}
|
||||
aria-label="Expand sidebar"
|
||||
title="Expand sidebar"
|
||||
>
|
||||
<ChevronsRightIcon />
|
||||
</button>
|
||||
);
|
||||
}, [sidebarCollapsed, onExpandSidebar]);
|
||||
|
||||
const documentsSurface = useMemo(() => {
|
||||
if (!documentsTableProps) {
|
||||
return null;
|
||||
}
|
||||
return createDocumentsSurface({
|
||||
tableProps: documentsTableProps,
|
||||
parentBreadcrumb,
|
||||
onNavigateParent: parentBreadcrumb ? onNavigateParent : null,
|
||||
renderSidebarToggle,
|
||||
detailProps: detailPanelProps,
|
||||
detailOpen: detailPanelOpen,
|
||||
});
|
||||
}, [
|
||||
documentsTableProps,
|
||||
parentBreadcrumb,
|
||||
onNavigateParent,
|
||||
renderSidebarToggle,
|
||||
detailPanelProps,
|
||||
detailPanelOpen,
|
||||
]);
|
||||
|
||||
const showPreviewWorkspace = workspaceMode !== 'skeuo' && Boolean(previewWorkspaceDocument);
|
||||
|
||||
const previewSurface = useMemo(() => {
|
||||
if (!showPreviewWorkspace || !previewWorkspaceDocument) {
|
||||
return null;
|
||||
}
|
||||
return createPreviewSurface({
|
||||
document: previewWorkspaceDocument,
|
||||
previewEntry: previewWorkspaceEntry,
|
||||
ensureAssetUrl,
|
||||
ensurePreviewData,
|
||||
getDocumentAsset,
|
||||
resolveApiPath,
|
||||
notifyApiError,
|
||||
onRegenerate: handleThumbnailRegeneration,
|
||||
onClose: closeDocumentPreview,
|
||||
renderSidebarToggle,
|
||||
});
|
||||
}, [
|
||||
showPreviewWorkspace,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
ensureAssetUrl,
|
||||
ensurePreviewData,
|
||||
getDocumentAsset,
|
||||
resolveApiPath,
|
||||
notifyApiError,
|
||||
handleThumbnailRegeneration,
|
||||
closeDocumentPreview,
|
||||
renderSidebarToggle,
|
||||
]);
|
||||
|
||||
const workspaceSurface = useMemo(() => {
|
||||
if (workspaceMode !== 'skeuo') {
|
||||
return null;
|
||||
}
|
||||
return createDesktopSurface({
|
||||
workspaceProps: skeuoWorkspaceProps,
|
||||
renderSidebarToggle,
|
||||
});
|
||||
}, [workspaceMode, skeuoWorkspaceProps, renderSidebarToggle]);
|
||||
|
||||
const surface = useMemo(() => {
|
||||
if (showPreviewWorkspace) {
|
||||
return previewSurface;
|
||||
}
|
||||
if (workspaceMode === 'skeuo') {
|
||||
return workspaceSurface;
|
||||
}
|
||||
return documentsSurface;
|
||||
}, [showPreviewWorkspace, workspaceMode, previewSurface, workspaceSurface, documentsSurface]);
|
||||
|
||||
return { surface };
|
||||
};
|
||||
Reference in New Issue
Block a user