From d0699c90fe24a7f745614ac115b9e62afccba265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Tue, 31 Mar 2026 17:59:16 +0200 Subject: [PATCH] refactor(api): extract timeline allocation update support --- ...meline-allocation-mutation-support.test.ts | 49 ------------- ...timeline-allocation-update-support.test.ts | 54 ++++++++++++++ .../timeline-allocation-inline-support.ts | 4 +- .../timeline-allocation-mutation-support.ts | 55 -------------- .../timeline-allocation-update-support.ts | 73 +++++++++++++++++++ 5 files changed, 129 insertions(+), 106 deletions(-) create mode 100644 packages/api/src/__tests__/timeline-allocation-update-support.test.ts create mode 100644 packages/api/src/router/timeline-allocation-update-support.ts diff --git a/packages/api/src/__tests__/timeline-allocation-mutation-support.test.ts b/packages/api/src/__tests__/timeline-allocation-mutation-support.test.ts index 0194e35..e5dd30a 100644 --- a/packages/api/src/__tests__/timeline-allocation-mutation-support.test.ts +++ b/packages/api/src/__tests__/timeline-allocation-mutation-support.test.ts @@ -2,30 +2,10 @@ import { TRPCError } from "@trpc/server"; import { describe, expect, it } from "vitest"; import { assertTimelineDateRangeValid, - buildTimelineAllocationEntryUpdate, - buildTimelineAllocationMetadata, validateTimelineAllocationDateRanges, } from "../router/timeline-allocation-mutation-support.js"; describe("timeline allocation mutation support", () => { - it("preserves existing metadata while updating includeSaturday", () => { - const result = buildTimelineAllocationMetadata({ - existingMetadata: { - recurrence: { frequency: "weekly", interval: 2 }, - includeSaturday: false, - }, - includeSaturday: true, - }); - - expect(result).toEqual({ - metadata: { - recurrence: { frequency: "weekly", interval: 2 }, - includeSaturday: true, - }, - includeSaturday: true, - }); - }); - it("rejects inverted date ranges", () => { expect(() => assertTimelineDateRangeValid( @@ -47,33 +27,4 @@ describe("timeline allocation mutation support", () => { }, ])).toThrowError(TRPCError); }); - - it("builds shared update payloads for demand and assignment changes", () => { - expect( - buildTimelineAllocationEntryUpdate({ - hoursPerDay: 7.5, - startDate: new Date("2026-04-01T00:00:00.000Z"), - endDate: new Date("2026-04-10T00:00:00.000Z"), - metadata: { includeSaturday: true }, - dailyCostCents: 48000, - role: "Architect", - }), - ).toEqual({ - demandRequirementUpdate: { - hoursPerDay: 7.5, - startDate: new Date("2026-04-01T00:00:00.000Z"), - endDate: new Date("2026-04-10T00:00:00.000Z"), - metadata: { includeSaturday: true }, - role: "Architect", - }, - assignmentUpdate: { - hoursPerDay: 7.5, - startDate: new Date("2026-04-01T00:00:00.000Z"), - endDate: new Date("2026-04-10T00:00:00.000Z"), - dailyCostCents: 48000, - metadata: { includeSaturday: true }, - role: "Architect", - }, - }); - }); }); diff --git a/packages/api/src/__tests__/timeline-allocation-update-support.test.ts b/packages/api/src/__tests__/timeline-allocation-update-support.test.ts new file mode 100644 index 0000000..c1e258d --- /dev/null +++ b/packages/api/src/__tests__/timeline-allocation-update-support.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, it } from "vitest"; +import { + buildTimelineAllocationEntryUpdate, + buildTimelineAllocationMetadata, +} from "../router/timeline-allocation-update-support.js"; + +describe("timeline allocation update support", () => { + it("preserves existing metadata while updating includeSaturday", () => { + const result = buildTimelineAllocationMetadata({ + existingMetadata: { + recurrence: { frequency: "weekly", interval: 2 }, + includeSaturday: false, + }, + includeSaturday: true, + }); + + expect(result).toEqual({ + metadata: { + recurrence: { frequency: "weekly", interval: 2 }, + includeSaturday: true, + }, + includeSaturday: true, + }); + }); + + it("builds shared update payloads for demand and assignment changes", () => { + expect( + buildTimelineAllocationEntryUpdate({ + hoursPerDay: 7.5, + startDate: new Date("2026-04-01T00:00:00.000Z"), + endDate: new Date("2026-04-10T00:00:00.000Z"), + metadata: { includeSaturday: true }, + dailyCostCents: 48000, + role: "Architect", + }), + ).toEqual({ + demandRequirementUpdate: { + hoursPerDay: 7.5, + startDate: new Date("2026-04-01T00:00:00.000Z"), + endDate: new Date("2026-04-10T00:00:00.000Z"), + metadata: { includeSaturday: true }, + role: "Architect", + }, + assignmentUpdate: { + hoursPerDay: 7.5, + startDate: new Date("2026-04-01T00:00:00.000Z"), + endDate: new Date("2026-04-10T00:00:00.000Z"), + dailyCostCents: 48000, + metadata: { includeSaturday: true }, + role: "Architect", + }, + }); + }); +}); diff --git a/packages/api/src/router/timeline-allocation-inline-support.ts b/packages/api/src/router/timeline-allocation-inline-support.ts index 17fb4e6..f62760c 100644 --- a/packages/api/src/router/timeline-allocation-inline-support.ts +++ b/packages/api/src/router/timeline-allocation-inline-support.ts @@ -2,12 +2,12 @@ import { loadAllocationEntry, updateAllocationEntry } from "@capakraken/applicat import type { PrismaClient } from "@capakraken/db"; import type { RecurrencePattern, WeekdayAvailability } from "@capakraken/shared"; import { TRPCError } from "@trpc/server"; +import { assertTimelineDateRangeValid } from "./timeline-allocation-mutation-support.js"; import { - assertTimelineDateRangeValid, buildTimelineAllocationEntryUpdate, buildTimelineAllocationMetadata, buildTimelineAllocationUpdateAuditChanges, -} from "./timeline-allocation-mutation-support.js"; +} from "./timeline-allocation-update-support.js"; import { calculateTimelineAllocationDailyCost } from "./timeline-cost-support.js"; export async function applyTimelineInlineAllocationUpdate(input: { diff --git a/packages/api/src/router/timeline-allocation-mutation-support.ts b/packages/api/src/router/timeline-allocation-mutation-support.ts index 9e866ab..7fc5293 100644 --- a/packages/api/src/router/timeline-allocation-mutation-support.ts +++ b/packages/api/src/router/timeline-allocation-mutation-support.ts @@ -35,58 +35,3 @@ export function validateTimelineAllocationDateRanges( assertTimelineDateRangeValid(range.startDate, range.endDate); } } - -export function buildTimelineAllocationUpdateAuditChanges(input: { - allocationId: string; - previousHoursPerDay: number; - previousStartDate: Date; - previousEndDate: Date; - nextAllocationId: string; - nextHoursPerDay: number; - nextStartDate: Date; - nextEndDate: Date; - includeSaturday: boolean; -}) { - return { - before: { - id: input.allocationId, - hoursPerDay: input.previousHoursPerDay, - startDate: input.previousStartDate, - endDate: input.previousEndDate, - }, - after: { - id: input.nextAllocationId, - hoursPerDay: input.nextHoursPerDay, - startDate: input.nextStartDate, - endDate: input.nextEndDate, - includeSaturday: input.includeSaturday, - }, - } as const; -} - -export function buildTimelineAllocationEntryUpdate(input: { - hoursPerDay: number; - startDate: Date; - endDate: Date; - metadata: Record; - dailyCostCents: number; - role?: string | undefined; -}) { - return { - demandRequirementUpdate: { - hoursPerDay: input.hoursPerDay, - startDate: input.startDate, - endDate: input.endDate, - metadata: input.metadata, - ...(input.role !== undefined ? { role: input.role } : {}), - }, - assignmentUpdate: { - hoursPerDay: input.hoursPerDay, - startDate: input.startDate, - endDate: input.endDate, - dailyCostCents: input.dailyCostCents, - metadata: input.metadata, - ...(input.role !== undefined ? { role: input.role } : {}), - }, - }; -} diff --git a/packages/api/src/router/timeline-allocation-update-support.ts b/packages/api/src/router/timeline-allocation-update-support.ts new file mode 100644 index 0000000..062ddd3 --- /dev/null +++ b/packages/api/src/router/timeline-allocation-update-support.ts @@ -0,0 +1,73 @@ +import { Prisma } from "@capakraken/db"; + +export function buildTimelineAllocationMetadata(input: { + existingMetadata: Record | null | undefined; + includeSaturday: boolean | undefined; +}): { metadata: Record; includeSaturday: boolean } { + const existingMetadata = input.existingMetadata ?? {}; + const metadata: Record = { + ...existingMetadata, + ...(input.includeSaturday !== undefined + ? { includeSaturday: input.includeSaturday } + : {}), + }; + const includeSaturday = + input.includeSaturday ?? (existingMetadata.includeSaturday as boolean | undefined) ?? false; + + return { metadata, includeSaturday }; +} + +export function buildTimelineAllocationUpdateAuditChanges(input: { + allocationId: string; + previousHoursPerDay: number; + previousStartDate: Date; + previousEndDate: Date; + nextAllocationId: string; + nextHoursPerDay: number; + nextStartDate: Date; + nextEndDate: Date; + includeSaturday: boolean; +}): Prisma.InputJsonValue { + return { + before: { + id: input.allocationId, + hoursPerDay: input.previousHoursPerDay, + startDate: input.previousStartDate, + endDate: input.previousEndDate, + }, + after: { + id: input.nextAllocationId, + hoursPerDay: input.nextHoursPerDay, + startDate: input.nextStartDate, + endDate: input.nextEndDate, + includeSaturday: input.includeSaturday, + }, + } as unknown as Prisma.InputJsonValue; +} + +export function buildTimelineAllocationEntryUpdate(input: { + hoursPerDay: number; + startDate: Date; + endDate: Date; + metadata: Record; + dailyCostCents: number; + role?: string | undefined; +}) { + return { + demandRequirementUpdate: { + hoursPerDay: input.hoursPerDay, + startDate: input.startDate, + endDate: input.endDate, + metadata: input.metadata, + ...(input.role !== undefined ? { role: input.role } : {}), + }, + assignmentUpdate: { + hoursPerDay: input.hoursPerDay, + startDate: input.startDate, + endDate: input.endDate, + dailyCostCents: input.dailyCostCents, + metadata: input.metadata, + ...(input.role !== undefined ? { role: input.role } : {}), + }, + }; +}