109 lines
3.0 KiB
TypeScript
109 lines
3.0 KiB
TypeScript
import { db } from '@/lib/prisma';
|
|
import { buildUniqueSlug, slugify } from '@/lib/slug';
|
|
import type { CreateComplexInput } from '@repo/api-contract';
|
|
import { v7 as uuidv7 } from 'uuid';
|
|
|
|
type DayOfWeek = 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY' | 'SUNDAY';
|
|
|
|
type CreateComplexInternalInput = CreateComplexInput & {
|
|
adminEmail: string;
|
|
userId?: string;
|
|
};
|
|
|
|
function toMinutes(value: string): number {
|
|
const [hours, minutes] = value.split(':').map((part) => Number(part));
|
|
return hours * 60 + minutes;
|
|
}
|
|
|
|
function assertAvailabilityRanges(
|
|
availability: Array<{
|
|
dayOfWeek: DayOfWeek;
|
|
startTime: string;
|
|
endTime: string;
|
|
}>
|
|
) {
|
|
for (const range of availability) {
|
|
const start = toMinutes(range.startTime);
|
|
const end = toMinutes(range.endTime);
|
|
if (start >= end) {
|
|
throw new Error(`El rango ${range.startTime}-${range.endTime} es inválido.`);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function createComplex(input: CreateComplexInternalInput) {
|
|
return db.$transaction(async (tx) => {
|
|
const base = slugify(input.complexName);
|
|
const fallback = base.length > 0 ? base : `complex-${uuidv7().slice(0, 8)}`;
|
|
const complexSlug = await buildUniqueSlug(fallback, (slug) =>
|
|
tx.complex.findFirst({ where: { complexSlug: slug }, select: { id: true } })
|
|
);
|
|
|
|
const complex = await tx.complex.create({
|
|
data: {
|
|
id: uuidv7(),
|
|
complexName: input.complexName,
|
|
physicalAddress: input.physicalAddress.trim(),
|
|
city: input.city?.trim() || null,
|
|
state: input.state?.trim() || null,
|
|
country: input.country?.trim() || null,
|
|
complexSlug,
|
|
adminEmail: input.adminEmail,
|
|
planCode: input.planCode,
|
|
},
|
|
});
|
|
|
|
if (input.userId) {
|
|
await tx.complexUser.create({
|
|
data: {
|
|
complexId: complex.id,
|
|
userId: input.userId,
|
|
role: 'ADMIN',
|
|
},
|
|
});
|
|
}
|
|
|
|
if (input.setupCourts && input.courtSportId) {
|
|
const sport = await tx.sport.findFirst({
|
|
where: { id: input.courtSportId, isActive: true },
|
|
select: { name: true },
|
|
});
|
|
|
|
if (!sport) {
|
|
throw new Error('El deporte seleccionado no existe o esta inactivo.');
|
|
}
|
|
|
|
const availability = (input.courtDaysOfWeek ?? []).map((day) => ({
|
|
dayOfWeek: day as DayOfWeek,
|
|
startTime: input.courtStartTime ?? '08:00',
|
|
endTime: input.courtEndTime ?? '22:00',
|
|
}));
|
|
|
|
assertAvailabilityRanges(availability);
|
|
|
|
const court = await tx.court.create({
|
|
data: {
|
|
id: uuidv7(),
|
|
complexId: complex.id,
|
|
sportId: input.courtSportId,
|
|
name: `${sport.name} 1`,
|
|
slotDurationMinutes: 60,
|
|
basePrice: 0,
|
|
},
|
|
});
|
|
|
|
await tx.courtAvailability.createMany({
|
|
data: availability.map((avail) => ({
|
|
id: uuidv7(),
|
|
courtId: court.id,
|
|
dayOfWeek: avail.dayOfWeek,
|
|
startTime: avail.startTime,
|
|
endTime: avail.endTime,
|
|
})),
|
|
});
|
|
}
|
|
|
|
return complex;
|
|
});
|
|
}
|