feat(db): m:m interest_berths junction + role flags

Introduces the multi-berth interest model from plan §3.1: a junction
between interests and berths with three role flags so the same berth
can be linked as the primary deal target, an EOI-bundle inclusion,
or a "just exploring" link without conflating semantics.

- 0028 schema migration creates interest_berths with the unique
  partial index "≤1 primary per interest", a unique compound on
  (interest_id, berth_id), and indexes for the public-map "under
  offer" lookup (where is_specific_interest=true).
- Same migration adds desired_length_ft / desired_width_ft /
  desired_draft_ft to interests for the recommender.
- Same migration runs the Phase 2 data migration: every interest
  with a non-null berth_id gets one junction row marked
  is_primary=true, is_specific_interest=true, and is_in_eoi_bundle =
  (eoi_status='signed'). Pre-flight check halts on dangling FKs
  (§14.3 critical case).
- New service src/lib/services/interest-berths.service.ts owns reads
  + writes of the junction. getPrimaryBerth / getPrimaryBerthsForInterests
  feed list pages; upsertInterestBerth demotes the prior primary in
  the same transaction so the unique index is never violated.
- interests.berth_id stays in place this commit so existing callers
  keep working; Phase 2b migrates them onto the helper service and a
  later migration drops the column.

53 dev rows seeded into the junction; tests still green at 996.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Ciaccio
2026-05-05 02:22:11 +02:00
parent 05257723f6
commit ff92a08620
6 changed files with 11249 additions and 2 deletions

View File

@@ -18,7 +18,7 @@ import {
} from './clients';
// Interests
import { interests, interestNotes, interestTags } from './interests';
import { interests, interestNotes, interestTags, interestBerths } from './interests';
// Yachts
import { yachts, yachtOwnershipHistory, yachtNotes, yachtTags } from './yachts';
@@ -274,6 +274,18 @@ export const interestsRelations = relations(interests, ({ one, many }) => ({
reminders: many(reminders),
berthRecommendations: many(berthRecommendations),
formSubmissions: many(formSubmissions),
interestBerths: many(interestBerths),
}));
export const interestBerthsRelations = relations(interestBerths, ({ one }) => ({
interest: one(interests, {
fields: [interestBerths.interestId],
references: [interests.id],
}),
berth: one(berths, {
fields: [interestBerths.berthId],
references: [berths.id],
}),
}));
export const interestNotesRelations = relations(interestNotes, ({ one }) => ({