import { ImportBatchStatus, StagedRecordStatus, DispoStagedRecordType, } from "@capakraken/db"; import { assessDispoImportReadiness, stageDispoImportBatch, } from "@capakraken/application"; import { z } from "zod"; import { adminProcedure, createTRPCRouter } from "../trpc.js"; import { commitImportBatch, cancelImportBatch, resolveStagedRecord } from "./dispo-management.js"; import { getImportBatch, listImportBatches, listStagedAssignments, listStagedProjects, listStagedResources, listStagedUnresolvedRecords, listStagedVacations, } from "./dispo-read.js"; // ─── Shared schemas ────────────────────────────────────────────────────────── const paginationSchema = z.object({ cursor: z.string().optional(), limit: z.number().int().min(1).max(200).default(50), }); const importBatchStatusSchema = z.nativeEnum(ImportBatchStatus); const stagedRecordStatusSchema = z.nativeEnum(StagedRecordStatus); const stagedRecordTypeSchema = z.nativeEnum(DispoStagedRecordType); const workbookPathSchema = z .string() .trim() .min(1, "Workbook path is required.") .refine((value) => value.toLowerCase().endsWith(".xlsx"), { message: "Only .xlsx workbook paths are supported.", }); // ─── Router ────────────────────────────────────────────────────────────────── export const dispoRouter = createTRPCRouter({ // ── 1. stageImportBatch ────────────────────────────────────────────────── stageImportBatch: adminProcedure .input( z.object({ chargeabilityWorkbookPath: workbookPathSchema, costWorkbookPath: workbookPathSchema.optional(), notes: z.string().nullish(), planningWorkbookPath: workbookPathSchema, referenceWorkbookPath: workbookPathSchema, rosterWorkbookPath: workbookPathSchema.optional(), }), ) .mutation(async ({ ctx, input }) => { return stageDispoImportBatch(ctx.db, { chargeabilityWorkbookPath: input.chargeabilityWorkbookPath, planningWorkbookPath: input.planningWorkbookPath, referenceWorkbookPath: input.referenceWorkbookPath, ...(input.costWorkbookPath !== undefined ? { costWorkbookPath: input.costWorkbookPath } : {}), ...(input.notes !== undefined ? { notes: input.notes } : {}), ...(input.rosterWorkbookPath !== undefined ? { rosterWorkbookPath: input.rosterWorkbookPath } : {}), }); }), // ── 2. validateImportBatch ─────────────────────────────────────────────── validateImportBatch: adminProcedure .input( z.object({ chargeabilityWorkbookPath: workbookPathSchema, costWorkbookPath: workbookPathSchema.optional(), importBatchId: z.string().optional(), notes: z.string().nullish(), planningWorkbookPath: workbookPathSchema, referenceWorkbookPath: workbookPathSchema, rosterWorkbookPath: workbookPathSchema.optional(), }), ) .query(async ({ input }) => { return assessDispoImportReadiness({ chargeabilityWorkbookPath: input.chargeabilityWorkbookPath, planningWorkbookPath: input.planningWorkbookPath, referenceWorkbookPath: input.referenceWorkbookPath, ...(input.costWorkbookPath !== undefined ? { costWorkbookPath: input.costWorkbookPath } : {}), ...(input.importBatchId !== undefined ? { importBatchId: input.importBatchId } : {}), ...(input.notes !== undefined ? { notes: input.notes } : {}), ...(input.rosterWorkbookPath !== undefined ? { rosterWorkbookPath: input.rosterWorkbookPath } : {}), }); }), // ── 3. listImportBatches ───────────────────────────────────────────────── listImportBatches: adminProcedure .input( paginationSchema.extend({ status: importBatchStatusSchema.optional(), }), ) .query(async ({ ctx, input }) => { return listImportBatches(ctx.db, input); }), // ── 4. getImportBatch ──────────────────────────────────────────────────── getImportBatch: adminProcedure .input(z.object({ id: z.string() })) .query(async ({ ctx, input }) => { return getImportBatch(ctx.db, input.id); }), // ── 5. cancelImportBatch ───────────────────────────────────────────────── cancelImportBatch: adminProcedure .input(z.object({ id: z.string() })) .mutation(async ({ ctx, input }) => { return cancelImportBatch(ctx.db, { id: input.id, userId: ctx.dbUser?.id }); }), // ── 6. listStagedResources ─────────────────────────────────────────────── listStagedResources: adminProcedure .input( paginationSchema.extend({ importBatchId: z.string(), status: stagedRecordStatusSchema.optional(), }), ) .query(async ({ ctx, input }) => { return listStagedResources(ctx.db, input); }), // ── 7. listStagedProjects ──────────────────────────────────────────────── listStagedProjects: adminProcedure .input( paginationSchema.extend({ importBatchId: z.string(), isTbd: z.boolean().optional(), status: stagedRecordStatusSchema.optional(), }), ) .query(async ({ ctx, input }) => { return listStagedProjects(ctx.db, input); }), // ── 8. listStagedAssignments ───────────────────────────────────────────── listStagedAssignments: adminProcedure .input( paginationSchema.extend({ importBatchId: z.string(), resourceExternalId: z.string().optional(), status: stagedRecordStatusSchema.optional(), }), ) .query(async ({ ctx, input }) => { return listStagedAssignments(ctx.db, input); }), // ── 9. listStagedVacations ─────────────────────────────────────────────── listStagedVacations: adminProcedure .input( paginationSchema.extend({ importBatchId: z.string(), resourceExternalId: z.string().optional(), }), ) .query(async ({ ctx, input }) => { return listStagedVacations(ctx.db, input); }), // ── 10. listStagedUnresolvedRecords ────────────────────────────────────── listStagedUnresolvedRecords: adminProcedure .input( paginationSchema.extend({ importBatchId: z.string(), recordType: stagedRecordTypeSchema.optional(), }), ) .query(async ({ ctx, input }) => { return listStagedUnresolvedRecords(ctx.db, input); }), // ── 11. resolveStagedRecord ────────────────────────────────────────────── resolveStagedRecord: adminProcedure .input( z.object({ action: z.enum(["APPROVE", "REJECT", "SKIP"]), id: z.string(), recordType: stagedRecordTypeSchema, }), ) .mutation(async ({ ctx, input }) => { return resolveStagedRecord(ctx.db, input); }), // ── 12. commitImportBatch ──────────────────────────────────────────────── commitImportBatch: adminProcedure .input( z.object({ allowTbdUnresolved: z.boolean().optional(), importBatchId: z.string(), importTbdProjects: z.boolean().optional(), }), ) .mutation(async ({ ctx, input }) => { return commitImportBatch(ctx.db, { importBatchId: input.importBatchId, allowTbdUnresolved: input.allowTbdUnresolved, importTbdProjects: input.importTbdProjects, userId: ctx.dbUser?.id, }); }), });