diff --git a/src/db/migrations/relations.ts b/src/db/migrations/relations.ts new file mode 100644 index 0000000..6d98a2a --- /dev/null +++ b/src/db/migrations/relations.ts @@ -0,0 +1,69 @@ +import { relations } from "drizzle-orm/relations"; +import { clients, notes, payments, phases, quoteItems, serviceCatalog, documents, tasks, deliverables } from "./schema"; + +export const notesRelations = relations(notes, ({one}) => ({ + client: one(clients, { + fields: [notes.clientId], + references: [clients.id] + }), +})); + +export const clientsRelations = relations(clients, ({many}) => ({ + notes: many(notes), + payments: many(payments), + phases: many(phases), + quoteItems: many(quoteItems), + documents: many(documents), +})); + +export const paymentsRelations = relations(payments, ({one}) => ({ + client: one(clients, { + fields: [payments.clientId], + references: [clients.id] + }), +})); + +export const phasesRelations = relations(phases, ({one, many}) => ({ + client: one(clients, { + fields: [phases.clientId], + references: [clients.id] + }), + tasks: many(tasks), +})); + +export const quoteItemsRelations = relations(quoteItems, ({one}) => ({ + client: one(clients, { + fields: [quoteItems.clientId], + references: [clients.id] + }), + serviceCatalog: one(serviceCatalog, { + fields: [quoteItems.serviceId], + references: [serviceCatalog.id] + }), +})); + +export const serviceCatalogRelations = relations(serviceCatalog, ({many}) => ({ + quoteItems: many(quoteItems), +})); + +export const documentsRelations = relations(documents, ({one}) => ({ + client: one(clients, { + fields: [documents.clientId], + references: [clients.id] + }), +})); + +export const tasksRelations = relations(tasks, ({one, many}) => ({ + phase: one(phases, { + fields: [tasks.phaseId], + references: [phases.id] + }), + deliverables: many(deliverables), +})); + +export const deliverablesRelations = relations(deliverables, ({one}) => ({ + task: one(tasks, { + fields: [deliverables.taskId], + references: [tasks.id] + }), +})); \ No newline at end of file diff --git a/src/db/migrations/schema.ts b/src/db/migrations/schema.ts new file mode 100644 index 0000000..10cfa70 --- /dev/null +++ b/src/db/migrations/schema.ts @@ -0,0 +1,139 @@ +import { pgTable, foreignKey, text, timestamp, unique, numeric, integer, boolean } from "drizzle-orm/pg-core" +import { sql } from "drizzle-orm" + + + +export const notes = pgTable("notes", { + id: text().primaryKey().notNull(), + clientId: text("client_id").notNull(), + body: text().notNull(), + createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), +}, (table) => [ + foreignKey({ + columns: [table.clientId], + foreignColumns: [clients.id], + name: "notes_client_id_clients_id_fk" + }).onDelete("cascade"), +]); + +export const comments = pgTable("comments", { + id: text().primaryKey().notNull(), + entityType: text("entity_type").notNull(), + entityId: text("entity_id").notNull(), + author: text().notNull(), + body: text().notNull(), + createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), +}); + +export const clients = pgTable("clients", { + id: text().primaryKey().notNull(), + name: text().notNull(), + brandName: text("brand_name").notNull(), + brief: text().notNull(), + token: text().notNull(), + acceptedTotal: numeric("accepted_total", { precision: 10, scale: 2 }).default('0'), + createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), +}, (table) => [ + unique("clients_token_unique").on(table.token), +]); + +export const payments = pgTable("payments", { + id: text().primaryKey().notNull(), + clientId: text("client_id").notNull(), + label: text().notNull(), + amount: numeric({ precision: 10, scale: 2 }).notNull(), + status: text().default('da_saldare').notNull(), + paidAt: timestamp("paid_at", { withTimezone: true, mode: 'string' }), +}, (table) => [ + foreignKey({ + columns: [table.clientId], + foreignColumns: [clients.id], + name: "payments_client_id_clients_id_fk" + }).onDelete("cascade"), +]); + +export const phases = pgTable("phases", { + id: text().primaryKey().notNull(), + clientId: text("client_id").notNull(), + title: text().notNull(), + sortOrder: integer("sort_order").default(0).notNull(), + status: text().default('upcoming').notNull(), +}, (table) => [ + foreignKey({ + columns: [table.clientId], + foreignColumns: [clients.id], + name: "phases_client_id_clients_id_fk" + }).onDelete("cascade"), +]); + +export const quoteItems = pgTable("quote_items", { + id: text().primaryKey().notNull(), + clientId: text("client_id").notNull(), + serviceId: text("service_id").notNull(), + quantity: numeric({ precision: 10, scale: 2 }).notNull(), + unitPrice: numeric("unit_price", { precision: 10, scale: 2 }).notNull(), + subtotal: numeric({ precision: 10, scale: 2 }).notNull(), +}, (table) => [ + foreignKey({ + columns: [table.clientId], + foreignColumns: [clients.id], + name: "quote_items_client_id_clients_id_fk" + }).onDelete("cascade"), + foreignKey({ + columns: [table.serviceId], + foreignColumns: [serviceCatalog.id], + name: "quote_items_service_id_service_catalog_id_fk" + }).onDelete("restrict"), +]); + +export const serviceCatalog = pgTable("service_catalog", { + id: text().primaryKey().notNull(), + name: text().notNull(), + description: text(), + unitPrice: numeric("unit_price", { precision: 10, scale: 2 }).notNull(), + active: boolean().default(true).notNull(), +}); + +export const documents = pgTable("documents", { + id: text().primaryKey().notNull(), + clientId: text("client_id").notNull(), + label: text().notNull(), + url: text().notNull(), + createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), +}, (table) => [ + foreignKey({ + columns: [table.clientId], + foreignColumns: [clients.id], + name: "documents_client_id_clients_id_fk" + }).onDelete("cascade"), +]); + +export const tasks = pgTable("tasks", { + id: text().primaryKey().notNull(), + phaseId: text("phase_id").notNull(), + title: text().notNull(), + description: text(), + status: text().default('todo').notNull(), + sortOrder: integer("sort_order").default(0).notNull(), +}, (table) => [ + foreignKey({ + columns: [table.phaseId], + foreignColumns: [phases.id], + name: "tasks_phase_id_phases_id_fk" + }).onDelete("cascade"), +]); + +export const deliverables = pgTable("deliverables", { + id: text().primaryKey().notNull(), + taskId: text("task_id").notNull(), + title: text().notNull(), + url: text(), + status: text().default('pending').notNull(), + approvedAt: timestamp("approved_at", { withTimezone: true, mode: 'string' }), +}, (table) => [ + foreignKey({ + columns: [table.taskId], + foreignColumns: [tasks.id], + name: "deliverables_task_id_tasks_id_fk" + }).onDelete("cascade"), +]);