Commit Graph

6 Commits

Author SHA1 Message Date
94c24a123a feat(uat-batch): Groups F + G + H — DocsHub/signing + admin consolidation + email
F27–F29, G30, G31, H32, H33 from the 2026-05-21 plan.

Shipped now:
  F28  Past-milestones expandable history. The Past strip on the
       Interest overview becomes an <Accordion> — each row collapses
       to the same one-line summary as before, expands to render the
       full <MilestoneSection> (steps list, sub-status, inline doc
       actions). Reuses the existing MilestoneSection so no new
       per-milestone rendering needs to be maintained.
  F29  Watchers configurable at document creation time. The unified
       create-document wizard gets a Watchers section with a
       multi-select checkbox list backed by /api/v1/admin/users/picker.
       Selected user ids are sent in the `watchers` array on the POST
       (replacing the prior hardcoded `[]`). UI matches the
       post-creation WatchersCard so reps see the same identity rows
       regardless of entry point.
  G30  /admin/invitations merged into /admin/users. The Users page
       now wraps the existing UserList + InvitationsManager in a
       Tabs control (Active users / Invitations). The standalone
       /admin/invitations route returns a redirect to the merged page
       for bookmark back-compat. Removed nav catalog entry +
       admin-sections-browser tile; extended the Users catalog
       keywords with "invitations / pending invites / onboarding"
       so command-K search still lands on the right surface.
  G31  /admin/ai picks up the berth-PDF-parser section + a "planned
       AI surfaces" placeholder. Berth PDF parser remains
       env-configured today; the page now documents it so admins
       don't hunt for the controls. Closes the "where do I configure
       AI?" loop.
  H32  Email settings explainer panel above the SMTP cards. Spells
       out why noreply + sales have separate credentials and which
       workflows ship from each mailbox. Existing field titles
       gained the "(noreply)" suffix so the model maps cleanly.
  H33  Supplemental-info-request email rebuilt to use the shared
       branded shell (logo + blurred overhead background + max-
       width 600 table layout) instead of the prior plain-HTML
       page. Per-port branding (logo / primary color / background /
       header / footer) flows from getPortBrandingConfig. CTA
       button picks up the port's primary color.

Already shipped (verified pre-shipped):
  F27  DocumentsHub root view already hides the breadcrumb via
       `selectedFolderId !== undefined` conditional.

Verified: tsc clean, vitest 1454/1454.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:40:48 +02:00
2d574172ec fix(uat-batch-1): wave-1 blocker bugs — supplemental gate, file FK, downloads, search dedup, notes stale, expense form, vocab
Surgical fixes for the 7 UAT blockers that prevent productive forward
testing. Each item has a corresponding entry in alpha-uat-master.md.

- supplemental-info route relocated out of (portal) so it bypasses the
  isPortalDisabledGlobally() kill-switch. URL unchanged.
- file upload service derives client_id/company_id/yacht_id from
  (entityType, entityId) when not explicitly passed, so interest-tab
  uploads no longer land with client_id=NULL and stay visible in the
  Attachments list.
- triggerBlobDownload / triggerUrlDownload helpers in src/lib/utils
  attach the anchor to the DOM before click so Chromium honours the
  download attribute; 7 sites refactored, file-named downloads stop
  arriving as bare UUIDs.
- search-nav-catalog dedupes by href at the result-collection layer so
  the same href can no longer surface twice in the command-K dropdown
  (kills the React duplicate-key warning); /admin/templates entries
  merged into a single richer-keyword variant.
- NotesList gains a parentInvalidateKey prop, wired through all five
  callers (interest, client, yacht, company, residential client/
  interest) so the Overview "Latest note" teaser refreshes when a note
  is added in the Notes tab.
- expense-form-dialog: setValue('receiptFileIds') / setValue(
  'noReceiptAcknowledged') on upload/clear/checkbox so the schema-level
  refine sees the field and Create stops silently no-op'ing on submit.
- bulk-add-berths-wizard: side-pontoon dropdown now reads through
  useVocabulary('berth_side_pontoon_options') instead of a wrong local
  enum ('Port', 'Starboard', 'Bow', 'Stern') — wizard data now matches
  the rest of the platform + honours admin-editable per-port overrides.

tsc clean. 1419/1419 vitest. lint clean on touched files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 16:50:58 +02:00
6b28459c45 feat(pipeline): 9→7 stage refactor + v1.1 hardening wave
Replaces the legacy 9-stage pipeline with 7 canonical stages
(enquiry → qualified → eoi → reservation → deposit_paid → contract →
nurturing) plus three doc sub-status columns (eoi_doc_status,
reservation_doc_status, contract_doc_status) that track sent/signed
within a single stage instead of branching it.

Schema (migration 0062):
- interests gains assigned_to, deposit_expected_amount/currency,
  three doc-status columns, two documenso-id columns, and
  date_reservation_signed.
- New tables: qualification_criteria (per-port admin-configurable),
  interest_qualifications (per-interest state), payments (deposit /
  balance / refund records keyed to interest + client).
