detailpanel
This commit is contained in:
@@ -1,92 +1,99 @@
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { DownloadIcon, TextScanIcon, AnalyzeIcon, CloseIcon } from '../ui/icons';
|
||||
import { describeDocumentSummary } from '../documents/documentSummary';
|
||||
import DocumentSummarySection, {
|
||||
buildCorrespondentOptions,
|
||||
sortCorrespondents,
|
||||
} from '../documents/DocumentSummarySection';
|
||||
import { createDocumentActionState } from '../documents/documentActions';
|
||||
|
||||
const formatDateTime = (value) => {
|
||||
if (!value) {
|
||||
return '—';
|
||||
}
|
||||
const date = new Date(value);
|
||||
return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();
|
||||
};
|
||||
|
||||
const PreviewWorkspace = ({
|
||||
document,
|
||||
previewEntry,
|
||||
tagLookupById,
|
||||
tagOptions,
|
||||
onTagAdd,
|
||||
onTagRemove,
|
||||
correspondents,
|
||||
onCorrespondentAdd,
|
||||
onCorrespondentRemove,
|
||||
onUpdateTitle,
|
||||
onUpdateIssued,
|
||||
resolveFolderPath,
|
||||
onFolderNavigate,
|
||||
}) => {
|
||||
const sortedCorrespondents = useMemo(
|
||||
() => sortCorrespondents(document?.correspondents || []),
|
||||
[document],
|
||||
);
|
||||
|
||||
const correspondentOptions = useMemo(
|
||||
() => buildCorrespondentOptions(Array.isArray(correspondents) ? correspondents : []),
|
||||
[correspondents],
|
||||
);
|
||||
|
||||
const metadataItems = useMemo(() => {
|
||||
if (!document) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{ label: 'Created at', value: formatDateTime(document.created_at) },
|
||||
{ label: 'Updated at', value: formatDateTime(document.updated_at) },
|
||||
{
|
||||
label: 'Filename',
|
||||
value: document.archive_path || document.filename || '—',
|
||||
},
|
||||
{
|
||||
label: 'Original filename',
|
||||
value: document.original_name || '—',
|
||||
},
|
||||
{
|
||||
label: 'SHA-256 checksum',
|
||||
value: document.current_version?.checksum || '—',
|
||||
},
|
||||
{
|
||||
label: 'Content type',
|
||||
value: document.content_type || '—',
|
||||
},
|
||||
];
|
||||
}, [document]);
|
||||
|
||||
const metadataPayload = useMemo(() => {
|
||||
if (!document || !document.metadata || Object.keys(document.metadata).length === 0) {
|
||||
return null;
|
||||
}
|
||||
return document.metadata;
|
||||
}, [document]);
|
||||
|
||||
if (!document) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const title = document.title;
|
||||
const summary = describeDocumentSummary(document);
|
||||
const correspondents = Array.isArray(document.correspondents)
|
||||
? document.correspondents.map((entry) => entry?.name).filter(Boolean).join(', ')
|
||||
: '';
|
||||
const tags = Array.isArray(document.tags)
|
||||
? document.tags.map((tag) => tag?.label).filter(Boolean).join(', ')
|
||||
: '';
|
||||
|
||||
const formatDateTime = (value) => {
|
||||
if (!value) {
|
||||
return '—';
|
||||
}
|
||||
const date = new Date(value);
|
||||
return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();
|
||||
};
|
||||
|
||||
const detailItems = [
|
||||
{ label: 'Title', value: summary.title || '—' },
|
||||
{ label: 'Archive Reference', value: document.archive_serial || '—' },
|
||||
{ label: 'Issued On', value: formatDateTime(document.issued_at) },
|
||||
{ label: 'Correspondent', value: correspondents || '—' },
|
||||
{
|
||||
label: 'Filename',
|
||||
value: document.archive_path || document.filename || '—',
|
||||
},
|
||||
{
|
||||
label: 'Original Filename',
|
||||
value: document.original_name || '—',
|
||||
},
|
||||
{ label: 'Tags', value: tags || '—' },
|
||||
];
|
||||
|
||||
const metadataItems = [
|
||||
{ label: 'Modified At', value: formatDateTime(document.updated_at) },
|
||||
{ label: 'Created At', value: formatDateTime(document.created_at) },
|
||||
{
|
||||
label: 'Media Filename',
|
||||
value: document.current_version?.filename || document.archive_path || '—',
|
||||
},
|
||||
{
|
||||
label: 'SHA-256 Checksum',
|
||||
value: document.current_version?.checksum || '—',
|
||||
},
|
||||
{
|
||||
label: 'Original File Size',
|
||||
value: summary.sizeLabel,
|
||||
},
|
||||
{
|
||||
label: 'Original MIME Type',
|
||||
value: document.content_type || '—',
|
||||
},
|
||||
];
|
||||
const metadata =
|
||||
document.metadata && Object.keys(document.metadata).length > 0 ? document.metadata : null;
|
||||
|
||||
return (
|
||||
<section className="preview-workspace">
|
||||
<div className="preview-workspace__details">
|
||||
<section className="preview-section">
|
||||
<h3 className="preview-section__title">Details</h3>
|
||||
<dl className="preview-section__list">
|
||||
{detailItems.map(({ label, value }) => (
|
||||
<div className="preview-section__item" key={label}>
|
||||
<dt>{label}</dt>
|
||||
<dd>{value || '—'}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</section>
|
||||
<section className="preview-section">
|
||||
<h3 className="preview-section__title">Content</h3>
|
||||
<p className="preview-section__placeholder">
|
||||
Full OCR text will appear here in a future update. Use the toolbar button to open the OCR view for now.
|
||||
</p>
|
||||
</section>
|
||||
<DocumentSummarySection
|
||||
document={document}
|
||||
tagLookupById={tagLookupById}
|
||||
tagOptions={tagOptions}
|
||||
onTagAdd={onTagAdd}
|
||||
onTagRemove={onTagRemove}
|
||||
correspondents={sortedCorrespondents}
|
||||
correspondentOptions={correspondentOptions}
|
||||
onCorrespondentAdd={onCorrespondentAdd}
|
||||
onCorrespondentRemove={onCorrespondentRemove}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
onUpdateIssued={onUpdateIssued}
|
||||
resolveFolderPath={resolveFolderPath}
|
||||
onFolderNavigate={onFolderNavigate}
|
||||
/>
|
||||
<section className="preview-section">
|
||||
<h3 className="preview-section__title">Metadata</h3>
|
||||
<dl className="preview-section__list">
|
||||
@@ -97,25 +104,13 @@ const PreviewWorkspace = ({
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
{metadata ? (
|
||||
{metadataPayload ? (
|
||||
<details className="preview-section__payload">
|
||||
<summary>Show metadata payload</summary>
|
||||
<pre>{JSON.stringify(metadata, null, 2)}</pre>
|
||||
<pre>{JSON.stringify(metadataPayload, null, 2)}</pre>
|
||||
</details>
|
||||
) : null}
|
||||
</section>
|
||||
<section className="preview-section">
|
||||
<h3 className="preview-section__title">Notes</h3>
|
||||
<p className="preview-section__placeholder">Custom notes will be editable here once the feature lands.</p>
|
||||
</section>
|
||||
<section className="preview-section">
|
||||
<h3 className="preview-section__title">History</h3>
|
||||
<p className="preview-section__placeholder">Change history will be displayed here in an upcoming release.</p>
|
||||
</section>
|
||||
<section className="preview-section">
|
||||
<h3 className="preview-section__title">Permissions</h3>
|
||||
<p className="preview-section__placeholder">Access control management is planned and will surface here.</p>
|
||||
</section>
|
||||
</div>
|
||||
<div className="preview-workspace__viewer">
|
||||
{!previewEntry?.url ? (
|
||||
@@ -123,7 +118,7 @@ const PreviewWorkspace = ({
|
||||
) : (
|
||||
<iframe
|
||||
src={previewEntry.url}
|
||||
title={`Preview of ${title}`}
|
||||
title={`Preview of ${document.title}`}
|
||||
className="preview-workspace__object"
|
||||
/>
|
||||
)}
|
||||
@@ -208,6 +203,17 @@ export const createPreviewSurface = ({
|
||||
onRegenerate,
|
||||
onClose,
|
||||
renderSidebarToggle,
|
||||
tagLookupById,
|
||||
tagOptions,
|
||||
onTagAdd,
|
||||
onTagRemove,
|
||||
correspondents,
|
||||
onCorrespondentAdd,
|
||||
onCorrespondentRemove,
|
||||
onUpdateTitle,
|
||||
onUpdateIssued,
|
||||
resolveFolderPath,
|
||||
onFolderNavigate,
|
||||
}) => {
|
||||
if (!document) {
|
||||
return null;
|
||||
@@ -255,7 +261,23 @@ export const createPreviewSurface = ({
|
||||
key: 'preview',
|
||||
variant: 'preview',
|
||||
header,
|
||||
content: <PreviewWorkspace document={document} previewEntry={previewEntry} />,
|
||||
content: (
|
||||
<PreviewWorkspace
|
||||
document={document}
|
||||
previewEntry={previewEntry}
|
||||
tagLookupById={tagLookupById}
|
||||
tagOptions={tagOptions}
|
||||
onTagAdd={onTagAdd}
|
||||
onTagRemove={onTagRemove}
|
||||
correspondents={correspondents}
|
||||
onCorrespondentAdd={onCorrespondentAdd}
|
||||
onCorrespondentRemove={onCorrespondentRemove}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
onUpdateIssued={onUpdateIssued}
|
||||
resolveFolderPath={resolveFolderPath}
|
||||
onFolderNavigate={onFolderNavigate}
|
||||
/>
|
||||
),
|
||||
supportsDetail: false,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user