This commit is contained in:
2025-10-28 01:18:35 +01:00
parent 1179f4fcd4
commit 7df9a6415a
17 changed files with 935 additions and 935 deletions
+33 -73
View File
@@ -14,13 +14,10 @@ import { getTagColorStyle } from '../utils/colors';
import { formatFileSize } from '../utils/format';
import { resolveDocumentAssetUrl, createAssetView } from '../asset_manager';
import { useAssetNavigator } from '../hooks/useAssetNavigator';
import { CORRESPONDENT_ROLES } from '../constants/correspondents';
import PreviewZoomOverlay from './PreviewZoomOverlay';
const MAX_PREVIEW_STACK_ITEMS = 15;
const normalizeRole = (role) => (role || '').toLowerCase();
const derivePreviewOrientation = (metadata) => {
const width = Number(metadata?.width);
const height = Number(metadata?.height);
@@ -30,56 +27,41 @@ const derivePreviewOrientation = (metadata) => {
return 'landscape';
};
const formatRoleLabel = (role) => {
const normalized = normalizeRole(role);
if (!normalized) return 'Other';
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
};
const compareCorrespondents = (a, b) => {
const indexA = CORRESPONDENT_ROLES.indexOf(a.role);
const indexB = CORRESPONDENT_ROLES.indexOf(b.role);
const rankedA = indexA === -1 ? Number.MAX_SAFE_INTEGER : indexA;
const rankedB = indexB === -1 ? Number.MAX_SAFE_INTEGER : indexB;
if (rankedA !== rankedB) {
return rankedA - rankedB;
}
return (a.name || '').localeCompare(b.name || '');
};
const sortCorrespondents = (entries = []) =>
entries
.map((entry) => ({
id: entry.id,
name: entry.name || '',
role: normalizeRole(entry.role),
count: entry.count,
}))
.sort(compareCorrespondents);
.sort((a, b) => (a.name || '').localeCompare(b.name || ''));
const CorrespondentPills = ({ entries = [], onRemove, showCount = false }) => (
<div className="correspondent-list">
{entries.length ? (
entries.map((entry) => (
<span key={`${entry.id}:${entry.role}`} className="correspondent-pill">
<span className="correspondent-pill__label">
<strong>{formatRoleLabel(entry.role)}</strong>
<span>
{entry.name}
{showCount && entry.count ? ` (${entry.count})` : ''}
entries.map((entry, index) => {
const key = entry.id ?? `${entry.name}-${index}`;
return (
<span key={key} className="correspondent-pill">
<span className="correspondent-pill__label">
<span>
{entry.name}
{showCount && entry.count ? ` (${entry.count})` : ''}
</span>
</span>
{onRemove ? (
<button
type="button"
className="correspondent-pill__remove"
onClick={() => onRemove(entry)}
aria-label={`Remove ${entry.name}`}
>
×
</button>
) : null}
</span>
{onRemove ? (
<button
type="button"
className="correspondent-pill__remove"
onClick={() => onRemove(entry)}
aria-label={`Remove ${entry.name} as ${formatRoleLabel(entry.role)}`}
>
×
</button>
) : null}
</span>
))
);
})
) : (
<span className="meta">No correspondents yet.</span>
)}
@@ -174,11 +156,9 @@ const CorrespondentSection = ({
event.preventDefault();
const form = event.currentTarget;
const nameInput = form.elements.correspondent;
const roleSelect = form.elements.role;
const value = nameInput.value.trim();
const role = roleSelect.value;
if (!value) return;
onAdd({ name: value, role, input: nameInput });
onAdd({ name: value, input: nameInput });
form.reset();
}}
>
@@ -187,13 +167,6 @@ const CorrespondentSection = ({
placeholder={addPlaceholder}
list={datalistId}
/>
<select name="role" defaultValue={CORRESPONDENT_ROLES[0]}>
{CORRESPONDENT_ROLES.map((role) => (
<option key={role} value={role}>
{role.charAt(0).toUpperCase() + role.slice(1)}
</option>
))}
</select>
<button type="submit">{addButtonLabel}</button>
{datalistId ? (
<datalist id={datalistId}>
@@ -705,17 +678,14 @@ const DetailPanel = ({
if (!doc?.id) return;
(doc.correspondents || []).forEach((entry) => {
if (!entry?.id) return;
const normalizedRole = normalizeRole(entry.role);
const key = `${entry.id}:${normalizedRole}`;
if (!map.has(key)) {
map.set(key, {
if (!map.has(entry.id)) {
map.set(entry.id, {
id: entry.id,
name: entry.name || '',
role: normalizedRole,
documentIds: new Set(),
});
}
map.get(key).documentIds.add(doc.id);
map.get(entry.id).documentIds.add(doc.id);
});
});
@@ -723,23 +693,20 @@ const DetailPanel = ({
.map((entry) => ({
id: entry.id,
name: entry.name,
role: entry.role,
documentIds: [...entry.documentIds],
count: entry.documentIds.size,
}))
.sort(compareCorrespondents);
.sort((a, b) => (a.name || '').localeCompare(b.name || ''));
}, [selectedDocuments]);
const handleBulkCorrespondentRemove = useCallback(
(entry) => {
if (!entry?.id) return;
const normalizedRole = normalizeRole(entry.role);
if (onBulkCorrespondentRemove) {
return onBulkCorrespondentRemove({
assignments: [
{
correspondent_id: entry.id,
role: normalizedRole,
},
],
documentIds: entry.documentIds,
@@ -750,11 +717,7 @@ const DetailPanel = ({
const targets = entry.documentIds && entry.documentIds.length
? entry.documentIds
: selectedDocuments
.filter((doc) =>
(doc.correspondents || []).some(
(item) => item.id === entry.id && normalizeRole(item.role) === normalizedRole,
),
)
.filter((doc) => (doc.correspondents || []).some((item) => item.id === entry.id))
.map((doc) => doc.id);
return Promise.all(
@@ -762,12 +725,11 @@ const DetailPanel = ({
onCorrespondentRemove({
documentId,
correspondentId: entry.id,
role: normalizedRole,
}),
),
).catch(() => {});
},
[bulkCorrespondents, onBulkCorrespondentRemove, onCorrespondentRemove, selectedDocuments],
[onBulkCorrespondentRemove, onCorrespondentRemove, selectedDocuments],
);
const openZoomPreview = useCallback((config) => {
@@ -1158,14 +1120,12 @@ const DetailPanel = ({
onCorrespondentRemove?.({
documentId: singleDoc.id,
correspondentId: entry.id,
role: entry.role,
})
}
onAdd={({ name, role, input }) =>
onAdd={({ name, input }) =>
onCorrespondentAdd?.({
document: singleDoc,
name,
role,
input,
})
}
@@ -1310,8 +1270,8 @@ const DetailPanel = ({
title="Correspondents"
entries={bulkCorrespondents}
onRemove={handleBulkCorrespondentRemove}
onAdd={({ name, role, input }) =>
onBulkCorrespondentAdd?.({ name, role, input })
onAdd={({ name, input }) =>
onBulkCorrespondentAdd?.({ name, input })
}
addPlaceholder="Add correspondent to selection"
datalistId="correspondent-catalog-bulk"