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 { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema'; import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema'; import { timestamps } from './shared.schema';
export const addresses = sqliteTable( export const addresses = sqliteTable(
@@ -8,6 +9,9 @@ export const addresses = sqliteTable(
id: text('id') id: text('id')
.primaryKey() .primaryKey()
.$defaultFn(() => crypto.randomUUID()), .$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id') clientId: text('client_id')
.notNull() .notNull()
.references(() => clients.id, { onDelete: 'restrict' }), .references(() => clients.id, { onDelete: 'restrict' }),
@@ -21,5 +25,8 @@ export const addresses = sqliteTable(
isPrimary: integer('is_primary', { mode: 'boolean' }).notNull().default(false), isPrimary: integer('is_primary', { mode: 'boolean' }).notNull().default(false),
...timestamps ...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 { index, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema'; import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { rooms } from './rooms.schema'; import { rooms } from './rooms.schema';
import { services } from './services.schema'; import { services } from './services.schema';
import { timestamps } from './shared.schema'; import { timestamps } from './shared.schema';
@@ -10,6 +11,9 @@ export const bookings = sqliteTable(
id: text('id') id: text('id')
.primaryKey() .primaryKey()
.$defaultFn(() => crypto.randomUUID()), .$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id') clientId: text('client_id')
.notNull() .notNull()
.references(() => clients.id, { onDelete: 'restrict' }), .references(() => clients.id, { onDelete: 'restrict' }),
@@ -24,6 +28,7 @@ export const bookings = sqliteTable(
...timestamps ...timestamps
}, },
(table) => [ (table) => [
index('bookings_organization_id_idx').on(table.organizationId),
index('bookings_client_id_idx').on(table.clientId), index('bookings_client_id_idx').on(table.clientId),
index('bookings_room_id_idx').on(table.roomId), index('bookings_room_id_idx').on(table.roomId),
index('bookings_service_id_idx').on(table.serviceId) index('bookings_service_id_idx').on(table.serviceId)
+4
View File
@@ -1,10 +1,14 @@
import { text, sqliteTable } from 'drizzle-orm/sqlite-core'; import { text, sqliteTable } from 'drizzle-orm/sqlite-core';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema'; import { timestamps } from './shared.schema';
export const clients = sqliteTable('clients', { export const clients = sqliteTable('clients', {
id: text('id') id: text('id')
.primaryKey() .primaryKey()
.$defaultFn(() => crypto.randomUUID()), .$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
name: text('name').notNull(), name: text('name').notNull(),
type: text('type').notNull().default('business'), type: text('type').notNull().default('business'),
status: text('status').notNull().default('active'), 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 { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema'; import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema'; import { timestamps } from './shared.schema';
export const contacts = sqliteTable( export const contacts = sqliteTable(
@@ -8,6 +9,9 @@ export const contacts = sqliteTable(
id: text('id') id: text('id')
.primaryKey() .primaryKey()
.$defaultFn(() => crypto.randomUUID()), .$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id') clientId: text('client_id')
.notNull() .notNull()
.references(() => clients.id, { onDelete: 'restrict' }), .references(() => clients.id, { onDelete: 'restrict' }),
@@ -19,5 +23,8 @@ export const contacts = sqliteTable(
notes: text('notes'), notes: text('notes'),
...timestamps ...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 { index, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema'; import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema'; import { timestamps } from './shared.schema';
export const contracts = sqliteTable( export const contracts = sqliteTable(
@@ -8,6 +9,9 @@ export const contracts = sqliteTable(
id: text('id') id: text('id')
.primaryKey() .primaryKey()
.$defaultFn(() => crypto.randomUUID()), .$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id') clientId: text('client_id')
.notNull() .notNull()
.references(() => clients.id, { onDelete: 'restrict' }), .references(() => clients.id, { onDelete: 'restrict' }),
@@ -19,5 +23,8 @@ export const contracts = sqliteTable(
notes: text('notes'), notes: text('notes'),
...timestamps ...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 { index, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from './clients.schema'; import { clients } from './clients.schema';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema'; import { timestamps } from './shared.schema';
export const invoices = sqliteTable( export const invoices = sqliteTable(
@@ -8,6 +9,9 @@ export const invoices = sqliteTable(
id: text('id') id: text('id')
.primaryKey() .primaryKey()
.$defaultFn(() => crypto.randomUUID()), .$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
clientId: text('client_id') clientId: text('client_id')
.notNull() .notNull()
.references(() => clients.id, { onDelete: 'restrict' }), .references(() => clients.id, { onDelete: 'restrict' }),
@@ -21,5 +25,8 @@ export const invoices = sqliteTable(
notes: text('notes'), notes: text('notes'),
...timestamps ...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 { integer, real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema'; import { timestamps } from './shared.schema';
export const rooms = sqliteTable('rooms', { export const rooms = sqliteTable('rooms', {
id: text('id') id: text('id')
.primaryKey() .primaryKey()
.$defaultFn(() => crypto.randomUUID()), .$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
name: text('name').notNull(), name: text('name').notNull(),
type: text('type').notNull().default('meeting_room'), type: text('type').notNull().default('meeting_room'),
sqFt: integer('sq_ft'), sqFt: integer('sq_ft'),
+4
View File
@@ -1,10 +1,14 @@
import { real, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { real, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { organizations } from './organizations.schema';
import { timestamps } from './shared.schema'; import { timestamps } from './shared.schema';
export const services = sqliteTable('services', { export const services = sqliteTable('services', {
id: text('id') id: text('id')
.primaryKey() .primaryKey()
.$defaultFn(() => crypto.randomUUID()), .$defaultFn(() => crypto.randomUUID()),
organizationId: text('organization_id')
.notNull()
.references(() => organizations.id, { onDelete: 'restrict' }),
name: text('name').notNull(), name: text('name').notNull(),
category: text('category'), category: text('category'),
unit: text('unit').notNull().default('each'), unit: text('unit').notNull().default('each'),
+2 -2
View File
@@ -5,7 +5,7 @@
import { Separator } from '$lib/components/ui/separator/index.js'; import { Separator } from '$lib/components/ui/separator/index.js';
import * as Sidebar from '$lib/components/ui/sidebar/index.js'; import * as Sidebar from '$lib/components/ui/sidebar/index.js';
const { children } = $props(); const { data, children } = $props();
type BreadcrumbItem = { type BreadcrumbItem = {
label: string; label: string;
@@ -79,7 +79,7 @@
</script> </script>
<Sidebar.Provider> <Sidebar.Provider>
<AppSidebar /> <AppSidebar organizations={data.organizations} activeOrganization={data.activeOrganization} />
<Sidebar.Inset> <Sidebar.Inset>
<header class="flex h-16 shrink-0 items-center gap-2 border-b px-4"> <header class="flex h-16 shrink-0 items-center gap-2 border-b px-4">
<Sidebar.Trigger class="-ms-1" /> <Sidebar.Trigger class="-ms-1" />
+12 -7
View File
@@ -1,16 +1,17 @@
import { asc, eq, isNull } from 'drizzle-orm'; import { and, asc, eq, isNull } from 'drizzle-orm';
import { message, superValidate } from 'sveltekit-superforms/server'; import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { addresses, clients } from '$lib/server/db/schema'; import { addresses, clients } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const clientRows = await db const clientRows = await db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)); .orderBy(asc(clients.name));
return { return {
@@ -18,22 +19,24 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async ({ locals }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(addresses) .from(addresses)
.where(isNull(addresses.archivedAt)) .where(and(eq(addresses.organizationId, activeOrganizationId), isNull(addresses.archivedAt)))
.orderBy(asc(addresses.label)); .orderBy(asc(addresses.label));
return { return {
records, records,
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
archiveForm: await superValidate(zod4(archiveSchema), { id: 'addresses-archive' }) archiveForm: await superValidate(zod4(archiveSchema), { id: 'addresses-archive' })
}; };
}; };
export const actions: Actions = { export const actions: Actions = {
archive: async (event) => { archive: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(archiveSchema), { id: 'addresses-archive' }); const form = await superValidate(event, zod4(archiveSchema), { id: 'addresses-archive' });
if (!form.valid) return message(form, 'Address id is required.', { status: 400 }); if (!form.valid) return message(form, 'Address id is required.', { status: 400 });
@@ -41,7 +44,9 @@ export const actions: Actions = {
await db await db
.update(addresses) .update(addresses)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(eq(addresses.id, form.data.id)); .where(
and(eq(addresses.id, form.data.id), eq(addresses.organizationId, activeOrganizationId))
);
return message(form, 'Address archived.'); return message(form, 'Address archived.');
} }
+12 -9
View File
@@ -1,27 +1,28 @@
import { asc, eq, isNull } from 'drizzle-orm'; import { and, asc, eq, isNull } from 'drizzle-orm';
import { message, superValidate } from 'sveltekit-superforms/server'; import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { bookings, clients, rooms, services } from '$lib/server/db/schema'; import { bookings, clients, rooms, services } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const [clientRows, roomRows, serviceRows] = await Promise.all([ const [clientRows, roomRows, serviceRows] = await Promise.all([
db db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)), .orderBy(asc(clients.name)),
db db
.select({ id: rooms.id, name: rooms.name }) .select({ id: rooms.id, name: rooms.name })
.from(rooms) .from(rooms)
.where(isNull(rooms.archivedAt)) .where(and(eq(rooms.organizationId, organizationId), isNull(rooms.archivedAt)))
.orderBy(asc(rooms.name)), .orderBy(asc(rooms.name)),
db db
.select({ id: services.id, name: services.name }) .select({ id: services.id, name: services.name })
.from(services) .from(services)
.where(isNull(services.archivedAt)) .where(and(eq(services.organizationId, organizationId), isNull(services.archivedAt)))
.orderBy(asc(services.name)) .orderBy(asc(services.name))
]); ]);
@@ -35,22 +36,24 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async ({ locals }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(bookings) .from(bookings)
.where(isNull(bookings.archivedAt)) .where(and(eq(bookings.organizationId, activeOrganizationId), isNull(bookings.archivedAt)))
.orderBy(asc(bookings.startsAt)); .orderBy(asc(bookings.startsAt));
return { return {
records, records,
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
archiveForm: await superValidate(zod4(archiveSchema), { id: 'bookings-archive' }) archiveForm: await superValidate(zod4(archiveSchema), { id: 'bookings-archive' })
}; };
}; };
export const actions: Actions = { export const actions: Actions = {
archive: async (event) => { archive: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(archiveSchema), { id: 'bookings-archive' }); const form = await superValidate(event, zod4(archiveSchema), { id: 'bookings-archive' });
if (!form.valid) return message(form, 'Booking id is required.', { status: 400 }); if (!form.valid) return message(form, 'Booking id is required.', { status: 400 });
@@ -58,7 +61,7 @@ export const actions: Actions = {
await db await db
.update(bookings) .update(bookings)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(eq(bookings.id, form.data.id)); .where(and(eq(bookings.id, form.data.id), eq(bookings.organizationId, activeOrganizationId)));
return message(form, 'Booking archived.'); return message(form, 'Booking archived.');
} }
+13 -5
View File
@@ -1,17 +1,19 @@
import { asc, eq, isNull } from 'drizzle-orm'; import { and, asc, eq, isNull } from 'drizzle-orm';
import { message, superValidate } from 'sveltekit-superforms/server'; import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { addresses, clients, contacts } from '$lib/server/db/schema'; import { addresses, clients, contacts } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import { clientEditSchema, clientOnboardingCreateSchema } from '$lib/schemas/clients.schema'; import { clientEditSchema, clientOnboardingCreateSchema } from '$lib/schemas/clients.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async ({ locals }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, activeOrganizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)); .orderBy(asc(clients.name));
return { return {
@@ -34,6 +36,7 @@ export const load: PageServerLoad = async () => {
export const actions: Actions = { export const actions: Actions = {
create: async (event) => { create: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(clientOnboardingCreateSchema), { const form = await superValidate(event, zod4(clientOnboardingCreateSchema), {
id: 'clients-create' id: 'clients-create'
}); });
@@ -47,6 +50,7 @@ export const actions: Actions = {
await tx.insert(clients).values({ await tx.insert(clients).values({
id: clientId, id: clientId,
organizationId: activeOrganizationId,
name: form.data.name, name: form.data.name,
type: form.data.type, type: form.data.type,
status: form.data.status, status: form.data.status,
@@ -58,6 +62,7 @@ export const actions: Actions = {
await tx.insert(contacts).values({ await tx.insert(contacts).values({
id: crypto.randomUUID(), id: crypto.randomUUID(),
organizationId: activeOrganizationId,
clientId, clientId,
name: form.data.primaryContactName, name: form.data.primaryContactName,
role: form.data.primaryContactRole || null, role: form.data.primaryContactRole || null,
@@ -71,6 +76,7 @@ export const actions: Actions = {
await tx.insert(addresses).values({ await tx.insert(addresses).values({
id: crypto.randomUUID(), id: crypto.randomUUID(),
organizationId: activeOrganizationId,
clientId, clientId,
label: form.data.primaryAddressLabel, label: form.data.primaryAddressLabel,
line1: form.data.primaryAddressLine1, line1: form.data.primaryAddressLine1,
@@ -92,6 +98,7 @@ export const actions: Actions = {
}, },
edit: async (event) => { edit: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(clientEditSchema), { id: 'clients-edit' }); const form = await superValidate(event, zod4(clientEditSchema), { id: 'clients-edit' });
if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 }); if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 });
@@ -107,7 +114,7 @@ export const actions: Actions = {
notes: form.data.notes || null, notes: form.data.notes || null,
updatedAt: new Date() updatedAt: new Date()
}) })
.where(eq(clients.id, form.data.id)); .where(and(eq(clients.id, form.data.id), eq(clients.organizationId, activeOrganizationId)));
} catch { } catch {
return message(form, 'Unable to update client.', { status: 400 }); return message(form, 'Unable to update client.', { status: 400 });
} }
@@ -116,6 +123,7 @@ export const actions: Actions = {
}, },
archive: async (event) => { archive: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(archiveSchema), { id: 'clients-archive' }); const form = await superValidate(event, zod4(archiveSchema), { id: 'clients-archive' });
if (!form.valid) return message(form, 'Client id is required.', { status: 400 }); if (!form.valid) return message(form, 'Client id is required.', { status: 400 });
@@ -123,7 +131,7 @@ export const actions: Actions = {
await db await db
.update(clients) .update(clients)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(eq(clients.id, form.data.id)); .where(and(eq(clients.id, form.data.id), eq(clients.organizationId, activeOrganizationId)));
return message(form, 'Client archived.'); return message(form, 'Client archived.');
} }
@@ -11,6 +11,7 @@ import {
rooms, rooms,
services services
} from '$lib/server/db/schema'; } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { clientEditSchema } from '$lib/schemas/clients.schema'; import { clientEditSchema } from '$lib/schemas/clients.schema';
import type { LayoutServerLoad } from './$types'; import type { LayoutServerLoad } from './$types';
import { superValidate } from 'sveltekit-superforms/server'; import { superValidate } from 'sveltekit-superforms/server';
@@ -30,22 +31,22 @@ function formValues(row: typeof clients.$inferSelect) {
}; };
} }
async function loadOptions() { async function loadOptions(organizationId: string) {
const [clientRows, roomRows, serviceRows] = await Promise.all([ const [clientRows, roomRows, serviceRows] = await Promise.all([
db db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)), .orderBy(asc(clients.name)),
db db
.select({ id: rooms.id, name: rooms.name }) .select({ id: rooms.id, name: rooms.name })
.from(rooms) .from(rooms)
.where(isNull(rooms.archivedAt)) .where(and(eq(rooms.organizationId, organizationId), isNull(rooms.archivedAt)))
.orderBy(asc(rooms.name)), .orderBy(asc(rooms.name)),
db db
.select({ id: services.id, name: services.name }) .select({ id: services.id, name: services.name })
.from(services) .from(services)
.where(isNull(services.archivedAt)) .where(and(eq(services.organizationId, organizationId), isNull(services.archivedAt)))
.orderBy(asc(services.name)) .orderBy(asc(services.name))
]); ]);
@@ -59,11 +60,18 @@ async function loadOptions() {
}; };
} }
export const load: LayoutServerLoad = async ({ params }) => { export const load: LayoutServerLoad = async ({ locals, params }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const [client] = await db const [client] = await db
.select() .select()
.from(clients) .from(clients)
.where(and(eq(clients.id, params.id), isNull(clients.archivedAt))) .where(
and(
eq(clients.id, params.id),
eq(clients.organizationId, activeOrganizationId),
isNull(clients.archivedAt)
)
)
.limit(1); .limit(1);
if (!client) error(404, 'Client not found'); if (!client) error(404, 'Client not found');
@@ -73,29 +81,59 @@ export const load: LayoutServerLoad = async ({ params }) => {
db db
.select() .select()
.from(addresses) .from(addresses)
.where(and(eq(addresses.clientId, params.id), isNull(addresses.archivedAt))) .where(
and(
eq(addresses.clientId, params.id),
eq(addresses.organizationId, activeOrganizationId),
isNull(addresses.archivedAt)
)
)
.orderBy(asc(addresses.label)), .orderBy(asc(addresses.label)),
db db
.select() .select()
.from(contacts) .from(contacts)
.where(and(eq(contacts.clientId, params.id), isNull(contacts.archivedAt))) .where(
and(
eq(contacts.clientId, params.id),
eq(contacts.organizationId, activeOrganizationId),
isNull(contacts.archivedAt)
)
)
.orderBy(asc(contacts.name)), .orderBy(asc(contacts.name)),
db db
.select() .select()
.from(invoices) .from(invoices)
.where(and(eq(invoices.clientId, params.id), isNull(invoices.archivedAt))) .where(
and(
eq(invoices.clientId, params.id),
eq(invoices.organizationId, activeOrganizationId),
isNull(invoices.archivedAt)
)
)
.orderBy(asc(invoices.invoiceNumber)), .orderBy(asc(invoices.invoiceNumber)),
db db
.select() .select()
.from(contracts) .from(contracts)
.where(and(eq(contracts.clientId, params.id), isNull(contracts.archivedAt))) .where(
and(
eq(contracts.clientId, params.id),
eq(contracts.organizationId, activeOrganizationId),
isNull(contracts.archivedAt)
)
)
.orderBy(asc(contracts.title)), .orderBy(asc(contracts.title)),
db db
.select() .select()
.from(bookings) .from(bookings)
.where(and(eq(bookings.clientId, params.id), isNull(bookings.archivedAt))) .where(
and(
eq(bookings.clientId, params.id),
eq(bookings.organizationId, activeOrganizationId),
isNull(bookings.archivedAt)
)
)
.orderBy(asc(bookings.startsAt)), .orderBy(asc(bookings.startsAt)),
loadOptions() loadOptions(activeOrganizationId)
]); ]);
return { return {
@@ -3,15 +3,16 @@ import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { addresses, clients } from '$lib/server/db/schema'; import { addresses, clients } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import { addressCreateSchema, addressEditSchema } from '$lib/schemas/addresses.schema'; import { addressCreateSchema, addressEditSchema } from '$lib/schemas/addresses.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const clientRows = await db const clientRows = await db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)); .orderBy(asc(clients.name));
return { return {
@@ -19,11 +20,18 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async ({ params }) => { export const load: PageServerLoad = async ({ locals, params }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(addresses) .from(addresses)
.where(and(eq(addresses.clientId, params.id), isNull(addresses.archivedAt))) .where(
and(
eq(addresses.clientId, params.id),
eq(addresses.organizationId, activeOrganizationId),
isNull(addresses.archivedAt)
)
)
.orderBy(asc(addresses.label)); .orderBy(asc(addresses.label));
return { return {
@@ -42,7 +50,7 @@ export const load: PageServerLoad = async ({ params }) => {
isPrimary: record.isPrimary ? 'true' : 'false' isPrimary: record.isPrimary ? 'true' : 'false'
} }
})), })),
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
createForm: await superValidate({ clientId: params.id }, zod4(addressCreateSchema), { createForm: await superValidate({ clientId: params.id }, zod4(addressCreateSchema), {
id: 'addresses-create' id: 'addresses-create'
}), }),
@@ -52,7 +60,8 @@ export const load: PageServerLoad = async ({ params }) => {
}; };
export const actions: Actions = { export const actions: Actions = {
create: async ({ params, request }) => { create: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(addressCreateSchema), { const form = await superValidate(formData, zod4(addressCreateSchema), {
@@ -64,6 +73,7 @@ export const actions: Actions = {
try { try {
await db.insert(addresses).values({ await db.insert(addresses).values({
id: crypto.randomUUID(), id: crypto.randomUUID(),
organizationId: activeOrganizationId,
clientId: form.data.clientId, clientId: form.data.clientId,
label: form.data.label, label: form.data.label,
line1: form.data.line1, line1: form.data.line1,
@@ -83,7 +93,8 @@ export const actions: Actions = {
return message(form, 'Address created.'); return message(form, 'Address created.');
}, },
edit: async ({ params, request }) => { edit: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(addressEditSchema), { id: 'addresses-edit' }); const form = await superValidate(formData, zod4(addressEditSchema), { id: 'addresses-edit' });
@@ -105,7 +116,13 @@ export const actions: Actions = {
isPrimary: form.data.isPrimary === 'true', isPrimary: form.data.isPrimary === 'true',
updatedAt: new Date() updatedAt: new Date()
}) })
.where(and(eq(addresses.id, form.data.id), eq(addresses.clientId, params.id))); .where(
and(
eq(addresses.id, form.data.id),
eq(addresses.clientId, params.id),
eq(addresses.organizationId, activeOrganizationId)
)
);
} catch { } catch {
return message(form, 'Unable to update address.', { status: 400 }); return message(form, 'Unable to update address.', { status: 400 });
} }
@@ -113,7 +130,8 @@ export const actions: Actions = {
return message(form, 'Address updated.'); return message(form, 'Address updated.');
}, },
archive: async ({ params, request }) => { archive: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const form = await superValidate(await request.formData(), zod4(archiveSchema), { const form = await superValidate(await request.formData(), zod4(archiveSchema), {
id: 'addresses-archive' id: 'addresses-archive'
}); });
@@ -123,7 +141,13 @@ export const actions: Actions = {
await db await db
.update(addresses) .update(addresses)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(and(eq(addresses.id, form.data.id), eq(addresses.clientId, params.id))); .where(
and(
eq(addresses.id, form.data.id),
eq(addresses.clientId, params.id),
eq(addresses.organizationId, activeOrganizationId)
)
);
return message(form, 'Address archived.'); return message(form, 'Address archived.');
} }
@@ -3,26 +3,27 @@ import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { bookings, clients, rooms, services } from '$lib/server/db/schema'; import { bookings, clients, rooms, services } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import { bookingCreateSchema, bookingEditSchema } from '$lib/schemas/bookings.schema'; import { bookingCreateSchema, bookingEditSchema } from '$lib/schemas/bookings.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const [clientRows, roomRows, serviceRows] = await Promise.all([ const [clientRows, roomRows, serviceRows] = await Promise.all([
db db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)), .orderBy(asc(clients.name)),
db db
.select({ id: rooms.id, name: rooms.name }) .select({ id: rooms.id, name: rooms.name })
.from(rooms) .from(rooms)
.where(isNull(rooms.archivedAt)) .where(and(eq(rooms.organizationId, organizationId), isNull(rooms.archivedAt)))
.orderBy(asc(rooms.name)), .orderBy(asc(rooms.name)),
db db
.select({ id: services.id, name: services.name }) .select({ id: services.id, name: services.name })
.from(services) .from(services)
.where(isNull(services.archivedAt)) .where(and(eq(services.organizationId, organizationId), isNull(services.archivedAt)))
.orderBy(asc(services.name)) .orderBy(asc(services.name))
]); ]);
@@ -36,11 +37,18 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async ({ params }) => { export const load: PageServerLoad = async ({ locals, params }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(bookings) .from(bookings)
.where(and(eq(bookings.clientId, params.id), isNull(bookings.archivedAt))) .where(
and(
eq(bookings.clientId, params.id),
eq(bookings.organizationId, activeOrganizationId),
isNull(bookings.archivedAt)
)
)
.orderBy(asc(bookings.startsAt)); .orderBy(asc(bookings.startsAt));
return { return {
@@ -57,7 +65,7 @@ export const load: PageServerLoad = async ({ params }) => {
notes: record.notes ?? '' notes: record.notes ?? ''
} }
})), })),
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
createForm: await superValidate({ clientId: params.id }, zod4(bookingCreateSchema), { createForm: await superValidate({ clientId: params.id }, zod4(bookingCreateSchema), {
id: 'bookings-create' id: 'bookings-create'
}), }),
@@ -67,7 +75,8 @@ export const load: PageServerLoad = async ({ params }) => {
}; };
export const actions: Actions = { export const actions: Actions = {
create: async ({ params, request }) => { create: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(bookingCreateSchema), { const form = await superValidate(formData, zod4(bookingCreateSchema), {
@@ -79,6 +88,7 @@ export const actions: Actions = {
try { try {
await db.insert(bookings).values({ await db.insert(bookings).values({
id: crypto.randomUUID(), id: crypto.randomUUID(),
organizationId: activeOrganizationId,
clientId: form.data.clientId, clientId: form.data.clientId,
roomId: form.data.roomId, roomId: form.data.roomId,
serviceId: form.data.serviceId || null, serviceId: form.data.serviceId || null,
@@ -96,7 +106,8 @@ export const actions: Actions = {
return message(form, 'Booking created.'); return message(form, 'Booking created.');
}, },
edit: async ({ params, request }) => { edit: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(bookingEditSchema), { const form = await superValidate(formData, zod4(bookingEditSchema), {
@@ -118,7 +129,13 @@ export const actions: Actions = {
notes: form.data.notes || null, notes: form.data.notes || null,
updatedAt: new Date() updatedAt: new Date()
}) })
.where(and(eq(bookings.id, form.data.id), eq(bookings.clientId, params.id))); .where(
and(
eq(bookings.id, form.data.id),
eq(bookings.clientId, params.id),
eq(bookings.organizationId, activeOrganizationId)
)
);
} catch { } catch {
return message(form, 'Unable to update booking.', { status: 400 }); return message(form, 'Unable to update booking.', { status: 400 });
} }
@@ -126,7 +143,8 @@ export const actions: Actions = {
return message(form, 'Booking updated.'); return message(form, 'Booking updated.');
}, },
archive: async ({ params, request }) => { archive: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const form = await superValidate(await request.formData(), zod4(archiveSchema), { const form = await superValidate(await request.formData(), zod4(archiveSchema), {
id: 'bookings-archive' id: 'bookings-archive'
}); });
@@ -136,7 +154,13 @@ export const actions: Actions = {
await db await db
.update(bookings) .update(bookings)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(and(eq(bookings.id, form.data.id), eq(bookings.clientId, params.id))); .where(
and(
eq(bookings.id, form.data.id),
eq(bookings.clientId, params.id),
eq(bookings.organizationId, activeOrganizationId)
)
);
return message(form, 'Booking archived.'); return message(form, 'Booking archived.');
} }
@@ -3,15 +3,16 @@ import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { contacts, clients } from '$lib/server/db/schema'; import { contacts, clients } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import { contactCreateSchema, contactEditSchema } from '$lib/schemas/contacts.schema'; import { contactCreateSchema, contactEditSchema } from '$lib/schemas/contacts.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const clientRows = await db const clientRows = await db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)); .orderBy(asc(clients.name));
return { return {
@@ -19,11 +20,18 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async ({ params }) => { export const load: PageServerLoad = async ({ locals, params }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(contacts) .from(contacts)
.where(and(eq(contacts.clientId, params.id), isNull(contacts.archivedAt))) .where(
and(
eq(contacts.clientId, params.id),
eq(contacts.organizationId, activeOrganizationId),
isNull(contacts.archivedAt)
)
)
.orderBy(asc(contacts.name)); .orderBy(asc(contacts.name));
return { return {
@@ -40,7 +48,7 @@ export const load: PageServerLoad = async ({ params }) => {
notes: record.notes ?? '' notes: record.notes ?? ''
} }
})), })),
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
createForm: await superValidate({ clientId: params.id }, zod4(contactCreateSchema), { createForm: await superValidate({ clientId: params.id }, zod4(contactCreateSchema), {
id: 'contacts-create' id: 'contacts-create'
}), }),
@@ -50,7 +58,8 @@ export const load: PageServerLoad = async ({ params }) => {
}; };
export const actions: Actions = { export const actions: Actions = {
create: async ({ params, request }) => { create: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(contactCreateSchema), { const form = await superValidate(formData, zod4(contactCreateSchema), {
@@ -62,6 +71,7 @@ export const actions: Actions = {
try { try {
await db.insert(contacts).values({ await db.insert(contacts).values({
id: crypto.randomUUID(), id: crypto.randomUUID(),
organizationId: activeOrganizationId,
clientId: form.data.clientId, clientId: form.data.clientId,
name: form.data.name, name: form.data.name,
role: form.data.role || null, role: form.data.role || null,
@@ -79,7 +89,8 @@ export const actions: Actions = {
return message(form, 'Contact created.'); return message(form, 'Contact created.');
}, },
edit: async ({ params, request }) => { edit: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(contactEditSchema), { id: 'contacts-edit' }); const form = await superValidate(formData, zod4(contactEditSchema), { id: 'contacts-edit' });
@@ -99,7 +110,13 @@ export const actions: Actions = {
notes: form.data.notes || null, notes: form.data.notes || null,
updatedAt: new Date() updatedAt: new Date()
}) })
.where(and(eq(contacts.id, form.data.id), eq(contacts.clientId, params.id))); .where(
and(
eq(contacts.id, form.data.id),
eq(contacts.clientId, params.id),
eq(contacts.organizationId, activeOrganizationId)
)
);
} catch { } catch {
return message(form, 'Unable to update contact.', { status: 400 }); return message(form, 'Unable to update contact.', { status: 400 });
} }
@@ -107,7 +124,8 @@ export const actions: Actions = {
return message(form, 'Contact updated.'); return message(form, 'Contact updated.');
}, },
archive: async ({ params, request }) => { archive: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const form = await superValidate(await request.formData(), zod4(archiveSchema), { const form = await superValidate(await request.formData(), zod4(archiveSchema), {
id: 'contacts-archive' id: 'contacts-archive'
}); });
@@ -117,7 +135,13 @@ export const actions: Actions = {
await db await db
.update(contacts) .update(contacts)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(and(eq(contacts.id, form.data.id), eq(contacts.clientId, params.id))); .where(
and(
eq(contacts.id, form.data.id),
eq(contacts.clientId, params.id),
eq(contacts.organizationId, activeOrganizationId)
)
);
return message(form, 'Contact archived.'); return message(form, 'Contact archived.');
} }
@@ -3,15 +3,16 @@ import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { contracts, clients } from '$lib/server/db/schema'; import { contracts, clients } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import { contractCreateSchema, contractEditSchema } from '$lib/schemas/contracts.schema'; import { contractCreateSchema, contractEditSchema } from '$lib/schemas/contracts.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const clientRows = await db const clientRows = await db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)); .orderBy(asc(clients.name));
return { return {
@@ -19,11 +20,18 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async ({ params }) => { export const load: PageServerLoad = async ({ locals, params }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(contracts) .from(contracts)
.where(and(eq(contracts.clientId, params.id), isNull(contracts.archivedAt))) .where(
and(
eq(contracts.clientId, params.id),
eq(contracts.organizationId, activeOrganizationId),
isNull(contracts.archivedAt)
)
)
.orderBy(asc(contracts.title)); .orderBy(asc(contracts.title));
return { return {
@@ -40,7 +48,7 @@ export const load: PageServerLoad = async ({ params }) => {
notes: record.notes ?? '' notes: record.notes ?? ''
} }
})), })),
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
createForm: await superValidate({ clientId: params.id }, zod4(contractCreateSchema), { createForm: await superValidate({ clientId: params.id }, zod4(contractCreateSchema), {
id: 'contracts-create' id: 'contracts-create'
}), }),
@@ -50,7 +58,8 @@ export const load: PageServerLoad = async ({ params }) => {
}; };
export const actions: Actions = { export const actions: Actions = {
create: async ({ params, request }) => { create: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(contractCreateSchema), { const form = await superValidate(formData, zod4(contractCreateSchema), {
@@ -62,6 +71,7 @@ export const actions: Actions = {
try { try {
await db.insert(contracts).values({ await db.insert(contracts).values({
id: crypto.randomUUID(), id: crypto.randomUUID(),
organizationId: activeOrganizationId,
clientId: form.data.clientId, clientId: form.data.clientId,
title: form.data.title, title: form.data.title,
startDate: form.data.startDate, startDate: form.data.startDate,
@@ -79,7 +89,8 @@ export const actions: Actions = {
return message(form, 'Contract created.'); return message(form, 'Contract created.');
}, },
edit: async ({ params, request }) => { edit: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(contractEditSchema), { const form = await superValidate(formData, zod4(contractEditSchema), {
@@ -101,7 +112,13 @@ export const actions: Actions = {
notes: form.data.notes || null, notes: form.data.notes || null,
updatedAt: new Date() updatedAt: new Date()
}) })
.where(and(eq(contracts.id, form.data.id), eq(contracts.clientId, params.id))); .where(
and(
eq(contracts.id, form.data.id),
eq(contracts.clientId, params.id),
eq(contracts.organizationId, activeOrganizationId)
)
);
} catch { } catch {
return message(form, 'Unable to update contract.', { status: 400 }); return message(form, 'Unable to update contract.', { status: 400 });
} }
@@ -109,7 +126,8 @@ export const actions: Actions = {
return message(form, 'Contract updated.'); return message(form, 'Contract updated.');
}, },
archive: async ({ params, request }) => { archive: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const form = await superValidate(await request.formData(), zod4(archiveSchema), { const form = await superValidate(await request.formData(), zod4(archiveSchema), {
id: 'contracts-archive' id: 'contracts-archive'
}); });
@@ -119,7 +137,13 @@ export const actions: Actions = {
await db await db
.update(contracts) .update(contracts)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(and(eq(contracts.id, form.data.id), eq(contracts.clientId, params.id))); .where(
and(
eq(contracts.id, form.data.id),
eq(contracts.clientId, params.id),
eq(contracts.organizationId, activeOrganizationId)
)
);
return message(form, 'Contract archived.'); return message(form, 'Contract archived.');
} }
@@ -3,15 +3,16 @@ import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { invoices, clients } from '$lib/server/db/schema'; import { invoices, clients } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import { invoiceCreateSchema, invoiceEditSchema } from '$lib/schemas/invoices.schema'; import { invoiceCreateSchema, invoiceEditSchema } from '$lib/schemas/invoices.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const clientRows = await db const clientRows = await db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)); .orderBy(asc(clients.name));
return { return {
@@ -19,11 +20,18 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async ({ params }) => { export const load: PageServerLoad = async ({ locals, params }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(invoices) .from(invoices)
.where(and(eq(invoices.clientId, params.id), isNull(invoices.archivedAt))) .where(
and(
eq(invoices.clientId, params.id),
eq(invoices.organizationId, activeOrganizationId),
isNull(invoices.archivedAt)
)
)
.orderBy(asc(invoices.invoiceNumber)); .orderBy(asc(invoices.invoiceNumber));
return { return {
@@ -42,7 +50,7 @@ export const load: PageServerLoad = async ({ params }) => {
notes: record.notes ?? '' notes: record.notes ?? ''
} }
})), })),
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
createForm: await superValidate({ clientId: params.id }, zod4(invoiceCreateSchema), { createForm: await superValidate({ clientId: params.id }, zod4(invoiceCreateSchema), {
id: 'invoices-create' id: 'invoices-create'
}), }),
@@ -52,7 +60,8 @@ export const load: PageServerLoad = async ({ params }) => {
}; };
export const actions: Actions = { export const actions: Actions = {
create: async ({ params, request }) => { create: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(invoiceCreateSchema), { const form = await superValidate(formData, zod4(invoiceCreateSchema), {
@@ -64,6 +73,7 @@ export const actions: Actions = {
try { try {
await db.insert(invoices).values({ await db.insert(invoices).values({
id: crypto.randomUUID(), id: crypto.randomUUID(),
organizationId: activeOrganizationId,
clientId: form.data.clientId, clientId: form.data.clientId,
invoiceNumber: form.data.invoiceNumber, invoiceNumber: form.data.invoiceNumber,
issueDate: form.data.issueDate, issueDate: form.data.issueDate,
@@ -83,7 +93,8 @@ export const actions: Actions = {
return message(form, 'Invoice created.'); return message(form, 'Invoice created.');
}, },
edit: async ({ params, request }) => { edit: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const formData = await request.formData(); const formData = await request.formData();
formData.set('clientId', params.id); formData.set('clientId', params.id);
const form = await superValidate(formData, zod4(invoiceEditSchema), { id: 'invoices-edit' }); const form = await superValidate(formData, zod4(invoiceEditSchema), { id: 'invoices-edit' });
@@ -105,7 +116,13 @@ export const actions: Actions = {
notes: form.data.notes || null, notes: form.data.notes || null,
updatedAt: new Date() updatedAt: new Date()
}) })
.where(and(eq(invoices.id, form.data.id), eq(invoices.clientId, params.id))); .where(
and(
eq(invoices.id, form.data.id),
eq(invoices.clientId, params.id),
eq(invoices.organizationId, activeOrganizationId)
)
);
} catch { } catch {
return message(form, 'Unable to update invoice.', { status: 400 }); return message(form, 'Unable to update invoice.', { status: 400 });
} }
@@ -113,7 +130,8 @@ export const actions: Actions = {
return message(form, 'Invoice updated.'); return message(form, 'Invoice updated.');
}, },
archive: async ({ params, request }) => { archive: async ({ locals, params, request }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const form = await superValidate(await request.formData(), zod4(archiveSchema), { const form = await superValidate(await request.formData(), zod4(archiveSchema), {
id: 'invoices-archive' id: 'invoices-archive'
}); });
@@ -123,7 +141,13 @@ export const actions: Actions = {
await db await db
.update(invoices) .update(invoices)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(and(eq(invoices.id, form.data.id), eq(invoices.clientId, params.id))); .where(
and(
eq(invoices.id, form.data.id),
eq(invoices.clientId, params.id),
eq(invoices.organizationId, activeOrganizationId)
)
);
return message(form, 'Invoice archived.'); return message(form, 'Invoice archived.');
} }
+10 -7
View File
@@ -1,16 +1,17 @@
import { asc, eq, isNull } from 'drizzle-orm'; import { and, asc, eq, isNull } from 'drizzle-orm';
import { message, superValidate } from 'sveltekit-superforms/server'; import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { contacts, clients } from '$lib/server/db/schema'; import { contacts, clients } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const clientRows = await db const clientRows = await db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)); .orderBy(asc(clients.name));
return { return {
@@ -18,22 +19,24 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async ({ locals }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(contacts) .from(contacts)
.where(isNull(contacts.archivedAt)) .where(and(eq(contacts.organizationId, activeOrganizationId), isNull(contacts.archivedAt)))
.orderBy(asc(contacts.name)); .orderBy(asc(contacts.name));
return { return {
records, records,
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
archiveForm: await superValidate(zod4(archiveSchema), { id: 'contacts-archive' }) archiveForm: await superValidate(zod4(archiveSchema), { id: 'contacts-archive' })
}; };
}; };
export const actions: Actions = { export const actions: Actions = {
archive: async (event) => { archive: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(archiveSchema), { id: 'contacts-archive' }); const form = await superValidate(event, zod4(archiveSchema), { id: 'contacts-archive' });
if (!form.valid) return message(form, 'Contact id is required.', { status: 400 }); if (!form.valid) return message(form, 'Contact id is required.', { status: 400 });
@@ -41,7 +44,7 @@ export const actions: Actions = {
await db await db
.update(contacts) .update(contacts)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(eq(contacts.id, form.data.id)); .where(and(eq(contacts.id, form.data.id), eq(contacts.organizationId, activeOrganizationId)));
return message(form, 'Contact archived.'); return message(form, 'Contact archived.');
} }
+12 -7
View File
@@ -1,16 +1,17 @@
import { asc, eq, isNull } from 'drizzle-orm'; import { and, asc, eq, isNull } from 'drizzle-orm';
import { message, superValidate } from 'sveltekit-superforms/server'; import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { contracts, clients } from '$lib/server/db/schema'; import { contracts, clients } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const clientRows = await db const clientRows = await db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)); .orderBy(asc(clients.name));
return { return {
@@ -18,22 +19,24 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async ({ locals }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(contracts) .from(contracts)
.where(isNull(contracts.archivedAt)) .where(and(eq(contracts.organizationId, activeOrganizationId), isNull(contracts.archivedAt)))
.orderBy(asc(contracts.title)); .orderBy(asc(contracts.title));
return { return {
records, records,
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
archiveForm: await superValidate(zod4(archiveSchema), { id: 'contracts-archive' }) archiveForm: await superValidate(zod4(archiveSchema), { id: 'contracts-archive' })
}; };
}; };
export const actions: Actions = { export const actions: Actions = {
archive: async (event) => { archive: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(archiveSchema), { id: 'contracts-archive' }); const form = await superValidate(event, zod4(archiveSchema), { id: 'contracts-archive' });
if (!form.valid) return message(form, 'Contract id is required.', { status: 400 }); if (!form.valid) return message(form, 'Contract id is required.', { status: 400 });
@@ -41,7 +44,9 @@ export const actions: Actions = {
await db await db
.update(contracts) .update(contracts)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(eq(contracts.id, form.data.id)); .where(
and(eq(contracts.id, form.data.id), eq(contracts.organizationId, activeOrganizationId))
);
return message(form, 'Contract archived.'); return message(form, 'Contract archived.');
} }
+10 -7
View File
@@ -1,16 +1,17 @@
import { asc, eq, isNull } from 'drizzle-orm'; import { and, asc, eq, isNull } from 'drizzle-orm';
import { message, superValidate } from 'sveltekit-superforms/server'; import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { invoices, clients } from '$lib/server/db/schema'; import { invoices, clients } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
async function loadOptions() { async function loadOptions(organizationId: string) {
const clientRows = await db const clientRows = await db
.select({ id: clients.id, name: clients.name }) .select({ id: clients.id, name: clients.name })
.from(clients) .from(clients)
.where(isNull(clients.archivedAt)) .where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name)); .orderBy(asc(clients.name));
return { return {
@@ -18,22 +19,24 @@ async function loadOptions() {
}; };
} }
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async ({ locals }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(invoices) .from(invoices)
.where(isNull(invoices.archivedAt)) .where(and(eq(invoices.organizationId, activeOrganizationId), isNull(invoices.archivedAt)))
.orderBy(asc(invoices.invoiceNumber)); .orderBy(asc(invoices.invoiceNumber));
return { return {
records, records,
options: await loadOptions(), options: await loadOptions(activeOrganizationId),
archiveForm: await superValidate(zod4(archiveSchema), { id: 'invoices-archive' }) archiveForm: await superValidate(zod4(archiveSchema), { id: 'invoices-archive' })
}; };
}; };
export const actions: Actions = { export const actions: Actions = {
archive: async (event) => { archive: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(archiveSchema), { id: 'invoices-archive' }); const form = await superValidate(event, zod4(archiveSchema), { id: 'invoices-archive' });
if (!form.valid) return message(form, 'Invoice id is required.', { status: 400 }); if (!form.valid) return message(form, 'Invoice id is required.', { status: 400 });
@@ -41,7 +44,7 @@ export const actions: Actions = {
await db await db
.update(invoices) .update(invoices)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(eq(invoices.id, form.data.id)); .where(and(eq(invoices.id, form.data.id), eq(invoices.organizationId, activeOrganizationId)));
return message(form, 'Invoice archived.'); return message(form, 'Invoice archived.');
} }
+14 -5
View File
@@ -1,17 +1,19 @@
import { asc, eq, isNull } from 'drizzle-orm'; import { and, asc, eq, isNull } from 'drizzle-orm';
import { message, superValidate } from 'sveltekit-superforms/server'; import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { rooms } from '$lib/server/db/schema'; import { rooms } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import { roomCreateSchema, roomEditSchema } from '$lib/schemas/rooms.schema'; import { roomCreateSchema, roomEditSchema } from '$lib/schemas/rooms.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async ({ locals }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(rooms) .from(rooms)
.where(isNull(rooms.archivedAt)) .where(and(eq(rooms.organizationId, activeOrganizationId), isNull(rooms.archivedAt)))
.orderBy(asc(rooms.name)); .orderBy(asc(rooms.name));
return { return {
@@ -39,11 +41,13 @@ export const load: PageServerLoad = async () => {
export const actions: Actions = { export const actions: Actions = {
create: async (event) => { create: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(roomCreateSchema), { id: 'rooms-create' }); const form = await superValidate(event, zod4(roomCreateSchema), { id: 'rooms-create' });
if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 }); if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 });
const sharedValues = { const sharedValues = {
organizationId: activeOrganizationId,
name: form.data.name, name: form.data.name,
type: form.data.type, type: form.data.type,
sqFt: null, sqFt: null,
@@ -96,6 +100,7 @@ export const actions: Actions = {
}, },
edit: async (event) => { edit: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(roomEditSchema), { id: 'rooms-edit' }); const form = await superValidate(event, zod4(roomEditSchema), { id: 'rooms-edit' });
if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 }); if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 });
@@ -140,7 +145,10 @@ export const actions: Actions = {
}; };
try { try {
await db.update(rooms).set(roomValues).where(eq(rooms.id, form.data.id)); await db
.update(rooms)
.set(roomValues)
.where(and(eq(rooms.id, form.data.id), eq(rooms.organizationId, activeOrganizationId)));
} catch { } catch {
return message(form, 'Unable to update room.', { status: 400 }); return message(form, 'Unable to update room.', { status: 400 });
} }
@@ -149,6 +157,7 @@ export const actions: Actions = {
}, },
archive: async (event) => { archive: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(archiveSchema), { id: 'rooms-archive' }); const form = await superValidate(event, zod4(archiveSchema), { id: 'rooms-archive' });
if (!form.valid) return message(form, 'Room id is required.', { status: 400 }); if (!form.valid) return message(form, 'Room id is required.', { status: 400 });
@@ -156,7 +165,7 @@ export const actions: Actions = {
await db await db
.update(rooms) .update(rooms)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(eq(rooms.id, form.data.id)); .where(and(eq(rooms.id, form.data.id), eq(rooms.organizationId, activeOrganizationId)));
return message(form, 'Room archived.'); return message(form, 'Room archived.');
} }
+13 -5
View File
@@ -1,17 +1,19 @@
import { asc, eq, isNull } from 'drizzle-orm'; import { and, asc, eq, isNull } from 'drizzle-orm';
import { message, superValidate } from 'sveltekit-superforms/server'; import { message, superValidate } from 'sveltekit-superforms/server';
import { zod4 } from 'sveltekit-superforms/adapters'; import { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { services } from '$lib/server/db/schema'; import { services } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema'; import { archiveSchema } from '$lib/schemas/shared.schema';
import { serviceCreateSchema, serviceEditSchema } from '$lib/schemas/services.schema'; import { serviceCreateSchema, serviceEditSchema } from '$lib/schemas/services.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async ({ locals }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
.select() .select()
.from(services) .from(services)
.where(isNull(services.archivedAt)) .where(and(eq(services.organizationId, activeOrganizationId), isNull(services.archivedAt)))
.orderBy(asc(services.name)); .orderBy(asc(services.name));
return { return {
@@ -34,6 +36,7 @@ export const load: PageServerLoad = async () => {
export const actions: Actions = { export const actions: Actions = {
create: async (event) => { create: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(serviceCreateSchema), { id: 'services-create' }); const form = await superValidate(event, zod4(serviceCreateSchema), { id: 'services-create' });
if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 }); if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 });
@@ -41,6 +44,7 @@ export const actions: Actions = {
try { try {
await db.insert(services).values({ await db.insert(services).values({
id: crypto.randomUUID(), id: crypto.randomUUID(),
organizationId: activeOrganizationId,
name: form.data.name, name: form.data.name,
category: form.data.category || null, category: form.data.category || null,
unit: form.data.unit, unit: form.data.unit,
@@ -57,6 +61,7 @@ export const actions: Actions = {
}, },
edit: async (event) => { edit: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(serviceEditSchema), { id: 'services-edit' }); const form = await superValidate(event, zod4(serviceEditSchema), { id: 'services-edit' });
if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 }); if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 });
@@ -72,7 +77,9 @@ export const actions: Actions = {
description: form.data.description || null, description: form.data.description || null,
updatedAt: new Date() updatedAt: new Date()
}) })
.where(eq(services.id, form.data.id)); .where(
and(eq(services.id, form.data.id), eq(services.organizationId, activeOrganizationId))
);
} catch { } catch {
return message(form, 'Unable to update service.', { status: 400 }); return message(form, 'Unable to update service.', { status: 400 });
} }
@@ -81,6 +88,7 @@ export const actions: Actions = {
}, },
archive: async (event) => { archive: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(archiveSchema), { id: 'services-archive' }); const form = await superValidate(event, zod4(archiveSchema), { id: 'services-archive' });
if (!form.valid) return message(form, 'Service id is required.', { status: 400 }); if (!form.valid) return message(form, 'Service id is required.', { status: 400 });
@@ -88,7 +96,7 @@ export const actions: Actions = {
await db await db
.update(services) .update(services)
.set({ archivedAt: new Date(), updatedAt: new Date() }) .set({ archivedAt: new Date(), updatedAt: new Date() })
.where(eq(services.id, form.data.id)); .where(and(eq(services.id, form.data.id), eq(services.organizationId, activeOrganizationId)));
return message(form, 'Service archived.'); return message(form, 'Service archived.');
} }