Commit Graph

7 Commits

Author SHA1 Message Date
e9509dc45c chore(audit-drain): rip out next-intl, RTL lint, sweeps, polish
Drain the long-tail audit queue captured in alpha-uat-master.md.

- next-intl ripped out (zero useTranslations callers ever existed):
  package.json, next.config.ts plugin wrap, src/i18n/, messages/, and
  the layout NextIntlClientProvider all gone; <html lang="en"> hardcoded.
- RTL lint nudge added: warn-only no-restricted-syntax on physical
  Tailwind utilities (ml-/mr-/pl-/pr-/text-left/text-right/border-l/
  border-r/rounded-l-/rounded-r-) inside JSX className literals.
  Existing ~1,000 sites grandfathered; new code trends toward logical.
- Icon-only button accessibility lint: jsx-a11y/control-has-associated-
  label enabled at warn; 4 empty <th>/<td> action placeholders gain
  sr-only labels.
- Currency: SUPPORTED_CURRENCIES drops the hardcoded English labels;
  new currencyLabel(code, locale?) helper resolves via Intl.DisplayNames.
  CurrencySelect + settings-manager migrated.
- Date locale sweep: 7 surfaces flip from toLocaleString('en-GB'|'en-US')
  to toLocaleString(undefined, ...) so dates honour runtime locale.
- Dialog/Sheet width: 10 document/EOI/entity-form dialogs gain a
  lg:max-w-4xl or lg:max-w-5xl step so wide desktops get breathing room.
- PaymentsSection collapsed-bar: slim one-line bar showing
  "Payments - Not received yet" or "Payments - \$X received - N payments
  - Expand"; per-interest collapse state persists in localStorage; the
  RecordPayment flow auto-expands.
- muted-foreground opacity sweep: 10 text-bearing
  text-muted-foreground/{60,70,80} hits dropped to plain
  text-muted-foreground for AA contrast on muted bg. Icon-only
  (aria-hidden) opacity hits left as-is.
- Micro-type bump: text-[10px] and text-[11px] -> text-xs (12px)
  across 87 files in src/components + src/app. Pure mechanical sweep.
- Audit-doc cleanup: alpha-uat-master.md stale 2026-05-25 summary
  rewritten with cumulative state through today. Items genuinely still
  open are now a short long-tail list.
- New docs/marketing-site-followups.md: Umami Phase 4a/3/5, email
  pixel E2E verification, and website-cutover work parked here so
  they don't get lost in the CRM audit doc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 18:48:46 +02:00
221ae5784e chore(autonomous-session): consolidate uncommitted work from prior session
Bundles the prior autonomous-session output that was sitting unstaged:

- Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances)
- country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that
  never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk
  after the per-subpath dynamic-import approach silently failed in webpack)
- Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index,
  redirects (ocr to ai, reports to dashboard, invitations to users),
  docs/admin-ia-proposal.md
- Per-template email tester (registry + endpoint + UI on Email admin page)
- Cancel-document mode picker (delete-from-Documenso vs keep-for-audit)
- Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers
- Customize-widgets per-region sortables at xl+ (charts/rails/feed); single
  flat sortable below xl when the layout stacks; per-viewport saved orders
- Audit doc updates capturing each shipped item
- Lint fixes: react-compiler immutability in DonutChart (reduce instead of
  let-reassign), set-state-in-effect disables in CountryFlag and
  UploadForSigning preview-bytes effect, unused 'confirm' destructures in
  interest contract + reservation tabs, unescaped apostrophe in test-template
  card copy
