refactor: scope dashboard records by organization

Attach clients, rooms, services, bookings, contacts, addresses, invoices, and contracts to organizations.

Filter dashboard loads and related option lists by the active organization from the session.

Ensure create, update, and archive actions only affect records in the active organization.
This commit is contained in:
2026-06-06 14:14:12 +01:00
parent b5ba84b19f
commit cbd2fb2921
23 changed files with 369 additions and 122 deletions
+8 -1
View File
@@ -1,5 +1,6 @@
import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema';
export const addresses = sqliteTable(
@@ -8,6 +9,9 @@ export const addresses = sqliteTable(
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id')
.notNull()
.references(() => clients.id, { onDelete: 'restrict' }),
@@ -21,5 +25,8 @@ export const addresses = sqliteTable(
isPrimary: integer('is_primary', { mode: 'boolean' }).notNull().default(false),
...timestamps
},
(table) => [index('addresses_client_id_idx').on(table.clientId)]
(table) => [
index('addresses_organization_id_idx').on(table.organizationId),
index('addresses_client_id_idx').on(table.clientId)
]
);
+5
View File
@@ -1,5 +1,6 @@
import { index, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { rooms } from './rooms.schema';
import { services } from './services.schema';
import { timestamps } from './shared.schema';
@@ -10,6 +11,9 @@ export const bookings = sqliteTable(
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id')
.notNull()
.references(() => clients.id, { onDelete: 'restrict' }),
@@ -24,6 +28,7 @@ export const bookings = sqliteTable(
...timestamps
},
(table) => [
index('bookings_organization_id_idx').on(table.organizationId),
index('bookings_client_id_idx').on(table.clientId),
index('bookings_room_id_idx').on(table.roomId),
index('bookings_service_id_idx').on(table.serviceId)
+4
View File
@@ -1,10 +1,14 @@
import { text, sqliteTable } from 'drizzle-orm/sqlite-core';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema';
export const clients = sqliteTable('clients', {
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
name: text('name').notNull(),
type: text('type').notNull().default('business'),
status: text('status').notNull().default('active'),
+8 -1
View File
@@ -1,5 +1,6 @@
import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema';
export const contacts = sqliteTable(
@@ -8,6 +9,9 @@ export const contacts = sqliteTable(
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id')
.notNull()
.references(() => clients.id, { onDelete: 'restrict' }),
@@ -19,5 +23,8 @@ export const contacts = sqliteTable(
notes: text('notes'),
...timestamps
},
(table) => [index('contacts_client_id_idx').on(table.clientId)]
(table) => [
index('contacts_organization_id_idx').on(table.organizationId),
index('contacts_client_id_idx').on(table.clientId)
]
);
+8 -1
View File
@@ -1,5 +1,6 @@
import { index, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema';
export const contracts = sqliteTable(
@@ -8,6 +9,9 @@ export const contracts = sqliteTable(
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id')
.notNull()
.references(() => clients.id, { onDelete: 'restrict' }),
@@ -19,5 +23,8 @@ export const contracts = sqliteTable(
notes: text('notes'),
...timestamps
},
(table) => [index('contracts_client_id_idx').on(table.clientId)]
(table) => [
index('contracts_organization_id_idx').on(table.organizationId),
index('contracts_client_id_idx').on(table.clientId)
]
);
+8 -1
View File
@@ -1,5 +1,6 @@
import { index, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema';
export const invoices = sqliteTable(
@@ -8,6 +9,9 @@ export const invoices = sqliteTable(
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id')
.notNull()
.references(() => clients.id, { onDelete: 'restrict' }),
@@ -21,5 +25,8 @@ export const invoices = sqliteTable(
notes: text('notes'),
...timestamps
},
(table) => [index('invoices_client_id_idx').on(table.clientId)]
(table) => [
index('invoices_organization_id_idx').on(table.organizationId),
index('invoices_client_id_idx').on(table.clientId)
]
);
+4
View File
@@ -1,10 +1,14 @@
import { integer, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema';
export const rooms = sqliteTable('rooms', {
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
name: text('name').notNull(),
type: text('type').notNull().default('meeting_room'),
sqFt: integer('sq_ft'),
+4
View File
@@ -1,10 +1,14 @@
import { real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema';
export const services = sqliteTable('services', {
id: text('id')
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
name: text('name').notNull(),
category: text('category'),
unit: text('unit').notNull().default('each'),