2026-05-11 12:39:03 +02:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useState } from 'react';
|
|
|
|
|
import Link from 'next/link';
|
|
|
|
|
import { ClipboardSignature, FileText, Eye } from 'lucide-react';
|
|
|
|
|
|
fix(documents-ui): a11y, mobile, realtime lift, type-safety, UI polish
- A2: lift useRealtimeInvalidation to DocumentsHub (covers all 3 render modes)
- B1-B4: aria-label, aria-pressed, aria-expanded, Lock SVG aria-hidden
- C1-C7: Sheet wrap for mobile sidebar, border axis fix, 44x44 tap targets
- Mobile Important: useMobileChrome title, FolderActionsMenu icon size, breadcrumb tap targets, signer email truncate
- Type-safety: ENTITY_TYPES guard, AggregatedSection discriminated union
- UI/UX: em-dash to colon in SigningDetailsDialog, Loading state normalize, StatusPill on HubRootView, view signing details as Button
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:56:05 +02:00
|
|
|
import { Button } from '@/components/ui/button';
|
2026-05-11 12:39:03 +02:00
|
|
|
import { AggregatedSection } from './aggregated-section';
|
|
|
|
|
import { SigningDetailsDialog } from './signing-details-dialog';
|
|
|
|
|
import { useAggregatedFiles, useAggregatedWorkflows } from '@/hooks/use-aggregated-listing';
|
|
|
|
|
import { StatusPill, type StatusPillStatus } from '@/components/ui/status-pill';
|
2026-05-11 12:44:48 +02:00
|
|
|
import type {
|
|
|
|
|
AggregatedFile,
|
|
|
|
|
AggregatedGroup,
|
fix(documents-ui): a11y, mobile, realtime lift, type-safety, UI polish
- A2: lift useRealtimeInvalidation to DocumentsHub (covers all 3 render modes)
- B1-B4: aria-label, aria-pressed, aria-expanded, Lock SVG aria-hidden
- C1-C7: Sheet wrap for mobile sidebar, border axis fix, 44x44 tap targets
- Mobile Important: useMobileChrome title, FolderActionsMenu icon size, breadcrumb tap targets, signer email truncate
- Type-safety: ENTITY_TYPES guard, AggregatedSection discriminated union
- UI/UX: em-dash to colon in SigningDetailsDialog, Loading state normalize, StatusPill on HubRootView, view signing details as Button
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:56:05 +02:00
|
|
|
AggregatedWorkflow,
|
2026-05-11 12:44:48 +02:00
|
|
|
} from '@/hooks/use-aggregated-listing';
|
2026-05-11 12:39:03 +02:00
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
portSlug: string;
|
|
|
|
|
entityType: 'client' | 'company' | 'yacht';
|
|
|
|
|
entityId: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mapWorkflowStatus(status: string): StatusPillStatus {
|
|
|
|
|
const known: Record<string, StatusPillStatus> = {
|
|
|
|
|
draft: 'draft',
|
|
|
|
|
sent: 'sent',
|
|
|
|
|
partially_signed: 'partial',
|
|
|
|
|
completed: 'completed',
|
|
|
|
|
expired: 'expired',
|
|
|
|
|
cancelled: 'cancelled',
|
|
|
|
|
};
|
|
|
|
|
return known[status] ?? 'pending';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function EntityFolderView({ portSlug, entityType, entityId }: Props) {
|
|
|
|
|
const [detailsId, setDetailsId] = useState<string | null>(null);
|
|
|
|
|
|
|
|
|
|
// Hook data is the bare AggregatedGroup<T>[] array (hooks unwrap the API envelope).
|
|
|
|
|
const { data: workflowGroups = [], isLoading: workflowsLoading } = useAggregatedWorkflows(
|
|
|
|
|
entityType,
|
|
|
|
|
entityId,
|
|
|
|
|
);
|
|
|
|
|
const { data: fileGroups = [], isLoading: filesLoading } = useAggregatedFiles(
|
|
|
|
|
entityType,
|
|
|
|
|
entityId,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-4">
|
fix(documents-ui): a11y, mobile, realtime lift, type-safety, UI polish
- A2: lift useRealtimeInvalidation to DocumentsHub (covers all 3 render modes)
- B1-B4: aria-label, aria-pressed, aria-expanded, Lock SVG aria-hidden
- C1-C7: Sheet wrap for mobile sidebar, border axis fix, 44x44 tap targets
- Mobile Important: useMobileChrome title, FolderActionsMenu icon size, breadcrumb tap targets, signer email truncate
- Type-safety: ENTITY_TYPES guard, AggregatedSection discriminated union
- UI/UX: em-dash to colon in SigningDetailsDialog, Loading state normalize, StatusPill on HubRootView, view signing details as Button
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:56:05 +02:00
|
|
|
<AggregatedSection<'workflows'>
|
2026-05-11 12:39:03 +02:00
|
|
|
title="Signing in progress"
|
fix(audit-wave-10): aria-hidden sweep on decorative Lucide icons (#69)
Mechanical codemod added \`aria-hidden\` to 444 self-closing single-line
Lucide icon JSX elements across 267 .tsx files in:
- shared/, layout/, dashboard/
- admin/ (all sections)
- clients/, berths/, yachts/, companies/, interests/, documents/
- reminders/, reservations/, residential/, expenses/, email/
The regex targeted only the safe pattern \`<IconName className="..." />\`
(no other props, self-closing, capitalized component name). Every match
inspected is a decorative companion to visible text or sits inside a
button whose accessible name comes from \`aria-label\` / sr-only text
— the icon itself should not be announced.
Screen readers no longer double-read the icon + the adjacent label
text (e.g. "Pencil Pencil Edit" → just "Edit"). The existing
@axe-core/playwright smoke test (\`20-accessibility.spec.ts\`) continues
to pass.
Test suite stays at 1315/1315 vitest. typescript clean.
Closes task #69 (aria-hidden sweep) from the AUDIT-2026-05-12 follow-ups
backlog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:37:22 +02:00
|
|
|
icon={<ClipboardSignature className="h-4 w-4 text-muted-foreground" aria-hidden />}
|
2026-05-11 12:39:03 +02:00
|
|
|
groups={workflowGroups}
|
|
|
|
|
loading={workflowsLoading}
|
|
|
|
|
emptyMessage="No workflows in flight for this entity."
|
|
|
|
|
renderRow={(w: AggregatedWorkflow, _group: AggregatedGroup<AggregatedWorkflow>) => (
|
|
|
|
|
<div className="flex items-center justify-between gap-2 text-sm">
|
|
|
|
|
<Link href={`/${portSlug}/documents/${w.id}`} className="truncate hover:underline">
|
|
|
|
|
{w.title}
|
|
|
|
|
</Link>
|
|
|
|
|
<StatusPill status={mapWorkflowStatus(w.status)}>
|
|
|
|
|
{w.status.replace(/_/g, ' ')}
|
|
|
|
|
</StatusPill>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
|
fix(documents-ui): a11y, mobile, realtime lift, type-safety, UI polish
- A2: lift useRealtimeInvalidation to DocumentsHub (covers all 3 render modes)
- B1-B4: aria-label, aria-pressed, aria-expanded, Lock SVG aria-hidden
- C1-C7: Sheet wrap for mobile sidebar, border axis fix, 44x44 tap targets
- Mobile Important: useMobileChrome title, FolderActionsMenu icon size, breadcrumb tap targets, signer email truncate
- Type-safety: ENTITY_TYPES guard, AggregatedSection discriminated union
- UI/UX: em-dash to colon in SigningDetailsDialog, Loading state normalize, StatusPill on HubRootView, view signing details as Button
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:56:05 +02:00
|
|
|
<AggregatedSection<'files'>
|
2026-05-11 12:39:03 +02:00
|
|
|
title="Files"
|
fix(audit-wave-10): aria-hidden sweep on decorative Lucide icons (#69)
Mechanical codemod added \`aria-hidden\` to 444 self-closing single-line
Lucide icon JSX elements across 267 .tsx files in:
- shared/, layout/, dashboard/
- admin/ (all sections)
- clients/, berths/, yachts/, companies/, interests/, documents/
- reminders/, reservations/, residential/, expenses/, email/
The regex targeted only the safe pattern \`<IconName className="..." />\`
(no other props, self-closing, capitalized component name). Every match
inspected is a decorative companion to visible text or sits inside a
button whose accessible name comes from \`aria-label\` / sr-only text
— the icon itself should not be announced.
Screen readers no longer double-read the icon + the adjacent label
text (e.g. "Pencil Pencil Edit" → just "Edit"). The existing
@axe-core/playwright smoke test (\`20-accessibility.spec.ts\`) continues
to pass.
Test suite stays at 1315/1315 vitest. typescript clean.
Closes task #69 (aria-hidden sweep) from the AUDIT-2026-05-12 follow-ups
backlog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:37:22 +02:00
|
|
|
icon={<FileText className="h-4 w-4 text-muted-foreground" aria-hidden />}
|
2026-05-11 12:39:03 +02:00
|
|
|
groups={fileGroups}
|
|
|
|
|
loading={filesLoading}
|
|
|
|
|
emptyMessage="No files for this entity yet."
|
|
|
|
|
renderRow={(f: AggregatedFile, _group: AggregatedGroup<AggregatedFile>) => {
|
2026-05-11 12:44:48 +02:00
|
|
|
const signedFromDocumentId = f.signedFromDocumentId;
|
2026-05-11 12:39:03 +02:00
|
|
|
return (
|
|
|
|
|
<div className="flex items-center justify-between gap-2 text-sm">
|
|
|
|
|
<span className="truncate">{f.filename}</span>
|
|
|
|
|
<div className="flex items-center gap-2 text-xs text-muted-foreground tabular-nums">
|
|
|
|
|
<span>{new Date(f.createdAt).toLocaleDateString('en-GB')}</span>
|
2026-05-11 12:44:48 +02:00
|
|
|
{signedFromDocumentId ? (
|
fix(documents-ui): a11y, mobile, realtime lift, type-safety, UI polish
- A2: lift useRealtimeInvalidation to DocumentsHub (covers all 3 render modes)
- B1-B4: aria-label, aria-pressed, aria-expanded, Lock SVG aria-hidden
- C1-C7: Sheet wrap for mobile sidebar, border axis fix, 44x44 tap targets
- Mobile Important: useMobileChrome title, FolderActionsMenu icon size, breadcrumb tap targets, signer email truncate
- Type-safety: ENTITY_TYPES guard, AggregatedSection discriminated union
- UI/UX: em-dash to colon in SigningDetailsDialog, Loading state normalize, StatusPill on HubRootView, view signing details as Button
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:56:05 +02:00
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
className="min-h-[44px] gap-1 px-2 text-xs text-brand"
|
2026-05-11 12:44:48 +02:00
|
|
|
onClick={() => setDetailsId(signedFromDocumentId)}
|
2026-05-11 12:39:03 +02:00
|
|
|
>
|
fix(audit-wave-10): aria-hidden sweep on decorative Lucide icons (#69)
Mechanical codemod added \`aria-hidden\` to 444 self-closing single-line
Lucide icon JSX elements across 267 .tsx files in:
- shared/, layout/, dashboard/
- admin/ (all sections)
- clients/, berths/, yachts/, companies/, interests/, documents/
- reminders/, reservations/, residential/, expenses/, email/
The regex targeted only the safe pattern \`<IconName className="..." />\`
(no other props, self-closing, capitalized component name). Every match
inspected is a decorative companion to visible text or sits inside a
button whose accessible name comes from \`aria-label\` / sr-only text
— the icon itself should not be announced.
Screen readers no longer double-read the icon + the adjacent label
text (e.g. "Pencil Pencil Edit" → just "Edit"). The existing
@axe-core/playwright smoke test (\`20-accessibility.spec.ts\`) continues
to pass.
Test suite stays at 1315/1315 vitest. typescript clean.
Closes task #69 (aria-hidden sweep) from the AUDIT-2026-05-12 follow-ups
backlog.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:37:22 +02:00
|
|
|
<Eye className="h-3 w-3" aria-hidden />
|
fix(documents-ui): a11y, mobile, realtime lift, type-safety, UI polish
- A2: lift useRealtimeInvalidation to DocumentsHub (covers all 3 render modes)
- B1-B4: aria-label, aria-pressed, aria-expanded, Lock SVG aria-hidden
- C1-C7: Sheet wrap for mobile sidebar, border axis fix, 44x44 tap targets
- Mobile Important: useMobileChrome title, FolderActionsMenu icon size, breadcrumb tap targets, signer email truncate
- Type-safety: ENTITY_TYPES guard, AggregatedSection discriminated union
- UI/UX: em-dash to colon in SigningDetailsDialog, Loading state normalize, StatusPill on HubRootView, view signing details as Button
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:56:05 +02:00
|
|
|
View signing details
|
|
|
|
|
</Button>
|
2026-05-11 12:39:03 +02:00
|
|
|
) : null}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<SigningDetailsDialog
|
|
|
|
|
documentId={detailsId}
|
|
|
|
|
open={Boolean(detailsId)}
|
|
|
|
|
onOpenChange={(open) => !open && setDetailsId(null)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|