import { PermissionKey } from "@capakraken/shared"; import { z } from "zod"; import { createTRPCRouter, controllerProcedure, planningReadProcedure, requirePermission } from "../trpc.js"; import { applyProjectScenario } from "./scenario-apply.js"; import { readProjectScenarioBaseline } from "./scenario-baseline.js"; import { simulateProjectScenario } from "./scenario-simulation.js"; const ScenarioChangeSchema = z.object({ /** Existing assignment to modify — omit to add a new allocation */ assignmentId: z.string().optional(), resourceId: z.string().optional(), roleId: z.string().optional(), startDate: z.coerce.date(), endDate: z.coerce.date(), hoursPerDay: z.number().min(0).max(24), /** Set to true to mark an existing assignment for removal */ remove: z.boolean().optional(), }); const SimulateInputSchema = z.object({ projectId: z.string(), changes: z.array(ScenarioChangeSchema).min(1), }); export const scenarioRouter = createTRPCRouter({ /** * Returns current allocations/costs for a project — the baseline for comparison. */ getProjectBaseline: planningReadProcedure .input(z.object({ projectId: z.string() })) .query(async ({ ctx, input }) => { requirePermission(ctx, PermissionKey.VIEW_COSTS); return readProjectScenarioBaseline(ctx.db, input.projectId); }), /** * Pure simulation: computes cost/hours/utilization impact of scenario changes * without persisting anything. */ simulate: controllerProcedure .input(SimulateInputSchema) .mutation(async ({ ctx, input }) => simulateProjectScenario(ctx.db, input)), /** * Applies a scenario: creates real assignments from scenario changes. * Manager+ access required. */ applyScenario: controllerProcedure .input(SimulateInputSchema) .mutation(async ({ ctx, input }) => applyProjectScenario(ctx.db, { ...input, userId: ctx.dbUser?.id, })), });