feat(yachts): add yachts, ownership history, notes, tags schema
This commit is contained in:
67
src/lib/db/migrations/0002_groovy_excalibur.sql
Normal file
67
src/lib/db/migrations/0002_groovy_excalibur.sql
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
CREATE TABLE "yacht_notes" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"yacht_id" text NOT NULL,
|
||||||
|
"author_id" text NOT NULL,
|
||||||
|
"content" text NOT NULL,
|
||||||
|
"mentions" text[],
|
||||||
|
"is_locked" boolean DEFAULT false NOT NULL,
|
||||||
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "yacht_ownership_history" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"yacht_id" text NOT NULL,
|
||||||
|
"owner_type" text NOT NULL,
|
||||||
|
"owner_id" text NOT NULL,
|
||||||
|
"start_date" timestamp with time zone NOT NULL,
|
||||||
|
"end_date" timestamp with time zone,
|
||||||
|
"transfer_reason" text,
|
||||||
|
"transfer_notes" text,
|
||||||
|
"created_by" text NOT NULL,
|
||||||
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "yacht_tags" (
|
||||||
|
"yacht_id" text NOT NULL,
|
||||||
|
"tag_id" text NOT NULL,
|
||||||
|
CONSTRAINT "yacht_tags_yacht_id_tag_id_pk" PRIMARY KEY("yacht_id","tag_id")
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE "yachts" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"port_id" text NOT NULL,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"hull_number" text,
|
||||||
|
"registration" text,
|
||||||
|
"flag" text,
|
||||||
|
"year_built" integer,
|
||||||
|
"builder" text,
|
||||||
|
"model" text,
|
||||||
|
"hull_material" text,
|
||||||
|
"length_ft" numeric,
|
||||||
|
"width_ft" numeric,
|
||||||
|
"draft_ft" numeric,
|
||||||
|
"length_m" numeric,
|
||||||
|
"width_m" numeric,
|
||||||
|
"draft_m" numeric,
|
||||||
|
"current_owner_type" text NOT NULL,
|
||||||
|
"current_owner_id" text NOT NULL,
|
||||||
|
"status" text DEFAULT 'active' NOT NULL,
|
||||||
|
"notes" text,
|
||||||
|
"archived_at" timestamp with time zone,
|
||||||
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "yacht_notes" ADD CONSTRAINT "yacht_notes_yacht_id_yachts_id_fk" FOREIGN KEY ("yacht_id") REFERENCES "public"."yachts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "yacht_ownership_history" ADD CONSTRAINT "yacht_ownership_history_yacht_id_yachts_id_fk" FOREIGN KEY ("yacht_id") REFERENCES "public"."yachts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "yacht_tags" ADD CONSTRAINT "yacht_tags_yacht_id_yachts_id_fk" FOREIGN KEY ("yacht_id") REFERENCES "public"."yachts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||||
|
ALTER TABLE "yachts" ADD CONSTRAINT "yachts_port_id_ports_id_fk" FOREIGN KEY ("port_id") REFERENCES "public"."ports"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||||
|
CREATE INDEX "idx_yn_yacht" ON "yacht_notes" USING btree ("yacht_id");--> statement-breakpoint
|
||||||
|
CREATE INDEX "idx_yoh_yacht" ON "yacht_ownership_history" USING btree ("yacht_id");--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX "idx_yoh_active" ON "yacht_ownership_history" USING btree ("yacht_id") WHERE "yacht_ownership_history"."end_date" IS NULL;--> statement-breakpoint
|
||||||
|
CREATE INDEX "idx_yachts_port" ON "yachts" USING btree ("port_id");--> statement-breakpoint
|
||||||
|
CREATE INDEX "idx_yachts_current_owner" ON "yachts" USING btree ("port_id","current_owner_type","current_owner_id");--> statement-breakpoint
|
||||||
|
CREATE INDEX "idx_yachts_name" ON "yachts" USING btree ("port_id","name");--> statement-breakpoint
|
||||||
|
CREATE INDEX "idx_yachts_archived" ON "yachts" USING btree ("port_id","archived_at");
|
||||||
7585
src/lib/db/migrations/meta/0002_snapshot.json
Normal file
7585
src/lib/db/migrations/meta/0002_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,13 @@
|
|||||||
"when": 1776185487775,
|
"when": 1776185487775,
|
||||||
"tag": "0001_soft_ender_wiggin",
|
"tag": "0001_soft_ender_wiggin",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 2,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1776958500747,
|
||||||
|
"tag": "0002_groovy_excalibur",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ export * from './users';
|
|||||||
// Clients
|
// Clients
|
||||||
export * from './clients';
|
export * from './clients';
|
||||||
|
|
||||||
|
// Yachts
|
||||||
|
export * from './yachts';
|
||||||
|
|
||||||
// Interests
|
// Interests
|
||||||
export * from './interests';
|
export * from './interests';
|
||||||
|
|
||||||
|
|||||||
119
src/lib/db/schema/yachts.ts
Normal file
119
src/lib/db/schema/yachts.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import {
|
||||||
|
pgTable,
|
||||||
|
text,
|
||||||
|
integer,
|
||||||
|
numeric,
|
||||||
|
timestamp,
|
||||||
|
boolean,
|
||||||
|
index,
|
||||||
|
uniqueIndex,
|
||||||
|
primaryKey,
|
||||||
|
} from 'drizzle-orm/pg-core';
|
||||||
|
import { sql } from 'drizzle-orm';
|
||||||
|
import { ports } from './ports';
|
||||||
|
|
||||||
|
export const yachts = pgTable(
|
||||||
|
'yachts',
|
||||||
|
{
|
||||||
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
|
portId: text('port_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => ports.id),
|
||||||
|
name: text('name').notNull(),
|
||||||
|
hullNumber: text('hull_number'),
|
||||||
|
registration: text('registration'),
|
||||||
|
flag: text('flag'),
|
||||||
|
yearBuilt: integer('year_built'),
|
||||||
|
builder: text('builder'),
|
||||||
|
model: text('model'),
|
||||||
|
hullMaterial: text('hull_material'),
|
||||||
|
lengthFt: numeric('length_ft'),
|
||||||
|
widthFt: numeric('width_ft'),
|
||||||
|
draftFt: numeric('draft_ft'),
|
||||||
|
lengthM: numeric('length_m'),
|
||||||
|
widthM: numeric('width_m'),
|
||||||
|
draftM: numeric('draft_m'),
|
||||||
|
currentOwnerType: text('current_owner_type').notNull(), // 'client' | 'company'
|
||||||
|
currentOwnerId: text('current_owner_id').notNull(),
|
||||||
|
status: text('status').notNull().default('active'), // 'active' | 'retired' | 'sold_away'
|
||||||
|
notes: text('notes'),
|
||||||
|
archivedAt: timestamp('archived_at', { withTimezone: true }),
|
||||||
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
index('idx_yachts_port').on(table.portId),
|
||||||
|
index('idx_yachts_current_owner').on(
|
||||||
|
table.portId,
|
||||||
|
table.currentOwnerType,
|
||||||
|
table.currentOwnerId,
|
||||||
|
),
|
||||||
|
index('idx_yachts_name').on(table.portId, table.name),
|
||||||
|
index('idx_yachts_archived').on(table.portId, table.archivedAt),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const yachtOwnershipHistory = pgTable(
|
||||||
|
'yacht_ownership_history',
|
||||||
|
{
|
||||||
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
|
yachtId: text('yacht_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => yachts.id, { onDelete: 'cascade' }),
|
||||||
|
ownerType: text('owner_type').notNull(),
|
||||||
|
ownerId: text('owner_id').notNull(),
|
||||||
|
startDate: timestamp('start_date', { withTimezone: true, mode: 'date' }).notNull(),
|
||||||
|
endDate: timestamp('end_date', { withTimezone: true, mode: 'date' }),
|
||||||
|
transferReason: text('transfer_reason'),
|
||||||
|
transferNotes: text('transfer_notes'),
|
||||||
|
createdBy: text('created_by').notNull(),
|
||||||
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
index('idx_yoh_yacht').on(table.yachtId),
|
||||||
|
uniqueIndex('idx_yoh_active')
|
||||||
|
.on(table.yachtId)
|
||||||
|
.where(sql`${table.endDate} IS NULL`),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const yachtNotes = pgTable(
|
||||||
|
'yacht_notes',
|
||||||
|
{
|
||||||
|
id: text('id')
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => crypto.randomUUID()),
|
||||||
|
yachtId: text('yacht_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => yachts.id, { onDelete: 'cascade' }),
|
||||||
|
authorId: text('author_id').notNull(),
|
||||||
|
content: text('content').notNull(),
|
||||||
|
mentions: text('mentions').array(),
|
||||||
|
isLocked: boolean('is_locked').notNull().default(false),
|
||||||
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
},
|
||||||
|
(table) => [index('idx_yn_yacht').on(table.yachtId)],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const yachtTags = pgTable(
|
||||||
|
'yacht_tags',
|
||||||
|
{
|
||||||
|
yachtId: text('yacht_id')
|
||||||
|
.notNull()
|
||||||
|
.references(() => yachts.id, { onDelete: 'cascade' }),
|
||||||
|
tagId: text('tag_id').notNull(),
|
||||||
|
},
|
||||||
|
(table) => [primaryKey({ columns: [table.yachtId, table.tagId] })],
|
||||||
|
);
|
||||||
|
|
||||||
|
export type Yacht = typeof yachts.$inferSelect;
|
||||||
|
export type NewYacht = typeof yachts.$inferInsert;
|
||||||
|
export type YachtOwnershipHistoryRow = typeof yachtOwnershipHistory.$inferSelect;
|
||||||
|
export type NewYachtOwnershipHistoryRow = typeof yachtOwnershipHistory.$inferInsert;
|
||||||
|
export type YachtNote = typeof yachtNotes.$inferSelect;
|
||||||
|
export type NewYachtNote = typeof yachtNotes.$inferInsert;
|
||||||
Reference in New Issue
Block a user