Files
alrahma_sunday_school_api/apps/api/dist/services/vehicleAvailabilityService.js
T
2026-06-11 03:22:12 -04:00

111 lines
4.3 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getVehicleAvailabilitySummary = getVehicleAvailabilitySummary;
const prisma_1 = require("../lib/prisma");
const BLOCKING_RESERVATION_STATUSES = ['DRAFT', 'CONFIRMED', 'ACTIVE'];
function startOfTodayUtc() {
const now = new Date();
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
}
function overlaps(range, source) {
return source.startDate < range.endDate && source.endDate > range.startDate;
}
function getNextAvailableAt(anchor, sources) {
const sorted = sources
.filter((source) => source.endDate > anchor)
.sort((a, b) => {
const startDelta = a.startDate.getTime() - b.startDate.getTime();
if (startDelta !== 0)
return startDelta;
return a.endDate.getTime() - b.endDate.getTime();
});
let candidate = anchor;
let advanced = false;
for (const source of sorted) {
if (source.endDate <= candidate)
continue;
if (source.startDate > candidate)
break;
candidate = new Date(Math.max(candidate.getTime(), source.endDate.getTime()));
advanced = true;
}
return advanced ? candidate : null;
}
async function getVehicleAvailabilitySummary(vehicleId, { range, includeDraftReservations = true, companyId, } = {}) {
const vehicle = await prisma_1.prisma.vehicle.findFirstOrThrow({
where: { id: vehicleId, ...(companyId ? { companyId } : {}) },
select: { status: true },
});
if (vehicle.status === 'OUT_OF_SERVICE') {
return {
available: false,
status: 'UNAVAILABLE',
nextAvailableAt: null,
blockingReason: 'STATUS',
};
}
const anchor = range?.startDate ?? startOfTodayUtc();
const reservationStatuses = includeDraftReservations
? [...BLOCKING_RESERVATION_STATUSES]
: BLOCKING_RESERVATION_STATUSES.filter((status) => status !== 'DRAFT');
const [reservations, blocks] = await Promise.all([
prisma_1.prisma.reservation.findMany({
where: {
vehicleId,
status: { in: reservationStatuses },
endDate: { gt: anchor },
},
select: { startDate: true, endDate: true },
orderBy: { startDate: 'asc' },
}),
prisma_1.prisma.vehicleCalendarBlock.findMany({
where: {
vehicleId,
endDate: { gt: anchor },
},
select: { startDate: true, endDate: true, type: true },
orderBy: { startDate: 'asc' },
}),
]);
const sources = [
...reservations.map((reservation) => ({
startDate: reservation.startDate,
endDate: reservation.endDate,
kind: 'RESERVATION',
})),
...blocks.map((block) => ({
startDate: block.startDate,
endDate: block.endDate,
kind: block.type === 'MAINTENANCE' ? 'MAINTENANCE' : 'BLOCK',
})),
];
const overlappingSources = range ? sources.filter((source) => overlaps(range, source)) : sources.filter((source) => source.startDate <= anchor && source.endDate > anchor);
const currentSource = overlappingSources.sort((a, b) => {
const priority = { MAINTENANCE: 0, BLOCK: 1, RESERVATION: 2 };
return priority[a.kind] - priority[b.kind];
})[0];
if (vehicle.status === 'MAINTENANCE') {
return {
available: false,
status: 'MAINTENANCE',
nextAvailableAt: getNextAvailableAt(anchor, sources) ?? null,
blockingReason: currentSource?.kind ?? 'STATUS',
};
}
if (!currentSource) {
return {
available: true,
status: 'AVAILABLE',
nextAvailableAt: null,
blockingReason: null,
};
}
const nextAvailableAnchor = currentSource.startDate > anchor ? currentSource.startDate : anchor;
return {
available: false,
status: currentSource.kind === 'RESERVATION' ? 'RESERVED' : currentSource.kind === 'MAINTENANCE' ? 'MAINTENANCE' : 'UNAVAILABLE',
nextAvailableAt: getNextAvailableAt(nextAvailableAnchor, sources),
blockingReason: currentSource.kind,
};
}
//# sourceMappingURL=vehicleAvailabilityService.js.map