refactor(api): extract holiday calendar catalog reads

This commit is contained in:
2026-03-31 09:05:18 +02:00
parent a23ef2c8b5
commit 5ff76e6aa2
3 changed files with 232 additions and 229 deletions
+4 -229
View File
@@ -11,10 +11,11 @@ import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { createAuditEntry } from "../lib/audit.js";
import { asHolidayResolverDb, getResolvedCalendarHolidays } from "../lib/holiday-availability.js";
import { createTRPCRouter, adminProcedure, protectedProcedure, type TRPCContext } from "../trpc.js";
import { createTRPCRouter, adminProcedure, protectedProcedure } from "../trpc.js";
import { holidayCalendarCatalogReadProcedures } from "./holiday-calendar-catalog-read.js";
import { asHolidayCalendarDb, type HolidayCalendarDb, type HolidayReadContext } from "./holiday-calendar-shared.js";
type HolidayCalendarScope = HolidayCalendarScopeInput;
type HolidayReadContext = Pick<TRPCContext, "db" | "dbUser">;
const HOLIDAY_SCOPE = {
COUNTRY: "COUNTRY",
@@ -22,28 +23,6 @@ const HOLIDAY_SCOPE = {
CITY: "CITY",
} as const satisfies Record<HolidayCalendarScope, HolidayCalendarScope>;
type HolidayCalendarDb = TRPCContext["db"] & {
holidayCalendar: {
findFirst: (args: unknown) => Promise<{ id: string } | null>;
findMany: (args: unknown) => Promise<any[]>;
findUnique: (args: unknown) => Promise<any | null>;
create: (args: unknown) => Promise<any>;
update: (args: unknown) => Promise<any>;
delete: (args: unknown) => Promise<any>;
};
holidayCalendarEntry: {
findFirst: (args: unknown) => Promise<{ id: string } | null>;
findUnique: (args: unknown) => Promise<any | null>;
create: (args: unknown) => Promise<any>;
update: (args: unknown) => Promise<any>;
delete: (args: unknown) => Promise<any>;
};
};
function asHolidayCalendarDb(db: TRPCContext["db"]): HolidayCalendarDb {
return db as unknown as HolidayCalendarDb;
}
function clampDate(date: Date): Date {
const value = new Date(date);
value.setUTCHours(0, 0, 0, 0);
@@ -54,71 +33,6 @@ function fmtDate(value: Date | null | undefined): string | null {
return value ? value.toISOString().slice(0, 10) : null;
}
function formatIsoDate(value: Date): string {
return value.toISOString().slice(0, 10);
}
function formatHolidayCalendarEntryDetail(entry: {
id: string;
date: Date;
name: string;
isRecurringAnnual?: boolean | null;
source?: string | null;
}) {
return {
id: entry.id,
date: formatIsoDate(entry.date),
name: entry.name,
isRecurringAnnual: entry.isRecurringAnnual ?? false,
source: entry.source ?? null,
};
}
function formatHolidayCalendarDetail(calendar: {
id: string;
name: string;
scopeType: string;
stateCode?: string | null;
isActive?: boolean | null;
priority?: number | null;
country?: { id: string; code: string; name: string } | null;
metroCity?: { id: string; name: string } | null;
_count?: { entries?: number | null } | null;
entries?: Array<{
id: string;
date: Date;
name: string;
isRecurringAnnual?: boolean | null;
source?: string | null;
}> | null;
}) {
const entries = calendar.entries?.map(formatHolidayCalendarEntryDetail) ?? [];
return {
id: calendar.id,
name: calendar.name,
scopeType: calendar.scopeType,
stateCode: calendar.stateCode ?? null,
isActive: calendar.isActive ?? true,
priority: calendar.priority ?? 0,
country: calendar.country
? {
id: calendar.country.id,
code: calendar.country.code,
name: calendar.country.name,
}
: null,
metroCity: calendar.metroCity
? {
id: calendar.metroCity.id,
name: calendar.metroCity.name,
}
: null,
entryCount: calendar._count?.entries ?? entries.length,
entries,
};
}
function canManageHolidayResourceReads(ctx: { dbUser: { systemRole: string } | null }): boolean {
const role = ctx.dbUser?.systemRole;
return role === "ADMIN" || role === "MANAGER";
@@ -243,92 +157,6 @@ const ResolveResourceHolidaysInputSchema = z.object({
}
});
async function readCalendarsSnapshot(
ctx: HolidayReadContext,
input?: {
includeInactive?: boolean | undefined;
countryCode?: string | undefined;
scopeType?: "COUNTRY" | "STATE" | "CITY" | undefined;
stateCode?: string | undefined;
metroCity?: string | undefined;
},
) {
const db = asHolidayCalendarDb(ctx.db);
const where = {
...(input?.includeInactive ? {} : { isActive: true }),
...(input?.countryCode
? {
country: { code: { equals: input.countryCode.trim().toUpperCase(), mode: "insensitive" as const } },
}
: {}),
...(input?.scopeType ? { scopeType: input.scopeType } : {}),
...(input?.stateCode ? { stateCode: input.stateCode.trim().toUpperCase() } : {}),
...(input?.metroCity
? {
metroCity: { name: { contains: input.metroCity.trim(), mode: "insensitive" as const } },
}
: {}),
};
return db.holidayCalendar.findMany({
where,
include: {
country: { select: { id: true, code: true, name: true } },
metroCity: { select: { id: true, name: true } },
_count: { select: { entries: true } },
entries: { orderBy: [{ date: "asc" }, { name: "asc" }] },
},
orderBy: [
{ country: { name: "asc" } },
{ scopeType: "asc" },
{ priority: "desc" },
{ name: "asc" },
],
});
}
async function readCalendarByIdentifierSnapshot(ctx: HolidayReadContext, identifier: string) {
const db = asHolidayCalendarDb(ctx.db);
const trimmedIdentifier = identifier.trim();
let calendar = await db.holidayCalendar.findUnique({
where: { id: trimmedIdentifier },
include: {
country: { select: { id: true, code: true, name: true } },
metroCity: { select: { id: true, name: true } },
entries: { orderBy: [{ date: "asc" }, { name: "asc" }] },
},
});
if (!calendar) {
calendar = await db.holidayCalendar.findFirst({
where: { name: { equals: trimmedIdentifier, mode: "insensitive" } },
include: {
country: { select: { id: true, code: true, name: true } },
metroCity: { select: { id: true, name: true } },
entries: { orderBy: [{ date: "asc" }, { name: "asc" }] },
},
});
}
if (!calendar) {
calendar = await db.holidayCalendar.findFirst({
where: { name: { contains: trimmedIdentifier, mode: "insensitive" } },
include: {
country: { select: { id: true, code: true, name: true } },
metroCity: { select: { id: true, name: true } },
entries: { orderBy: [{ date: "asc" }, { name: "asc" }] },
},
});
}
if (!calendar) {
throw new TRPCError({ code: "NOT_FOUND", message: `Holiday calendar not found: ${trimmedIdentifier}` });
}
return calendar;
}
async function readPreviewResolvedHolidaysSnapshot(
ctx: HolidayReadContext,
input: z.infer<typeof PreviewResolvedHolidaysSchema>,
@@ -589,60 +417,7 @@ async function assertScopeConsistency(
}
export const holidayCalendarRouter = createTRPCRouter({
listCalendars: adminProcedure
.input(z.object({
includeInactive: z.boolean().optional(),
countryCode: z.string().trim().min(1).optional(),
scopeType: z.enum(["COUNTRY", "STATE", "CITY"]).optional(),
stateCode: z.string().trim().min(1).optional(),
metroCity: z.string().trim().min(1).optional(),
}).optional())
.query(async ({ ctx, input }) => readCalendarsSnapshot(ctx, input)),
listCalendarsDetail: adminProcedure
.input(z.object({
includeInactive: z.boolean().optional(),
countryCode: z.string().trim().min(1).optional(),
scopeType: z.enum(["COUNTRY", "STATE", "CITY"]).optional(),
stateCode: z.string().trim().min(1).optional(),
metroCity: z.string().trim().min(1).optional(),
}).optional())
.query(async ({ ctx, input }) => {
const calendars = await readCalendarsSnapshot(ctx, input);
return {
count: calendars.length,
calendars: calendars.map(formatHolidayCalendarDetail),
};
}),
getCalendarByIdentifier: adminProcedure
.input(z.object({ identifier: z.string().trim().min(1) }))
.query(async ({ ctx, input }) => readCalendarByIdentifierSnapshot(ctx, input.identifier)),
getCalendarByIdentifierDetail: adminProcedure
.input(z.object({ identifier: z.string().trim().min(1) }))
.query(async ({ ctx, input }) => {
const calendar = await readCalendarByIdentifierSnapshot(ctx, input.identifier);
return formatHolidayCalendarDetail(calendar);
}),
getCalendarById: adminProcedure
.input(z.object({ id: z.string() }))
.query(async ({ ctx, input }) => {
const db = asHolidayCalendarDb(ctx.db);
return findUniqueOrThrow(
db.holidayCalendar.findUnique({
where: { id: input.id },
include: {
country: { select: { id: true, code: true, name: true } },
metroCity: { select: { id: true, name: true } },
entries: { orderBy: [{ date: "asc" }, { name: "asc" }] },
},
}),
"Holiday calendar",
);
}),
...holidayCalendarCatalogReadProcedures,
createCalendar: adminProcedure
.input(CreateHolidayCalendarSchema)