refactor(api): extract scenario procedures
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { PermissionKey } from "@capakraken/shared";
|
||||
import { z } from "zod";
|
||||
import type { TRPCContext } from "../trpc.js";
|
||||
import { requirePermission } from "../trpc.js";
|
||||
import { applyProjectScenario } from "./scenario-apply.js";
|
||||
import { readProjectScenarioBaseline } from "./scenario-baseline.js";
|
||||
import { simulateProjectScenario } from "./scenario-simulation.js";
|
||||
|
||||
export const ScenarioChangeSchema = z.object({
|
||||
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),
|
||||
remove: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const ScenarioProjectIdInputSchema = z.object({
|
||||
projectId: z.string(),
|
||||
});
|
||||
|
||||
export const ScenarioSimulationInputSchema = z.object({
|
||||
projectId: z.string(),
|
||||
changes: z.array(ScenarioChangeSchema).min(1),
|
||||
});
|
||||
|
||||
type ScenarioBaselineContext = Pick<TRPCContext, "db" | "dbUser"> & {
|
||||
permissions: Set<PermissionKey>;
|
||||
};
|
||||
|
||||
export async function getProjectScenarioBaseline(
|
||||
ctx: ScenarioBaselineContext,
|
||||
input: z.infer<typeof ScenarioProjectIdInputSchema>,
|
||||
) {
|
||||
requirePermission(ctx, PermissionKey.VIEW_COSTS);
|
||||
return readProjectScenarioBaseline(ctx.db, input.projectId);
|
||||
}
|
||||
|
||||
export async function simulateScenario(
|
||||
ctx: Pick<TRPCContext, "db">,
|
||||
input: z.infer<typeof ScenarioSimulationInputSchema>,
|
||||
) {
|
||||
return simulateProjectScenario(ctx.db, input);
|
||||
}
|
||||
|
||||
export async function applyScenario(
|
||||
ctx: Pick<TRPCContext, "db" | "dbUser">,
|
||||
input: z.infer<typeof ScenarioSimulationInputSchema>,
|
||||
) {
|
||||
return applyProjectScenario(ctx.db, {
|
||||
...input,
|
||||
...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
|
||||
});
|
||||
}
|
||||
@@ -1,54 +1,22 @@
|
||||
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),
|
||||
});
|
||||
import { createTRPCRouter, controllerProcedure, planningReadProcedure } from "../trpc.js";
|
||||
import {
|
||||
applyScenario,
|
||||
getProjectScenarioBaseline,
|
||||
ScenarioProjectIdInputSchema,
|
||||
ScenarioSimulationInputSchema,
|
||||
simulateScenario,
|
||||
} from "./scenario-procedure-support.js";
|
||||
|
||||
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);
|
||||
}),
|
||||
.input(ScenarioProjectIdInputSchema)
|
||||
.query(({ ctx, input }) => getProjectScenarioBaseline(ctx, input)),
|
||||
|
||||
/**
|
||||
* 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)),
|
||||
.input(ScenarioSimulationInputSchema)
|
||||
.mutation(({ ctx, input }) => simulateScenario(ctx, 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,
|
||||
})),
|
||||
.input(ScenarioSimulationInputSchema)
|
||||
.mutation(({ ctx, input }) => applyScenario(ctx, input)),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user