Compare commits

...

1 Commits

Author SHA1 Message Date
kdaniel2410 a6818607c8 fix: enforce room type scheduling rules 2026-06-26 11:36:51 +01:00
3 changed files with 47 additions and 4 deletions
@@ -9,6 +9,8 @@ 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';
const bookingRoomType = 'meeting_room';
async function loadOptions(organizationId: string) { async function loadOptions(organizationId: string) {
const [clientRows, roomRows, serviceRows] = await Promise.all([ const [clientRows, roomRows, serviceRows] = await Promise.all([
db db
@@ -19,7 +21,13 @@ async function loadOptions(organizationId: string) {
db db
.select({ id: rooms.id, name: rooms.name }) .select({ id: rooms.id, name: rooms.name })
.from(rooms) .from(rooms)
.where(and(eq(rooms.organizationId, organizationId), isNull(rooms.archivedAt))) .where(
and(
eq(rooms.organizationId, organizationId),
eq(rooms.type, bookingRoomType),
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 })
@@ -38,6 +46,23 @@ async function loadOptions(organizationId: string) {
}; };
} }
async function isValidBookingRoom(organizationId: string, roomId: string) {
const [room] = await db
.select({ id: rooms.id })
.from(rooms)
.where(
and(
eq(rooms.id, roomId),
eq(rooms.organizationId, organizationId),
eq(rooms.type, bookingRoomType),
isNull(rooms.archivedAt)
)
)
.limit(1);
return Boolean(room);
}
export const load: PageServerLoad = async ({ locals, params }) => { export const load: PageServerLoad = async ({ locals, params }) => {
const { activeOrganizationId } = await loadOrganizationContext(locals); const { activeOrganizationId } = await loadOrganizationContext(locals);
const records = await db const records = await db
@@ -93,6 +118,10 @@ export const actions: Actions = {
if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 }); if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 });
if (!(await isValidBookingRoom(activeOrganizationId, form.data.roomId))) {
return setError(form, 'roomId', 'Select a valid meeting room.');
}
const conflict = await findBookingRoomConflict({ const conflict = await findBookingRoomConflict({
organizationId: activeOrganizationId, organizationId: activeOrganizationId,
roomId: form.data.roomId, roomId: form.data.roomId,
@@ -133,6 +162,10 @@ export const actions: Actions = {
if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 }); if (!form.valid) return message(form, 'Check the highlighted fields.', { status: 400 });
if (!(await isValidBookingRoom(activeOrganizationId, form.data.roomId))) {
return setError(form, 'roomId', 'Select a valid meeting room.');
}
const conflict = await findBookingRoomConflict({ const conflict = await findBookingRoomConflict({
organizationId: activeOrganizationId, organizationId: activeOrganizationId,
roomId: form.data.roomId, roomId: form.data.roomId,
@@ -13,6 +13,8 @@ import {
} from '$lib/schemas/contracts.schema'; } from '$lib/schemas/contracts.schema';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
const contractRoomTypes = ['private_office', 'coworking_desk'] as const;
async function loadOptions(organizationId: string) { async function loadOptions(organizationId: string) {
const [roomRows, serviceRows] = await Promise.all([ const [roomRows, serviceRows] = await Promise.all([
db db
@@ -26,7 +28,7 @@ async function loadOptions(organizationId: string) {
.where( .where(
and( and(
eq(rooms.organizationId, organizationId), eq(rooms.organizationId, organizationId),
inArray(rooms.type, ['private_office', 'coworking_desk']), inArray(rooms.type, contractRoomTypes),
isNull(rooms.archivedAt) isNull(rooms.archivedAt)
) )
) )
@@ -155,7 +157,7 @@ async function selectionError(
and( and(
inArray(rooms.id, uniqueRoomIds), inArray(rooms.id, uniqueRoomIds),
eq(rooms.organizationId, organizationId), eq(rooms.organizationId, organizationId),
inArray(rooms.type, ['private_office', 'coworking_desk']), inArray(rooms.type, contractRoomTypes),
isNull(rooms.archivedAt) isNull(rooms.archivedAt)
) )
); );
@@ -371,6 +373,14 @@ export const actions: Actions = {
.select({ roomId: contractRooms.roomId }) .select({ roomId: contractRooms.roomId })
.from(contractRooms) .from(contractRooms)
.where(eq(contractRooms.contractId, form.data.id)); .where(eq(contractRooms.contractId, form.data.id));
const invalidSelection = await selectionError(
activeOrganizationId,
roomLinks.map((link) => link.roomId),
''
);
if (invalidSelection) {
return message(form, invalidSelection.message, { status: 400 });
}
const conflict = await findActiveContractRoomConflict({ const conflict = await findActiveContractRoomConflict({
organizationId: activeOrganizationId, organizationId: activeOrganizationId,
roomIds: roomLinks.map((link) => link.roomId), roomIds: roomLinks.map((link) => link.roomId),
@@ -215,7 +215,7 @@
{#if data.invoice.notes} {#if data.invoice.notes}
<section class="rounded-lg border border-border p-4"> <section class="rounded-lg border border-border p-4">
<h2 class="text-sm font-semibold">Notes</h2> <h2 class="text-sm font-semibold">Notes</h2>
<p class="mt-2 whitespace-pre-wrap text-sm text-muted-foreground">{data.invoice.notes}</p> <p class="mt-2 text-sm whitespace-pre-wrap text-muted-foreground">{data.invoice.notes}</p>
</section> </section>
{/if} {/if}
</div> </div>