rename
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { ChevronsRightIcon } from '../ui/icons';
|
import { ChevronsRightIcon } from '../ui/icons';
|
||||||
import { createDocumentsSurface } from '../documents/DocumentsPanel';
|
import { createDocumentsSurface } from '../documents/DocumentsPanel';
|
||||||
import { createPreviewSurface } from '../preview/PreviewWorkspace';
|
import { createDocumentViewerSurface } from '../preview/DocumentViewerPanel';
|
||||||
import { createDesktopSurface } from '../DesktopWorkspace';
|
import { createDesktopSurface } from '../DesktopWorkspace';
|
||||||
|
|
||||||
export const useWorkspaceSurface = ({
|
export const useWorkspaceSurface = ({
|
||||||
@@ -83,7 +83,7 @@ export const useWorkspaceSurface = ({
|
|||||||
resolveFolderPath,
|
resolveFolderPath,
|
||||||
onFolderNavigate,
|
onFolderNavigate,
|
||||||
} = detailExtras;
|
} = detailExtras;
|
||||||
return createPreviewSurface({
|
return createDocumentViewerSurface({
|
||||||
documentId: previewDocumentId,
|
documentId: previewDocumentId,
|
||||||
document: previewWorkspaceDocument,
|
document: previewWorkspaceDocument,
|
||||||
previewEntry: previewWorkspaceEntry,
|
previewEntry: previewWorkspaceEntry,
|
||||||
|
|||||||
@@ -753,8 +753,6 @@ const DetailPanel = ({
|
|||||||
onCorrespondentRemove={onCorrespondentRemove}
|
onCorrespondentRemove={onCorrespondentRemove}
|
||||||
onUpdateTitle={onUpdateTitle}
|
onUpdateTitle={onUpdateTitle}
|
||||||
onUpdateIssued={onUpdateIssued}
|
onUpdateIssued={onUpdateIssued}
|
||||||
resolveFolderPath={resolveFolderPath}
|
|
||||||
onFolderNavigate={onFolderNavigate}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -244,10 +244,7 @@ const DocumentSummarySection = ({
|
|||||||
onCorrespondentAdd,
|
onCorrespondentAdd,
|
||||||
onCorrespondentRemove,
|
onCorrespondentRemove,
|
||||||
onUpdateTitle,
|
onUpdateTitle,
|
||||||
onUpdateIssued,
|
onUpdateIssued
|
||||||
resolveFolderPath,
|
|
||||||
onFolderNavigate,
|
|
||||||
showFolderPath = true,
|
|
||||||
}) => {
|
}) => {
|
||||||
const summary = useMemo(() => {
|
const summary = useMemo(() => {
|
||||||
if (!document) {
|
if (!document) {
|
||||||
@@ -283,74 +280,6 @@ const DocumentSummarySection = ({
|
|||||||
return sortCorrespondents(document?.correspondents || []);
|
return sortCorrespondents(document?.correspondents || []);
|
||||||
}, [correspondents, document?.correspondents]);
|
}, [correspondents, document?.correspondents]);
|
||||||
|
|
||||||
const folderPath = useMemo(() => {
|
|
||||||
if (
|
|
||||||
!showFolderPath
|
|
||||||
|| !document?.folder_id
|
|
||||||
|| typeof resolveFolderPath !== 'function'
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const segments = resolveFolderPath(document.folder_id);
|
|
||||||
const filtered = Array.isArray(segments)
|
|
||||||
? segments.filter((segment) => segment?.id && segment.id !== 'root')
|
|
||||||
: null;
|
|
||||||
if (!filtered || !filtered.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return filtered;
|
|
||||||
}, [document?.folder_id, resolveFolderPath, showFolderPath]);
|
|
||||||
|
|
||||||
const folderDisplayNode = useMemo(() => {
|
|
||||||
if (!folderPath || !folderPath.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<span className="detail-folder-path">
|
|
||||||
{folderPath.map((segment, index) => {
|
|
||||||
const label = segment?.name || '…';
|
|
||||||
const targetId = segment?.id || null;
|
|
||||||
const key = `${targetId || label}-${index}`;
|
|
||||||
const isClickable = Boolean(targetId) && typeof onFolderNavigate === 'function';
|
|
||||||
const href = !isClickable
|
|
||||||
? null
|
|
||||||
: targetId === 'root'
|
|
||||||
? '/documents'
|
|
||||||
: `/documents/folder/${targetId}`;
|
|
||||||
return (
|
|
||||||
<React.Fragment key={key}>
|
|
||||||
{index > 0 ? <span className="detail-folder-path__separator">/</span> : null}
|
|
||||||
{isClickable ? (
|
|
||||||
<a
|
|
||||||
href={href}
|
|
||||||
className="detail-folder-path__link"
|
|
||||||
onClick={(event) => {
|
|
||||||
if (
|
|
||||||
event.button !== 0 ||
|
|
||||||
event.metaKey ||
|
|
||||||
event.ctrlKey ||
|
|
||||||
event.shiftKey ||
|
|
||||||
event.altKey
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
onFolderNavigate(targetId);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<span className="detail-folder-path__segment">{label}</span>
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}, [folderPath, onFolderNavigate]);
|
|
||||||
|
|
||||||
const metaRows = useMemo(() => {
|
const metaRows = useMemo(() => {
|
||||||
const rows = [];
|
const rows = [];
|
||||||
const currentVersionNumber = document?.current_version?.version_number;
|
const currentVersionNumber = document?.current_version?.version_number;
|
||||||
@@ -526,11 +455,6 @@ const DocumentSummarySection = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{showFolderPath && folderDisplayNode ? (
|
|
||||||
<div className="doc-title-row__path" aria-label="Folder path">
|
|
||||||
{folderDisplayNode}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
{titleError ? <div className="status-inline error">{titleError}</div> : null}
|
{titleError ? <div className="status-inline error">{titleError}</div> : null}
|
||||||
|
|
||||||
|
|||||||
+44
-28
@@ -14,7 +14,7 @@ const formatDateTime = (value) => {
|
|||||||
return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();
|
return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const PreviewWorkspace = ({
|
const DocumentViewerPanel = ({
|
||||||
document,
|
document,
|
||||||
documentId,
|
documentId,
|
||||||
previewEntry,
|
previewEntry,
|
||||||
@@ -27,8 +27,6 @@ const PreviewWorkspace = ({
|
|||||||
onCorrespondentRemove,
|
onCorrespondentRemove,
|
||||||
onUpdateTitle,
|
onUpdateTitle,
|
||||||
onUpdateIssued,
|
onUpdateIssued,
|
||||||
resolveFolderPath,
|
|
||||||
onFolderNavigate,
|
|
||||||
}) => {
|
}) => {
|
||||||
const sortedCorrespondents = useMemo(
|
const sortedCorrespondents = useMemo(
|
||||||
() => sortCorrespondents(document?.correspondents || []),
|
() => sortCorrespondents(document?.correspondents || []),
|
||||||
@@ -66,6 +64,34 @@ const PreviewWorkspace = ({
|
|||||||
];
|
];
|
||||||
}, [document]);
|
}, [document]);
|
||||||
|
|
||||||
|
const contentType = (document?.content_type || '').toLowerCase();
|
||||||
|
const previewContent = useMemo(() => {
|
||||||
|
if (!previewEntry?.url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = document?.title || document?.original_name || 'Document';
|
||||||
|
|
||||||
|
if (contentType.startsWith('image/')) {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={previewEntry.url}
|
||||||
|
alt={`Preview of ${title}`}
|
||||||
|
className="document-viewer__object document-viewer__object--image"
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<iframe
|
||||||
|
src={previewEntry.url}
|
||||||
|
title={`Preview of ${title}`}
|
||||||
|
className="document-viewer__object"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}, [previewEntry?.url, contentType, document?.title, document?.original_name]);
|
||||||
|
|
||||||
const metadataPayload = useMemo(() => {
|
const metadataPayload = useMemo(() => {
|
||||||
if (!document || !document.metadata || Object.keys(document.metadata).length === 0) {
|
if (!document || !document.metadata || Object.keys(document.metadata).length === 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -75,22 +101,22 @@ const PreviewWorkspace = ({
|
|||||||
|
|
||||||
if (!document) {
|
if (!document) {
|
||||||
return (
|
return (
|
||||||
<section className="preview-workspace preview-workspace--loading">
|
<section className="document-viewer document-viewer--loading">
|
||||||
<div className="preview-workspace__details">
|
<div className="document-viewer__details">
|
||||||
<div className="preview-workspace__message">
|
<div className="document-viewer__message">
|
||||||
Loading document{documentId ? ` ${documentId}` : ''}…
|
Loading document{documentId ? ` ${documentId}` : ''}…
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="preview-workspace__viewer">
|
<div className="document-viewer__viewport">
|
||||||
<div className="preview-workspace__message">Preparing preview…</div>
|
<div className="document-viewer__message">Preparing preview…</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="preview-workspace">
|
<section className="document-viewer">
|
||||||
<div className="preview-workspace__details">
|
<div className="document-viewer__details">
|
||||||
<DocumentSummarySection
|
<DocumentSummarySection
|
||||||
document={document}
|
document={document}
|
||||||
tagLookupById={tagLookupById}
|
tagLookupById={tagLookupById}
|
||||||
@@ -103,8 +129,6 @@ const PreviewWorkspace = ({
|
|||||||
onCorrespondentRemove={onCorrespondentRemove}
|
onCorrespondentRemove={onCorrespondentRemove}
|
||||||
onUpdateTitle={onUpdateTitle}
|
onUpdateTitle={onUpdateTitle}
|
||||||
onUpdateIssued={onUpdateIssued}
|
onUpdateIssued={onUpdateIssued}
|
||||||
resolveFolderPath={resolveFolderPath}
|
|
||||||
onFolderNavigate={onFolderNavigate}
|
|
||||||
/>
|
/>
|
||||||
<section className="preview-section">
|
<section className="preview-section">
|
||||||
<h3 className="preview-section__title">Metadata</h3>
|
<h3 className="preview-section__title">Metadata</h3>
|
||||||
@@ -124,24 +148,20 @@ const PreviewWorkspace = ({
|
|||||||
) : null}
|
) : null}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div className="preview-workspace__viewer">
|
<div className="document-viewer__viewport">
|
||||||
{!previewEntry?.url ? (
|
{!previewEntry?.url ? (
|
||||||
<div className="preview-workspace__message">Loading preview…</div>
|
<div className="document-viewer__message">Loading preview…</div>
|
||||||
) : (
|
) : (
|
||||||
<iframe
|
previewContent
|
||||||
src={previewEntry.url}
|
|
||||||
title={`Preview of ${document.title}`}
|
|
||||||
className="preview-workspace__object"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PreviewWorkspace;
|
export default DocumentViewerPanel;
|
||||||
|
|
||||||
export const createPreviewWorkspaceHeaderActions = ({
|
export const createDocumentViewerHeaderActions = ({
|
||||||
document,
|
document,
|
||||||
ensureAssetUrl,
|
ensureAssetUrl,
|
||||||
ensurePreviewData,
|
ensurePreviewData,
|
||||||
@@ -204,7 +224,7 @@ export const createPreviewWorkspaceHeaderActions = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createPreviewSurface = ({
|
export const createDocumentViewerSurface = ({
|
||||||
documentId,
|
documentId,
|
||||||
document,
|
document,
|
||||||
previewEntry,
|
previewEntry,
|
||||||
@@ -226,7 +246,6 @@ export const createPreviewSurface = ({
|
|||||||
onUpdateTitle,
|
onUpdateTitle,
|
||||||
onUpdateIssued,
|
onUpdateIssued,
|
||||||
resolveFolderPath,
|
resolveFolderPath,
|
||||||
onFolderNavigate,
|
|
||||||
}) => {
|
}) => {
|
||||||
if (!documentId && !document) {
|
if (!documentId && !document) {
|
||||||
return null;
|
return null;
|
||||||
@@ -274,7 +293,7 @@ export const createPreviewSurface = ({
|
|||||||
title,
|
title,
|
||||||
subtitle: null,
|
subtitle: null,
|
||||||
leading,
|
leading,
|
||||||
actions: createPreviewWorkspaceHeaderActions({
|
actions: createDocumentViewerHeaderActions({
|
||||||
document,
|
document,
|
||||||
ensureAssetUrl,
|
ensureAssetUrl,
|
||||||
ensurePreviewData,
|
ensurePreviewData,
|
||||||
@@ -291,7 +310,7 @@ export const createPreviewSurface = ({
|
|||||||
variant: 'preview',
|
variant: 'preview',
|
||||||
header,
|
header,
|
||||||
content: (
|
content: (
|
||||||
<PreviewWorkspace
|
<DocumentViewerPanel
|
||||||
document={document}
|
document={document}
|
||||||
documentId={documentId}
|
documentId={documentId}
|
||||||
previewEntry={previewEntry}
|
previewEntry={previewEntry}
|
||||||
@@ -304,9 +323,6 @@ export const createPreviewSurface = ({
|
|||||||
onCorrespondentRemove={onCorrespondentRemove}
|
onCorrespondentRemove={onCorrespondentRemove}
|
||||||
onUpdateTitle={onUpdateTitle}
|
onUpdateTitle={onUpdateTitle}
|
||||||
onUpdateIssued={onUpdateIssued}
|
onUpdateIssued={onUpdateIssued}
|
||||||
resolveFolderPath={resolveFolderPath}
|
|
||||||
onFolderNavigate={onFolderNavigate}
|
|
||||||
showFolderPath={false}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
supportsDetail: false,
|
supportsDetail: false,
|
||||||
@@ -44,7 +44,7 @@ const DocumentViewerRoute = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!previewWorkspaceDocument || previewWorkspaceDocument.id !== documentId) {
|
if (!previewWorkspaceDocument || previewWorkspaceDocument.id !== documentId) {
|
||||||
return <div className="preview-workspace__message">Loading preview…</div>;
|
return <div className="document-viewer__message">Loading preview…</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Navigate to="/documents" replace />;
|
return <Navigate to="/documents" replace />;
|
||||||
|
|||||||
+17
-8
@@ -752,13 +752,13 @@ button.danger:hover:not([disabled]) {
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-workspace {
|
.document-viewer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 30em) minmax(0, 1fr);
|
grid-template-columns: minmax(0, 30em) minmax(0, 1fr);
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.document-drag-preview {
|
.document-drag-preview {
|
||||||
@@ -859,7 +859,7 @@ button.danger:hover:not([disabled]) {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-workspace__details {
|
.document-viewer__details {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
@@ -937,23 +937,32 @@ button.danger:hover:not([disabled]) {
|
|||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-workspace__viewer {
|
.document-viewer__viewport {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
background: var(--surface-subtle);
|
|
||||||
box-shadow: inset 0 0 0 1px var(--outline-subtle);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-workspace__object {
|
.document-viewer__object {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.document-viewer__object--image {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
background: var(--surface);
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.tags-panel__body {
|
.tags-panel__body {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
@@ -1128,7 +1137,7 @@ button.danger:hover:not([disabled]) {
|
|||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-workspace__message {
|
.document-viewer__message {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
|||||||
Reference in New Issue
Block a user