refactor(api): extract timeline allocation mutation schemas
This commit is contained in:
@@ -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 type { PrismaClient } from "@capakraken/db";
|
||||||
import {
|
import { PermissionKey } from "@capakraken/shared";
|
||||||
AllocationStatus,
|
|
||||||
PermissionKey,
|
|
||||||
UpdateAllocationHoursSchema,
|
|
||||||
} from "@capakraken/shared";
|
|
||||||
import { z } from "zod";
|
|
||||||
import { emitAllocationUpdated } from "../sse/event-bus.js";
|
import { emitAllocationUpdated } from "../sse/event-bus.js";
|
||||||
import { managerProcedure, requirePermission } from "../trpc.js";
|
import { managerProcedure, requirePermission } from "../trpc.js";
|
||||||
import {
|
import {
|
||||||
@@ -12,6 +7,12 @@ import {
|
|||||||
createTimelineQuickAssignment,
|
createTimelineQuickAssignment,
|
||||||
shiftTimelineAllocations,
|
shiftTimelineAllocations,
|
||||||
} from "./timeline-allocation-procedure-support.js";
|
} 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";
|
import { applyTimelineInlineAllocationUpdate } from "./timeline-allocation-inline-support.js";
|
||||||
|
|
||||||
export const timelineAllocationMutationProcedures = {
|
export const timelineAllocationMutationProcedures = {
|
||||||
@@ -39,18 +40,7 @@ export const timelineAllocationMutationProcedures = {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
quickAssign: managerProcedure
|
quickAssign: managerProcedure
|
||||||
.input(
|
.input(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),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
|
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
|
||||||
return createTimelineQuickAssignment(ctx.db as PrismaClient, {
|
return createTimelineQuickAssignment(ctx.db as PrismaClient, {
|
||||||
@@ -60,26 +50,7 @@ export const timelineAllocationMutationProcedures = {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
batchQuickAssign: managerProcedure
|
batchQuickAssign: managerProcedure
|
||||||
.input(
|
.input(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),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
|
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
|
||||||
return createTimelineBatchQuickAssignments(ctx.db as PrismaClient, {
|
return createTimelineBatchQuickAssignments(ctx.db as PrismaClient, {
|
||||||
@@ -91,13 +62,7 @@ export const timelineAllocationMutationProcedures = {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
batchShiftAllocations: managerProcedure
|
batchShiftAllocations: managerProcedure
|
||||||
.input(
|
.input(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"),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
|
requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
|
||||||
return shiftTimelineAllocations(ctx.db as PrismaClient, {
|
return shiftTimelineAllocations(ctx.db as PrismaClient, {
|
||||||
|
|||||||
Reference in New Issue
Block a user