refactor(api): extract timeline quick assign support
This commit is contained in:
@@ -5,9 +5,6 @@ import {
|
|||||||
buildTimelineAllocationEntryUpdate,
|
buildTimelineAllocationEntryUpdate,
|
||||||
buildTimelineAllocationMetadata,
|
buildTimelineAllocationMetadata,
|
||||||
buildTimelineBatchShiftAuditChanges,
|
buildTimelineBatchShiftAuditChanges,
|
||||||
buildTimelineQuickAssignAssignmentInput,
|
|
||||||
buildTimelineQuickAssignMetadata,
|
|
||||||
calculateTimelineAllocationPercentage,
|
|
||||||
shiftTimelineAllocationWindow,
|
shiftTimelineAllocationWindow,
|
||||||
validateTimelineAllocationDateRanges,
|
validateTimelineAllocationDateRanges,
|
||||||
} from "../router/timeline-allocation-mutation-support.js";
|
} from "../router/timeline-allocation-mutation-support.js";
|
||||||
@@ -39,43 +36,6 @@ describe("timeline allocation mutation support", () => {
|
|||||||
)).toThrowError(TRPCError);
|
)).toThrowError(TRPCError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rounds allocation percentages and clamps them to 100", () => {
|
|
||||||
expect(calculateTimelineAllocationPercentage(3.9)).toBe(49);
|
|
||||||
expect(calculateTimelineAllocationPercentage(9)).toBe(100);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("builds source metadata for quick assign operations", () => {
|
|
||||||
expect(buildTimelineQuickAssignMetadata("quickAssign")).toEqual({ source: "quickAssign" });
|
|
||||||
expect(buildTimelineQuickAssignMetadata("batchQuickAssign")).toEqual({ source: "batchQuickAssign" });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("builds assignment create payloads for quick assign flows", () => {
|
|
||||||
expect(
|
|
||||||
buildTimelineQuickAssignAssignmentInput({
|
|
||||||
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: 6,
|
|
||||||
role: "Engineer",
|
|
||||||
roleId: "role_1",
|
|
||||||
status: "PROPOSED",
|
|
||||||
source: "quickAssign",
|
|
||||||
}),
|
|
||||||
).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: 6,
|
|
||||||
percentage: 75,
|
|
||||||
role: "Engineer",
|
|
||||||
roleId: "role_1",
|
|
||||||
status: "PROPOSED",
|
|
||||||
metadata: { source: "quickAssign" },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shifts and clamps allocation windows for each batch-shift mode", () => {
|
it("shifts and clamps allocation windows for each batch-shift mode", () => {
|
||||||
expect(
|
expect(
|
||||||
shiftTimelineAllocationWindow({
|
shiftTimelineAllocationWindow({
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import {
|
||||||
|
buildTimelineQuickAssignAssignmentInput,
|
||||||
|
buildTimelineQuickAssignMetadata,
|
||||||
|
calculateTimelineAllocationPercentage,
|
||||||
|
} from "../router/timeline-allocation-quick-assign-support.js";
|
||||||
|
|
||||||
|
describe("timeline allocation quick assign support", () => {
|
||||||
|
it("rounds allocation percentages and clamps them to 100", () => {
|
||||||
|
expect(calculateTimelineAllocationPercentage(3.9)).toBe(49);
|
||||||
|
expect(calculateTimelineAllocationPercentage(9)).toBe(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("builds source metadata for quick assign operations", () => {
|
||||||
|
expect(buildTimelineQuickAssignMetadata("quickAssign")).toEqual({ source: "quickAssign" });
|
||||||
|
expect(buildTimelineQuickAssignMetadata("batchQuickAssign")).toEqual({ source: "batchQuickAssign" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("builds assignment create payloads for quick assign flows", () => {
|
||||||
|
expect(
|
||||||
|
buildTimelineQuickAssignAssignmentInput({
|
||||||
|
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: 6,
|
||||||
|
role: "Engineer",
|
||||||
|
roleId: "role_1",
|
||||||
|
status: "PROPOSED",
|
||||||
|
source: "quickAssign",
|
||||||
|
}),
|
||||||
|
).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: 6,
|
||||||
|
percentage: 75,
|
||||||
|
role: "Engineer",
|
||||||
|
roleId: "role_1",
|
||||||
|
status: "PROPOSED",
|
||||||
|
metadata: { source: "quickAssign" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Prisma } from "@capakraken/db";
|
import { Prisma } from "@capakraken/db";
|
||||||
import { AllocationStatus } from "@capakraken/shared";
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
export type TimelineBatchShiftMode = "move" | "resize-start" | "resize-end";
|
export type TimelineBatchShiftMode = "move" | "resize-start" | "resize-end";
|
||||||
@@ -32,39 +31,6 @@ export function buildTimelineAllocationMetadata(input: {
|
|||||||
return { metadata, includeSaturday };
|
return { metadata, includeSaturday };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculateTimelineAllocationPercentage(hoursPerDay: number): number {
|
|
||||||
return Math.min(100, Math.round((hoursPerDay / 8) * 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildTimelineQuickAssignMetadata(source: "quickAssign" | "batchQuickAssign") {
|
|
||||||
return { source } satisfies Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildTimelineQuickAssignAssignmentInput(input: {
|
|
||||||
resourceId: string;
|
|
||||||
projectId: string;
|
|
||||||
startDate: Date;
|
|
||||||
endDate: Date;
|
|
||||||
hoursPerDay: number;
|
|
||||||
role: string;
|
|
||||||
roleId?: string | undefined;
|
|
||||||
status: AllocationStatus;
|
|
||||||
source: "quickAssign" | "batchQuickAssign";
|
|
||||||
}) {
|
|
||||||
return {
|
|
||||||
resourceId: input.resourceId,
|
|
||||||
projectId: input.projectId,
|
|
||||||
startDate: input.startDate,
|
|
||||||
endDate: input.endDate,
|
|
||||||
hoursPerDay: input.hoursPerDay,
|
|
||||||
percentage: calculateTimelineAllocationPercentage(input.hoursPerDay),
|
|
||||||
role: input.role,
|
|
||||||
...(input.roleId !== undefined ? { roleId: input.roleId } : {}),
|
|
||||||
status: input.status,
|
|
||||||
metadata: buildTimelineQuickAssignMetadata(input.source),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateTimelineAllocationDateRanges(
|
export function validateTimelineAllocationDateRanges(
|
||||||
ranges: Array<{ startDate: Date; endDate: Date }>,
|
ranges: Array<{ startDate: Date; endDate: Date }>,
|
||||||
): void {
|
): void {
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import {
|
|||||||
} from "../sse/event-bus.js";
|
} from "../sse/event-bus.js";
|
||||||
import {
|
import {
|
||||||
assertTimelineDateRangeValid,
|
assertTimelineDateRangeValid,
|
||||||
buildTimelineQuickAssignAssignmentInput,
|
|
||||||
validateTimelineAllocationDateRanges,
|
validateTimelineAllocationDateRanges,
|
||||||
} from "./timeline-allocation-mutation-support.js";
|
} from "./timeline-allocation-mutation-support.js";
|
||||||
|
import { buildTimelineQuickAssignAssignmentInput } from "./timeline-allocation-quick-assign-support.js";
|
||||||
import { applyTimelineBatchAllocationShift } from "./timeline-allocation-shift-support.js";
|
import { applyTimelineBatchAllocationShift } from "./timeline-allocation-shift-support.js";
|
||||||
|
|
||||||
export async function createTimelineQuickAssignment(
|
export async function createTimelineQuickAssignment(
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { AllocationStatus } from "@capakraken/shared";
|
||||||
|
|
||||||
|
export function calculateTimelineAllocationPercentage(hoursPerDay: number): number {
|
||||||
|
return Math.min(100, Math.round((hoursPerDay / 8) * 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildTimelineQuickAssignMetadata(source: "quickAssign" | "batchQuickAssign") {
|
||||||
|
return { source } satisfies Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildTimelineQuickAssignAssignmentInput(input: {
|
||||||
|
resourceId: string;
|
||||||
|
projectId: string;
|
||||||
|
startDate: Date;
|
||||||
|
endDate: Date;
|
||||||
|
hoursPerDay: number;
|
||||||
|
role: string;
|
||||||
|
roleId?: string | undefined;
|
||||||
|
status: AllocationStatus;
|
||||||
|
source: "quickAssign" | "batchQuickAssign";
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
resourceId: input.resourceId,
|
||||||
|
projectId: input.projectId,
|
||||||
|
startDate: input.startDate,
|
||||||
|
endDate: input.endDate,
|
||||||
|
hoursPerDay: input.hoursPerDay,
|
||||||
|
percentage: calculateTimelineAllocationPercentage(input.hoursPerDay),
|
||||||
|
role: input.role,
|
||||||
|
...(input.roleId !== undefined ? { roleId: input.roleId } : {}),
|
||||||
|
status: input.status,
|
||||||
|
metadata: buildTimelineQuickAssignMetadata(input.source),
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user