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:
@@ -3,26 +3,27 @@ import { message, superValidate } from 'sveltekit-superforms/server';
|
||||
import { zod4 } from 'sveltekit-superforms/adapters';
|
||||
import { db } from '$lib/server/db';
|
||||
import { bookings, clients, rooms, services } from '$lib/server/db/schema';
|
||||
import { loadOrganizationContext } from '$lib/server/organizations';
|
||||
import { archiveSchema } from '$lib/schemas/shared.schema';
|
||||
import { bookingCreateSchema, bookingEditSchema } from '$lib/schemas/bookings.schema';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
|
||||
async function loadOptions() {
|
||||
async function loadOptions(organizationId: string) {
|
||||
const [clientRows, roomRows, serviceRows] = await Promise.all([
|
||||
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)),
|
||||
db
|
||||
.select({ id: rooms.id, name: rooms.name })
|
||||
.from(rooms)
|
||||
.where(isNull(rooms.archivedAt))
|
||||
.where(and(eq(rooms.organizationId, organizationId), isNull(rooms.archivedAt)))
|
||||
.orderBy(asc(rooms.name)),
|
||||
db
|
||||
.select({ id: services.id, name: services.name })
|
||||
.from(services)
|
||||
.where(isNull(services.archivedAt))
|
||||
.where(and(eq(services.organizationId, organizationId), isNull(services.archivedAt)))
|
||||
.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
|
||||
.select()
|
||||
.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));
|
||||
|
||||
return {
|
||||
@@ -57,7 +65,7 @@ export const load: PageServerLoad = async ({ params }) => {
|
||||
notes: record.notes ?? ''
|
||||
}
|
||||
})),
|
||||
options: await loadOptions(),
|
||||
options: await loadOptions(activeOrganizationId),
|
||||
createForm: await superValidate({ clientId: params.id }, zod4(bookingCreateSchema), {
|
||||
id: 'bookings-create'
|
||||
}),
|
||||
@@ -67,7 +75,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
create: async ({ params, request }) => {
|
||||
create: async ({ locals, params, request }) => {
|
||||
const { activeOrganizationId } = await loadOrganizationContext(locals);
|
||||
const formData = await request.formData();
|
||||
formData.set('clientId', params.id);
|
||||
const form = await superValidate(formData, zod4(bookingCreateSchema), {
|
||||
@@ -79,6 +88,7 @@ export const actions: Actions = {
|
||||
try {
|
||||
await db.insert(bookings).values({
|
||||
id: crypto.randomUUID(),
|
||||
organizationId: activeOrganizationId,
|
||||
clientId: form.data.clientId,
|
||||
roomId: form.data.roomId,
|
||||
serviceId: form.data.serviceId || null,
|
||||
@@ -96,7 +106,8 @@ export const actions: Actions = {
|
||||
return message(form, 'Booking created.');
|
||||
},
|
||||
|
||||
edit: async ({ params, request }) => {
|
||||
edit: async ({ locals, params, request }) => {
|
||||
const { activeOrganizationId } = await loadOrganizationContext(locals);
|
||||
const formData = await request.formData();
|
||||
formData.set('clientId', params.id);
|
||||
const form = await superValidate(formData, zod4(bookingEditSchema), {
|
||||
@@ -118,7 +129,13 @@ export const actions: Actions = {
|
||||
notes: form.data.notes || null,
|
||||
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 {
|
||||
return message(form, 'Unable to update booking.', { status: 400 });
|
||||
}
|
||||
@@ -126,7 +143,8 @@ export const actions: Actions = {
|
||||
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), {
|
||||
id: 'bookings-archive'
|
||||
});
|
||||
@@ -136,7 +154,13 @@ export const actions: Actions = {
|
||||
await db
|
||||
.update(bookings)
|
||||
.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.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user