refactor(api): extract timeline allocation mutation schemas

This commit is contained in:
2026-03-31 18:06:09 +02:00
parent cb70e81e17
commit 0b72eef61f
3 changed files with 116 additions and 45 deletions
@@ -0,0 +1,68 @@
import { AllocationStatus } from "@capakraken/shared";
import { describe, expect, it } from "vitest";
import {
timelineBatchQuickAssignInputSchema,
timelineBatchShiftAllocationsInputSchema,
timelineQuickAssignInputSchema,
} from "../router/timeline-allocation-mutation-schema-support.js";
describe("timeline allocation mutation schema support", () => {
it("applies defaults for quick assign input", () => {
expect(
timelineQuickAssignInputSchema.parse({
resourceId: "resource_1",
projectId: "project_1",
startDate: "2026-04-01T00:00:00.000Z",
endDate: "2026-04-05T00:00:00.000Z",
}),
).toEqual({
resourceId: "resource_1",
projectId: "project_1",
startDate: new Date("2026-04-01T00:00:00.000Z"),
endDate: new Date("2026-04-05T00:00:00.000Z"),
hoursPerDay: 8,
role: "Team Member",
status: AllocationStatus.PROPOSED,
});
});
it("applies defaults to each batch quick assign entry", () => {
expect(
timelineBatchQuickAssignInputSchema.parse({
assignments: [
{
resourceId: "resource_1",
projectId: "project_1",
startDate: "2026-04-01T00:00:00.000Z",
endDate: "2026-04-05T00:00:00.000Z",
},
],
}),
).toEqual({
assignments: [
{
resourceId: "resource_1",
projectId: "project_1",
startDate: new Date("2026-04-01T00:00:00.000Z"),
endDate: new Date("2026-04-05T00:00:00.000Z"),
hoursPerDay: 8,
role: "Team Member",
status: AllocationStatus.PROPOSED,
},
],
});
});
it("defaults batch shift mode to move", () => {
expect(
timelineBatchShiftAllocationsInputSchema.parse({
allocationIds: ["allocation_1"],
daysDelta: 3,
}),
).toEqual({
allocationIds: ["allocation_1"],
daysDelta: 3,
mode: "move",
});
});
});
@@ -0,0 +1,38 @@
import { AllocationStatus, UpdateAllocationHoursSchema } from "@capakraken/shared";
import { z } from "zod";
export { UpdateAllocationHoursSchema };
export const timelineQuickAssignInputSchema = z.object({
resourceId: z.string(),
projectId: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date(),
hoursPerDay: z.number().min(0.5).max(24).default(8),
role: z.string().min(1).max(200).default("Team Member"),
roleId: z.string().optional(),
status: z.nativeEnum(AllocationStatus).default(AllocationStatus.PROPOSED),
});
export const timelineBatchQuickAssignInputSchema = z.object({
assignments: z
.array(
z.object({
resourceId: z.string(),
projectId: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date(),
hoursPerDay: z.number().min(0.5).max(24).default(8),
role: z.string().min(1).max(200).default("Team Member"),
status: z.nativeEnum(AllocationStatus).default(AllocationStatus.PROPOSED),
}),
)
.min(1)
.max(50),
});
export const timelineBatchShiftAllocationsInputSchema = z.object({
allocationIds: z.array(z.string()).min(1).max(100),
daysDelta: z.number().int().min(-3650).max(3650),
mode: z.enum(["move", "resize-start", "resize-end"]).default("move"),
});
@@ -1,10 +1,5 @@
import type { PrismaClient } from "@capakraken/db";
import {
AllocationStatus,
PermissionKey,
UpdateAllocationHoursSchema,
} from "@capakraken/shared";
import { z } from "zod";
import { PermissionKey } from "@capakraken/shared";
import { emitAllocationUpdated } from "../sse/event-bus.js";
import { managerProcedure, requirePermission } from "../trpc.js";
import {
@@ -12,6 +7,12 @@ import {
createTimelineQuickAssignment,
shiftTimelineAllocations,
} from "./timeline-allocation-procedure-support.js";
import {
UpdateAllocationHoursSchema,
timelineBatchQuickAssignInputSchema,
timelineBatchShiftAllocationsInputSchema,
timelineQuickAssignInputSchema,
} from "./timeline-allocation-mutation-schema-support.js";
import { applyTimelineInlineAllocationUpdate } from "./timeline-allocation-inline-support.js";
export const timelineAllocationMutationProcedures = {
@@ -39,18 +40,7 @@ export const timelineAllocationMutationProcedures = {
}),
quickAssign: managerProcedure
.input(
z.object({
resourceId: z.string(),
projectId: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date(),
hoursPerDay: z.number().min(0.5).max(24).default(8),
role: z.string().min(1).max(200).default("Team Member"),
roleId: z.string().optional(),
status: z.nativeEnum(AllocationStatus).default(AllocationStatus.PROPOSED),
}),
)
.input(timelineQuickAssignInputSchema)
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
return createTimelineQuickAssignment(ctx.db as PrismaClient, {
@@ -60,26 +50,7 @@ export const timelineAllocationMutationProcedures = {
}),
batchQuickAssign: managerProcedure
.input(
z.object({
assignments: z
.array(
z.object({
resourceId: z.string(),
projectId: z.string(),
startDate: z.coerce.date(),
endDate: z.coerce.date(),
hoursPerDay: z.number().min(0.5).max(24).default(8),
role: z.string().min(1).max(200).default("Team Member"),
status: z
.nativeEnum(AllocationStatus)
.default(AllocationStatus.PROPOSED),
}),
)
.min(1)
.max(50),
}),
)
.input(timelineBatchQuickAssignInputSchema)
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
return createTimelineBatchQuickAssignments(ctx.db as PrismaClient, {
@@ -91,13 +62,7 @@ export const timelineAllocationMutationProcedures = {
}),
batchShiftAllocations: managerProcedure
.input(
z.object({
allocationIds: z.array(z.string()).min(1).max(100),
daysDelta: z.number().int().min(-3650).max(3650),
mode: z.enum(["move", "resize-start", "resize-end"]).default("move"),
}),
)
.input(timelineBatchShiftAllocationsInputSchema)
.mutation(async ({ ctx, input }) => {
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
return shiftTimelineAllocations(ctx.db as PrismaClient, {