2026-05-23 00:52:59 +02:00
c8ea9ec0a0 fix(audit-wave-10): aria-hidden sweep on decorative Lucide icons (#69)
Mechanical codemod added \`aria-hidden\` to 444 self-closing single-line
Lucide icon JSX elements across 267 .tsx files in:

- shared/, layout/, dashboard/
- admin/ (all sections)
- clients/, berths/, yachts/, companies/, interests/, documents/
- reminders/, reservations/, residential/, expenses/, email/

The regex targeted only the safe pattern \`<IconName className="..." />\`
(no other props, self-closing, capitalized component name). Every match
inspected is a decorative companion to visible text or sits inside a
button whose accessible name comes from \`aria-label\` / sr-only text
— the icon itself should not be announced.

Screen readers no longer double-read the icon + the adjacent label
text (e.g. "Pencil Pencil Edit" → just "Edit"). The existing
@axe-core/playwright smoke test (\`20-accessibility.spec.ts\`) continues
to pass.

Test suite stays at 1315/1315 vitest. typescript clean.

Closes task #69 (aria-hidden sweep) from the AUDIT-2026-05-12 follow-ups
backlog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:37:22 +02:00
3ffee79f3f feat(ui): broad consistency sweep — sources, dates, comboboxes, milestones
Mobile + responsive
- berth-form full-width on phones (was 480px fixed → overflowed iPhone)
- currency-input switched to inputMode=decimal with live thousands separator
- client-form Country/Timezone/Source/Preferred-Contact full-width <sm
- contacts row restructured so Primary toggle + Remove get their own strip
- customize-dashboard footer stacks vertically on mobile; Done full-width
- interest-form client/berth pickers no longer cmdk-filter on UUID (typing
  "Carlos" now returns Carlos Vega instead of "No clients found")

Data + consistency
- SOURCES + SOURCE_LABELS + formatSource() in lib/constants; 9 surfaces
  now resolve interest/client source from one place
- INTEREST_OUTCOMES adds lost_other (picker, badge, timeline)
- Berth options natural-sort A1 → A2 → … → A10 via lib/utils/mooring-sort
- archiver downgraded ^8 → ^7.0.1 so the GDPR export route compiles
- TableBody last-row uses border-b-0 (not border-0); colored left-accent
  on the bottom berth row now renders
- Hide Invite-to-Portal until port setting === true (was !== false default-show)
- OwnerPicker primer query resolves entity name on first paint (no more
  UUID flash before the popover opens)

Terminology
- Replaced user-facing "Documenso" with "signing service" / "Generated EOI" /
  "Manual EOI" in 8 components (admin/internal references kept)
- Plainer status-change copy on berth-detail-header

Forms + editing
- InlineEditableField gained a `date` variant (native picker); applied to
  company incorporation date and ready for other YYYY-MM-DD plaintext fields
- Inline source picker on interest-tabs detail (was free text)
- TagPicker self-hides when port has no tags AND nothing is selected
- New ReminderDaysInput with preset chips (1d / 3d / 1wk / 2wk / 1mo / custom)
- Compose dialog follow-up is now a toggle that reveals datetime picker

Pipeline milestones
- changeStageSchema accepts optional milestoneDate; service stamps it on the
  matching date column instead of always using now
- MilestoneAdvanceButton popover collects a back-date before stage advance
- Applied to every "Mark X manually" surface on the interest overview

EOI / linked-berths polish
- Add-bypass row aligned inline with toggle descriptions
- Tooltips on "Specifically pitching" / "Mark in EOI bundle" explain their
  legal vs. public-map consequences

Surfaces
- Companies list now has the column picker + persisted hidden-column prefs
- NotesList aggregate flag enabled on clients, companies, residential_clients
  (yachts already aggregated)

ft/m unit toggle (interim, before drift fix)
- "Berth size desired" gets a section-level ft/m toggle; per-field hint shows
  the converted value. Storage stays canonical-ft for now; the drift-safe
  persistence migration is the next step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:50:58 +02:00
Matt Ciaccio
3017ce4b3a fix(clients): list contacts join + nationality backfill + col redesign
Wire primary email + primary phone into the /clients list service so
the redesigned columns (Name · Email · Phone · Country · Source ·
Latest stage · Created) actually have data. Picks the row marked
is_primary=true; falls back to most-recent created_at when the flag
is unset.

- 0026 schema migration: unique partial index
  idx_cc_one_primary_per_channel on (client_id, channel) WHERE
  is_primary=true. Prevents the §14.2 "multiple primaries" ambiguity.
- 0027 data migration: backfill clients.nationality_iso from the
  primary phone's value_country. 218 -> 36 missing on dev. Idempotent.
- listClients: add a fifth parallel query for client_contacts; build
  primaryEmailMap / primaryPhoneMap in-memory from the pre-sorted
  result.
- client-columns: drop Yachts/Companies/Tags from the default view
  per §5.1; add Email/Phone/Country/Latest-stage columns; rename
  "Nationality" -> "Country" since phone country is a proxy (§14.2).
- client-card: prefer email, fall back to phone, for the line under
  the name; replaces the old `contacts.find(isPrimary)` lookup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 02:15:03 +02:00
Matt Ciaccio
886119cbde refactor(sales): consolidate pipeline stages + wire EOI auto-advance
The 8→9 stage refresh from earlier today only updated constants.ts and the DB —
20 component/service files still hardcoded the old enum, leaving labels blank,
filter dropdowns wrong, kanban columns mismatched, and the analytics funnel
silently dropping new-stage rows. The platform also never advanced
pipelineStage on EOI lifecycle events: documents.service.ts wrote eoiStatus
but left the user-visible stage stuck.

This commit closes both gaps:

  1. Single source of truth in src/lib/constants.ts — adds STAGE_LABELS,
     STAGE_BADGE, STAGE_DOT, STAGE_WEIGHTS, STAGE_TRANSITIONS plus
     stageLabel / stageBadgeClass / stageDotClass / safeStage /
     canTransitionStage helpers. components/clients/pipeline-constants.ts
     becomes a re-export shim so existing imports keep working.

  2. 18 stale-enum surfaces migrated — interest list (table, card, filters,
     form, stage picker), pipeline board, client card, berth interests tab,
     portal client interests page, dashboard pipeline / funnel / revenue-
     forecast charts, settings pipeline_weights default, dashboard.service
     weights, analytics.service funnel stages, alert-rules stale-interest
     filter, interest-scoring stage rank.

  3. Documents tab wired into interest detail — replaced the placeholder in
     interest-tabs.tsx with InterestDocumentsTab + InterestFilesTab so the
     EOI launcher is back where salespeople work.

  4. Auto-advance — new advanceStageIfBehind() in interests.service.ts
     (forward-only, no-op if interest is already past the target). Called
     from documents.service.ts on send (→ eoi_sent), Documenso completed
     webhook (→ eoi_signed), and manual signed-EOI upload (→ eoi_signed).

  5. Transition guard — canTransitionStage() blocks egregious skips
     (e.g. completed → open, open → contract_signed). Enforced in
     changeInterestStage before the DB write.

Tests updated to reflect the 9-stage model. tsc clean, vitest 832/832,
ESLint clean on every file touched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 23:33:53 +02:00
Matt Ciaccio
6009ccb7de feat(mobile): mobile card view for clients + interests lists
Adds optional cardRender prop to <DataTable> that switches the layout
to a vertical card list below lg: while keeping the same TanStack
table instance powering both views (pagination, sort, selection).

New shared shell:
  - <ListCard>          rounded card with optional left status accent bar,
                        whole-card link to detail page, top-right actions
                        slot, and tactile hover/active states.
  - <ListCardAvatar>    40px brand-tinted circle (initials or domain icon).
  - <ListCardMeta>      inline icon + muted text segment.
  - deriveInitials()    shared helper that ignores numeric tokens (so
                        "Recovery Test 1777" -> "RT", not "R1").

Clients and interests pages now render mobile cards via cardRender
using this shell; desktop view (lg+) is unchanged. Interests cards
encode pipeline stage as a left-edge accent strip whose saturation
deepens with pipeline progression (open -> completed). Berths display
with an Anchor icon; null-berth interests fall back to a Compass +
"General interest" italic label. Hot leads get a discreet "Hot" pill.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 15:27:53 +02:00