Migrates Tailwind v3 -> v4 (CSS-first, theme tokens for brand, workflow
states and charts) and adds 23 shadcn/ui components on Radix.
- lib/workflow.ts is now the single source of truth for state name, colour,
icon and available actions; "Financial Review" is the canonical served
name, with "Finance Review" kept as an alias so those rows stop falling
through to the unstyled fallback and dropping out of every count
- record view: TanStack Table with server-side sort/paginate, company column
with initials avatar, page totals, density and a URL-synced query state
- dashboard: one cached analytics fetch shared by tiles and charts, donut +
aging + vendor spend, click-to-filter
- detail view: rebuilt against the live detailview payload — restores the PO
number and payment terms that the old key heuristics silently dropped,
parses match_summary into a 3-way-match checklist, and separates the
department's and reviewer's own words from AI-generated content
- form modal on Radix Dialog (focus trap, scroll lock, escape handling)
The "Create New Invoice" button was configured all along, on the rv_screen
layout rather than the screen layout, and was being dropped twice over:
RecordViewTable fetched the rv_screen but kept only rv_template_uid and
threw away `layout`, and ScreenRenderer matched only the legacy
params.activity_id_init while current studio writes activity_uid_init.
collectStartButtons deep-walks either shape — the button nested under a
node's `config` as screen layouts do, or carrying type/params/job_template
at its own top level as rv_screens do — and accepts activity_uid_init,
activity_id_init, activity_uid and activity_id, newest key first.
RecordViewTable renders any start_state_machine buttons from its rv_screen
in the toolbar alongside the injected toolbarAction; ScreenRenderer routes
through the same helper and now skips buttons with no resolvable activity
instead of rendering a dead control.
Verified against app 169's live rv_screen config: yields exactly one button,
"Create New Invoice" -> Submit Invoice, ignoring the show_detailview_popup
entry.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>