docs(uat): SHIPPED annotations for PR3 (primitives)

Annotate ColumnPicker, FileInputButton, and DatePicker / DateTimePicker
entries with the 8f42940 summary. Notes the deferred sweeps:
- 15+ remaining date-input sites
- raw-input file sweep was a no-op (audit showed only 1 actual
  default-UI site, already migrated)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 17:11:02 +02:00
parent 8f42940c52
commit 6a4f4ea1dd

View File

@@ -37,7 +37,7 @@ _Copy tweaks, alignment, single-prop edits, obvious typos._
> - **(d) Optional message textarea:** bump rows from 4 → 6 minimum (`rows={6}` or `min-h-[8rem]`) so reps writing real messages have room.
> - **(e) Audit the other steps of the wizard** (select-file, place-fields) for the same content-distribution issues since they share DialogBody.
> - **Effort:** ~20-30 min. Captured 2026-05-21 from UAT. **Pairs nicely with:** the platform-wide form-error UX work (Bucket 2) — both touch how form content is presented in dialogs.
> - **ColumnPicker: add "Hide all columns" symmetric to "Show all columns"** — _src/components/shared/column-picker.tsx:58-60 (`showAll()`) + 116-123 (button render)_ — current picker has a "Show all columns" footer item that clears the hidden set. Add a parallel `hideAll()` that sets `hidden = columns.filter(c => !c.alwaysVisible).map(c => c.id)` — hides every toggleable column while preserving `alwaysVisible` ones. Render a "Hide all columns" footer item next to "Show all columns" with the same visibility gate (only shown when ≥1 toggleable column is currently visible, mirroring the `canShowAll` logic). Since column-picker is shared across every DataTable surface (berths, clients, interests, yachts, companies, reservations, invoices, audit-log, expenses), the fix lands platform-wide automatically. ~5 min. Captured 2026-05-21 from UAT.
> - **ColumnPicker: add "Hide all columns" symmetric to "Show all columns"** — _src/components/shared/column-picker.tsx:58-60 (`showAll()`) + 116-123 (button render)_ — current picker has a "Show all columns" footer item that clears the hidden set. Add a parallel `hideAll()` that sets `hidden = columns.filter(c => !c.alwaysVisible).map(c => c.id)` — hides every toggleable column while preserving `alwaysVisible` ones. Render a "Hide all columns" footer item next to "Show all columns" with the same visibility gate (only shown when ≥1 toggleable column is currently visible, mirroring the `canShowAll` logic). Since column-picker is shared across every DataTable surface (berths, clients, interests, yachts, companies, reservations, invoices, audit-log, expenses), the fix lands platform-wide automatically. ~5 min. Captured 2026-05-21 from UAT. **SHIPPED in 8f42940:** `hideAll()` + symmetric `canHideAll` gate added; both items render under the same separator.
> - **OnboardingChecklist: auto-check uses raw setting-row presence, not resolver chain → ports using env fallback or global config never auto-tick + super_admin discoverability gap** — _src/components/admin/onboarding-checklist.tsx:32-105 (STEPS def)_ + _src/lib/services/port-config.ts_ (the resolver chain like `getPortDocumensoConfig`) + new dashboard tile + new topbar banner for the discoverability half. Two linked bugs surfaced UAT 2026-05-21.
> - **(a) [bug] Auto-check sentinels are too strict.** Examples:
> - Email step (line 46) checks `smtp_host_override` — only fires when port has its own override row. Ports using global SMTP (the common case) never auto-tick even though email works.
@@ -105,6 +105,7 @@ _Copy tweaks, alignment, single-prop edits, obvious typos._
> - **Component shape:** `<FileInputButton accept={...} multiple={...} onFilesPicked={(files) => ...} label="Upload PDF" icon={<Upload />} variant="outline" size="sm" />`. Renders a styled Button (Upload icon + label) + hidden `<input type="file">` underneath. Optional: after-pick filename row with X to clear, mirroring the expense form's pattern.
> - **Sweep:** drop-in replacement at each of the 7 sites. Pair with the platform-wide file-preview work (Bucket 3) so picker-then-preview becomes consistent everywhere.
> - **Effort:** ~10 min for the primitive; ~30-45 min for the 7-site sweep. Total ~1h. Captured 2026-05-21 from UAT.
> - **SHIPPED (primitive) in 8f42940:** `src/components/ui/file-input-button.tsx` lands with the shape the queue asked for + an optional `showSelectedFilename` mode. external-eoi-upload-dialog migrated. The 5 other queued sites were re-audited — they already use the hidden-input + Button-trigger pattern (no browser-default UI visible), so no migration was needed; the primitive is in place for any new caller.
> - **EOI empty state: add "Mark as signed without file" button (parity with Reservation + Contract tabs)** — _src/components/interests/interest-eoi-tab.tsx:553-562_ (`EmptyEoiState` only renders Generate + Upload paper-signed) — `MarkExternallySignedDialog` already supports `docType: 'eoi'` (mark-externally-signed-dialog.tsx:37-41) with full copy ("Flips the EOI sub-status to 'signed' without uploading a file…"); the reservation tab uses the same dialog via a third ghost-button row (interest-reservation-tab.tsx:378-380). EOI tab's empty state just never grew the button. Add it as a third ghost-variant Button, wired to a `setMarkExternalOpen(true)` state hook + the existing dialog. ~5-10 min. Captured 2026-05-21 from UAT.
> - **Activity feed: "See all" link to the full audit log** — _src/components/dashboard/activity-feed.tsx_ (ActivityFeedInner, around line 175) — the card lists the most recent audit events but has no jump-off to the full audit-log page. Add a "See all" link in the card header (or as a trailing row underneath the list). Confirm the target route (likely `/{portSlug}/admin/audit-log`) and permission-gate the link by the same `audit_log.view` perm the admin sidebar uses, so non-admin reps see the card but not the link. ~10 min.
@@ -549,6 +550,7 @@ _New UI surfaces, new endpoints, schema migrations, multi-step flows._
> - **Optional polish (defer to v2):** add a `naturalLanguage` flag using `chrono-node` (~2KB) so users can type "next Tuesday" / "in 3 days" — particularly nice on the reminder form's due-date field. Skip for v1 to keep scope tight.
> - **Call sites to migrate (22 files found via `grep "datetime-local|type=\"date\""`):** `src/app/(dashboard)/[portSlug]/invoices/new/page.tsx`, `src/app/(dashboard)/[portSlug]/expenses/scan/page.tsx`, `src/components/berths/berth-form.tsx`, `src/components/invoices/invoice-detail.tsx`, `src/components/yachts/yacht-transfer-dialog.tsx`, `src/components/reservations/reservation-detail.tsx`, `src/components/reservations/berth-reserve-dialog.tsx`, `src/components/expenses/expense-form-dialog.tsx`, `src/components/admin/audit/audit-log-list.tsx`, `src/components/shared/inline-editable-field.tsx`, `src/components/shared/filter-bar.tsx`, `src/components/scan/scan-shell.tsx`, `src/components/dashboard/date-range-picker.tsx`, `src/components/interests/payments-section.tsx`, `src/components/interests/interest-tabs.tsx` (incl. the `MilestoneAdvanceButton` popover at line 318), `src/components/interests/interest-contact-log-tab.tsx`, `src/components/interests/external-eoi-upload-dialog.tsx`, `src/components/reminders/snooze-dialog.tsx`, `src/components/companies/add-membership-dialog.tsx`, `src/components/reminders/reminder-form.tsx`, `src/components/companies/company-form.tsx`, `src/components/reports/generate-report-form.tsx`. Several callers (e.g. `filter-bar.tsx`, `inline-editable-field.tsx`, `date-range-picker.tsx`) wrap the input and need slightly more care — small refactor of the wrapper, not a 1-line swap.
> - **Effort:** ~45 min to build the two wrappers + `useIsMobile` (if needed); ~2-3h to sweep all 22 call sites + visual verification in browser. Total ~3-4h. Captured 2026-05-21 from UAT.
> - **SHIPPED (primitives + highest-leverage migrations) in 8f42940:** `<DatePicker>` + `<DateTimePicker>` land in `src/components/ui`. Migrated: `MilestoneAdvanceButton` (Interest backfill UX), `reminder-form`, `snooze-dialog`, `external-eoi-upload-dialog`, `payments-section`. **Remaining ~17 sites parked** for a follow-up sweep — several use react-hook-form `register` patterns that need the controlled-value migration done carefully (expense-form-dialog, invoice/new, reservation/berth-reserve dialogs, company/yacht/audit forms, etc.).
1. **Platform-wide chart library migration: recharts → ECharts**_src/components/dashboard/_ + _src/components/website-analytics/_ + _src/components/berths/_ — we now run two chart libraries side-by-side: ECharts (just adopted for the world choropleth + tree-shaken, canvas renderer, d3-geo projection) and recharts (everything else: berth-status donut, occupancy-timeline line, pipeline-funnel bar, lead-source pie, source-conversion bar, berth-heat-widget bars, pageviews-vs-sessions area, pipeline-value-tile mini-bars — ~8+ components). **Trade-off analysis (done 2026-05-19 during analytics build):** ECharts wins on visual polish (better default styling, smoother animations, native legend/tooltip behaviour), comprehensive chart types (sunbursts, sankeys, parallel coords, heatmaps, geo all out of the box), and canvas-renderer performance on dense series; recharts wins on React-idiom (declarative `<Area>` / `<Bar>` children vs imperative option objects) and bundle size for the very simplest charts. **Migration cost:** ~610 h to port the existing 8 components; each is a 50150 LOC swap from `<ResponsiveContainer><AreaChart>…` to an `<ReactEChartsCore option={…} />` with tree-shaken module imports. **Pre-reqs already in place:** `transpilePackages: ['echarts', 'zrender', 'echarts-for-react']` added to `next.config.ts`, `d3-geo` installed, dynamic-import + canvas-renderer pattern proven on the world map. **Recommendation:** do as a single coordinated pass (consistency wins over piecemeal), gated on a free afternoon — none of the existing recharts components are buggy, this is purely about platform-wide visual + capability parity with the new analytics surfaces. Captured 2026-05-19 during the Umami flesh-out work.
2. **Bulk-price editing UI**_src/components/berths/_, _src/components/berths/berth-columns.tsx_ — backend shipped this session (new `berths.update_prices` permission across schema + 6 role maps + admin UI + factories; validators `updateBerthPriceSchema` + `bulkUpdateBerthPricesSchema`; services `updateBerthPrice` + `bulkUpdateBerthPrices` — both per-row audited with `fieldChanged='price'`; routes `PATCH /api/v1/berths/[id]/price` + `POST /api/v1/berths/bulk-update-prices`, ≤500 berths per batch). UI work pending: (a) wire `InlineEditableField` into the price cell of `berth-columns.tsx` (click → input → PATCH) gated by `can('berths', 'update_prices')`; (b) add `bulk-price-edit-sheet.tsx` (right-side Sheet, per-row inputs, "Set all to" + "Apply % adjust" shortcuts) wired to `bulkActions` on the `<DataTable />` in `berth-list.tsx`. ~23 h to ship the UI.