Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a6818607c8 |
@@ -82,54 +82,6 @@ export async function findBookingRoomConflict({
|
|||||||
return conflict ?? null;
|
return conflict ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function findBookingContractRoomConflict({
|
|
||||||
organizationId,
|
|
||||||
roomId,
|
|
||||||
startsAt,
|
|
||||||
endsAt,
|
|
||||||
status
|
|
||||||
}: {
|
|
||||||
organizationId: string;
|
|
||||||
roomId: string;
|
|
||||||
startsAt: string;
|
|
||||||
endsAt: string;
|
|
||||||
status: string;
|
|
||||||
}): Promise<ContractRoomConflict | null> {
|
|
||||||
if (status === 'cancelled') return null;
|
|
||||||
|
|
||||||
const startDate = startsAt.slice(0, 10);
|
|
||||||
const endDate = endsAt.slice(0, 10);
|
|
||||||
|
|
||||||
const [conflict] = await db
|
|
||||||
.select({
|
|
||||||
contractId: contracts.id,
|
|
||||||
roomId: contractRooms.roomId,
|
|
||||||
roomName: rooms.name,
|
|
||||||
startDate: contracts.startDate,
|
|
||||||
endDate: contracts.endDate
|
|
||||||
})
|
|
||||||
.from(contractRooms)
|
|
||||||
.innerJoin(contracts, eq(contractRooms.contractId, contracts.id))
|
|
||||||
.innerJoin(rooms, eq(contractRooms.roomId, rooms.id))
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(contracts.organizationId, organizationId),
|
|
||||||
eq(contracts.status, 'active'),
|
|
||||||
isNull(contracts.archivedAt),
|
|
||||||
eq(contractRooms.roomId, roomId),
|
|
||||||
eq(rooms.organizationId, organizationId),
|
|
||||||
inArray(rooms.type, rentableContractRoomTypes),
|
|
||||||
isNull(rooms.archivedAt),
|
|
||||||
lte(contracts.startDate, endDate),
|
|
||||||
gte(contracts.endDate, startDate)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.orderBy(asc(contracts.startDate), asc(rooms.name))
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
return conflict ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function findActiveContractRoomConflict({
|
export async function findActiveContractRoomConflict({
|
||||||
organizationId,
|
organizationId,
|
||||||
roomIds,
|
roomIds,
|
||||||
|
|||||||
@@ -4,16 +4,13 @@ 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 { loadOrganizationContext } from '$lib/server/organizations';
|
||||||
import {
|
import { bookingConflictMessage, findBookingRoomConflict } from '$lib/server/scheduling';
|
||||||
bookingConflictMessage,
|
|
||||||
contractConflictMessage,
|
|
||||||
findBookingContractRoomConflict,
|
|
||||||
findBookingRoomConflict
|
|
||||||
} from '$lib/server/scheduling';
|
|
||||||
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';
|
||||||
|
|
||||||
|
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
|
||||||
@@ -24,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 })
|
||||||
@@ -43,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
|
||||||
@@ -98,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,
|
||||||
@@ -107,17 +131,6 @@ export const actions: Actions = {
|
|||||||
});
|
});
|
||||||
if (conflict) return setError(form, 'roomId', bookingConflictMessage(conflict));
|
if (conflict) return setError(form, 'roomId', bookingConflictMessage(conflict));
|
||||||
|
|
||||||
const contractConflict = await findBookingContractRoomConflict({
|
|
||||||
organizationId: activeOrganizationId,
|
|
||||||
roomId: form.data.roomId,
|
|
||||||
startsAt: form.data.startsAt,
|
|
||||||
endsAt: form.data.endsAt,
|
|
||||||
status: form.data.status
|
|
||||||
});
|
|
||||||
if (contractConflict) {
|
|
||||||
return setError(form, 'roomId', contractConflictMessage(contractConflict));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.insert(bookings).values({
|
await db.insert(bookings).values({
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
@@ -149,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,
|
||||||
@@ -159,17 +176,6 @@ export const actions: Actions = {
|
|||||||
});
|
});
|
||||||
if (conflict) return setError(form, 'roomId', bookingConflictMessage(conflict));
|
if (conflict) return setError(form, 'roomId', bookingConflictMessage(conflict));
|
||||||
|
|
||||||
const contractConflict = await findBookingContractRoomConflict({
|
|
||||||
organizationId: activeOrganizationId,
|
|
||||||
roomId: form.data.roomId,
|
|
||||||
startsAt: form.data.startsAt,
|
|
||||||
endsAt: form.data.endsAt,
|
|
||||||
status: form.data.status
|
|
||||||
});
|
|
||||||
if (contractConflict) {
|
|
||||||
return setError(form, 'roomId', contractConflictMessage(contractConflict));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db
|
await db
|
||||||
.update(bookings)
|
.update(bookings)
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
Reference in New Issue
Block a user