Commit Graph

7 Commits

Author SHA1 Message Date
Matt Ciaccio
27cdbcc695 chore(i18n): drop legacy free-text country/nationality columns
Test-data only — no production migration needed (per earlier decision).
Schema is now ISO-only; readers convert ISO codes to localized names where
human-readable output is required (EOI documents, invoices, portal).

Migration 0016 drops:
  - clients.nationality
  - companies.incorporation_country
  - client_addresses.{state_province, country}
  - company_addresses.{state_province, country}

Code paths that previously read free-text values now read the ISO column
and pass through `getCountryName()` / `getSubdivisionName()` for rendering.
Document templates ({{client.nationality}}), portal client view, EOI/
reservation-agreement contexts, and invoice billing addresses all updated.

Public yacht-interest endpoint (/api/public/interests) drops the legacy
fields from its insert path and writes ISO codes only. The Zod validators
no longer accept the legacy fields — older website builds posting raw
'incorporationCountry' / 'country' / 'stateProvince' will get 400s.
Server-side phone normalization is unchanged.

Seed data updated to use ISO codes (GB/FR/ES/GR/SE/IT/GH/MC/PA), spread
across continents to keep test fixtures realistic.

Test assertions updated to match the new render shape (e.g.
'United States' not 'US', 'California' not 'CA').

Vitest: 741 -> 741 (unchanged count; assertions updated, no new tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 19:00:57 +02:00
Matt Ciaccio
31fa3d08ec chore(cleanup): Phase 1 — gap closure across audit, alerts, soft-delete, perms
Multi-area cleanup pass closing partial-implementation gaps surfaced by the
post-i18n audit. No behavior changes for happy-path users; closes real
correctness/security holes.

PR1a Public yacht-interest endpoint i18n. /api/public/interests now accepts
     phoneE164/phoneCountry, nationalityIso, address.{countryIso, subdivisionIso},
     and company.{incorporationCountryIso, incorporationSubdivisionIso}.
     Server-side parsePhone() fallback for legacy raw phone strings.

PR1b Alert rule registry trim. Two rule slots ('document.expiring_soon',
     'audit.suspicious_login') were registered but evaluators returned [].
     Both required schema/instrumentation that hadn't landed. Removed from
     the registry; comments record the dependencies needed to revive them.
     Effective rule count: 8 active.

PR1c vi.mock hoist + flake fix. Hoisted vi.mock calls to top-level in 5
     integration test files; webhook-delivery uses vi.hoisted for the
     queue-add ref. Vitest no longer warns about non-top-level mocks.
     Deflaked the 'short value' assertion in security-encryption.test.ts
     by switching plaintext from 'ab' to 'XY' (non-hex chars). 5/5 runs green.

PR1d Soft-delete reference audit. listClientOptions and listYachtsForOwner
     now filter by isNull(archivedAt). Berths use status (no archivedAt).

PR1e Permission-matrix audit script + report. scripts/audit-permissions.ts
     walks every src/app/api/v1/**/route.ts and reports handlers without a
     withPermission() wrapper. Initial run found 33 violations.
     - Allow-listed 17 with explicit reasons (self-data, admin, alerts,
       search, currency, ai, custom-fields — some marked TODO).
     - Wrapped 7 routes with concrete permissions: clients/options
       (clients:view), berths/options (berths:view), dashboard/*
       (reports:view_dashboard), analytics (reports:view_analytics).
     Audit report at docs/runbooks/permission-audit.md. Script exits
     non-zero on any unallow-listed violation so it can become a CI gate.

Vitest: 741 -> 741 (no new tests; existing suite covers the changes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 18:48:22 +02:00
Matt Ciaccio
0ed401d083 refactor(clients): drop deprecated yacht/company/proxy columns
PR 13: now that all reads are migrated to the dedicated yacht / company
/ membership entities, drop the columns that mirrored them on `clients`:
companyName, isProxy, proxyType, actualOwnerName, relationshipNotes,
yachtName, yachtLength{Ft,M}, yachtWidth{Ft,M}, yachtDraft{Ft,M},
berthSizeDesired.

Migration `0008_loud_ikaris.sql` issues the destructive ALTER TABLE
DROP COLUMN statements. Run `pnpm db:push` (or the migration runner) to
apply.

Caller cleanup (zero behavioral change to remaining flows):

- Drops the legacy `generateEoi` flow entirely (route, service function,
  pdfme template, validator schema). The dual-path generate-and-sign
  service from PR 11 has fully replaced it; the route was no longer
  wired to the UI.
- `clients.service`: company-name search column / WHERE / audit value
  removed; search now ranks by full name only.
- `interests.service`: `resolveLeadCategory` reads dimensions from
  `yachts` via `interest.yachtId` instead of the dropped
  `client.yachtLength{Ft,M}`.
- `record-export`: client-summary now lists yachts via owner-side
  lookup (direct + active company memberships); interest-summary fetches
  yacht via `interest.yachtId`. Both PDF templates updated to read
  yacht details from the new entity.
- `client-detail-header`, `client-picker`, `command-search`,
  `search-result-item`, `use-search` hook, `types/domain.ts`,
  `search.service` — drop the companyName badge / sub-label / typed
  field everywhere it was rendered or fetched.
- `ai.ts` worker: drop the company / yacht context lines from the
  prompt (will be re-added later sourced from the new entities).
- `validators/interests.ts`: remove the deprecated public-form flat
  yacht/company fields. The route already ignores them.
- `factories.ts`: drop the `isProxy: false` default.

Tests: 652/652 green; type-check clean. The
`security-sensitive-data` tests use `companyName` / `isProxy` as
arbitrary record keys for a generic util — left unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 13:57:54 +02:00
Matt Ciaccio
1fd05a886d feat(public-interest): atomic client+yacht+company+interest trio
Restructures the public interest endpoint to create the yacht as a
first-class row (owned by the new client, or by a newly upserted
company when a company block is provided) and writes the yacht_id
onto the new interest. All writes now run inside a single
transaction instead of the previous unwrapped sequence.

The public validator gains structured `yacht` (required) and
`company` (optional) sub-objects; legacy flat fields remain in the
schema for backward compatibility but are silently ignored.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 15:42:45 +02:00
Matt Ciaccio
f9cb8003b5 feat(interests): wire yachtId, enforce ownership + stage-gate
- Add yachtId (optional) to createInterestSchema + listInterestsSchema
  (updateInterestSchema inherits it via partial() automatically).
- Add assertYachtBelongsToClient helper that accepts direct client
  ownership OR company-represented clients with an active membership
  in the owning company.
- createInterest + updateInterest validate yacht ownership whenever
  yachtId is supplied/changed.
- changeInterestStage rejects moving out of stage=open with yachtId
  null (ValidationError).
- listInterests filter supports yachtId.
- Integration tests cover all 7 paths; validator test for yachtId.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 15:34:44 +02:00
ae19170da8 feat: expand public interest schema with name split, address, berth
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 12:53:13 -04:00
67d7e6e3d5 Initial commit: Port Nimara CRM (Layers 0-4)
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00