import { buildSplitAllocationReadModel } from "@capakraken/application"; import { VacationType } from "@capakraken/db"; import { asHolidayResolverDb, getResolvedCalendarHolidays } from "../lib/holiday-availability.js"; import { buildTimelineHolidayResourceIds, buildTimelineHolidayResourceWhere, } from "./timeline-holiday-support.js"; import { loadTimelineEntriesReadModel, TimelineEntriesDbClient, TimelineEntriesFilters, } from "./timeline-read-shared.js"; export async function loadTimelineHolidayOverlays( db: TimelineEntriesDbClient, input: TimelineEntriesFilters, ) { const readModel = await loadTimelineEntriesReadModel(db, input); return loadTimelineHolidayOverlaysForReadModel(db, input, readModel); } export async function loadTimelineHolidayOverlaysForReadModel( db: TimelineEntriesDbClient, input: TimelineEntriesFilters, readModel: ReturnType, ) { const resourceWhere = buildTimelineHolidayResourceWhere({ chapters: input.chapters, eids: input.eids, countryCodes: input.countryCodes, }); const matchingResources = resourceWhere ? await db.resource.findMany({ where: resourceWhere, select: { id: true }, }) : []; const resourceIds = buildTimelineHolidayResourceIds({ assignmentResourceIds: readModel.assignments.map((assignment) => assignment.resourceId), requestedResourceIds: input.resourceIds, matchingFilterResourceIds: matchingResources.map((resource) => resource.id), }); if (resourceIds.length === 0) { return []; } const resources = await db.resource.findMany({ where: { id: { in: resourceIds } }, select: { id: true, countryId: true, federalState: true, metroCityId: true, country: { select: { code: true, name: true } }, metroCity: { select: { name: true } }, }, }); // Group resources by location key to deduplicate holiday resolution. // Resources sharing the same (countryId, federalState, metroCityId) get // identical holidays, so we resolve once per location instead of once per resource. const locationGroups = new Map< string, { locationResource: (typeof resources)[0]; resourceIds: string[] } >(); for (const resource of resources) { const key = `${resource.countryId ?? ""}:${resource.federalState ?? ""}:${resource.metroCityId ?? ""}`; const existing = locationGroups.get(key); if (existing) { existing.resourceIds.push(resource.id); } else { locationGroups.set(key, { locationResource: resource, resourceIds: [resource.id] }); } } const resolverDb = asHolidayResolverDb(db); const overlays = await Promise.all( [...locationGroups.values()].map(async ({ locationResource, resourceIds }) => { const holidays = await getResolvedCalendarHolidays(resolverDb, { periodStart: input.startDate, periodEnd: input.endDate, countryId: locationResource.countryId, countryCode: locationResource.country?.code ?? null, federalState: locationResource.federalState, metroCityId: locationResource.metroCityId, metroCityName: locationResource.metroCity?.name ?? null, }); return resourceIds.flatMap((resourceId) => { const resource = resources.find((r) => r.id === resourceId)!; return holidays.map((holiday) => { const holidayDate = new Date(`${holiday.date}T00:00:00.000Z`); return { id: `calendar-holiday:${resourceId}:${holiday.date}`, resourceId, type: VacationType.PUBLIC_HOLIDAY, status: "APPROVED" as const, startDate: holidayDate, endDate: holidayDate, note: holiday.name, scope: holiday.scope, calendarName: holiday.calendarName, sourceType: holiday.sourceType, countryCode: resource.country?.code ?? null, countryName: resource.country?.name ?? null, federalState: resource.federalState ?? null, metroCityName: resource.metroCity?.name ?? null, }; }); }); }), ); return overlays.flat().sort((left, right) => { if (left.resourceId !== right.resourceId) { return left.resourceId.localeCompare(right.resourceId); } return left.startDate.getTime() - right.startDate.getTime(); }); }