- Default qualification criteria seeded for every existing port.
- Dummy-data UPDATEs collapse Sent/Signed pairs and 'completed' into
  the new stage + doc-status + outcome shape.

Migration 0063 adds interest_contact_log.voice_transcript and
template_used columns for v1.1-A/B (quick-template buttons + voice
transcription via Web Speech API).

v1.1 phase work bundled here:
- A/B: Quick-template buttons (Call / Visit / Email) + mic toggle on
       the contact-log compose dialog (useVoiceTranscription hook).
- C:   berth-rules-engine wraps state writes in pg_advisory_xact_lock
       with an idempotent re-read; emits rule_evaluated audit traces.
- D:   Documenso webhook: reservation/contract sub-status stamping
       moved out of the PDF-download try-block so a download failure
       no longer swallows the stamp. New integration test coverage.
- E:   /admin/qualification-criteria CRUD page + admin component.
- F:   default_new_interest_owner exposed in System Settings.
- G:   recentActivityCount + active_engagement deal-pulse signal
       surfaced as a chip on interests + hot-deals card.
- H:   interest_assigned notification on assignedTo change (skips
       self-assign, uses a dedupe key).

Plus the supporting components: AssignedToChip, DealPulseChip,
PaymentsSection, QualificationChecklist, MultiEoiChip,
SkipAheadBanner, WonStatusPanel, InterestBerthStatusBanner,
SupplementalInfoRequestButton, UserPicker.

Tests: 1370/1370 vitest pass (added deal-health unit suite +
expanded constants/validators/pipeline-transitions coverage). tsc
clean, eslint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 03:39:21 +02:00
4b9743a594 audit: 33-agent comprehensive audit + critical fixes
Full team audit run, all reports verbatim in docs/AUDIT-2026-05-12.md
(5900+ lines, 30+ critical findings). Already-fixed this commit:
- permission-overrides PUT: self-target block + RolePermissions allow-list + cross-tenant guard
- /api/auth/resolve-identifier: rate-limit + synthetic miss-email kill enumeration
- admin email-change: rotates account.accountId + revokes sessions
- middleware: token-gated email confirm/cancel routes whitelisted
- NAV_CATALOG: 10 dead-link sweeps to existing /admin/<x> targets

Feature work landing same commit: optional username sign-in
(migration 0054), per-user permission overrides (0055) with three-state
matrix tabbed inside UserForm, user disable button, role + outcome +
stage label normalisation across the platform, admin email-change
with auto-notification template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:52:35 +02:00
660553c074 feat(admin+search): user-mgmt polish, role labels, search keyword index
Admin search now matches against per-card keyword lists so typing
"client portal", "smtp", "tier ladder" lands on the System Settings card
(which hosts those flags). The same keyword list extends the topbar
global search (NAV_CATALOG) so any setting key resolves from the cmd-K
input — settings results sort to the bottom of the dropdown beneath
entity hits.

User management:
- Third action button (Power/PowerOff) enables/disables sign-in from the
  desktop list; mobile card dropdown gains the same item. Backed by the
  existing userProfiles.isActive flag — withAuth already refuses
  disabled sessions with 403.
- UserForm collects first + last name (canonical) alongside displayName,
  with admin email-change behind a confirmation modal. On confirm we
  send the OLD address an automated "your admin changed your sign-in
  email" notice (new template at admin-email-change.ts) and rewrite
  the Better Auth user row.
- Phone field swaps the bare tel input for the shared PhoneInput
  (country combobox + AsYouType formatting + E.164 storage).
- "Manage permissions" link points to /admin/roles?focusUser=… as
  a stepping stone for the future fine-tuned-permissions UI.

Role names normalize through a new ROLE_LABELS + formatRole() helper
in constants.ts. Replaces the ad-hoc humanizeRole in sidebar and the
prettifyRoleName in role-list; user-list and user-card now render
"Sales Agent" instead of "sales_agent". Custom roles pass through
unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:14:12 +02:00
267c2b6d1f feat(search): full-platform search overhaul + view tracking + notes bucket
Service rewrite covers 14 entity buckets (clients, residential clients,
yachts, companies, interests, residential interests, berths, invoices,
expenses, documents, files, reminders, brochures, tags, notes, navigation)
with prefix tsquery + trigram fallback, phone-digit normalization,
and JOINs to client_contacts for email matching.

New `notes` bucket searches across the four note tables (client,
interest, yacht, company) via UNION + parent-entity label resolution
(berth mooring for interests, name for yachts/companies). Renders at
the bottom of the dropdown so broad-content matches don't crowd
entity-specific hits — per the user's "low-noise" preference.

Recently-viewed tracking persists last 20 entity views per user in
Redis sorted set; CommandSearch surfaces them as the dropdown's
default state and applies affinity ranking when the user types.

ID-resolve endpoint accepts pasted UUIDs (or invoice numbers like
`INV-2025-001`) and routes the rep straight to the entity, skipping
the normal search bucket.

Audit search service gains `entityIds[]` array filter for the new
loadClientActivityAggregated() path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:58:34 +02:00