merge: resolve conflicts between feat/impersonation-backend and main
Keep both backend impersonation (schema, routes, types) and main's additions (settings, branding, dev login, full customer portal UI). Portal frontend files retain main's versions (complete UI with sidebar, sections, mock impersonation). Wiring frontend to real impersonation backend API remains as follow-up work. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE IF NOT EXISTS "business_settings" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"business_name" text DEFAULT 'GroomBook' NOT NULL,
|
||||
"logo_base64" text,
|
||||
"logo_mime_type" text,
|
||||
"primary_color" text DEFAULT '#4f8a6f' NOT NULL,
|
||||
"accent_color" text DEFAULT '#8b7355' NOT NULL,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
|
||||
-- Seed a default row so GET always returns something
|
||||
INSERT INTO "business_settings" ("business_name", "primary_color", "accent_color")
|
||||
VALUES ('GroomBook', '#4f8a6f', '#8b7355')
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Add client status (soft-delete support)
|
||||
CREATE TYPE "client_status" AS ENUM ('active', 'disabled');
|
||||
|
||||
ALTER TABLE "clients"
|
||||
ADD COLUMN "status" "client_status" NOT NULL DEFAULT 'active',
|
||||
ADD COLUMN "disabled_at" timestamp;
|
||||
@@ -57,6 +57,20 @@
|
||||
"when": 1773820800000,
|
||||
"tag": "0007_tip_splitting",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "7",
|
||||
"when": 1773907200000,
|
||||
"tag": "0008_business_settings",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"version": "7",
|
||||
"when": 1773993600000,
|
||||
"tag": "0009_client_soft_delete",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,8 +7,8 @@
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"default": "./dist/index.js"
|
||||
"default": "./dist/index.js",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -42,6 +42,11 @@ export const paymentMethodEnum = pgEnum("payment_method", [
|
||||
"other",
|
||||
]);
|
||||
|
||||
export const clientStatusEnum = pgEnum("client_status", [
|
||||
"active",
|
||||
"disabled",
|
||||
]);
|
||||
|
||||
// ─── Tables ───────────────────────────────────────────────────────────────────
|
||||
|
||||
export const clients = pgTable("clients", {
|
||||
@@ -53,6 +58,8 @@ export const clients = pgTable("clients", {
|
||||
notes: text("notes"),
|
||||
// Set to true if the client has opted out of email reminders/notifications
|
||||
emailOptOut: boolean("email_opt_out").notNull().default(false),
|
||||
status: clientStatusEnum("status").notNull().default("active"),
|
||||
disabledAt: timestamp("disabled_at"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
});
|
||||
@@ -252,6 +259,17 @@ export const impersonationAuditLogs = pgTable("impersonation_audit_logs", {
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const businessSettings = pgTable("business_settings", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
businessName: text("business_name").notNull().default("GroomBook"),
|
||||
logoBase64: text("logo_base64"),
|
||||
logoMimeType: text("logo_mime_type"),
|
||||
primaryColor: text("primary_color").notNull().default("#4f8a6f"),
|
||||
accentColor: text("accent_color").notNull().default("#8b7355"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const groomingVisitLogs = pgTable("grooming_visit_logs", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
petId: uuid("pet_id")
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"default": "./dist/index.js"
|
||||
"default": "./dist/index.js",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -8,6 +8,8 @@ export type AppointmentStatus =
|
||||
| "cancelled"
|
||||
| "no_show";
|
||||
|
||||
export type ClientStatus = "active" | "disabled";
|
||||
|
||||
export interface Client {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -16,6 +18,8 @@ export interface Client {
|
||||
address: string | null;
|
||||
notes: string | null;
|
||||
emailOptOut: boolean;
|
||||
status: ClientStatus;
|
||||
disabledAt: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
@@ -170,6 +174,17 @@ export interface ImpersonationAuditLog {
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface BusinessSettings {
|
||||
id: string;
|
||||
businessName: string;
|
||||
logoBase64: string | null;
|
||||
logoMimeType: string | null;
|
||||
primaryColor: string;
|
||||
accentColor: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// Paginated list response
|
||||
export interface PaginatedList<T> {
|
||||
items: T[];
|
||||
|
||||
Reference in New Issue
Block a user