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>
This commit is contained in:
@@ -4,15 +4,78 @@ export const interestSummaryTemplate: Template = {
|
||||
basePdf: 'BLANK_PDF' as unknown as string,
|
||||
schemas: [
|
||||
[
|
||||
{ name: 'portName', type: 'text', position: { x: 20, y: 15 }, width: 100, height: 10, fontSize: 16 },
|
||||
{ name: 'title', type: 'text', position: { x: 20, y: 30 }, width: 170, height: 8, fontSize: 14 },
|
||||
{ name: 'clientInfo', type: 'text', position: { x: 20, y: 45 }, width: 80, height: 30, fontSize: 9 },
|
||||
{ name: 'berthInfo', type: 'text', position: { x: 110, y: 45 }, width: 80, height: 30, fontSize: 9 },
|
||||
{ name: 'stageAndCategory', type: 'text', position: { x: 20, y: 80 }, width: 170, height: 15, fontSize: 9 },
|
||||
{ name: 'milestones', type: 'text', position: { x: 20, y: 100 }, width: 170, height: 40, fontSize: 8 },
|
||||
{ name: 'notes', type: 'text', position: { x: 20, y: 145 }, width: 170, height: 30, fontSize: 9 },
|
||||
{ name: 'recentTimeline', type: 'text', position: { x: 20, y: 180 }, width: 170, height: 85, fontSize: 8 },
|
||||
{ name: 'generatedAt', type: 'text', position: { x: 20, y: 275 }, width: 170, height: 6, fontSize: 7 },
|
||||
{
|
||||
name: 'portName',
|
||||
type: 'text',
|
||||
position: { x: 20, y: 15 },
|
||||
width: 100,
|
||||
height: 10,
|
||||
fontSize: 16,
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
position: { x: 20, y: 30 },
|
||||
width: 170,
|
||||
height: 8,
|
||||
fontSize: 14,
|
||||
},
|
||||
{
|
||||
name: 'clientInfo',
|
||||
type: 'text',
|
||||
position: { x: 20, y: 45 },
|
||||
width: 80,
|
||||
height: 30,
|
||||
fontSize: 9,
|
||||
},
|
||||
{
|
||||
name: 'berthInfo',
|
||||
type: 'text',
|
||||
position: { x: 110, y: 45 },
|
||||
width: 80,
|
||||
height: 30,
|
||||
fontSize: 9,
|
||||
},
|
||||
{
|
||||
name: 'stageAndCategory',
|
||||
type: 'text',
|
||||
position: { x: 20, y: 80 },
|
||||
width: 170,
|
||||
height: 15,
|
||||
fontSize: 9,
|
||||
},
|
||||
{
|
||||
name: 'milestones',
|
||||
type: 'text',
|
||||
position: { x: 20, y: 100 },
|
||||
width: 170,
|
||||
height: 40,
|
||||
fontSize: 8,
|
||||
},
|
||||
{
|
||||
name: 'notes',
|
||||
type: 'text',
|
||||
position: { x: 20, y: 145 },
|
||||
width: 170,
|
||||
height: 30,
|
||||
fontSize: 9,
|
||||
},
|
||||
{
|
||||
name: 'recentTimeline',
|
||||
type: 'text',
|
||||
position: { x: 20, y: 180 },
|
||||
width: 170,
|
||||
height: 85,
|
||||
fontSize: 8,
|
||||
},
|
||||
{
|
||||
name: 'generatedAt',
|
||||
type: 'text',
|
||||
position: { x: 20, y: 275 },
|
||||
width: 170,
|
||||
height: 6,
|
||||
fontSize: 7,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
@@ -25,16 +88,16 @@ function formatDate(d: Date | string | null | undefined): string {
|
||||
export function buildInterestSummaryInputs(
|
||||
interest: Record<string, unknown>,
|
||||
client: Record<string, unknown>,
|
||||
yacht: Record<string, unknown> | null,
|
||||
berth: Record<string, unknown> | null,
|
||||
timeline: Record<string, unknown>[],
|
||||
port: Record<string, unknown>,
|
||||
): Record<string, string> {
|
||||
const clientInfo = [
|
||||
`Name: ${client?.fullName ?? 'N/A'}`,
|
||||
client?.companyName ? `Company: ${client.companyName}` : null,
|
||||
client?.yachtName ? `Yacht: ${client.yachtName}` : null,
|
||||
client?.yachtLengthFt
|
||||
? `Length: ${client.yachtLengthFt}ft${client.yachtLengthM ? ` / ${client.yachtLengthM}m` : ''}`
|
||||
yacht?.name ? `Yacht: ${yacht.name}` : null,
|
||||
yacht?.lengthFt
|
||||
? `Length: ${yacht.lengthFt}ft${yacht.lengthM ? ` / ${yacht.lengthM}m` : ''}`
|
||||
: null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
@@ -45,7 +108,9 @@ export function buildInterestSummaryInputs(
|
||||
`Mooring: ${berth.mooringNumber}`,
|
||||
berth.area ? `Area: ${berth.area}` : null,
|
||||
berth.lengthFt ? `Length: ${berth.lengthFt}ft` : null,
|
||||
berth.price ? `Price: ${berth.priceCurrency ?? 'USD'} ${Number(berth.price).toLocaleString()}` : null,
|
||||
berth.price
|
||||
? `Price: ${berth.priceCurrency ?? 'USD'} ${Number(berth.price).toLocaleString()}`
|
||||
: null,
|
||||
`Status: ${berth.status ?? 'available'}`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
@@ -73,9 +138,7 @@ export function buildInterestSummaryInputs(
|
||||
`Deposit received: ${formatDate(interest.dateDepositReceived as Date | string | null | undefined)}`,
|
||||
].join('\n');
|
||||
|
||||
const notesText = interest.notes
|
||||
? `Notes:\n${interest.notes}`
|
||||
: 'No notes';
|
||||
const notesText = interest.notes ? `Notes:\n${interest.notes}` : 'No notes';
|
||||
|
||||
const timelineText =
|
||||
timeline.length > 0
|
||||
|
||||
Reference in New Issue
Block a user