127 lines
4.2 KiB
TypeScript
127 lines
4.2 KiB
TypeScript
|
|
/**
|
||
|
|
* One-shot: load the 117-berth NocoDB snapshot into the port-nimara
|
||
|
|
* port, skipping any moorings that already exist.
|
||
|
|
*
|
||
|
|
* The original seed only seeded 12 hand-rolled berths into port-nimara
|
||
|
|
* (A-01..D-03), but the migration's interest rows reference moorings
|
||
|
|
* across A-01..E-18. This loads the full set so interest→berth links
|
||
|
|
* resolve cleanly on the next migration run.
|
||
|
|
*/
|
||
|
|
import 'dotenv/config';
|
||
|
|
import { eq, and, sql, inArray } from 'drizzle-orm';
|
||
|
|
|
||
|
|
import { db } from '@/lib/db';
|
||
|
|
import { ports } from '@/lib/db/schema/ports';
|
||
|
|
import { berths } from '@/lib/db/schema/berths';
|
||
|
|
import berthSnapshot from '@/lib/db/seed-data/berths.json';
|
||
|
|
|
||
|
|
interface SnapshotBerth {
|
||
|
|
mooringNumber: string;
|
||
|
|
area: string;
|
||
|
|
status: 'available' | 'under_offer' | 'sold';
|
||
|
|
lengthFt: number | null;
|
||
|
|
widthFt: number | null;
|
||
|
|
draftFt: number | null;
|
||
|
|
lengthM: number | null;
|
||
|
|
widthM: number | null;
|
||
|
|
draftM: number | null;
|
||
|
|
widthIsMinimum: boolean;
|
||
|
|
nominalBoatSize: number | null;
|
||
|
|
nominalBoatSizeM: number | null;
|
||
|
|
waterDepth: number | null;
|
||
|
|
waterDepthM: number | null;
|
||
|
|
waterDepthIsMinimum: boolean;
|
||
|
|
sidePontoon: string | null;
|
||
|
|
powerCapacity: number | null;
|
||
|
|
voltage: number | null;
|
||
|
|
mooringType: string | null;
|
||
|
|
cleatType: string | null;
|
||
|
|
cleatCapacity: string | null;
|
||
|
|
bollardType: string | null;
|
||
|
|
bollardCapacity: string | null;
|
||
|
|
access: string | null;
|
||
|
|
price: number | null;
|
||
|
|
bowFacing: string | null;
|
||
|
|
berthApproved: boolean;
|
||
|
|
statusOverrideMode: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
const [port] = await db
|
||
|
|
.select({ id: ports.id })
|
||
|
|
.from(ports)
|
||
|
|
.where(eq(ports.slug, 'port-nimara'))
|
||
|
|
.limit(1);
|
||
|
|
if (!port) throw new Error('port-nimara not found');
|
||
|
|
|
||
|
|
const snapshot = berthSnapshot as unknown as SnapshotBerth[];
|
||
|
|
|
||
|
|
// Existing moorings — skip these.
|
||
|
|
const existingRows = await db
|
||
|
|
.select({ mooringNumber: berths.mooringNumber })
|
||
|
|
.from(berths)
|
||
|
|
.where(eq(berths.portId, port.id));
|
||
|
|
const existingMoorings = new Set(existingRows.map((r) => r.mooringNumber));
|
||
|
|
|
||
|
|
const toInsert = snapshot.filter((b) => !existingMoorings.has(b.mooringNumber));
|
||
|
|
console.log(
|
||
|
|
`Snapshot: ${snapshot.length} berths, existing in port-nimara: ${existingRows.length}, to insert: ${toInsert.length}`,
|
||
|
|
);
|
||
|
|
|
||
|
|
if (toInsert.length === 0) {
|
||
|
|
console.log('Nothing to do.');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const inserted = await db
|
||
|
|
.insert(berths)
|
||
|
|
.values(
|
||
|
|
toInsert.map((b) => ({
|
||
|
|
portId: port.id,
|
||
|
|
mooringNumber: b.mooringNumber,
|
||
|
|
area: b.area,
|
||
|
|
status: b.status,
|
||
|
|
lengthFt: b.lengthFt != null ? String(b.lengthFt) : null,
|
||
|
|
widthFt: b.widthFt != null ? String(b.widthFt) : null,
|
||
|
|
draftFt: b.draftFt != null ? String(b.draftFt) : null,
|
||
|
|
lengthM: b.lengthM != null ? String(b.lengthM) : null,
|
||
|
|
widthM: b.widthM != null ? String(b.widthM) : null,
|
||
|
|
draftM: b.draftM != null ? String(b.draftM) : null,
|
||
|
|
widthIsMinimum: b.widthIsMinimum,
|
||
|
|
nominalBoatSize: b.nominalBoatSize != null ? String(b.nominalBoatSize) : null,
|
||
|
|
nominalBoatSizeM: b.nominalBoatSizeM != null ? String(b.nominalBoatSizeM) : null,
|
||
|
|
waterDepth: b.waterDepth != null ? String(b.waterDepth) : null,
|
||
|
|
waterDepthM: b.waterDepthM != null ? String(b.waterDepthM) : null,
|
||
|
|
waterDepthIsMinimum: b.waterDepthIsMinimum,
|
||
|
|
sidePontoon: b.sidePontoon,
|
||
|
|
powerCapacity: b.powerCapacity != null ? String(b.powerCapacity) : null,
|
||
|
|
voltage: b.voltage != null ? String(b.voltage) : null,
|
||
|
|
mooringType: b.mooringType,
|
||
|
|
cleatType: b.cleatType,
|
||
|
|
cleatCapacity: b.cleatCapacity,
|
||
|
|
bollardType: b.bollardType,
|
||
|
|
bollardCapacity: b.bollardCapacity,
|
||
|
|
access: b.access,
|
||
|
|
price: b.price != null ? String(b.price) : null,
|
||
|
|
priceCurrency: 'USD',
|
||
|
|
bowFacing: b.bowFacing,
|
||
|
|
berthApproved: b.berthApproved,
|
||
|
|
statusOverrideMode: b.statusOverrideMode,
|
||
|
|
tenureType: 'permanent' as const,
|
||
|
|
})),
|
||
|
|
)
|
||
|
|
.returning({ id: berths.id, mooringNumber: berths.mooringNumber });
|
||
|
|
|
||
|
|
console.log(`Inserted ${inserted.length} berths.`);
|
||
|
|
|
||
|
|
// Suppress unused-import warning if eslint is strict.
|
||
|
|
void and;
|
||
|
|
void sql;
|
||
|
|
void inArray;
|
||
|
|
}
|
||
|
|
|
||
|
|
main().catch((e) => {
|
||
|
|
console.error(e);
|
||
|
|
process.exit(1);
|
||
|
|
});
|