The old seed only had 12 berths with made-up area names ("North Pier",
"Central Basin", etc.) and placeholder dimensions. Devs now get the real
117 berths exported from the legacy NocoDB Berths table — every editable
column populated with real production values.
What's in the snapshot (src/lib/db/seed-data/berths.json):
- 117 berths total (61 available / 45 under_offer / 11 sold)
- Areas A through E (matches NocoDB single-select)
- All numeric fields filled: length / width / draft (ft + m), water depth,
nominal boat size, power capacity (kW), voltage (V)
- All NocoDB single-selects filled where present: side pontoon,
mooring type, cleat/bollard type+capacity, access
- Bow facing, status_override_mode, berth_approved carried forward as-is
- Status normalized to lowercase snake_case ("Under Offer" -> "under_offer")
- Mooring numbers reformatted A1 -> A-01 to keep the existing "Letter-NN"
convention used elsewhere in the codebase
Pre-sorted to preserve seed semantics:
idx 0..4 -> 5 available (small) -- "open" / "details_sent" interests
idx 5..9 -> 5 under_offer (medium) -- "eoi_signed" / "deposit" / "contract"
idx 10..11 -> 2 sold (large) -- "completed" interests
This means existing interest/reservation seeds that index berthRows[0..11]
keep their semantic alignment without code changes.
End-to-end verified by clearing Marina Azzurra and re-seeding:
Port "Marina Azzurra" -- 117 berths, 8 clients, 3 companies, 12 yachts,
15 interests, 8 reservations
Future devs running `pnpm db:seed` on a fresh DB will now get realistic
berth data automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
Adds a new per-port document_templates row of type 'eoi' containing an
HTML EOI / Letter of Intent body with {{section.field}} merge tokens
that mirror the EoiContext shape. Enables the in-app pdfme PDF path as
an alternative to the Documenso template flow.
- New getStandardEoiTemplateHtml() returns the Letter-sized HTML body
with Applicant / Yacht / Owner / Berth / Interest / Signatures blocks
- STANDARD_EOI_MERGE_FIELDS exported for resolveTemplate wiring (11.4)
- seed-data.ts inserts one document_templates row per port inside the
existing withTransaction block, between ownership transfers and
interests, using SEED_USER_ID for audit consistency
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Split seed into orchestrator (seed.ts) + per-port fixture builder
(seed-data.ts). Creates three ports (Port Nimara, Marina Azzurra,
Harbor Royale) and seeds each with a realistic multi-cardinality
dataset: 12 berths (5 available / 5 reserved / 2 sold), 8 clients
with contacts and primary addresses, 3 companies (2 active / 1
dissolved) with billing addresses, memberships exercising dual-
company ownership and ended state, 12 yachts (7 client-owned /
5 company-owned) plus matching open ownership-history rows, 3
completed ownership transfers per port (client <-> company), 15
interests spanning all pipeline stages, and 8 reservations (5
active on distinct berths / 2 ended / 1 cancelled). Seed wraps
per-port work in withTransaction and is idempotent: re-running
detects existing company rows and skips.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>