refactor(api): extract holiday calendar catalog reads
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
import { findUniqueOrThrow } from "../db/helpers.js";
|
||||
import { adminProcedure } from "../trpc.js";
|
||||
import { asHolidayCalendarDb, type HolidayReadContext } from "./holiday-calendar-shared.js";
|
||||
|
||||
const HolidayCalendarCatalogInputSchema = 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();
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
async function readCalendarsSnapshot(
|
||||
ctx: HolidayReadContext,
|
||||
input?: z.infer<typeof HolidayCalendarCatalogInputSchema>,
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
||||
export const holidayCalendarCatalogReadProcedures = {
|
||||
listCalendars: adminProcedure
|
||||
.input(HolidayCalendarCatalogInputSchema)
|
||||
.query(async ({ ctx, input }) => readCalendarsSnapshot(ctx, input)),
|
||||
|
||||
listCalendarsDetail: adminProcedure
|
||||
.input(HolidayCalendarCatalogInputSchema)
|
||||
.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",
|
||||
);
|
||||
}),
|
||||
};
|
||||
Reference in New Issue
Block a user