2026-05-10 11:49:26 +02:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useState } from '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 { ChevronRight, Folder, FolderOpen, FolderTree, Inbox, Lock } from 'lucide-react';
|
2026-05-10 11:49:26 +02:00
|
|
|
|
|
|
|
|
import { Button } from '@/components/ui/button';
|
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 { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
|
2026-05-10 11:49:26 +02:00
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
|
import { useDocumentFolders, type FolderNode } from '@/hooks/use-document-folders';
|
|
|
|
|
|
|
|
|
|
interface FolderTreeSidebarProps {
|
|
|
|
|
/** Currently-selected folder id, or `null` for root, or `undefined`
|
|
|
|
|
* for "All documents" (no folder filter). */
|
|
|
|
|
selectedFolderId: string | null | undefined;
|
|
|
|
|
onSelect: (folderId: string | null | undefined) => void;
|
|
|
|
|
/** Slot below the tree for a "New folder" affordance from the parent. */
|
|
|
|
|
footer?: React.ReactNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Collapsed-by-default tree. Each row shows a chevron that toggles its
|
|
|
|
|
* children; clicking the row label selects the folder. The "All
|
|
|
|
|
* documents" + "Root" pseudo-rows at the top let reps filter to the
|
|
|
|
|
* full set or to docs without a folder.
|
|
|
|
|
*
|
|
|
|
|
* Designed for unlimited depth — only the top level renders by default
|
|
|
|
|
* so deep trees don't blow out the page; reps drill in by expanding.
|
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
|
|
|
*
|
|
|
|
|
* On mobile (< sm) the sidebar collapses into a Sheet drawer triggered by
|
|
|
|
|
* a "Show folders" button so the main listing isn't pushed below a
|
|
|
|
|
* full-width folder stack.
|
2026-05-10 11:49:26 +02:00
|
|
|
*/
|
2026-05-10 11:53:21 +02:00
|
|
|
export function FolderTreeSidebar({ selectedFolderId, onSelect, footer }: FolderTreeSidebarProps) {
|
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
|
|
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
|
|
|
|
|
|
|
|
const handleMobileSelect = (id: string | null | undefined) => {
|
|
|
|
|
onSelect(id);
|
|
|
|
|
setMobileOpen(false);
|
|
|
|
|
};
|
2026-05-10 11:49:26 +02:00
|
|
|
|
|
|
|
|
return (
|
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
|
|
|
<>
|
|
|
|
|
{/* Mobile-only trigger that opens the drawer; hidden at sm+. */}
|
|
|
|
|
<div className="sm:hidden px-3 pt-3">
|
|
|
|
|
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
|
|
|
|
|
<SheetTrigger asChild>
|
|
|
|
|
<Button variant="outline" size="sm" className="min-h-[44px]">
|
|
|
|
|
<FolderTree className="mr-2 h-4 w-4" />
|
|
|
|
|
Show folders
|
|
|
|
|
</Button>
|
|
|
|
|
</SheetTrigger>
|
|
|
|
|
<SheetContent side="left" className="w-3/4 max-w-xs p-0">
|
|
|
|
|
<SheetHeader className="border-b px-3 py-3">
|
|
|
|
|
<SheetTitle className="text-sm">Folders</SheetTitle>
|
|
|
|
|
</SheetHeader>
|
|
|
|
|
<div className="p-2 overflow-y-auto">
|
|
|
|
|
<TreeBody
|
|
|
|
|
selectedFolderId={selectedFolderId}
|
|
|
|
|
onSelect={handleMobileSelect}
|
|
|
|
|
footer={footer}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</SheetContent>
|
|
|
|
|
</Sheet>
|
2026-05-10 11:49:26 +02:00
|
|
|
</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
|
|
|
|
|
|
|
|
{/* Desktop sidebar: hidden on mobile (the Sheet trigger replaces it). */}
|
|
|
|
|
<aside className="hidden sm:block w-60 shrink-0 border-b sm:border-b-0 sm:border-r bg-muted/40 p-2">
|
|
|
|
|
<div className="mb-2 px-2 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
|
|
|
Folders
|
|
|
|
|
</div>
|
feat: round 2 — stage prompts, berth header, EOI inline edit, measurement units
Berth surfaces
- New compact mooring-chip header (colored plate + status pill, dock-label
in tooltip) replaces the redundant "Berth B1 / Sold / B DOCK" stack
- Berth list gains a "Latest deal stage" column showing the most-advanced
pipeline stage of any active linked interest (server-aggregated, ranks by
PIPELINE_STAGES index)
- "Linked prospect" Select on the status-change dialog rebuilt as a Command
combobox: search, recent-first sort, stage-coloured pills
Pipeline UX
- Reverting an interest to Open with linked berths now prompts: keep the
links, unlink and reset, or cancel. Silent when no berths are linked
- Activity feed + entity-activity feed normalise enum field values via
STAGE_LABELS / formatSource: "deposit_10pct → contract_sent" reads as
"10% Deposit → Contract Sent"
EOI generate dialog
- Inline-editable rows for client name, nationality (country combobox), and
yacht name — pencil affordance saves directly via clients/yachts PATCH
- Replaces the single "Edit on client's page" link with two contextual links
framed by short copy explaining what's inline vs what needs the canonical
page
- Backend EoiContext now includes client.id + yacht.id so the dialog can
PATCH without an extra round-trip
Company form
- New "Connections" section lets the rep attach members (clients) and yachts
during create. Yacht attach uses the existing transfer endpoint so audit
log + ownership history capture the change
- Inline "+ New client" / "+ New yacht" buttons open the canonical forms
stacked over the company sheet
- After save, the form chains to a yacht pull-in prompt (if any attached
client owns yachts not yet linked) and an optional "Create interest" step
pre-filled with the first attached client
Admin
- /admin landing gains a searchable index — typed query flattens groups into
a result list matching label + description + group title
- "Documenso & EOI" card relabelled to "EOI signing service" (consistent
with the user-facing language rename from round 1)
Measurement units (migration 0053)
- interests gains desired_*_m columns + desired_*_unit discriminators so
the rep's literal entry (ft OR m) is preserved verbatim instead of being
reconstructed from a single canonical column on every render
- yachts + berths gain matching *_unit columns alongside their existing
ft + m pairs; defaults to 'ft' so legacy rows still render normally
- Interest form POST/PATCH now sends both ft + m + unit; computed m is
derived from the ft canonical to keep the recommender SQL unchanged
Misc
- Active-deals tile + topbar type their Link href as `Route` instead of `any`
- Unused REPORT_TYPE_LABELS const dropped from generate-report-form
- Test fixtures (fill-eoi-form, documenso-payload, public-berths) updated
to include the new id + unit fields on the EoiContext / Berth shapes
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 15:28:22 +02:00
|
|
|
<TreeBody selectedFolderId={selectedFolderId} onSelect={onSelect} footer={footer} />
|
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
|
|
|
</aside>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function TreeBody({
|
|
|
|
|
selectedFolderId,
|
|
|
|
|
onSelect,
|
|
|
|
|
footer,
|
|
|
|
|
}: {
|
|
|
|
|
selectedFolderId: string | null | undefined;
|
|
|
|
|
onSelect: (folderId: string | null | undefined) => void;
|
|
|
|
|
footer?: React.ReactNode;
|
|
|
|
|
}) {
|
|
|
|
|
const { data: tree = [], isLoading, isError } = useDocumentFolders();
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
2026-05-10 11:49:26 +02:00
|
|
|
<div className="space-y-0.5">
|
|
|
|
|
<PseudoRow
|
|
|
|
|
label="All documents"
|
|
|
|
|
icon={Inbox}
|
|
|
|
|
active={selectedFolderId === undefined}
|
|
|
|
|
onClick={() => onSelect(undefined)}
|
|
|
|
|
/>
|
|
|
|
|
<PseudoRow
|
2026-05-12 14:50:58 +02:00
|
|
|
label="Root"
|
2026-05-10 11:49:26 +02:00
|
|
|
icon={Folder}
|
|
|
|
|
active={selectedFolderId === null}
|
|
|
|
|
onClick={() => onSelect(null)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-3 space-y-0.5">
|
|
|
|
|
{isLoading ? (
|
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
|
|
|
<p className="px-2 text-xs text-muted-foreground">Loading...</p>
|
2026-05-10 11:53:21 +02:00
|
|
|
) : isError ? (
|
|
|
|
|
<p className="px-2 text-xs text-destructive">Failed to load folders.</p>
|
2026-05-10 11:49:26 +02:00
|
|
|
) : tree.length === 0 ? (
|
|
|
|
|
<p className="px-2 text-xs text-muted-foreground">No folders yet.</p>
|
|
|
|
|
) : (
|
|
|
|
|
tree.map((node) => (
|
|
|
|
|
<FolderRow
|
|
|
|
|
key={node.id}
|
|
|
|
|
node={node}
|
|
|
|
|
depth={0}
|
|
|
|
|
selectedFolderId={selectedFolderId}
|
|
|
|
|
onSelect={onSelect}
|
|
|
|
|
/>
|
|
|
|
|
))
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
{footer ? <div className="mt-4 border-t pt-3">{footer}</div> : null}
|
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
|
|
|
</>
|
2026-05-10 11:49:26 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function PseudoRow({
|
|
|
|
|
label,
|
|
|
|
|
icon: Icon,
|
|
|
|
|
active,
|
|
|
|
|
onClick,
|
|
|
|
|
}: {
|
|
|
|
|
label: string;
|
|
|
|
|
icon: typeof Inbox;
|
|
|
|
|
active: boolean;
|
|
|
|
|
onClick: () => void;
|
|
|
|
|
}) {
|
|
|
|
|
return (
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
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
|
|
|
className={cn(
|
|
|
|
|
'w-full min-h-[44px] justify-start font-normal',
|
|
|
|
|
active && 'bg-accent text-foreground',
|
|
|
|
|
)}
|
2026-05-10 11:49:26 +02:00
|
|
|
onClick={onClick}
|
|
|
|
|
>
|
|
|
|
|
<Icon className="mr-2 h-4 w-4" />
|
|
|
|
|
{label}
|
|
|
|
|
</Button>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function FolderRow({
|
|
|
|
|
node,
|
|
|
|
|
depth,
|
|
|
|
|
selectedFolderId,
|
|
|
|
|
onSelect,
|
|
|
|
|
}: {
|
|
|
|
|
node: FolderNode;
|
|
|
|
|
depth: number;
|
|
|
|
|
selectedFolderId: string | null | undefined;
|
|
|
|
|
onSelect: (folderId: string | null) => void;
|
|
|
|
|
}) {
|
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
|
const hasChildren = node.children.length > 0;
|
|
|
|
|
const isActive = selectedFolderId === node.id;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
'group flex items-center gap-0.5 rounded-md px-1 py-0.5 text-sm',
|
|
|
|
|
isActive && 'bg-accent text-foreground',
|
|
|
|
|
)}
|
|
|
|
|
style={{ paddingLeft: `${depth * 12 + 4}px` }}
|
|
|
|
|
>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
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
|
|
|
aria-label={`${open ? 'Collapse' : 'Expand'} ${node.name}`}
|
|
|
|
|
aria-expanded={hasChildren ? open : undefined}
|
2026-05-10 16:50:02 +02:00
|
|
|
aria-hidden={!hasChildren}
|
|
|
|
|
tabIndex={hasChildren ? 0 : -1}
|
2026-05-10 11:49:26 +02:00
|
|
|
onClick={() => setOpen((o) => !o)}
|
|
|
|
|
className={cn(
|
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
|
|
|
'flex min-h-[44px] min-w-[44px] items-center justify-center text-muted-foreground hover:text-foreground',
|
2026-05-10 11:49:26 +02:00
|
|
|
!hasChildren && 'invisible',
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<ChevronRight className={cn('h-3.5 w-3.5 transition-transform', open && 'rotate-90')} />
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => onSelect(node.id)}
|
2026-05-11 12:34:10 +02:00
|
|
|
className={cn(
|
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
|
|
|
'flex min-h-[44px] flex-1 items-center gap-1.5 truncate py-2 text-left',
|
2026-05-11 12:34:10 +02:00
|
|
|
node.archivedAt != null && 'text-muted-foreground',
|
|
|
|
|
)}
|
2026-05-10 11:49:26 +02:00
|
|
|
>
|
|
|
|
|
{open && hasChildren ? (
|
|
|
|
|
<FolderOpen className="h-4 w-4 shrink-0" />
|
|
|
|
|
) : (
|
|
|
|
|
<Folder className="h-4 w-4 shrink-0" />
|
|
|
|
|
)}
|
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
|
|
|
<span className="truncate">
|
|
|
|
|
{node.name}
|
|
|
|
|
{node.systemManaged ? <span className="sr-only"> (system folder)</span> : null}
|
|
|
|
|
</span>
|
2026-05-11 12:34:10 +02:00
|
|
|
{node.systemManaged ? (
|
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
|
|
|
<Lock className="ml-1 h-3 w-3 shrink-0 text-muted-foreground" aria-hidden="true" />
|
2026-05-11 12:34:10 +02:00
|
|
|
) : null}
|
2026-05-10 11:49:26 +02:00
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
{open
|
|
|
|
|
? node.children.map((child) => (
|
|
|
|
|
<FolderRow
|
|
|
|
|
key={child.id}
|
|
|
|
|
node={child}
|
|
|
|
|
depth={depth + 1}
|
|
|
|
|
selectedFolderId={selectedFolderId}
|
|
|
|
|
onSelect={onSelect}
|
|
|
|
|
/>
|
|
|
|
|
))
|
|
|
|
|
: null}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|