wacrm/src/components
Arnas Donauskas cbcad71fda
feat(ui): front-end correctness pass from multi-user review (S1,S2,S6-S8,P8,P9)
PR C of the post-review fix series. Bundles six related front-end
correctness / portability fixes. No behavior change for happy-path
users — these are belt-and-braces against edge cases.

S1 — useCan exhaustiveness (`src/hooks/use-can.ts`)
- Add a `default` branch with `const _: never = action` so adding
  a new `CanAction` without a case fails the typecheck instead of
  silently returning `undefined` at runtime.

S2 + P8 — GatedButton helper (`src/components/ui/gated-button.tsx`)
- `<Button disabled title="...">` doesn't render a tooltip in
  Safari or older Firefox: those browsers don't fire mouse events
  on disabled controls, so the title never surfaces. Viewers on
  those browsers saw silently-disabled CTAs with no explanation.
- New `<GatedButton canAct={...} gateReason="...">` wraps the
  Button in a non-disabled `<span>` that owns the `title`. The
  span receives mouseover everywhere; the tooltip works.
- Also subsumes the duplicated `READ_ONLY_TITLE = "..."` constants
  across 5 list pages. `gateReason` is the verb phrase that
  completes "Read-only — your role can't <gateReason>".
- Migrated call sites: broadcasts / automations / flows / contacts
  / pipelines list pages + inbox composer (Send + Templates
  buttons). The composer's textarea keeps its inline title since
  the GatedButton pattern only applies to buttons.

S6 — Revert optimistic role-change on error (`members-tab.tsx`)
- Pre-fix: PATCH failure showed a toast and left the local
  member-row state with the new role. Next interaction
  (re-toggling to the same role) was a no-op via the
  `member.role === nextRole` guard at the top, so the row
  appeared stuck. Now we capture `previousRole` before the
  optimistic flip and revert on both 4xx and network failure.

S7 — Sidebar flag check matches settings (`sidebar.tsx`)
- Pre-fix: sidebar's `accountSharingEnabled` didn't guard on
  `profileLoading`, so the account-name strip briefly was hidden
  during the initial profile fetch then popped in. Settings page
  did this guard correctly. Now they match.

S8 — Referrer-Policy: no-referrer on /join (`join/layout.tsx`)
- The plaintext invite token lives in the URL path. Belt-and-
  braces against any future external resource on the join page
  leaking the token via the Referer header. Set via Next 16's
  `metadata` export → `<meta name="referrer" content="no-referrer">`.
  Also marks the route `noindex, nofollow` so a join URL can't end
  up in search results.

P9 — Client-side label length validation (`invite-member-dialog.tsx`)
- Server caps `label` at 80 chars and returns 400 on overflow.
  Add a `MAX_LABEL_LEN` constant on the client, point both the
  `<Input maxLength>` and the pre-submit check at it. Catches the
  paste-then-submit path where a paste lands an over-limit string
  in state before the keystroke-level cap engages.

Verification
- `npm run lint`      → 0 errors (same 19 pre-existing warnings)
- `npm run typecheck` → clean (the new `never` cast catches typos)
- `npm test`          → 366/366 pass
- `npm run build`     → succeeds

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 19:33:28 +03:00
..
auth feat(auth): extend useAuth with account context + RequireRole + useCan 2026-05-31 17:58:53 +03:00
automations feat(theme): sweep remaining violet-* → primary across all surfaces 2026-05-22 17:00:32 +03:00
broadcasts chore(templates): polish pass from senior backend / FE / UX review 2026-05-29 17:50:11 +02:00
contacts feat(theme): sweep remaining violet-* → primary across all surfaces 2026-05-22 17:00:32 +03:00
dashboard feat(dashboard): adopt Tremor for charts — first integration 2026-05-31 14:42:10 +03:00
flows perf(db,webhook): backend follow-ups from the multi-user review 2026-05-31 19:18:58 +03:00
inbox feat(ui): front-end correctness pass from multi-user review (S1,S2,S6-S8,P8,P9) 2026-05-31 19:33:28 +03:00
layout feat(ui): front-end correctness pass from multi-user review (S1,S2,S6-S8,P8,P9) 2026-05-31 19:33:28 +03:00
pipelines feat(theme): sweep remaining violet-* → primary across all surfaces 2026-05-22 17:00:32 +03:00
settings feat(ui): front-end correctness pass from multi-user review (S1,S2,S6-S8,P8,P9) 2026-05-31 19:33:28 +03:00
tremor feat(dashboard): adopt Tremor for charts — first integration 2026-05-31 14:42:10 +03:00
ui feat(ui): front-end correctness pass from multi-user review (S1,S2,S6-S8,P8,P9) 2026-05-31 19:33:28 +03:00