folder in summary
This commit is contained in:
@@ -4,6 +4,7 @@ import { SidebarExpandIcon } from '../ui/icons';
|
||||
import DocumentsPanel from '../documents/panel/DocumentsPanel';
|
||||
import DocumentViewerPanel from '../preview/DocumentViewerPanel';
|
||||
import { usePanelManager } from './PanelManagerContext';
|
||||
import { FolderManagerProvider } from '../folders/FolderManagerContext';
|
||||
|
||||
type Identifier = string | number;
|
||||
|
||||
@@ -91,8 +92,15 @@ export const useWorkspaceSurface = ({
|
||||
|
||||
const detail = detailPanelOpen && detailPanelProps
|
||||
? (() => {
|
||||
const { onClose, onOpenPreview, tags: tagOptions, ...restDetailProps } = detailPanelProps;
|
||||
return (
|
||||
const {
|
||||
onClose,
|
||||
onOpenPreview,
|
||||
tags: tagOptions,
|
||||
folderNodes,
|
||||
ensureFolderData,
|
||||
...restDetailProps
|
||||
} = detailPanelProps;
|
||||
const viewer = (
|
||||
<DocumentViewerPanel
|
||||
variant="sidebar"
|
||||
onCollapsePanel={onClose}
|
||||
@@ -101,6 +109,16 @@ export const useWorkspaceSurface = ({
|
||||
{...restDetailProps}
|
||||
/>
|
||||
);
|
||||
if (folderNodes && ensureFolderData) {
|
||||
return (
|
||||
<FolderManagerProvider folderNodes={folderNodes} ensureFolderData={ensureFolderData}>
|
||||
{viewer}
|
||||
</FolderManagerProvider>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>{viewer}</>
|
||||
);
|
||||
})()
|
||||
: null;
|
||||
|
||||
@@ -140,35 +158,44 @@ export const useWorkspaceSurface = ({
|
||||
onUpdateTitle,
|
||||
onUpdateIssued,
|
||||
resolveFolderPath,
|
||||
folderNodes,
|
||||
ensureFolderData,
|
||||
} = detailExtras;
|
||||
|
||||
return {
|
||||
content: (
|
||||
<DocumentViewerPanel
|
||||
document={previewWorkspaceDocument || null}
|
||||
documentLink={documentLink}
|
||||
hydrateDocument={ensurePreviewData}
|
||||
tagLookupById={tagLookupById}
|
||||
tagOptions={tagOptions}
|
||||
onTagAdd={onTagAdd}
|
||||
onTagRemove={onTagRemove}
|
||||
correspondents={correspondents}
|
||||
onCorrespondentAdd={onCorrespondentAdd}
|
||||
onCorrespondentRemove={onCorrespondentRemove}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
onUpdateIssued={onUpdateIssued}
|
||||
ensureAssetUrl={ensureAssetUrl}
|
||||
getDocumentAsset={getDocumentAsset}
|
||||
ensurePreviewData={ensurePreviewData}
|
||||
resolveApiPath={resolveApiPath}
|
||||
notifyApiError={notifyApiError}
|
||||
sidebarToggle={sidebarToggle}
|
||||
onClosePanel={closeDocumentPreview}
|
||||
resolveFolderPath={resolveFolderPath}
|
||||
/>
|
||||
),
|
||||
detail: null,
|
||||
};
|
||||
const viewer = (
|
||||
<DocumentViewerPanel
|
||||
document={previewWorkspaceDocument || null}
|
||||
documentLink={documentLink}
|
||||
hydrateDocument={ensurePreviewData}
|
||||
tagLookupById={tagLookupById}
|
||||
tagOptions={tagOptions}
|
||||
onTagAdd={onTagAdd}
|
||||
onTagRemove={onTagRemove}
|
||||
correspondents={correspondents}
|
||||
onCorrespondentAdd={onCorrespondentAdd}
|
||||
onCorrespondentRemove={onCorrespondentRemove}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
onUpdateIssued={onUpdateIssued}
|
||||
ensureAssetUrl={ensureAssetUrl}
|
||||
getDocumentAsset={getDocumentAsset}
|
||||
ensurePreviewData={ensurePreviewData}
|
||||
resolveApiPath={resolveApiPath}
|
||||
notifyApiError={notifyApiError}
|
||||
sidebarToggle={sidebarToggle}
|
||||
onClosePanel={closeDocumentPreview}
|
||||
resolveFolderPath={resolveFolderPath}
|
||||
/>
|
||||
);
|
||||
|
||||
const content = folderNodes && ensureFolderData
|
||||
? (
|
||||
<FolderManagerProvider folderNodes={folderNodes} ensureFolderData={ensureFolderData}>
|
||||
{viewer}
|
||||
</FolderManagerProvider>
|
||||
)
|
||||
: viewer;
|
||||
|
||||
return { content, detail: null };
|
||||
}, [
|
||||
showPreviewWorkspace,
|
||||
previewWorkspaceDocument,
|
||||
|
||||
@@ -25,11 +25,6 @@ interface FolderNode {
|
||||
parentId?: Identifier | 'root';
|
||||
}
|
||||
|
||||
type DocumentLink = {
|
||||
url?: string;
|
||||
contentType?: string | null;
|
||||
} | null;
|
||||
|
||||
interface UseDetailWorkspaceArgs {
|
||||
documents: DocumentLike[];
|
||||
selectionOrder: string[];
|
||||
@@ -39,7 +34,6 @@ interface UseDetailWorkspaceArgs {
|
||||
ensureFolderData: (folderId: Identifier | 'root', options?: { force?: boolean; includeDocuments?: boolean }) => Promise<void>;
|
||||
detailPanelControlRef: MutableRefObject<{ open?: (args?: { documentIds?: Identifier[] }) => void; close?: () => void } | null>;
|
||||
detailFolderFetchRef: MutableRefObject<Set<Identifier | 'root'>>;
|
||||
documentLinks: Map<Identifier, DocumentLink>;
|
||||
previewDocumentId?: Identifier | null;
|
||||
activePreviewId?: Identifier | null;
|
||||
openDocumentPreview?: (args: { documentIds: Identifier[] }) => void;
|
||||
@@ -67,7 +61,6 @@ interface UseDetailWorkspaceResult {
|
||||
inspectDocument: (docId: Identifier | null) => void;
|
||||
previewActive: boolean;
|
||||
previewWorkspaceDocument: DocumentLike | null;
|
||||
documentLink: DocumentLink;
|
||||
resolveThumbnailUrlForDoc: (doc: DocumentLike | null) => string | null;
|
||||
resolveFolderPath: (folderId?: Identifier | 'root') => Array<{ id: Identifier | 'root'; name: string }>;
|
||||
}
|
||||
@@ -81,7 +74,6 @@ const useDetailWorkspace = ({
|
||||
ensureFolderData,
|
||||
detailPanelControlRef,
|
||||
detailFolderFetchRef,
|
||||
documentLinks,
|
||||
previewDocumentId,
|
||||
activePreviewId,
|
||||
openDocumentPreview,
|
||||
@@ -239,11 +231,6 @@ const useDetailWorkspace = ({
|
||||
[folderNodes],
|
||||
);
|
||||
|
||||
const documentLink = useMemo(
|
||||
() => (detailPanelDocument ? documentLinks.get(detailPanelDocument.id) || null : null),
|
||||
[detailPanelDocument, documentLinks],
|
||||
);
|
||||
|
||||
const previewWorkspaceDocument = useMemo(() => {
|
||||
if (!previewDocumentId) {
|
||||
return null;
|
||||
@@ -285,7 +272,6 @@ const useDetailWorkspace = ({
|
||||
tagLookupById,
|
||||
onTagAdd: handleDocumentTagAdd,
|
||||
onTagRemove: handleTagRemove,
|
||||
documentLink,
|
||||
onOpenPreview: openDocumentPreview,
|
||||
activePreviewId,
|
||||
onUpdateTitle: handleDocumentTitleUpdate,
|
||||
@@ -299,6 +285,8 @@ const useDetailWorkspace = ({
|
||||
onFolderNavigate: selectFolder,
|
||||
onClose: handleDetailPanelClose,
|
||||
resolveFolderPath,
|
||||
folderNodes,
|
||||
ensureFolderData,
|
||||
}),
|
||||
[
|
||||
activePreviewId,
|
||||
@@ -313,11 +301,12 @@ const useDetailWorkspace = ({
|
||||
handleDocumentIssuedUpdate,
|
||||
handleDocumentTitleUpdate,
|
||||
handleTagRemove,
|
||||
folderNodes,
|
||||
ensureFolderData,
|
||||
openDocumentPreview,
|
||||
resolveApiPath,
|
||||
resolveFolderPath,
|
||||
selectFolder,
|
||||
documentLink,
|
||||
tags,
|
||||
tagLookupById,
|
||||
],
|
||||
@@ -332,7 +321,6 @@ const useDetailWorkspace = ({
|
||||
inspectDocument,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
documentLink,
|
||||
resolveThumbnailUrlForDoc,
|
||||
resolveFolderPath,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState, type FormEvent } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { EditIcon, IconX, CheckIcon, PlusIcon } from '../ui/icons';
|
||||
import SelectionAssignmentMenu, {
|
||||
SelectionAssignmentMenuItem,
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
} from '../utils/date';
|
||||
import { describeDocumentSummary, type DocumentSummaryRow } from './documentSummary';
|
||||
import { isPlainObject } from '../utils/typeGuards';
|
||||
import { useFolderManager } from '../folders/FolderManagerContext';
|
||||
|
||||
type Identifier = string | number;
|
||||
|
||||
@@ -31,6 +33,7 @@ interface DocumentLike {
|
||||
id?: Identifier;
|
||||
title?: string;
|
||||
issued_at?: string | null;
|
||||
folder_id?: string | null;
|
||||
current_version?: { version_number?: number } | null;
|
||||
tags?: TagEntry[];
|
||||
correspondents?: CorrespondentEntry[];
|
||||
@@ -71,6 +74,7 @@ export interface DocumentSummarySectionProps {
|
||||
onCorrespondentRemove?: (payload: { documentId: Identifier | undefined; correspondentId: Identifier | undefined }) => void;
|
||||
onUpdateTitle?: (docId: Identifier | undefined, title: string) => Promise<boolean> | boolean;
|
||||
onUpdateIssued?: (docId: Identifier | undefined, timestamp: number | null) => Promise<boolean> | boolean;
|
||||
onFolderNavigate?: (folderId: string | null) => void;
|
||||
layout?: 'default' | 'compact';
|
||||
}
|
||||
|
||||
@@ -456,8 +460,10 @@ const DocumentSummarySection: React.FC<DocumentSummarySectionProps> = ({
|
||||
onCorrespondentRemove,
|
||||
onUpdateTitle,
|
||||
onUpdateIssued,
|
||||
onFolderNavigate,
|
||||
layout = 'default',
|
||||
}) => {
|
||||
const folderManager = useFolderManager();
|
||||
const isCompactLayout = layout === 'compact';
|
||||
const summaryRows = useMemo(() => describeDocumentSummary(document), [document]);
|
||||
const issuedDateLabel = useMemo(
|
||||
@@ -499,6 +505,39 @@ const DocumentSummarySection: React.FC<DocumentSummarySectionProps> = ({
|
||||
return rows;
|
||||
}, [document?.current_version?.version_number]);
|
||||
|
||||
const resolvedFolderId = document?.folder_id ?? null;
|
||||
|
||||
const [folderName, setFolderName] = useState<string | null>(() => folderManager.getNameSync(resolvedFolderId));
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
const cached = folderManager.getNameSync(resolvedFolderId);
|
||||
setFolderName(cached);
|
||||
if (!cached && resolvedFolderId != null) {
|
||||
folderManager.resolveName(resolvedFolderId).then((name) => {
|
||||
if (active) {
|
||||
setFolderName(name);
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, [resolvedFolderId, folderManager]);
|
||||
|
||||
const folderHref = resolvedFolderId == null ? '/documents' : `/documents/folder/${resolvedFolderId}`;
|
||||
|
||||
const handleFolderClick = useCallback(
|
||||
(event: React.MouseEvent) => {
|
||||
if (!onFolderNavigate) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
onFolderNavigate(resolvedFolderId);
|
||||
},
|
||||
[onFolderNavigate, resolvedFolderId],
|
||||
);
|
||||
|
||||
const [titleDraft, setTitleDraft] = useState('');
|
||||
const [titleSaving, setTitleSaving] = useState(false);
|
||||
const [titleError, setTitleError] = useState(null);
|
||||
@@ -741,11 +780,22 @@ const DocumentSummarySection: React.FC<DocumentSummarySectionProps> = ({
|
||||
/>
|
||||
);
|
||||
|
||||
const folderValueContent = (
|
||||
<Link
|
||||
className="document-summary__folder-link"
|
||||
to={folderHref}
|
||||
onClick={handleFolderClick}
|
||||
>
|
||||
{folderName}
|
||||
</Link>
|
||||
);
|
||||
|
||||
const summaryRowOverrides = {
|
||||
title: { valueContent: titleMetaDisplay, error: titleError },
|
||||
issued: { valueContent: issuedDisplay, error: issuedError },
|
||||
tags: { valueContent: tagsValueContent },
|
||||
correspondents: { valueContent: correspondentsValueContent },
|
||||
folder: { valueContent: folderValueContent },
|
||||
} as Record<string, { valueContent?: React.ReactNode | null; error?: string | null }>;
|
||||
|
||||
const baseRows: MetaItem[] = [...summaryRows, ...extraSummaryRows].map((row) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { formatFileSize } from '../utils/format';
|
||||
import { formatDateTime as defaultFormatDateTime } from '../utils/date';
|
||||
import { DEFAULT_FOLDER_NAME } from '../app/appLayoutUtils';
|
||||
|
||||
interface DocumentPageMetadata {
|
||||
page_count?: number | string | null;
|
||||
@@ -24,11 +25,13 @@ export interface SummaryDocument {
|
||||
original_name?: string | null;
|
||||
filename?: string | null;
|
||||
content_type?: string | null;
|
||||
folder_id?: string | null;
|
||||
folder_name?: string;
|
||||
current_version?: DocumentVersion | null;
|
||||
created_at?: string | null;
|
||||
updated_at?: string | null;
|
||||
issued_at?: string | null;
|
||||
folder_path?: string | null;
|
||||
folder_path?: string;
|
||||
tags?: TagEntry[] | null;
|
||||
correspondents?: CorrespondentEntry[] | null;
|
||||
}
|
||||
@@ -37,7 +40,7 @@ interface DescribeSummaryOptions {
|
||||
formatDateTime?: typeof defaultFormatDateTime;
|
||||
}
|
||||
|
||||
export type DocumentSummaryRowType = 'text' | 'editable-title' | 'editable-issued' | 'tags' | 'correspondents';
|
||||
export type DocumentSummaryRowType = 'text' | 'editable-title' | 'editable-issued' | 'tags' | 'correspondents' | 'folder';
|
||||
|
||||
export interface DocumentSummaryRow {
|
||||
key: string;
|
||||
@@ -86,6 +89,7 @@ export const describeDocumentSummary = (document?: SummaryDocument | null, optio
|
||||
const metadata = doc.current_version?.metadata || null;
|
||||
const pageCount = coercePageCount(metadata);
|
||||
const pageCountLabel = Number.isFinite(pageCount) ? String(pageCount) : '—';
|
||||
const folderLabel = doc.folder_id == null ? DEFAULT_FOLDER_NAME : `Folder ${doc.folder_id}`;
|
||||
const tags = sanitizeArray<TagEntry>(doc.tags);
|
||||
const correspondents = sanitizeArray<CorrespondentEntry>(doc.correspondents);
|
||||
const tagLabels = tags.map((tag) => tag.label).filter(Boolean) as string[];
|
||||
@@ -99,6 +103,7 @@ export const describeDocumentSummary = (document?: SummaryDocument | null, optio
|
||||
{ key: 'issued', label: 'Issued', value: formatDateLabel(doc.issued_at), kind: 'editable-issued' },
|
||||
{ key: 'created', label: 'Created at', value: formatDateLabel(doc.created_at) },
|
||||
{ key: 'updated', label: 'Updated at', value: formatDateLabel(doc.updated_at) },
|
||||
{ key: 'folder', label: 'Folder', value: folderLabel, kind: 'folder' },
|
||||
{ key: 'size', label: 'Size', value: sizeLabel },
|
||||
{ key: 'content-type', label: 'Content type', value: doc.content_type || 'Unknown' },
|
||||
{ key: 'pages', label: 'Pages', value: pageCountLabel },
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import React, { createContext, useContext, useMemo, type ReactNode } from 'react';
|
||||
import { DEFAULT_FOLDER_NAME } from '../app/appLayoutUtils';
|
||||
|
||||
type FolderId = string | null;
|
||||
|
||||
export interface FolderManager {
|
||||
getNameSync: (folderId: FolderId) => string | null;
|
||||
resolveName: (folderId: FolderId) => Promise<string>;
|
||||
}
|
||||
|
||||
const defaultManager: FolderManager = {
|
||||
getNameSync: (folderId) => (folderId == null ? DEFAULT_FOLDER_NAME : `Folder ${folderId}`),
|
||||
resolveName: async (folderId) => (folderId == null ? DEFAULT_FOLDER_NAME : `Folder ${folderId}`),
|
||||
};
|
||||
|
||||
const FolderManagerContext = createContext<FolderManager>(defaultManager);
|
||||
|
||||
interface FolderManagerProviderProps {
|
||||
folderNodes?: Map<string | 'root', { name?: string | null }>;
|
||||
ensureFolderData?: (folderId: string | 'root', options?: { force?: boolean; includeDocuments?: boolean }) => Promise<void>;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const FolderManagerProvider: React.FC<FolderManagerProviderProps> = ({
|
||||
folderNodes,
|
||||
ensureFolderData,
|
||||
children,
|
||||
}) => {
|
||||
const value = useMemo<FolderManager>(() => {
|
||||
if (!folderNodes || !ensureFolderData) {
|
||||
return defaultManager;
|
||||
}
|
||||
|
||||
const getNameSync = (folderId: FolderId) => {
|
||||
if (folderId == null) return DEFAULT_FOLDER_NAME;
|
||||
return folderNodes.get(folderId)?.name ?? null;
|
||||
};
|
||||
|
||||
const resolveName = async (folderId: FolderId) => {
|
||||
const cached = getNameSync(folderId);
|
||||
if (cached) return cached;
|
||||
if (folderId == null) return DEFAULT_FOLDER_NAME;
|
||||
await ensureFolderData(folderId, { includeDocuments: false });
|
||||
return getNameSync(folderId) ?? `Folder ${folderId}`;
|
||||
};
|
||||
|
||||
return { getNameSync, resolveName };
|
||||
}, [folderNodes, ensureFolderData]);
|
||||
|
||||
return (
|
||||
<FolderManagerContext.Provider value={value}>
|
||||
{children}
|
||||
</FolderManagerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useFolderManager = (): FolderManager => useContext(FolderManagerContext);
|
||||
|
||||
export default FolderManagerContext;
|
||||
@@ -1233,7 +1233,6 @@ const useDocumentsWorkspace = ({
|
||||
inspectDocument,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
documentLink,
|
||||
resolveFolderPath,
|
||||
} = useDetailWorkspace({
|
||||
documents: viewDocuments,
|
||||
@@ -1244,7 +1243,6 @@ const useDocumentsWorkspace = ({
|
||||
ensureFolderData,
|
||||
detailPanelControlRef,
|
||||
detailFolderFetchRef,
|
||||
documentLinks,
|
||||
previewDocumentId,
|
||||
activePreviewId,
|
||||
openDocumentPreview: openDocumentPreviewForDetail,
|
||||
@@ -1599,7 +1597,6 @@ const useDocumentsWorkspace = ({
|
||||
revokePasskey,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
documentLink,
|
||||
previewDocumentId,
|
||||
closeDocumentPreview,
|
||||
handleThumbnailRegeneration,
|
||||
@@ -1650,7 +1647,6 @@ const useDocumentsWorkspace = ({
|
||||
revokePasskey,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
documentLink,
|
||||
previewDocumentId,
|
||||
closeDocumentPreview,
|
||||
handleThumbnailRegeneration,
|
||||
|
||||
@@ -34,6 +34,7 @@ interface DocumentLike {
|
||||
title?: string;
|
||||
content_type?: string | null;
|
||||
issued_at?: string | null;
|
||||
folder_id?: string | null;
|
||||
correspondents?: Array<{ id?: string | number; name?: string }>;
|
||||
current_version?: {
|
||||
version_number?: number;
|
||||
@@ -56,11 +57,6 @@ interface AssetLike {
|
||||
|
||||
interface DocumentViewerPanelProps extends DocumentSummarySectionProps {
|
||||
document: DocumentLike | null;
|
||||
documentLink?: {
|
||||
url?: string;
|
||||
contentType?: string | null;
|
||||
filename?: string | null;
|
||||
} | null;
|
||||
ensureAssetUrl?: (docId: string | number, asset: AssetLike, options?: { force?: boolean }) => Promise<unknown>;
|
||||
getDocumentAsset?: (doc: DocumentLike | null, type: string) => AssetLike | null;
|
||||
ensurePreviewData?: (docId: string | number, options?: { signal?: AbortSignal }) => Promise<DocumentLike | null>;
|
||||
@@ -78,7 +74,6 @@ interface DocumentViewerPanelProps extends DocumentSummarySectionProps {
|
||||
export const createDocumentViewerHeaderActions = ({
|
||||
document,
|
||||
actionState,
|
||||
documentLink,
|
||||
onZoom,
|
||||
canZoom = false,
|
||||
}) => {
|
||||
@@ -86,7 +81,7 @@ export const createDocumentViewerHeaderActions = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const downloadHref = actionState?.downloadHref || documentLink?.url;
|
||||
const downloadHref = actionState?.downloadHref;
|
||||
if (!downloadHref && !(canZoom && onZoom)) {
|
||||
return null;
|
||||
}
|
||||
@@ -122,7 +117,6 @@ export const createDocumentViewerHeaderActions = ({
|
||||
|
||||
const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
document,
|
||||
documentLink,
|
||||
tagLookupById,
|
||||
tagOptions,
|
||||
onTagAdd,
|
||||
@@ -168,6 +162,16 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
return Boolean(getDocumentAsset(document, 'ocr-text'));
|
||||
}, [document, getDocumentAsset]);
|
||||
|
||||
const navigateToFolder = useCallback(
|
||||
(folderId) => {
|
||||
const target = folderId == null
|
||||
? '/documents'
|
||||
: `/documents/folder/${folderId}`;
|
||||
navigate(target);
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
|
||||
const summaryProps = useMemo(
|
||||
() => ({
|
||||
tagLookupById,
|
||||
@@ -180,6 +184,7 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
onCorrespondentRemove,
|
||||
onUpdateTitle,
|
||||
onUpdateIssued,
|
||||
onFolderNavigate: navigateToFolder,
|
||||
}),
|
||||
[
|
||||
tagLookupById,
|
||||
@@ -192,6 +197,7 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
onCorrespondentRemove,
|
||||
onUpdateTitle,
|
||||
onUpdateIssued,
|
||||
navigateToFolder,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -251,7 +257,7 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
|
||||
const [zoomOverlayOpen, setZoomOverlayOpen] = useState(false);
|
||||
|
||||
const fallbackDocumentLink = useMemo(() => {
|
||||
const resolvedDocumentLink = useMemo(() => {
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
@@ -272,14 +278,12 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
};
|
||||
}, [document, resolveApiPath]);
|
||||
|
||||
const effectiveDocumentLink = documentLink?.url ? documentLink : fallbackDocumentLink;
|
||||
|
||||
const handleZoomOpen = useCallback(() => {
|
||||
if (!effectiveDocumentLink?.url) {
|
||||
if (!resolvedDocumentLink?.url) {
|
||||
return;
|
||||
}
|
||||
setZoomOverlayOpen(true);
|
||||
}, [effectiveDocumentLink?.url]);
|
||||
}, [resolvedDocumentLink?.url]);
|
||||
|
||||
const handleZoomClose = useCallback(() => {
|
||||
setZoomOverlayOpen(false);
|
||||
@@ -287,7 +291,7 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
setZoomOverlayOpen(false);
|
||||
}, [effectiveDocumentLink?.url, document?.id]);
|
||||
}, [resolvedDocumentLink?.url, document?.id]);
|
||||
|
||||
const panelRef = useRef<HTMLDivElement | HTMLFormElement | HTMLElement | null>(null);
|
||||
const isStackedLayout = useViewerLayoutMode(panelRef, document?.id);
|
||||
@@ -344,19 +348,6 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
];
|
||||
}, [document, resolveFolderPath]);
|
||||
|
||||
const handleBreadcrumbNavigate = useCallback(
|
||||
(crumb) => {
|
||||
if (!crumb?.id) {
|
||||
return;
|
||||
}
|
||||
const target = crumb.id === 'root'
|
||||
? '/documents'
|
||||
: `/documents/folder/${crumb.id}`;
|
||||
navigate(target);
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
|
||||
const breadcrumbTrailEntries = useMemo(() => {
|
||||
if (!breadcrumbs.length) {
|
||||
return [];
|
||||
@@ -365,28 +356,27 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
return breadcrumbs.map((crumb, index) => ({
|
||||
id: crumb.id,
|
||||
label: crumb.name,
|
||||
onClick: index < lastIndex ? () => handleBreadcrumbNavigate(crumb) : null,
|
||||
onClick: index < lastIndex ? () => navigateToFolder(crumb.id) : null,
|
||||
}));
|
||||
}, [breadcrumbs, handleBreadcrumbNavigate]);
|
||||
}, [breadcrumbs, navigateToFolder]);
|
||||
|
||||
const zoomDisplay = useMemo(() => {
|
||||
if (!effectiveDocumentLink?.url || !document) {
|
||||
if (!resolvedDocumentLink?.url || !document) {
|
||||
return null;
|
||||
}
|
||||
const docContentType = document.content_type;
|
||||
const versionContentType = document.current_version?.version?.content_type;
|
||||
const normalizedContentType = effectiveDocumentLink.contentType || docContentType || versionContentType || null;
|
||||
const normalizedContentType = resolvedDocumentLink.contentType || docContentType || versionContentType || null;
|
||||
return {
|
||||
url: effectiveDocumentLink.url,
|
||||
url: resolvedDocumentLink.url,
|
||||
alt: document.title,
|
||||
contentType: normalizedContentType || undefined,
|
||||
};
|
||||
}, [effectiveDocumentLink?.url, effectiveDocumentLink?.contentType, document]);
|
||||
}, [resolvedDocumentLink?.url, resolvedDocumentLink?.contentType, document]);
|
||||
|
||||
const headerActions = createDocumentViewerHeaderActions({
|
||||
document,
|
||||
actionState,
|
||||
documentLink: effectiveDocumentLink,
|
||||
onZoom: zoomDisplay ? handleZoomOpen : null,
|
||||
canZoom: Boolean(zoomDisplay),
|
||||
});
|
||||
@@ -486,7 +476,7 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
|
||||
<section className={viewerClassName}>
|
||||
<DocumentViewerLayout
|
||||
document={document}
|
||||
documentLink={effectiveDocumentLink}
|
||||
documentLink={resolvedDocumentLink}
|
||||
summaryProps={summaryProps}
|
||||
metadataPayload={metadataPayload}
|
||||
contentTabConfig={contentTabConfig}
|
||||
|
||||
Reference in New Issue
Block a user