fix(uat): batch — timeline overshoot, name-sync, reset-password, dashboard cleanup, queue/seed hygiene + alpha UAT findings doc

UAT findings landed across the last few Playwright + React Grab passes;
single grouped commit so the index doesn't fragment into 30 one-liners.

User & auth:
- `user-settings`: name now updates the avatar + topbar menu after save
  (was reading stale session).
- `me/password-reset`: 3 bugs (token validation, error response shape,
  redirect chain).
- Admin user permission-overrides route honours the same envelope as
  the rest of the admin surface.

Dashboard:
- Removed obsolete `revenue-breakdown-chart` + `dashboard-widgets-card`
  (replaced by the customisable widget grid).
- Strip `revenue_breakdown` from analytics route + use-analytics +
  service + integration test so nothing renders an empty card.
- Activity log timeline overshoot fix (`interest-timeline` +
  `entity-activity-feed`).
- Tightened tiles: active-deals, berth-heat-widget, pipeline-value, kpi-tile.
- `dev-mode-banner`: derive dismissed state synchronously instead of
  via an effect (set-state-in-effect lint rule).

Forms & lists (assorted polish):
- client / company / yacht / interest / reminder forms — validation +
  empty-state copy + tab transitions.
- companies/yachts list tweaks; berth recommender panel; qualification
  checklist; supplemental info request button.

Infra & misc:
- Queue workers (ai / email / notifications) — log shape +
  per-job timeout consistency.
- Auth / brochures / users schema small adjustments; seeds reflect
  permissions matrix changes.
- Scan shell + scanner manifest + AI admin page small fixes.
- `next.config.transpilePackages` adds `echarts`/`zrender`/`echarts-for-react`
  (recommended config from echarts-for-react inside Next).

Docs:
- `docs/superpowers/audits/alpha-uat-master.md` — single rolling
  cross-cutting UAT findings doc (per CLAUDE.md convention).
- `docs/BACKLOG.md`: dashboard stats cards (§I) + activity-log
  normalization (§J).
- 2026-05-18 audit log updated with this batch.
- `CLAUDE.md` — small manual UAT scaffold notes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 15:56:11 +02:00
parent 8c669e2918
commit 449b9497ab
59 changed files with 1831 additions and 631 deletions

View File

@@ -317,6 +317,58 @@ Future PDF-related work (carry-over from §A of the PDF overhaul spec):
---
## J. Activity / timeline copy normalization
Every "Activity" or "Timeline" surface across the app currently leaks
raw schema details — camelCase field names, UUID values, boolean
`on`/`off` — straight into the user-visible copy. Real examples seen
in production:
- `Updated owner → mEcsLxo5kyFMyhbOSehxJjYSSD7CiLvv` (user UUID)
- `Updated primary berth → a53e3b1d-d589-4f11-9f7b-3b3a3c1ebb8e` (berth UUID)
- `Updated primary berth → a53e..., isInEoiBundle → on` (raw camelCase + boolean)
Two distinct renderers need a single source of truth:
1. **`InterestTimeline`** (`src/components/interests/interest-timeline.tsx`) reads pre-built `description` strings from `/api/v1/interests/[id]/timeline/route.ts` — see `buildAuditDescription` + `describeUpdateDiff` + `formatDiffValue`. Field-label catalog is partial; FK values are unresolved.
2. **`EntityActivityFeed`** (`src/components/shared/entity-activity-feed.tsx`) — used by clients, companies, yachts, berths, residential clients, residential interests. Builds copy client-side via `sentence()` + `formatValueForField`. Catalog is even thinner (only `pipelineStage` / `source` / `leadCategory` / `outcome` get human labels).
**Plan-of-work:**
- Build a shared `src/lib/audit/format-audit.ts` with:
- `FIELD_LABELS` per entity type (interest, client, company, yacht, berth, residential\_\*) covering every column we actually surface in audits. Today's gaps: `isInEoiBundle`, `isSpecificInterest`, `isPrimary`, `assignedTo`, `currentOwnerType/Id`, `companyId`, `parentCompanyId`, `mooringNumber`, `priceCurrency`, all the `*_at`/date fields beyond the EOI/contract handful.
- Value formatter that handles: booleans contextually (e.g. `isInEoiBundle: true` → "added to EOI bundle" / `false` → "removed from EOI bundle"; never `on`/`off`), enums via the `formatEnum`/`STAGE_LABELS`/`OUTCOME_LABELS` helpers in `src/lib/constants.ts`, currency+amount pairs, dates via `formatDate`.
- FK resolution: take a `Record<fkField, displayName>` lookup that callers prefill (mooring number for berthId, user name for assignedTo, client name for clientId, etc.) so values render as "→ Anna Schmidt" not "→ mEcs…".
- Update `/timeline` (interests) AND the 6 `/activity` route handlers to: (a) collect FK ids per row, (b) batch-resolve in one query per FK type, (c) pass the lookup into the shared formatter. The audit log itself stores IDs — resolution happens at read time so historical entries stay correct even after renames/deletes (in which case fall back to "(deleted yacht)" etc.).
- Migrate `EntityActivityFeed` to call the same shared formatter on the row's `fieldChanged` + `oldValue`/`newValue` so the strikethrough+arrow rendering uses the same vocabulary.
- Audit-log writes that have meaningful application context but don't fit the column-diff model (e.g. interest-berth flag toggles, EOI bundle membership changes) probably should set `metadata.type` so the formatter can route to a dedicated phrase ("Added berth A12 to EOI bundle", "Made A12 the primary berth") instead of best-effort diffing.
Acceptance: spot-check the timeline tab on a recently-edited interest, client, yacht, company, and berth. No UUIDs visible; no camelCase field names; no `on`/`off` booleans without context; all enum values render in their human label.
**Done while scoping (cosmetic fix):**
- Vertical-connector overshoot in `InterestTimeline` and `EntityActivityFeed` — both renderers used a container-level absolute line that trailed past the last bubble. Replaced with per-item connectors that omit on `isLast`.
---
## I. Dashboard widget wishlist
User-driven enhancements to the customizable main dashboard
(`src/components/dashboard/widget-registry.tsx`). Each entry is a new
opt-in tile users can add via the widget picker.
- **More website-analytics stats cards** — expand the dashboard widget
catalogue with additional Umami-backed tiles users can pick from
(e.g. unique visitors, avg session duration, bounce rate, top
country, top referrer of the day, mobile vs desktop split,
pages-per-visit, returning vs new). Today only `WebsiteGlanceTile`
exists. Source data already flows through
`src/lib/services/umami.service.ts` and `useWebsiteAnalytics`. Each
new tile = one `KpiTile`-shaped component + a registry entry. Size:
small per tile, scope grows with the catalogue.
---
## F. Historical audit docs (mostly resolved)
These dossiers drove the audit-fix commit waves on 2026-05-05/06. Items