import { VacationStatus, VacationType } from "@capakraken/db"; import { RESOURCE_BRIEF_SELECT } from "../db/selects.js"; import { type ResourceHolidayContext } from "../lib/resource-holiday-context.js"; import { VACATION_BALANCE_TYPES } from "../lib/vacation-deduction-snapshot.js"; import { countCalendarDaysInPeriod, countVacationChargeableDays } from "../lib/vacation-day-count.js"; import type { TRPCContext } from "../trpc.js"; type VacationReadDb = TRPCContext["db"]; type VacationPreviewInput = { type: VacationType; startDate: Date; endDate: Date; isHalfDay: boolean; holidayContext: ResourceHolidayContext; }; function resolveHolidayDetailSource( isoDate: string, holidayContext: ResourceHolidayContext, ): "CALENDAR" | "LEGACY_PUBLIC_HOLIDAY" | "CALENDAR_AND_LEGACY" { const inCalendar = holidayContext.calendarHolidayStrings.includes(isoDate); const inLegacy = holidayContext.publicHolidayStrings.includes(isoDate); if (inCalendar && inLegacy) { return "CALENDAR_AND_LEGACY"; } return inCalendar ? "CALENDAR" : "LEGACY_PUBLIC_HOLIDAY"; } export function buildVacationPreview( input: VacationPreviewInput, ) { const vacation = { startDate: input.startDate, endDate: input.endDate, isHalfDay: input.isHalfDay, }; const requestedDays = countCalendarDaysInPeriod(vacation); const effectiveDays = VACATION_BALANCE_TYPES.has(input.type) ? countVacationChargeableDays({ vacation, countryCode: input.holidayContext.countryCode, federalState: input.holidayContext.federalState, metroCityName: input.holidayContext.metroCityName, calendarHolidayStrings: input.holidayContext.calendarHolidayStrings, publicHolidayStrings: input.holidayContext.publicHolidayStrings, }) : requestedDays; const publicHolidayDates = [...new Set([ ...input.holidayContext.calendarHolidayStrings, ...input.holidayContext.publicHolidayStrings, ])].sort(); return { requestedDays, effectiveDays, deductedDays: VACATION_BALANCE_TYPES.has(input.type) ? effectiveDays : 0, publicHolidayDates, holidayDetails: publicHolidayDates.map((date) => ({ date, source: resolveHolidayDetailSource(date, input.holidayContext), })), holidayContext: { countryCode: input.holidayContext.countryCode ?? null, countryName: input.holidayContext.countryName ?? null, federalState: input.holidayContext.federalState ?? null, metroCityName: input.holidayContext.metroCityName ?? null, sources: { hasCalendarHolidays: input.holidayContext.calendarHolidayStrings.length > 0, hasLegacyPublicHolidayEntries: input.holidayContext.publicHolidayStrings.length > 0, }, }, }; } export async function findVacationResourceChapter( db: VacationReadDb, resourceId: string, ): Promise { const resource = await db.resource.findUnique({ where: { id: resourceId }, select: { chapter: true }, }); return resource?.chapter ?? null; } export async function listChapterVacationOverlaps( db: VacationReadDb, input: { chapter: string; resourceId: string; startDate: Date; endDate: Date; }, ) { return db.vacation.findMany({ where: { resource: { chapter: input.chapter }, resourceId: { not: input.resourceId }, status: { in: [VacationStatus.APPROVED, VacationStatus.PENDING] }, startDate: { lte: input.endDate }, endDate: { gte: input.startDate }, }, include: { resource: { select: RESOURCE_BRIEF_SELECT }, }, orderBy: { startDate: "asc" }, take: 20, }); }