From 6b300626a0ce71f39341c9e9a14aecbd40876012 Mon Sep 17 00:00:00 2001 From: Paperclip Date: Sun, 12 Apr 2026 23:18:32 +0000 Subject: [PATCH] GRO-598/GRO-194 Phase 1.1: SMS schema - add consent fields to clients, channel to reminderLogs, E.164 phone validation Co-Authored-By: Paperclip --- apps/api/src/routes/clients.ts | 25 +- packages/db/migrations/0026_boring_storm.sql | 9 + .../db/migrations/meta/0021_snapshot.json | 2028 ++++++++++++++-- .../db/migrations/meta/0022_snapshot.json | 2035 +++++++++++++++-- packages/db/migrations/meta/_journal.json | 7 + packages/db/src/factories.ts | 4 + packages/db/src/schema.ts | 9 +- 7 files changed, 3642 insertions(+), 475 deletions(-) create mode 100644 packages/db/migrations/0026_boring_storm.sql diff --git a/apps/api/src/routes/clients.ts b/apps/api/src/routes/clients.ts index fe639c5..cda5bef 100644 --- a/apps/api/src/routes/clients.ts +++ b/apps/api/src/routes/clients.ts @@ -4,12 +4,35 @@ import { z } from "zod/v3"; import { and, eq, exists, getDb, or, clients, appointments } from "@groombook/db"; import type { AppEnv } from "../middleware/rbac.js"; +function normalizeE164(phone: string): string | null { + const digits = phone.replace(/\D/g, ""); + if (digits.length === 10) return `+1${digits}`; + if (digits.length === 11 && digits.startsWith("1")) return `+${digits}`; + if (digits.length > 11 && digits.startsWith("1")) return `+${digits.slice(0, 11)}`; + return null; +} + +function e164String() { + return z.string().transform((v, ctx) => { + if (!v) return v as unknown as undefined; + const normalized = normalizeE164(v); + if (!normalized) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Invalid phone number. Must be a valid E.164 number (e.g. +12125551234).", + }); + return z.NEVER; + } + return normalized; + }); +} + export const clientsRouter = new Hono(); const createClientSchema = z.object({ name: z.string().min(1).max(200), email: z.string().email().optional(), - phone: z.string().max(50).optional(), + phone: e164String().optional(), address: z.string().max(500).optional(), notes: z.string().max(2000).optional(), }); diff --git a/packages/db/migrations/0026_boring_storm.sql b/packages/db/migrations/0026_boring_storm.sql new file mode 100644 index 0000000..f15d204 --- /dev/null +++ b/packages/db/migrations/0026_boring_storm.sql @@ -0,0 +1,9 @@ +ALTER TABLE "reminder_logs" DROP CONSTRAINT "reminder_logs_appointment_id_reminder_type_unique";--> statement-breakpoint +ALTER TABLE "business_settings" ADD COLUMN "logo_key" text;--> statement-breakpoint +ALTER TABLE "clients" ADD COLUMN "sms_opt_in" boolean DEFAULT false NOT NULL;--> statement-breakpoint +ALTER TABLE "clients" ADD COLUMN "sms_consent_date" timestamp;--> statement-breakpoint +ALTER TABLE "clients" ADD COLUMN "sms_opt_out_date" timestamp;--> statement-breakpoint +ALTER TABLE "clients" ADD COLUMN "sms_consent_text" text;--> statement-breakpoint +ALTER TABLE "pets" ADD COLUMN "image" text;--> statement-breakpoint +ALTER TABLE "reminder_logs" ADD COLUMN "channel" text DEFAULT 'email' NOT NULL;--> statement-breakpoint +ALTER TABLE "reminder_logs" ADD CONSTRAINT "reminder_logs_appointment_id_reminder_type_channel_unique" UNIQUE("appointment_id","reminder_type","channel"); \ No newline at end of file diff --git a/packages/db/migrations/meta/0021_snapshot.json b/packages/db/migrations/meta/0021_snapshot.json index 7a57e53..fee2c0b 100644 --- a/packages/db/migrations/meta/0021_snapshot.json +++ b/packages/db/migrations/meta/0021_snapshot.json @@ -1,5 +1,5 @@ { - "id": "b3a381ca-f7a4-450f-aa7e-fdc2d652dc97", + "id": "5815c09a-3b34-4d82-8718-e00c518b1e89", "prevId": "5983a2e9-f185-4f8a-a73f-5a7c0a0eea9c", "version": "7", "dialect": "postgresql", @@ -8,22 +8,103 @@ "name": "account", "schema": "", "columns": { - "id": { "name": "id", "type": "text", "primaryKey": true, "notNull": true }, - "account_id": { "name": "account_id", "type": "text", "primaryKey": false, "notNull": true }, - "provider_id": { "name": "provider_id", "type": "text", "primaryKey": false, "notNull": true }, - "user_id": { "name": "user_id", "type": "text", "primaryKey": false, "notNull": true }, - "access_token": { "name": "access_token", "type": "text", "primaryKey": false, "notNull": false }, - "refresh_token": { "name": "refresh_token", "type": "text", "primaryKey": false, "notNull": false }, - "id_token": { "name": "id_token", "type": "text", "primaryKey": false, "notNull": false }, - "access_token_expires_at": { "name": "access_token_expires_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "refresh_token_expires_at": { "name": "refresh_token_expires_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "scope": { "name": "scope", "type": "text", "primaryKey": false, "notNull": false }, - "password": { "name": "password", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "account_user_id_user_id_fk": { "name": "account_user_id_user_id_fk", "tableFrom": "account", "tableTo": "user", "columnsFrom": ["user_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -34,14 +115,56 @@ "name": "appointment_groups", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "appointment_groups_client_id_clients_id_fk": { "name": "appointment_groups_client_id_clients_id_fk", "tableFrom": "appointment_groups", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" } }, + "foreignKeys": { + "appointment_groups_client_id_clients_id_fk": { + "name": "appointment_groups_client_id_clients_id_fk", + "tableFrom": "appointment_groups", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -52,40 +175,243 @@ "name": "appointments", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "pet_id": { "name": "pet_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "service_id": { "name": "service_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "staff_id": { "name": "staff_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "bather_staff_id": { "name": "bather_staff_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "status": { "name": "status", "type": "appointment_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'scheduled'" }, - "start_time": { "name": "start_time", "type": "timestamp", "primaryKey": false, "notNull": true }, - "end_time": { "name": "end_time", "type": "timestamp", "primaryKey": false, "notNull": true }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "price_cents": { "name": "price_cents", "type": "integer", "primaryKey": false, "notNull": false }, - "series_id": { "name": "series_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "series_index": { "name": "series_index", "type": "integer", "primaryKey": false, "notNull": false }, - "group_id": { "name": "group_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "confirmation_status": { "name": "confirmation_status", "type": "text", "primaryKey": false, "notNull": true, "default": "'pending'" }, - "confirmed_at": { "name": "confirmed_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "cancelled_at": { "name": "cancelled_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "confirmation_token": { "name": "confirmation_token", "type": "text", "primaryKey": false, "notNull": false }, - "customer_notes": { "name": "customer_notes", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pet_id": { + "name": "pet_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "service_id": { + "name": "service_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "staff_id": { + "name": "staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "bather_staff_id": { + "name": "bather_staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "appointment_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "start_time": { + "name": "start_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price_cents": { + "name": "price_cents", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "series_id": { + "name": "series_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "series_index": { + "name": "series_index", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "group_id": { + "name": "group_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "confirmation_status": { + "name": "confirmation_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "confirmation_token": { + "name": "confirmation_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_notes": { + "name": "customer_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { - "appointments_client_id_clients_id_fk": { "name": "appointments_client_id_clients_id_fk", "tableFrom": "appointments", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "appointments_pet_id_pets_id_fk": { "name": "appointments_pet_id_pets_id_fk", "tableFrom": "appointments", "tableTo": "pets", "columnsFrom": ["pet_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "appointments_service_id_services_id_fk": { "name": "appointments_service_id_services_id_fk", "tableFrom": "appointments", "tableTo": "services", "columnsFrom": ["service_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "appointments_staff_id_staff_id_fk": { "name": "appointments_staff_id_staff_id_fk", "tableFrom": "appointments", "tableTo": "staff", "columnsFrom": ["staff_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" }, - "appointments_bather_staff_id_staff_id_fk": { "name": "appointments_bather_staff_id_staff_id_fk", "tableFrom": "appointments", "tableTo": "staff", "columnsFrom": ["bather_staff_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" }, - "appointments_series_id_recurring_series_id_fk": { "name": "appointments_series_id_recurring_series_id_fk", "tableFrom": "appointments", "tableTo": "recurring_series", "columnsFrom": ["series_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" }, - "appointments_group_id_appointment_groups_id_fk": { "name": "appointments_group_id_appointment_groups_id_fk", "tableFrom": "appointments", "tableTo": "appointment_groups", "columnsFrom": ["group_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } + "appointments_client_id_clients_id_fk": { + "name": "appointments_client_id_clients_id_fk", + "tableFrom": "appointments", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "appointments_pet_id_pets_id_fk": { + "name": "appointments_pet_id_pets_id_fk", + "tableFrom": "appointments", + "tableTo": "pets", + "columnsFrom": [ + "pet_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "appointments_service_id_services_id_fk": { + "name": "appointments_service_id_services_id_fk", + "tableFrom": "appointments", + "tableTo": "services", + "columnsFrom": [ + "service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "appointments_staff_id_staff_id_fk": { + "name": "appointments_staff_id_staff_id_fk", + "tableFrom": "appointments", + "tableTo": "staff", + "columnsFrom": [ + "staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "appointments_bather_staff_id_staff_id_fk": { + "name": "appointments_bather_staff_id_staff_id_fk", + "tableFrom": "appointments", + "tableTo": "staff", + "columnsFrom": [ + "bather_staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "appointments_series_id_recurring_series_id_fk": { + "name": "appointments_series_id_recurring_series_id_fk", + "tableFrom": "appointments", + "tableTo": "recurring_series", + "columnsFrom": [ + "series_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "appointments_group_id_appointment_groups_id_fk": { + "name": "appointments_group_id_appointment_groups_id_fk", + "tableFrom": "appointments", + "tableTo": "appointment_groups", + "columnsFrom": [ + "group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, - "uniqueConstraints": { "appointments_confirmation_token_unique": { "name": "appointments_confirmation_token_unique", "nullsNotDistinct": false, "columns": ["confirmation_token"] } }, + "uniqueConstraints": { + "appointments_confirmation_token_unique": { + "name": "appointments_confirmation_token_unique", + "nullsNotDistinct": false, + "columns": [ + "confirmation_token" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -94,14 +420,60 @@ "name": "business_settings", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "business_name": { "name": "business_name", "type": "text", "primaryKey": false, "notNull": true, "default": "'GroomBook'" }, - "logo_base64": { "name": "logo_base64", "type": "text", "primaryKey": false, "notNull": false }, - "logo_mime_type": { "name": "logo_mime_type", "type": "text", "primaryKey": false, "notNull": false }, - "primary_color": { "name": "primary_color", "type": "text", "primaryKey": false, "notNull": true, "default": "'#4f8a6f'" }, - "accent_color": { "name": "accent_color", "type": "text", "primaryKey": false, "notNull": true, "default": "'#8b7355'" }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "business_name": { + "name": "business_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'GroomBook'" + }, + "logo_base64": { + "name": "logo_base64", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo_mime_type": { + "name": "logo_mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "primary_color": { + "name": "primary_color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#4f8a6f'" + }, + "accent_color": { + "name": "accent_color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#8b7355'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, @@ -115,17 +487,78 @@ "name": "clients", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "email": { "name": "email", "type": "text", "primaryKey": false, "notNull": false }, - "phone": { "name": "phone", "type": "text", "primaryKey": false, "notNull": false }, - "address": { "name": "address", "type": "text", "primaryKey": false, "notNull": false }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "email_opt_out": { "name": "email_opt_out", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "status": { "name": "status", "type": "client_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'active'" }, - "disabled_at": { "name": "disabled_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_opt_out": { + "name": "email_opt_out", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "status": { + "name": "status", + "type": "client_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "disabled_at": { + "name": "disabled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, @@ -139,21 +572,105 @@ "name": "grooming_visit_logs", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "pet_id": { "name": "pet_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "appointment_id": { "name": "appointment_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "staff_id": { "name": "staff_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "cut_style": { "name": "cut_style", "type": "text", "primaryKey": false, "notNull": false }, - "products_used": { "name": "products_used", "type": "text", "primaryKey": false, "notNull": false }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "groomed_at": { "name": "groomed_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "pet_id": { + "name": "pet_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "appointment_id": { + "name": "appointment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "staff_id": { + "name": "staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cut_style": { + "name": "cut_style", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "products_used": { + "name": "products_used", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "groomed_at": { + "name": "groomed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { - "grooming_visit_logs_pet_id_pets_id_fk": { "name": "grooming_visit_logs_pet_id_pets_id_fk", "tableFrom": "grooming_visit_logs", "tableTo": "pets", "columnsFrom": ["pet_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, - "grooming_visit_logs_appointment_id_appointments_id_fk": { "name": "grooming_visit_logs_appointment_id_appointments_id_fk", "tableFrom": "grooming_visit_logs", "tableTo": "appointments", "columnsFrom": ["appointment_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" }, - "grooming_visit_logs_staff_id_staff_id_fk": { "name": "grooming_visit_logs_staff_id_staff_id_fk", "tableFrom": "grooming_visit_logs", "tableTo": "staff", "columnsFrom": ["staff_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } + "grooming_visit_logs_pet_id_pets_id_fk": { + "name": "grooming_visit_logs_pet_id_pets_id_fk", + "tableFrom": "grooming_visit_logs", + "tableTo": "pets", + "columnsFrom": [ + "pet_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "grooming_visit_logs_appointment_id_appointments_id_fk": { + "name": "grooming_visit_logs_appointment_id_appointments_id_fk", + "tableFrom": "grooming_visit_logs", + "tableTo": "appointments", + "columnsFrom": [ + "appointment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "grooming_visit_logs_staff_id_staff_id_fk": { + "name": "grooming_visit_logs_staff_id_staff_id_fk", + "tableFrom": "grooming_visit_logs", + "tableTo": "staff", + "columnsFrom": [ + "staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -165,15 +682,77 @@ "name": "impersonation_audit_logs", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "session_id": { "name": "session_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "action": { "name": "action", "type": "text", "primaryKey": false, "notNull": true }, - "page_visited": { "name": "page_visited", "type": "text", "primaryKey": false, "notNull": false }, - "metadata": { "name": "metadata", "type": "jsonb", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "session_id": { + "name": "session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "page_visited": { + "name": "page_visited", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "impersonation_audit_logs_session_id_idx": { + "name": "impersonation_audit_logs_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impersonation_audit_logs_session_id_impersonation_sessions_id_fk": { + "name": "impersonation_audit_logs_session_id_impersonation_sessions_id_fk", + "tableFrom": "impersonation_audit_logs", + "tableTo": "impersonation_sessions", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } }, - "indexes": { "impersonation_audit_logs_session_id_idx": { "name": "impersonation_audit_logs_session_id_idx", "columns": [{ "expression": "session_id", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} } }, - "foreignKeys": { "impersonation_audit_logs_session_id_impersonation_sessions_id_fk": { "name": "impersonation_audit_logs_session_id_impersonation_sessions_id_fk", "tableFrom": "impersonation_audit_logs", "tableTo": "impersonation_sessions", "columnsFrom": ["session_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -184,23 +763,131 @@ "name": "impersonation_sessions", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "staff_id": { "name": "staff_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "reason": { "name": "reason", "type": "text", "primaryKey": false, "notNull": false }, - "status": { "name": "status", "type": "impersonation_session_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'active'" }, - "started_at": { "name": "started_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "ended_at": { "name": "ended_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "expires_at": { "name": "expires_at", "type": "timestamp", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "staff_id": { + "name": "staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "impersonation_session_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": { - "impersonation_sessions_staff_id_status_idx": { "name": "impersonation_sessions_staff_id_status_idx", "columns": [{ "expression": "staff_id", "isExpression": false, "asc": true, "nulls": "last" }, { "expression": "status", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} }, - "impersonation_sessions_client_id_idx": { "name": "impersonation_sessions_client_id_idx", "columns": [{ "expression": "client_id", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} } + "impersonation_sessions_staff_id_status_idx": { + "name": "impersonation_sessions_staff_id_status_idx", + "columns": [ + { + "expression": "staff_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "impersonation_sessions_client_id_idx": { + "name": "impersonation_sessions_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } }, "foreignKeys": { - "impersonation_sessions_staff_id_staff_id_fk": { "name": "impersonation_sessions_staff_id_staff_id_fk", "tableFrom": "impersonation_sessions", "tableTo": "staff", "columnsFrom": ["staff_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "impersonation_sessions_client_id_clients_id_fk": { "name": "impersonation_sessions_client_id_clients_id_fk", "tableFrom": "impersonation_sessions", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" } + "impersonation_sessions_staff_id_staff_id_fk": { + "name": "impersonation_sessions_staff_id_staff_id_fk", + "tableFrom": "impersonation_sessions", + "tableTo": "staff", + "columnsFrom": [ + "staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "impersonation_sessions_client_id_clients_id_fk": { + "name": "impersonation_sessions_client_id_clients_id_fk", + "tableFrom": "impersonation_sessions", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -212,16 +899,68 @@ "name": "invoice_line_items", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "invoice_id": { "name": "invoice_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "description": { "name": "description", "type": "text", "primaryKey": false, "notNull": true }, - "quantity": { "name": "quantity", "type": "integer", "primaryKey": false, "notNull": true, "default": 1 }, - "unit_price_cents": { "name": "unit_price_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "total_cents": { "name": "total_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "invoice_id": { + "name": "invoice_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "unit_price_cents": { + "name": "unit_price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_cents": { + "name": "total_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "invoice_line_items_invoice_id_invoices_id_fk": { "name": "invoice_line_items_invoice_id_invoices_id_fk", "tableFrom": "invoice_line_items", "tableTo": "invoices", "columnsFrom": ["invoice_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "invoice_line_items_invoice_id_invoices_id_fk": { + "name": "invoice_line_items_invoice_id_invoices_id_fk", + "tableFrom": "invoice_line_items", + "tableTo": "invoices", + "columnsFrom": [ + "invoice_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -232,18 +971,79 @@ "name": "invoice_tip_splits", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "invoice_id": { "name": "invoice_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "staff_id": { "name": "staff_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "staff_name": { "name": "staff_name", "type": "text", "primaryKey": false, "notNull": true }, - "share_pct": { "name": "share_pct", "type": "numeric(5, 2)", "primaryKey": false, "notNull": true }, - "share_cents": { "name": "share_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "invoice_id": { + "name": "invoice_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "staff_id": { + "name": "staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "staff_name": { + "name": "staff_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "share_pct": { + "name": "share_pct", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true + }, + "share_cents": { + "name": "share_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { - "invoice_tip_splits_invoice_id_invoices_id_fk": { "name": "invoice_tip_splits_invoice_id_invoices_id_fk", "tableFrom": "invoice_tip_splits", "tableTo": "invoices", "columnsFrom": ["invoice_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, - "invoice_tip_splits_staff_id_staff_id_fk": { "name": "invoice_tip_splits_staff_id_staff_id_fk", "tableFrom": "invoice_tip_splits", "tableTo": "staff", "columnsFrom": ["staff_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } + "invoice_tip_splits_invoice_id_invoices_id_fk": { + "name": "invoice_tip_splits_invoice_id_invoices_id_fk", + "tableFrom": "invoice_tip_splits", + "tableTo": "invoices", + "columnsFrom": [ + "invoice_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invoice_tip_splits_staff_id_staff_id_fk": { + "name": "invoice_tip_splits_staff_id_staff_id_fk", + "tableFrom": "invoice_tip_splits", + "tableTo": "staff", + "columnsFrom": [ + "staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -255,24 +1055,121 @@ "name": "invoices", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "appointment_id": { "name": "appointment_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "subtotal_cents": { "name": "subtotal_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "tax_cents": { "name": "tax_cents", "type": "integer", "primaryKey": false, "notNull": true, "default": 0 }, - "tip_cents": { "name": "tip_cents", "type": "integer", "primaryKey": false, "notNull": true, "default": 0 }, - "total_cents": { "name": "total_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "status": { "name": "status", "type": "invoice_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'draft'" }, - "payment_method": { "name": "payment_method", "type": "payment_method", "typeSchema": "public", "primaryKey": false, "notNull": false }, - "paid_at": { "name": "paid_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "appointment_id": { + "name": "appointment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subtotal_cents": { + "name": "subtotal_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tax_cents": { + "name": "tax_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tip_cents": { + "name": "tip_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_cents": { + "name": "total_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "invoice_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_method", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { - "invoices_appointment_id_appointments_id_fk": { "name": "invoices_appointment_id_appointments_id_fk", "tableFrom": "invoices", "tableTo": "appointments", "columnsFrom": ["appointment_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "invoices_client_id_clients_id_fk": { "name": "invoices_client_id_clients_id_fk", "tableFrom": "invoices", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" } + "invoices_appointment_id_appointments_id_fk": { + "name": "invoices_appointment_id_appointments_id_fk", + "tableFrom": "invoices", + "tableTo": "appointments", + "columnsFrom": [ + "appointment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "invoices_client_id_clients_id_fk": { + "name": "invoices_client_id_clients_id_fk", + "tableFrom": "invoices", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -284,27 +1181,135 @@ "name": "pets", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "species": { "name": "species", "type": "text", "primaryKey": false, "notNull": true }, - "breed": { "name": "breed", "type": "text", "primaryKey": false, "notNull": false }, - "weight_kg": { "name": "weight_kg", "type": "numeric(5, 2)", "primaryKey": false, "notNull": false }, - "date_of_birth": { "name": "date_of_birth", "type": "timestamp", "primaryKey": false, "notNull": false }, - "health_alerts": { "name": "health_alerts", "type": "text", "primaryKey": false, "notNull": false }, - "grooming_notes": { "name": "grooming_notes", "type": "text", "primaryKey": false, "notNull": false }, - "cut_style": { "name": "cut_style", "type": "text", "primaryKey": false, "notNull": false }, - "shampoo_preference": { "name": "shampoo_preference", "type": "text", "primaryKey": false, "notNull": false }, - "special_care_notes": { "name": "special_care_notes", "type": "text", "primaryKey": false, "notNull": false }, - "custom_fields": { "name": "custom_fields", "type": "jsonb", "primaryKey": false, "notNull": true, "default": "'{}'::jsonb" }, - "photo_key": { "name": "photo_key", "type": "text", "primaryKey": false, "notNull": false }, - "photo_uploaded_at": { "name": "photo_uploaded_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "image": { "name": "image", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "species": { + "name": "species", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "breed": { + "name": "breed", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weight_kg": { + "name": "weight_kg", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "date_of_birth": { + "name": "date_of_birth", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "health_alerts": { + "name": "health_alerts", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grooming_notes": { + "name": "grooming_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cut_style": { + "name": "cut_style", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shampoo_preference": { + "name": "shampoo_preference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "special_care_notes": { + "name": "special_care_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_fields": { + "name": "custom_fields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "photo_key": { + "name": "photo_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "photo_uploaded_at": { + "name": "photo_uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "pets_client_id_clients_id_fk": { "name": "pets_client_id_clients_id_fk", "tableFrom": "pets", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "pets_client_id_clients_id_fk": { + "name": "pets_client_id_clients_id_fk", + "tableFrom": "pets", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -315,9 +1320,26 @@ "name": "recurring_series", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "frequency_weeks": { "name": "frequency_weeks", "type": "integer", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "frequency_weeks": { + "name": "frequency_weeks", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, @@ -331,15 +1353,60 @@ "name": "reminder_logs", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "appointment_id": { "name": "appointment_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "reminder_type": { "name": "reminder_type", "type": "text", "primaryKey": false, "notNull": true }, - "sent_at": { "name": "sent_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "appointment_id": { + "name": "appointment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "reminder_type": { + "name": "reminder_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "reminder_logs_appointment_id_appointments_id_fk": { "name": "reminder_logs_appointment_id_appointments_id_fk", "tableFrom": "reminder_logs", "tableTo": "appointments", "columnsFrom": ["appointment_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "reminder_logs_appointment_id_appointments_id_fk": { + "name": "reminder_logs_appointment_id_appointments_id_fk", + "tableFrom": "reminder_logs", + "tableTo": "appointments", + "columnsFrom": [ + "appointment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, - "uniqueConstraints": { "reminder_logs_appointment_id_reminder_type_unique": { "name": "reminder_logs_appointment_id_reminder_type_unique", "nullsNotDistinct": false, "columns": ["appointment_id", "reminder_type"] } }, + "uniqueConstraints": { + "reminder_logs_appointment_id_reminder_type_unique": { + "name": "reminder_logs_appointment_id_reminder_type_unique", + "nullsNotDistinct": false, + "columns": [ + "appointment_id", + "reminder_type" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -348,19 +1415,71 @@ "name": "services", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "description": { "name": "description", "type": "text", "primaryKey": false, "notNull": false }, - "base_price_cents": { "name": "base_price_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "duration_minutes": { "name": "duration_minutes", "type": "integer", "primaryKey": false, "notNull": true }, - "active": { "name": "active", "type": "boolean", "primaryKey": false, "notNull": true, "default": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_price_cents": { + "name": "base_price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "duration_minutes": { + "name": "duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": { "services_name_unique": { "name": "services_name_unique", "nullsNotDistinct": false, "columns": ["name"] } }, + "uniqueConstraints": { + "services_name_unique": { + "name": "services_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -369,19 +1488,83 @@ "name": "session", "schema": "", "columns": { - "id": { "name": "id", "type": "text", "primaryKey": true, "notNull": true }, - "expires_at": { "name": "expires_at", "type": "timestamp", "primaryKey": false, "notNull": true }, - "token": { "name": "token", "type": "text", "primaryKey": false, "notNull": true }, - "ip_address": { "name": "ip_address", "type": "text", "primaryKey": false, "notNull": false }, - "user_agent": { "name": "user_agent", "type": "text", "primaryKey": false, "notNull": false }, - "user_id": { "name": "user_id", "type": "text", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "session_user_id_user_id_fk": { "name": "session_user_id_user_id_fk", "tableFrom": "session", "tableTo": "user", "columnsFrom": ["user_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, - "uniqueConstraints": { "session_token_unique": { "name": "session_token_unique", "nullsNotDistinct": false, "columns": ["token"] } }, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -390,25 +1573,119 @@ "name": "staff", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "email": { "name": "email", "type": "text", "primaryKey": false, "notNull": true }, - "oidc_sub": { "name": "oidc_sub", "type": "text", "primaryKey": false, "notNull": false }, - "user_id": { "name": "user_id", "type": "text", "primaryKey": false, "notNull": false }, - "role": { "name": "role", "type": "staff_role", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'groomer'" }, - "is_super_user": { "name": "is_super_user", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "active": { "name": "active", "type": "boolean", "primaryKey": false, "notNull": true, "default": true }, - "ical_token": { "name": "ical_token", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oidc_sub": { + "name": "oidc_sub", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "staff_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'groomer'" + }, + "is_super_user": { + "name": "is_super_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "ical_token": { + "name": "ical_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "staff_user_id_user_id_fk": { "name": "staff_user_id_user_id_fk", "tableFrom": "staff", "tableTo": "user", "columnsFrom": ["user_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } }, + "foreignKeys": { + "staff_user_id_user_id_fk": { + "name": "staff_user_id_user_id_fk", + "tableFrom": "staff", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": { - "staff_email_unique": { "name": "staff_email_unique", "nullsNotDistinct": false, "columns": ["email"] }, - "staff_oidc_sub_unique": { "name": "staff_oidc_sub_unique", "nullsNotDistinct": false, "columns": ["oidc_sub"] }, - "staff_ical_token_unique": { "name": "staff_ical_token_unique", "nullsNotDistinct": false, "columns": ["ical_token"] } + "staff_email_unique": { + "name": "staff_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "staff_oidc_sub_unique": { + "name": "staff_oidc_sub_unique", + "nullsNotDistinct": false, + "columns": [ + "oidc_sub" + ] + }, + "staff_ical_token_unique": { + "name": "staff_ical_token_unique", + "nullsNotDistinct": false, + "columns": [ + "ical_token" + ] + } }, "policies": {}, "checkConstraints": {}, @@ -418,18 +1695,64 @@ "name": "user", "schema": "", "columns": { - "id": { "name": "id", "type": "text", "primaryKey": true, "notNull": true }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "email": { "name": "email", "type": "text", "primaryKey": false, "notNull": true }, - "email_verified": { "name": "email_verified", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "image": { "name": "image", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": { "user_email_unique": { "name": "user_email_unique", "nullsNotDistinct": false, "columns": ["email"] } }, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -438,12 +1761,44 @@ "name": "verification", "schema": "", "columns": { - "id": { "name": "id", "type": "text", "primaryKey": true, "notNull": true }, - "identifier": { "name": "identifier", "type": "text", "primaryKey": false, "notNull": true }, - "value": { "name": "value", "type": "text", "primaryKey": false, "notNull": true }, - "expires_at": { "name": "expires_at", "type": "timestamp", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, @@ -457,27 +1812,165 @@ "name": "waitlist_entries", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "pet_id": { "name": "pet_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "service_id": { "name": "service_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "preferred_date": { "name": "preferred_date", "type": "text", "primaryKey": false, "notNull": true }, - "preferred_time": { "name": "preferred_time", "type": "text", "primaryKey": false, "notNull": true }, - "status": { "name": "status", "type": "waitlist_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'active'" }, - "notified_at": { "name": "notified_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "expires_at": { "name": "expires_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pet_id": { + "name": "pet_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "service_id": { + "name": "service_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "preferred_date": { + "name": "preferred_date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "preferred_time": { + "name": "preferred_time", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "waitlist_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "notified_at": { + "name": "notified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": { - "idx_waitlist_client_id": { "name": "idx_waitlist_client_id", "columns": [{ "expression": "client_id", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} }, - "idx_waitlist_preferred_date": { "name": "idx_waitlist_preferred_date", "columns": [{ "expression": "preferred_date", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} }, - "idx_waitlist_status": { "name": "idx_waitlist_status", "columns": [{ "expression": "status", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} } + "idx_waitlist_client_id": { + "name": "idx_waitlist_client_id", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_waitlist_preferred_date": { + "name": "idx_waitlist_preferred_date", + "columns": [ + { + "expression": "preferred_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_waitlist_status": { + "name": "idx_waitlist_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } }, "foreignKeys": { - "waitlist_entries_client_id_clients_id_fk": { "name": "waitlist_entries_client_id_clients_id_fk", "tableFrom": "waitlist_entries", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, - "waitlist_entries_pet_id_pets_id_fk": { "name": "waitlist_entries_pet_id_pets_id_fk", "tableFrom": "waitlist_entries", "tableTo": "pets", "columnsFrom": ["pet_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, - "waitlist_entries_service_id_services_id_fk": { "name": "waitlist_entries_service_id_services_id_fk", "tableFrom": "waitlist_entries", "tableTo": "services", "columnsFrom": ["service_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } + "waitlist_entries_client_id_clients_id_fk": { + "name": "waitlist_entries_client_id_clients_id_fk", + "tableFrom": "waitlist_entries", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "waitlist_entries_pet_id_pets_id_fk": { + "name": "waitlist_entries_pet_id_pets_id_fk", + "tableFrom": "waitlist_entries", + "tableTo": "pets", + "columnsFrom": [ + "pet_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "waitlist_entries_service_id_services_id_fk": { + "name": "waitlist_entries_service_id_services_id_fk", + "tableFrom": "waitlist_entries", + "tableTo": "services", + "columnsFrom": [ + "service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -487,18 +1980,83 @@ } }, "enums": { - "public.appointment_status": { "name": "appointment_status", "schema": "public", "values": ["scheduled", "confirmed", "in_progress", "completed", "cancelled", "no_show"] }, - "public.client_status": { "name": "client_status", "schema": "public", "values": ["active", "disabled"] }, - "public.impersonation_session_status": { "name": "impersonation_session_status", "schema": "public", "values": ["active", "ended", "expired"] }, - "public.invoice_status": { "name": "invoice_status", "schema": "public", "values": ["draft", "pending", "paid", "void"] }, - "public.payment_method": { "name": "payment_method", "schema": "public", "values": ["cash", "card", "check", "other"] }, - "public.staff_role": { "name": "staff_role", "schema": "public", "values": ["groomer", "receptionist", "manager"] }, - "public.waitlist_status": { "name": "waitlist_status", "schema": "public", "values": ["active", "notified", "expired", "cancelled"] } + "public.appointment_status": { + "name": "appointment_status", + "schema": "public", + "values": [ + "scheduled", + "confirmed", + "in_progress", + "completed", + "cancelled", + "no_show" + ] + }, + "public.client_status": { + "name": "client_status", + "schema": "public", + "values": [ + "active", + "disabled" + ] + }, + "public.impersonation_session_status": { + "name": "impersonation_session_status", + "schema": "public", + "values": [ + "active", + "ended", + "expired" + ] + }, + "public.invoice_status": { + "name": "invoice_status", + "schema": "public", + "values": [ + "draft", + "pending", + "paid", + "void" + ] + }, + "public.payment_method": { + "name": "payment_method", + "schema": "public", + "values": [ + "cash", + "card", + "check", + "other" + ] + }, + "public.staff_role": { + "name": "staff_role", + "schema": "public", + "values": [ + "groomer", + "receptionist", + "manager" + ] + }, + "public.waitlist_status": { + "name": "waitlist_status", + "schema": "public", + "values": [ + "active", + "notified", + "expired", + "cancelled" + ] + } }, "schemas": {}, "sequences": {}, "roles": {}, "policies": {}, "views": {}, - "_meta": { "columns": {}, "schemas": {}, "tables": {} } + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } } \ No newline at end of file diff --git a/packages/db/migrations/meta/0022_snapshot.json b/packages/db/migrations/meta/0022_snapshot.json index a803ed0..cc2b31a 100644 --- a/packages/db/migrations/meta/0022_snapshot.json +++ b/packages/db/migrations/meta/0022_snapshot.json @@ -1,6 +1,6 @@ { "id": "9e8d3f2a-1c7b-4a6d-8f0e-5c2b9a3d7e1f", - "prevId": "b3a381ca-f7a4-450f-aa7e-fdc2d652dc97", + "prevId": "5815c09a-3b34-4d82-8718-e00c518b1e89", "version": "7", "dialect": "postgresql", "tables": { @@ -8,22 +8,103 @@ "name": "account", "schema": "", "columns": { - "id": { "name": "id", "type": "text", "primaryKey": true, "notNull": true }, - "account_id": { "name": "account_id", "type": "text", "primaryKey": false, "notNull": true }, - "provider_id": { "name": "provider_id", "type": "text", "primaryKey": false, "notNull": true }, - "user_id": { "name": "user_id", "type": "text", "primaryKey": false, "notNull": true }, - "access_token": { "name": "access_token", "type": "text", "primaryKey": false, "notNull": false }, - "refresh_token": { "name": "refresh_token", "type": "text", "primaryKey": false, "notNull": false }, - "id_token": { "name": "id_token", "type": "text", "primaryKey": false, "notNull": false }, - "access_token_expires_at": { "name": "access_token_expires_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "refresh_token_expires_at": { "name": "refresh_token_expires_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "scope": { "name": "scope", "type": "text", "primaryKey": false, "notNull": false }, - "password": { "name": "password", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "account_user_id_user_id_fk": { "name": "account_user_id_user_id_fk", "tableFrom": "account", "tableTo": "user", "columnsFrom": ["user_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -34,14 +115,56 @@ "name": "appointment_groups", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "appointment_groups_client_id_clients_id_fk": { "name": "appointment_groups_client_id_clients_id_fk", "tableFrom": "appointment_groups", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" } }, + "foreignKeys": { + "appointment_groups_client_id_clients_id_fk": { + "name": "appointment_groups_client_id_clients_id_fk", + "tableFrom": "appointment_groups", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -52,40 +175,243 @@ "name": "appointments", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "pet_id": { "name": "pet_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "service_id": { "name": "service_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "staff_id": { "name": "staff_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "bather_staff_id": { "name": "bather_staff_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "status": { "name": "status", "type": "appointment_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'scheduled'" }, - "start_time": { "name": "start_time", "type": "timestamp", "primaryKey": false, "notNull": true }, - "end_time": { "name": "end_time", "type": "timestamp", "primaryKey": false, "notNull": true }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "price_cents": { "name": "price_cents", "type": "integer", "primaryKey": false, "notNull": false }, - "series_id": { "name": "series_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "series_index": { "name": "series_index", "type": "integer", "primaryKey": false, "notNull": false }, - "group_id": { "name": "group_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "confirmation_status": { "name": "confirmation_status", "type": "text", "primaryKey": false, "notNull": true, "default": "'pending'" }, - "confirmed_at": { "name": "confirmed_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "cancelled_at": { "name": "cancelled_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "confirmation_token": { "name": "confirmation_token", "type": "text", "primaryKey": false, "notNull": false }, - "customer_notes": { "name": "customer_notes", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pet_id": { + "name": "pet_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "service_id": { + "name": "service_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "staff_id": { + "name": "staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "bather_staff_id": { + "name": "bather_staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "appointment_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "start_time": { + "name": "start_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price_cents": { + "name": "price_cents", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "series_id": { + "name": "series_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "series_index": { + "name": "series_index", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "group_id": { + "name": "group_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "confirmation_status": { + "name": "confirmation_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "confirmed_at": { + "name": "confirmed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "confirmation_token": { + "name": "confirmation_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_notes": { + "name": "customer_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { - "appointments_client_id_clients_id_fk": { "name": "appointments_client_id_clients_id_fk", "tableFrom": "appointments", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "appointments_pet_id_pets_id_fk": { "name": "appointments_pet_id_pets_id_fk", "tableFrom": "appointments", "tableTo": "pets", "columnsFrom": ["pet_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "appointments_service_id_services_id_fk": { "name": "appointments_service_id_services_id_fk", "tableFrom": "appointments", "tableTo": "services", "columnsFrom": ["service_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "appointments_staff_id_staff_id_fk": { "name": "appointments_staff_id_staff_id_fk", "tableFrom": "appointments", "tableTo": "staff", "columnsFrom": ["staff_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" }, - "appointments_bather_staff_id_staff_id_fk": { "name": "appointments_bather_staff_id_staff_id_fk", "tableFrom": "appointments", "tableTo": "staff", "columnsFrom": ["bather_staff_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" }, - "appointments_series_id_recurring_series_id_fk": { "name": "appointments_series_id_recurring_series_id_fk", "tableFrom": "appointments", "tableTo": "recurring_series", "columnsFrom": ["series_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" }, - "appointments_group_id_appointment_groups_id_fk": { "name": "appointments_group_id_appointment_groups_id_fk", "tableFrom": "appointments", "tableTo": "appointment_groups", "columnsFrom": ["group_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } + "appointments_client_id_clients_id_fk": { + "name": "appointments_client_id_clients_id_fk", + "tableFrom": "appointments", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "appointments_pet_id_pets_id_fk": { + "name": "appointments_pet_id_pets_id_fk", + "tableFrom": "appointments", + "tableTo": "pets", + "columnsFrom": [ + "pet_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "appointments_service_id_services_id_fk": { + "name": "appointments_service_id_services_id_fk", + "tableFrom": "appointments", + "tableTo": "services", + "columnsFrom": [ + "service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "appointments_staff_id_staff_id_fk": { + "name": "appointments_staff_id_staff_id_fk", + "tableFrom": "appointments", + "tableTo": "staff", + "columnsFrom": [ + "staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "appointments_bather_staff_id_staff_id_fk": { + "name": "appointments_bather_staff_id_staff_id_fk", + "tableFrom": "appointments", + "tableTo": "staff", + "columnsFrom": [ + "bather_staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "appointments_series_id_recurring_series_id_fk": { + "name": "appointments_series_id_recurring_series_id_fk", + "tableFrom": "appointments", + "tableTo": "recurring_series", + "columnsFrom": [ + "series_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "appointments_group_id_appointment_groups_id_fk": { + "name": "appointments_group_id_appointment_groups_id_fk", + "tableFrom": "appointments", + "tableTo": "appointment_groups", + "columnsFrom": [ + "group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, - "uniqueConstraints": { "appointments_confirmation_token_unique": { "name": "appointments_confirmation_token_unique", "nullsNotDistinct": false, "columns": ["confirmation_token"] } }, + "uniqueConstraints": { + "appointments_confirmation_token_unique": { + "name": "appointments_confirmation_token_unique", + "nullsNotDistinct": false, + "columns": [ + "confirmation_token" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -94,15 +420,66 @@ "name": "business_settings", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "business_name": { "name": "business_name", "type": "text", "primaryKey": false, "notNull": true, "default": "'GroomBook'" }, - "logo_base64": { "name": "logo_base64", "type": "text", "primaryKey": false, "notNull": false }, - "logo_mime_type": { "name": "logo_mime_type", "type": "text", "primaryKey": false, "notNull": false }, - "logo_key": { "name": "logo_key", "type": "text", "primaryKey": false, "notNull": false }, - "primary_color": { "name": "primary_color", "type": "text", "primaryKey": false, "notNull": true, "default": "'#4f8a6f'" }, - "accent_color": { "name": "accent_color", "type": "text", "primaryKey": false, "notNull": true, "default": "'#8b7355'" }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "business_name": { + "name": "business_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'GroomBook'" + }, + "logo_base64": { + "name": "logo_base64", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo_mime_type": { + "name": "logo_mime_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo_key": { + "name": "logo_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "primary_color": { + "name": "primary_color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#4f8a6f'" + }, + "accent_color": { + "name": "accent_color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#8b7355'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, @@ -116,17 +493,78 @@ "name": "clients", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "email": { "name": "email", "type": "text", "primaryKey": false, "notNull": false }, - "phone": { "name": "phone", "type": "text", "primaryKey": false, "notNull": false }, - "address": { "name": "address", "type": "text", "primaryKey": false, "notNull": false }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "email_opt_out": { "name": "email_opt_out", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "status": { "name": "status", "type": "client_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'active'" }, - "disabled_at": { "name": "disabled_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_opt_out": { + "name": "email_opt_out", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "status": { + "name": "status", + "type": "client_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "disabled_at": { + "name": "disabled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, @@ -140,21 +578,105 @@ "name": "grooming_visit_logs", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "pet_id": { "name": "pet_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "appointment_id": { "name": "appointment_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "staff_id": { "name": "staff_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "cut_style": { "name": "cut_style", "type": "text", "primaryKey": false, "notNull": false }, - "products_used": { "name": "products_used", "type": "text", "primaryKey": false, "notNull": false }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "groomed_at": { "name": "groomed_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "pet_id": { + "name": "pet_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "appointment_id": { + "name": "appointment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "staff_id": { + "name": "staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cut_style": { + "name": "cut_style", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "products_used": { + "name": "products_used", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "groomed_at": { + "name": "groomed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { - "grooming_visit_logs_pet_id_pets_id_fk": { "name": "grooming_visit_logs_pet_id_pets_id_fk", "tableFrom": "grooming_visit_logs", "tableTo": "pets", "columnsFrom": ["pet_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, - "grooming_visit_logs_appointment_id_appointments_id_fk": { "name": "grooming_visit_logs_appointment_id_appointments_id_fk", "tableFrom": "grooming_visit_logs", "tableTo": "appointments", "columnsFrom": ["appointment_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" }, - "grooming_visit_logs_staff_id_staff_id_fk": { "name": "grooming_visit_logs_staff_id_staff_id_fk", "tableFrom": "grooming_visit_logs", "tableTo": "staff", "columnsFrom": ["staff_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } + "grooming_visit_logs_pet_id_pets_id_fk": { + "name": "grooming_visit_logs_pet_id_pets_id_fk", + "tableFrom": "grooming_visit_logs", + "tableTo": "pets", + "columnsFrom": [ + "pet_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "grooming_visit_logs_appointment_id_appointments_id_fk": { + "name": "grooming_visit_logs_appointment_id_appointments_id_fk", + "tableFrom": "grooming_visit_logs", + "tableTo": "appointments", + "columnsFrom": [ + "appointment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "grooming_visit_logs_staff_id_staff_id_fk": { + "name": "grooming_visit_logs_staff_id_staff_id_fk", + "tableFrom": "grooming_visit_logs", + "tableTo": "staff", + "columnsFrom": [ + "staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -166,15 +688,77 @@ "name": "impersonation_audit_logs", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "session_id": { "name": "session_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "action": { "name": "action", "type": "text", "primaryKey": false, "notNull": true }, - "page_visited": { "name": "page_visited", "type": "text", "primaryKey": false, "notNull": false }, - "metadata": { "name": "metadata", "type": "jsonb", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "session_id": { + "name": "session_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "page_visited": { + "name": "page_visited", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "impersonation_audit_logs_session_id_idx": { + "name": "impersonation_audit_logs_session_id_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impersonation_audit_logs_session_id_impersonation_sessions_id_fk": { + "name": "impersonation_audit_logs_session_id_impersonation_sessions_id_fk", + "tableFrom": "impersonation_audit_logs", + "tableTo": "impersonation_sessions", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } }, - "indexes": { "impersonation_audit_logs_session_id_idx": { "name": "impersonation_audit_logs_session_id_idx", "columns": [{ "expression": "session_id", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} } }, - "foreignKeys": { "impersonation_audit_logs_session_id_impersonation_sessions_id_fk": { "name": "impersonation_audit_logs_session_id_impersonation_sessions_id_fk", "tableFrom": "impersonation_audit_logs", "tableTo": "impersonation_sessions", "columnsFrom": ["session_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -185,23 +769,131 @@ "name": "impersonation_sessions", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "staff_id": { "name": "staff_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "reason": { "name": "reason", "type": "text", "primaryKey": false, "notNull": false }, - "status": { "name": "status", "type": "impersonation_session_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'active'" }, - "started_at": { "name": "started_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "ended_at": { "name": "ended_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "expires_at": { "name": "expires_at", "type": "timestamp", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "staff_id": { + "name": "staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "impersonation_session_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": { - "impersonation_sessions_staff_id_status_idx": { "name": "impersonation_sessions_staff_id_status_idx", "columns": [{ "expression": "staff_id", "isExpression": false, "asc": true, "nulls": "last" }, { "expression": "status", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} }, - "impersonation_sessions_client_id_idx": { "name": "impersonation_sessions_client_id_idx", "columns": [{ "expression": "client_id", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} } + "impersonation_sessions_staff_id_status_idx": { + "name": "impersonation_sessions_staff_id_status_idx", + "columns": [ + { + "expression": "staff_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "impersonation_sessions_client_id_idx": { + "name": "impersonation_sessions_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } }, "foreignKeys": { - "impersonation_sessions_staff_id_staff_id_fk": { "name": "impersonation_sessions_staff_id_staff_id_fk", "tableFrom": "impersonation_sessions", "tableTo": "staff", "columnsFrom": ["staff_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "impersonation_sessions_client_id_clients_id_fk": { "name": "impersonation_sessions_client_id_clients_id_fk", "tableFrom": "impersonation_sessions", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" } + "impersonation_sessions_staff_id_staff_id_fk": { + "name": "impersonation_sessions_staff_id_staff_id_fk", + "tableFrom": "impersonation_sessions", + "tableTo": "staff", + "columnsFrom": [ + "staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "impersonation_sessions_client_id_clients_id_fk": { + "name": "impersonation_sessions_client_id_clients_id_fk", + "tableFrom": "impersonation_sessions", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -213,16 +905,68 @@ "name": "invoice_line_items", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "invoice_id": { "name": "invoice_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "description": { "name": "description", "type": "text", "primaryKey": false, "notNull": true }, - "quantity": { "name": "quantity", "type": "integer", "primaryKey": false, "notNull": true, "default": 1 }, - "unit_price_cents": { "name": "unit_price_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "total_cents": { "name": "total_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "invoice_id": { + "name": "invoice_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "unit_price_cents": { + "name": "unit_price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_cents": { + "name": "total_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "invoice_line_items_invoice_id_invoices_id_fk": { "name": "invoice_line_items_invoice_id_invoices_id_fk", "tableFrom": "invoice_line_items", "tableTo": "invoices", "columnsFrom": ["invoice_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "invoice_line_items_invoice_id_invoices_id_fk": { + "name": "invoice_line_items_invoice_id_invoices_id_fk", + "tableFrom": "invoice_line_items", + "tableTo": "invoices", + "columnsFrom": [ + "invoice_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -233,18 +977,79 @@ "name": "invoice_tip_splits", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "invoice_id": { "name": "invoice_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "staff_id": { "name": "staff_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "staff_name": { "name": "staff_name", "type": "text", "primaryKey": false, "notNull": true }, - "share_pct": { "name": "share_pct", "type": "numeric(5, 2)", "primaryKey": false, "notNull": true }, - "share_cents": { "name": "share_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "invoice_id": { + "name": "invoice_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "staff_id": { + "name": "staff_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "staff_name": { + "name": "staff_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "share_pct": { + "name": "share_pct", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true + }, + "share_cents": { + "name": "share_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { - "invoice_tip_splits_invoice_id_invoices_id_fk": { "name": "invoice_tip_splits_invoice_id_invoices_id_fk", "tableFrom": "invoice_tip_splits", "tableTo": "invoices", "columnsFrom": ["invoice_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, - "invoice_tip_splits_staff_id_staff_id_fk": { "name": "invoice_tip_splits_staff_id_staff_id_fk", "tableFrom": "invoice_tip_splits", "tableTo": "staff", "columnsFrom": ["staff_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } + "invoice_tip_splits_invoice_id_invoices_id_fk": { + "name": "invoice_tip_splits_invoice_id_invoices_id_fk", + "tableFrom": "invoice_tip_splits", + "tableTo": "invoices", + "columnsFrom": [ + "invoice_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invoice_tip_splits_staff_id_staff_id_fk": { + "name": "invoice_tip_splits_staff_id_staff_id_fk", + "tableFrom": "invoice_tip_splits", + "tableTo": "staff", + "columnsFrom": [ + "staff_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -256,24 +1061,121 @@ "name": "invoices", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "appointment_id": { "name": "appointment_id", "type": "uuid", "primaryKey": false, "notNull": false }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "subtotal_cents": { "name": "subtotal_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "tax_cents": { "name": "tax_cents", "type": "integer", "primaryKey": false, "notNull": true, "default": 0 }, - "tip_cents": { "name": "tip_cents", "type": "integer", "primaryKey": false, "notNull": true, "default": 0 }, - "total_cents": { "name": "total_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "status": { "name": "status", "type": "invoice_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'draft'" }, - "payment_method": { "name": "payment_method", "type": "payment_method", "typeSchema": "public", "primaryKey": false, "notNull": false }, - "paid_at": { "name": "paid_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "notes": { "name": "notes", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "appointment_id": { + "name": "appointment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subtotal_cents": { + "name": "subtotal_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tax_cents": { + "name": "tax_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "tip_cents": { + "name": "tip_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_cents": { + "name": "total_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "invoice_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "payment_method": { + "name": "payment_method", + "type": "payment_method", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": { - "invoices_appointment_id_appointments_id_fk": { "name": "invoices_appointment_id_appointments_id_fk", "tableFrom": "invoices", "tableTo": "appointments", "columnsFrom": ["appointment_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" }, - "invoices_client_id_clients_id_fk": { "name": "invoices_client_id_clients_id_fk", "tableFrom": "invoices", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "restrict", "onUpdate": "no action" } + "invoices_appointment_id_appointments_id_fk": { + "name": "invoices_appointment_id_appointments_id_fk", + "tableFrom": "invoices", + "tableTo": "appointments", + "columnsFrom": [ + "appointment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "invoices_client_id_clients_id_fk": { + "name": "invoices_client_id_clients_id_fk", + "tableFrom": "invoices", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -285,27 +1187,135 @@ "name": "pets", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "species": { "name": "species", "type": "text", "primaryKey": false, "notNull": true }, - "breed": { "name": "breed", "type": "text", "primaryKey": false, "notNull": false }, - "weight_kg": { "name": "weight_kg", "type": "numeric(5, 2)", "primaryKey": false, "notNull": false }, - "date_of_birth": { "name": "date_of_birth", "type": "timestamp", "primaryKey": false, "notNull": false }, - "health_alerts": { "name": "health_alerts", "type": "text", "primaryKey": false, "notNull": false }, - "grooming_notes": { "name": "grooming_notes", "type": "text", "primaryKey": false, "notNull": false }, - "cut_style": { "name": "cut_style", "type": "text", "primaryKey": false, "notNull": false }, - "shampoo_preference": { "name": "shampoo_preference", "type": "text", "primaryKey": false, "notNull": false }, - "special_care_notes": { "name": "special_care_notes", "type": "text", "primaryKey": false, "notNull": false }, - "custom_fields": { "name": "custom_fields", "type": "jsonb", "primaryKey": false, "notNull": true, "default": "'{}'::jsonb" }, - "photo_key": { "name": "photo_key", "type": "text", "primaryKey": false, "notNull": false }, - "photo_uploaded_at": { "name": "photo_uploaded_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "image": { "name": "image", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "species": { + "name": "species", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "breed": { + "name": "breed", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weight_kg": { + "name": "weight_kg", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "date_of_birth": { + "name": "date_of_birth", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "health_alerts": { + "name": "health_alerts", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grooming_notes": { + "name": "grooming_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cut_style": { + "name": "cut_style", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shampoo_preference": { + "name": "shampoo_preference", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "special_care_notes": { + "name": "special_care_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "custom_fields": { + "name": "custom_fields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "photo_key": { + "name": "photo_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "photo_uploaded_at": { + "name": "photo_uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "pets_client_id_clients_id_fk": { "name": "pets_client_id_clients_id_fk", "tableFrom": "pets", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "pets_client_id_clients_id_fk": { + "name": "pets_client_id_clients_id_fk", + "tableFrom": "pets", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -316,9 +1326,26 @@ "name": "recurring_series", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "frequency_weeks": { "name": "frequency_weeks", "type": "integer", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "frequency_weeks": { + "name": "frequency_weeks", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, @@ -332,15 +1359,60 @@ "name": "reminder_logs", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "appointment_id": { "name": "appointment_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "reminder_type": { "name": "reminder_type", "type": "text", "primaryKey": false, "notNull": true }, - "sent_at": { "name": "sent_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "appointment_id": { + "name": "appointment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "reminder_type": { + "name": "reminder_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "reminder_logs_appointment_id_appointments_id_fk": { "name": "reminder_logs_appointment_id_appointments_id_fk", "tableFrom": "reminder_logs", "tableTo": "appointments", "columnsFrom": ["appointment_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "reminder_logs_appointment_id_appointments_id_fk": { + "name": "reminder_logs_appointment_id_appointments_id_fk", + "tableFrom": "reminder_logs", + "tableTo": "appointments", + "columnsFrom": [ + "appointment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, - "uniqueConstraints": { "reminder_logs_appointment_id_reminder_type_unique": { "name": "reminder_logs_appointment_id_reminder_type_unique", "nullsNotDistinct": false, "columns": ["appointment_id", "reminder_type"] } }, + "uniqueConstraints": { + "reminder_logs_appointment_id_reminder_type_unique": { + "name": "reminder_logs_appointment_id_reminder_type_unique", + "nullsNotDistinct": false, + "columns": [ + "appointment_id", + "reminder_type" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -349,19 +1421,71 @@ "name": "services", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "description": { "name": "description", "type": "text", "primaryKey": false, "notNull": false }, - "base_price_cents": { "name": "base_price_cents", "type": "integer", "primaryKey": false, "notNull": true }, - "duration_minutes": { "name": "duration_minutes", "type": "integer", "primaryKey": false, "notNull": true }, - "active": { "name": "active", "type": "boolean", "primaryKey": false, "notNull": true, "default": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_price_cents": { + "name": "base_price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "duration_minutes": { + "name": "duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": { "services_name_unique": { "name": "services_name_unique", "nullsNotDistinct": false, "columns": ["name"] } }, + "uniqueConstraints": { + "services_name_unique": { + "name": "services_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -370,19 +1494,83 @@ "name": "session", "schema": "", "columns": { - "id": { "name": "id", "type": "text", "primaryKey": true, "notNull": true }, - "expires_at": { "name": "expires_at", "type": "timestamp", "primaryKey": false, "notNull": true }, - "token": { "name": "token", "type": "text", "primaryKey": false, "notNull": true }, - "ip_address": { "name": "ip_address", "type": "text", "primaryKey": false, "notNull": false }, - "user_agent": { "name": "user_agent", "type": "text", "primaryKey": false, "notNull": false }, - "user_id": { "name": "user_id", "type": "text", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "session_user_id_user_id_fk": { "name": "session_user_id_user_id_fk", "tableFrom": "session", "tableTo": "user", "columnsFrom": ["user_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, - "uniqueConstraints": { "session_token_unique": { "name": "session_token_unique", "nullsNotDistinct": false, "columns": ["token"] } }, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -391,25 +1579,119 @@ "name": "staff", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "email": { "name": "email", "type": "text", "primaryKey": false, "notNull": true }, - "oidc_sub": { "name": "oidc_sub", "type": "text", "primaryKey": false, "notNull": false }, - "user_id": { "name": "user_id", "type": "text", "primaryKey": false, "notNull": false }, - "role": { "name": "role", "type": "staff_role", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'groomer'" }, - "is_super_user": { "name": "is_super_user", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "active": { "name": "active", "type": "boolean", "primaryKey": false, "notNull": true, "default": true }, - "ical_token": { "name": "ical_token", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oidc_sub": { + "name": "oidc_sub", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "staff_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'groomer'" + }, + "is_super_user": { + "name": "is_super_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "ical_token": { + "name": "ical_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, - "foreignKeys": { "staff_user_id_user_id_fk": { "name": "staff_user_id_user_id_fk", "tableFrom": "staff", "tableTo": "user", "columnsFrom": ["user_id"], "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "no action" } }, + "foreignKeys": { + "staff_user_id_user_id_fk": { + "name": "staff_user_id_user_id_fk", + "tableFrom": "staff", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": { - "staff_email_unique": { "name": "staff_email_unique", "nullsNotDistinct": false, "columns": ["email"] }, - "staff_oidc_sub_unique": { "name": "staff_oidc_sub_unique", "nullsNotDistinct": false, "columns": ["oidc_sub"] }, - "staff_ical_token_unique": { "name": "staff_ical_token_unique", "nullsNotDistinct": false, "columns": ["ical_token"] } + "staff_email_unique": { + "name": "staff_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "staff_oidc_sub_unique": { + "name": "staff_oidc_sub_unique", + "nullsNotDistinct": false, + "columns": [ + "oidc_sub" + ] + }, + "staff_ical_token_unique": { + "name": "staff_ical_token_unique", + "nullsNotDistinct": false, + "columns": [ + "ical_token" + ] + } }, "policies": {}, "checkConstraints": {}, @@ -419,18 +1701,64 @@ "name": "user", "schema": "", "columns": { - "id": { "name": "id", "type": "text", "primaryKey": true, "notNull": true }, - "name": { "name": "name", "type": "text", "primaryKey": false, "notNull": true }, - "email": { "name": "email", "type": "text", "primaryKey": false, "notNull": true }, - "email_verified": { "name": "email_verified", "type": "boolean", "primaryKey": false, "notNull": true, "default": false }, - "image": { "name": "image", "type": "text", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": { "user_email_unique": { "name": "user_email_unique", "nullsNotDistinct": false, "columns": ["email"] } }, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -439,12 +1767,44 @@ "name": "verification", "schema": "", "columns": { - "id": { "name": "id", "type": "text", "primaryKey": true, "notNull": true }, - "identifier": { "name": "identifier", "type": "text", "primaryKey": false, "notNull": true }, - "value": { "name": "value", "type": "text", "primaryKey": false, "notNull": true }, - "expires_at": { "name": "expires_at", "type": "timestamp", "primaryKey": false, "notNull": true }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": {}, "foreignKeys": {}, @@ -458,27 +1818,165 @@ "name": "waitlist_entries", "schema": "", "columns": { - "id": { "name": "id", "type": "uuid", "primaryKey": true, "notNull": true, "default": "gen_random_uuid()" }, - "client_id": { "name": "client_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "pet_id": { "name": "pet_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "service_id": { "name": "service_id", "type": "uuid", "primaryKey": false, "notNull": true }, - "preferred_date": { "name": "preferred_date", "type": "text", "primaryKey": false, "notNull": true }, - "preferred_time": { "name": "preferred_time", "type": "text", "primaryKey": false, "notNull": true }, - "status": { "name": "status", "type": "waitlist_status", "typeSchema": "public", "primaryKey": false, "notNull": true, "default": "'active'" }, - "notified_at": { "name": "notified_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "expires_at": { "name": "expires_at", "type": "timestamp", "primaryKey": false, "notNull": false }, - "created_at": { "name": "created_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" }, - "updated_at": { "name": "updated_at", "type": "timestamp", "primaryKey": false, "notNull": true, "default": "now()" } + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "client_id": { + "name": "client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pet_id": { + "name": "pet_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "service_id": { + "name": "service_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "preferred_date": { + "name": "preferred_date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "preferred_time": { + "name": "preferred_time", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "waitlist_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "notified_at": { + "name": "notified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } }, "indexes": { - "idx_waitlist_client_id": { "name": "idx_waitlist_client_id", "columns": [{ "expression": "client_id", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} }, - "idx_waitlist_preferred_date": { "name": "idx_waitlist_preferred_date", "columns": [{ "expression": "preferred_date", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} }, - "idx_waitlist_status": { "name": "idx_waitlist_status", "columns": [{ "expression": "status", "isExpression": false, "asc": true, "nulls": "last" }], "isUnique": false, "concurrently": false, "method": "btree", "with": {} } + "idx_waitlist_client_id": { + "name": "idx_waitlist_client_id", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_waitlist_preferred_date": { + "name": "idx_waitlist_preferred_date", + "columns": [ + { + "expression": "preferred_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_waitlist_status": { + "name": "idx_waitlist_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } }, "foreignKeys": { - "waitlist_entries_client_id_clients_id_fk": { "name": "waitlist_entries_client_id_clients_id_fk", "tableFrom": "waitlist_entries", "tableTo": "clients", "columnsFrom": ["client_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, - "waitlist_entries_pet_id_pets_id_fk": { "name": "waitlist_entries_pet_id_pets_id_fk", "tableFrom": "waitlist_entries", "tableTo": "pets", "columnsFrom": ["pet_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, - "waitlist_entries_service_id_services_id_fk": { "name": "waitlist_entries_service_id_services_id_fk", "tableFrom": "waitlist_entries", "tableTo": "services", "columnsFrom": ["service_id"], "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } + "waitlist_entries_client_id_clients_id_fk": { + "name": "waitlist_entries_client_id_clients_id_fk", + "tableFrom": "waitlist_entries", + "tableTo": "clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "waitlist_entries_pet_id_pets_id_fk": { + "name": "waitlist_entries_pet_id_pets_id_fk", + "tableFrom": "waitlist_entries", + "tableTo": "pets", + "columnsFrom": [ + "pet_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "waitlist_entries_service_id_services_id_fk": { + "name": "waitlist_entries_service_id_services_id_fk", + "tableFrom": "waitlist_entries", + "tableTo": "services", + "columnsFrom": [ + "service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, @@ -488,18 +1986,83 @@ } }, "enums": { - "public.appointment_status": { "name": "appointment_status", "schema": "public", "values": ["scheduled", "confirmed", "in_progress", "completed", "cancelled", "no_show"] }, - "public.client_status": { "name": "client_status", "schema": "public", "values": ["active", "disabled"] }, - "public.impersonation_session_status": { "name": "impersonation_session_status", "schema": "public", "values": ["active", "ended", "expired"] }, - "public.invoice_status": { "name": "invoice_status", "schema": "public", "values": ["draft", "pending", "paid", "void"] }, - "public.payment_method": { "name": "payment_method", "schema": "public", "values": ["cash", "card", "check", "other"] }, - "public.staff_role": { "name": "staff_role", "schema": "public", "values": ["groomer", "receptionist", "manager"] }, - "public.waitlist_status": { "name": "waitlist_status", "schema": "public", "values": ["active", "notified", "expired", "cancelled"] } + "public.appointment_status": { + "name": "appointment_status", + "schema": "public", + "values": [ + "scheduled", + "confirmed", + "in_progress", + "completed", + "cancelled", + "no_show" + ] + }, + "public.client_status": { + "name": "client_status", + "schema": "public", + "values": [ + "active", + "disabled" + ] + }, + "public.impersonation_session_status": { + "name": "impersonation_session_status", + "schema": "public", + "values": [ + "active", + "ended", + "expired" + ] + }, + "public.invoice_status": { + "name": "invoice_status", + "schema": "public", + "values": [ + "draft", + "pending", + "paid", + "void" + ] + }, + "public.payment_method": { + "name": "payment_method", + "schema": "public", + "values": [ + "cash", + "card", + "check", + "other" + ] + }, + "public.staff_role": { + "name": "staff_role", + "schema": "public", + "values": [ + "groomer", + "receptionist", + "manager" + ] + }, + "public.waitlist_status": { + "name": "waitlist_status", + "schema": "public", + "values": [ + "active", + "notified", + "expired", + "cancelled" + ] + } }, "schemas": {}, "sequences": {}, "roles": {}, "policies": {}, "views": {}, - "_meta": { "columns": {}, "schemas": {}, "tables": {} } + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } } \ No newline at end of file diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index 7ca1dc5..cea9320 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -183,6 +183,13 @@ "when": 1775482467192, "tag": "0025_rate_limit", "breakpoints": true + }, + { + "idx": 26, + "version": "7", + "when": 1776035812477, + "tag": "0026_boring_storm", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/factories.ts b/packages/db/src/factories.ts index ca7cf4b..7ff3cc0 100644 --- a/packages/db/src/factories.ts +++ b/packages/db/src/factories.ts @@ -71,6 +71,10 @@ export function buildClient(overrides: Partial = {}): ClientRow { address: "1 Main St, Springfield, CA 90000", notes: null, emailOptOut: false, + smsOptIn: false, + smsConsentDate: null, + smsOptOutDate: null, + smsConsentText: null, status: "active", disabledAt: null, createdAt: new Date("2025-01-01T00:00:00Z"), diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts index 9698b52..8612713 100644 --- a/packages/db/src/schema.ts +++ b/packages/db/src/schema.ts @@ -109,8 +109,11 @@ export const clients = pgTable("clients", { phone: text("phone"), address: text("address"), notes: text("notes"), - // Set to true if the client has opted out of email reminders/notifications emailOptOut: boolean("email_opt_out").notNull().default(false), + smsOptIn: boolean("sms_opt_in").notNull().default(false), + smsConsentDate: timestamp("sms_consent_date"), + smsOptOutDate: timestamp("sms_opt_out_date"), + smsConsentText: text("sms_consent_text"), status: clientStatusEnum("status").notNull().default("active"), disabledAt: timestamp("disabled_at"), createdAt: timestamp("created_at").notNull().defaultNow(), @@ -305,11 +308,11 @@ export const reminderLogs = pgTable( appointmentId: uuid("appointment_id") .notNull() .references(() => appointments.id, { onDelete: "cascade" }), - // "confirmation" | "24h" | "2h" reminderType: text("reminder_type").notNull(), + channel: text("channel").notNull().default("email"), sentAt: timestamp("sent_at").notNull().defaultNow(), }, - (t) => [unique().on(t.appointmentId, t.reminderType)] + (t) => [unique().on(t.appointmentId, t.reminderType, t.channel)] ); // ─── Impersonation ──────────────────────────────────────────────────────────