import { CreateHolidayCalendarEntrySchema, CreateHolidayCalendarSchema, UpdateHolidayCalendarEntrySchema, UpdateHolidayCalendarSchema, } from "@capakraken/shared"; import { z } from "zod"; import { findUniqueOrThrow } from "../db/helpers.js"; import { createAuditEntry } from "../lib/audit.js"; import { createTRPCRouter, adminProcedure } from "../trpc.js"; import { holidayCalendarCatalogReadProcedures } from "./holiday-calendar-catalog-read.js"; import { holidayCalendarResolutionReadProcedures } from "./holiday-calendar-resolution-read.js"; import { asHolidayCalendarDb } from "./holiday-calendar-shared.js"; import { buildHolidayCalendarCreateData, buildHolidayCalendarEntryCreateData, buildHolidayCalendarEntryUpdateData, buildHolidayCalendarUpdateData, holidayCalendarDetailInclude, } from "./holiday-calendar-support.js"; import { assertHolidayCalendarEntryDateAvailable, assertHolidayCalendarScopeConsistency, clampHolidayCalendarDate, normalizeHolidayCalendarScopeInput, resolveHolidayCalendarUpdateScope, } from "./holiday-calendar-write-support.js"; export const holidayCalendarRouter = createTRPCRouter({ ...holidayCalendarCatalogReadProcedures, ...holidayCalendarResolutionReadProcedures, createCalendar: adminProcedure .input(CreateHolidayCalendarSchema) .mutation(async ({ ctx, input }) => { const db = asHolidayCalendarDb(ctx.db); await findUniqueOrThrow( ctx.db.country.findUnique({ where: { id: input.countryId }, select: { id: true, name: true }, }), "Country", ); await assertHolidayCalendarScopeConsistency(db, { scopeType: input.scopeType, countryId: input.countryId, ...normalizeHolidayCalendarScopeInput({ stateCode: input.stateCode, metroCityId: input.metroCityId, }), }); const normalizedScope = normalizeHolidayCalendarScopeInput({ stateCode: input.stateCode, metroCityId: input.metroCityId, }); const created = await db.holidayCalendar.create({ data: buildHolidayCalendarCreateData({ ...input, normalizedScope, }), include: holidayCalendarDetailInclude, }); void createAuditEntry({ db: ctx.db, entityType: "HolidayCalendar", entityId: created.id, entityName: created.name, action: "CREATE", userId: ctx.dbUser?.id, after: created as unknown as Record, source: "ui", }); return created; }), updateCalendar: adminProcedure .input(z.object({ id: z.string(), data: UpdateHolidayCalendarSchema })) .mutation(async ({ ctx, input }) => { const db = asHolidayCalendarDb(ctx.db); const existing = await findUniqueOrThrow( db.holidayCalendar.findUnique({ where: { id: input.id } }), "Holiday calendar", ); const { stateCode, metroCityId } = resolveHolidayCalendarUpdateScope({ existing, data: input.data, }); await assertHolidayCalendarScopeConsistency(db, { scopeType: existing.scopeType, countryId: existing.countryId, stateCode, metroCityId, }, existing.id); const updated = await db.holidayCalendar.update({ where: { id: input.id }, data: buildHolidayCalendarUpdateData({ data: input.data, resolvedScope: { stateCode, metroCityId, }, }), include: holidayCalendarDetailInclude, }); void createAuditEntry({ db: ctx.db, entityType: "HolidayCalendar", entityId: updated.id, entityName: updated.name, action: "UPDATE", userId: ctx.dbUser?.id, before: existing as unknown as Record, after: updated as unknown as Record, source: "ui", }); return updated; }), deleteCalendar: adminProcedure .input(z.object({ id: z.string() })) .mutation(async ({ ctx, input }) => { const db = asHolidayCalendarDb(ctx.db); const existing = await findUniqueOrThrow( db.holidayCalendar.findUnique({ where: { id: input.id }, include: { entries: true }, }), "Holiday calendar", ); await db.holidayCalendar.delete({ where: { id: input.id } }); void createAuditEntry({ db: ctx.db, entityType: "HolidayCalendar", entityId: existing.id, entityName: existing.name, action: "DELETE", userId: ctx.dbUser?.id, before: existing as unknown as Record, source: "ui", }); return { success: true, id: existing.id, name: existing.name }; }), createEntry: adminProcedure .input(CreateHolidayCalendarEntrySchema) .mutation(async ({ ctx, input }) => { const db = asHolidayCalendarDb(ctx.db); await findUniqueOrThrow( db.holidayCalendar.findUnique({ where: { id: input.holidayCalendarId }, select: { id: true, name: true }, }), "Holiday calendar", ); await assertHolidayCalendarEntryDateAvailable(db, { holidayCalendarId: input.holidayCalendarId, date: input.date, }); const created = await db.holidayCalendarEntry.create({ data: buildHolidayCalendarEntryCreateData({ data: input, date: clampHolidayCalendarDate(input.date), }), }); void createAuditEntry({ db: ctx.db, entityType: "HolidayCalendarEntry", entityId: created.id, entityName: created.name, action: "CREATE", userId: ctx.dbUser?.id, after: created as unknown as Record, source: "ui", }); return created; }), updateEntry: adminProcedure .input(z.object({ id: z.string(), data: UpdateHolidayCalendarEntrySchema })) .mutation(async ({ ctx, input }) => { const db = asHolidayCalendarDb(ctx.db); const existing = await findUniqueOrThrow( db.holidayCalendarEntry.findUnique({ where: { id: input.id } }), "Holiday calendar entry", ); const nextDate = input.data.date !== undefined ? clampHolidayCalendarDate(input.data.date) : existing.date; await assertHolidayCalendarEntryDateAvailable(db, { holidayCalendarId: existing.holidayCalendarId, date: nextDate, }, existing.id); const updated = await db.holidayCalendarEntry.update({ where: { id: input.id }, data: buildHolidayCalendarEntryUpdateData({ data: input.data, date: nextDate, }), }); void createAuditEntry({ db: ctx.db, entityType: "HolidayCalendarEntry", entityId: updated.id, entityName: updated.name, action: "UPDATE", userId: ctx.dbUser?.id, before: existing as unknown as Record, after: updated as unknown as Record, source: "ui", }); return updated; }), deleteEntry: adminProcedure .input(z.object({ id: z.string() })) .mutation(async ({ ctx, input }) => { const db = asHolidayCalendarDb(ctx.db); const existing = await findUniqueOrThrow( db.holidayCalendarEntry.findUnique({ where: { id: input.id } }), "Holiday calendar entry", ); await db.holidayCalendarEntry.delete({ where: { id: input.id } }); void createAuditEntry({ db: ctx.db, entityType: "HolidayCalendarEntry", entityId: existing.id, entityName: existing.name, action: "DELETE", userId: ctx.dbUser?.id, before: existing as unknown as Record, source: "ui", }); return { success: true, id: existing.id, name: existing.name }; }), });