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
+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 { zod4 } from 'sveltekit-superforms/adapters';
import { db } from '$lib/server/db';
import { contacts, clients } from '$lib/server/db/schema';
import { loadOrganizationContext } from '$lib/server/organizations';
import { archiveSchema } from '$lib/schemas/shared.schema';
import type { Actions, PageServerLoad } from './$types';
async function loadOptions() {
async function loadOptions(organizationId: string) {
const clientRows = await db
.select({ id: clients.id, name: clients.name })
.from(clients)
.where(isNull(clients.archivedAt))
.where(and(eq(clients.organizationId, organizationId), isNull(clients.archivedAt)))
.orderBy(asc(clients.name));
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
.select()
.from(contacts)
.where(isNull(contacts.archivedAt))
.where(and(eq(contacts.organizationId, activeOrganizationId), isNull(contacts.archivedAt)))
.orderBy(asc(contacts.name));
return {
records,
options: await loadOptions(),
options: await loadOptions(activeOrganizationId),
archiveForm: await superValidate(zod4(archiveSchema), { id: 'contacts-archive' })
};
};
export const actions: Actions = {
archive: async (event) => {
const { activeOrganizationId } = await loadOrganizationContext(event.locals);
const form = await superValidate(event, zod4(archiveSchema), { id: 'contacts-archive' });
if (!form.valid) return message(form, 'Contact id is required.', { status: 400 });
@@ -41,7 +44,7 @@ export const actions: Actions = {
await db
.update(contacts)
.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.');
}