# `/deliveries` DN Items Expand — Design ## Purpose On `/deliveries`, each delivery-note row currently shows only header metadata (DN number, status badge, customer / company / order / signed-copy indicator). The operator can't see *which items are on that DN* without leaving to `/delivery-notes/[id]`, which breaks the at-a-glance matching workflow against the printed paper in their hand. This spec adds a click-to-expand chevron on each DN row that reveals a small inline list of that DN's line items (description + qty) so the operator can match against the paper before loading the DN. ## Scope decisions (confirmed during brainstorming) - **Interaction**: click-to-expand via a chevron at the left edge of the DN row. Independent state per DN (no "expand all"). No persistence across refresh. - **Item content**: description + qty only (no SKU, no price, no invoice number — operator matches against the printed paper, which shows description and qty). - **Data source**: client-side filter of the items already loaded by `/api/v1/deliveries`. No new server endpoint. Items not in the current page slice (200-item cap) get a "see full DN →" link to `/delivery-notes/[id]`. - **No PB schema changes.** Pure rendering work. ## Architecture **Modified files (1):** ``` src/app/(app)/deliveries/page.tsx ``` No service-layer changes. No new `/api/v1/*` routes. No new MCP tools. No new PB schema. ### State management The existing `DeliveryNoteRowComponent` is a stateless component that takes a single `dn` row. To add expand state without changing every caller, lift state up to the page-level: - The page keeps `const [expandedDnIds, setExpandedDnIds] = useState>(new Set())`. - Pass `expanded: boolean`, `onToggle: () => void` into `DeliveryNoteRowComponent`. - When toggled, page updates the set (`setExpandedDnIds(prev => { const next = new Set(prev); next.has(id) ? next.delete(id) : next.add(id); return next; })`). The page already groups items + DNs by customer → order, and we already pass the expanded state per-DN. State hoisting to the page component keeps the data ownership clear (page has the items array, so it knows which DN items belong to). --- ## Feature 1 — DN row expand/collapse ### Current DN row (post the just-shipped compact subline change) ``` [DN DN26082701] [Draft] Benjamin · Caliber Machine... · ORD-2026-08-013 · [📎 signed copy] [Mark loaded] [Signed copy] ``` ### Target The chevron sits at the left edge of the row, just inside the existing `` icon column: ``` ▸ [DN DN26082701] [Draft] Benjamin · Caliber Machine... · ORD-2026-08-013 · [📎 signed copy] [Mark loaded] [Signed copy] (when expanded:) ▾ [DN DN26082701] [Draft] Benjamin · Caliber Machine... · ORD-2026-08-013 · [📎 signed copy] [Mark loaded] [Signed copy] ┌──────────────────────────────────────────────────────┐ │ 3 items · Total 10 EA │ │ ─────────────────────────────────────────────────── │ │ JEI HSS CUTTER LONG 24mm ... Qty 2 │ │ JEI HSS CUTTER LONG 22mm ... Qty 2 │ │ JEI HSS CUTTER LONG 18mm ... Qty 6 │ └──────────────────────────────────────────────────────┘ ``` The items panel sits **inside the same `
` as the DN row** but below the existing `
` button column. Visual hierarchy: existing row keeps its cream/amber background; the items panel uses a slightly tinted neutral (`bg-muted/30`) so the operator's eye reads "DN row + its items" as one unit. ### Interaction details - **Chevron button** (lucide `ChevronRight` / `ChevronDown`): - Position: left edge of the row, before `` - Size: `h-4 w-4` (matches the existing icon size used elsewhere on the page) - `aria-expanded={expanded}`, `aria-controls={items-panel-id}`, `aria-label="Show items on this delivery note"` - Hover background: subtle (`hover:bg-amber-100/40` to match the amber row tint) - **Independent state per DN.** Each DN toggles its own expand. The page-level `Set` of expanded DN ids survives re-renders but resets on refresh. - **No "expand all" / "collapse all"** — out of scope. YAGNI. - **Keyboard**: chevron is a `