refactor(api): extract timeline project procedure support

This commit is contained in:
2026-03-31 17:38:09 +02:00
parent 1bdae5816f
commit 7a64fe5ce5
3 changed files with 422 additions and 117 deletions
+10 -117
View File
@@ -1,55 +1,18 @@
import { listAssignmentBookings } from "@capakraken/application";
import { computeBudgetStatus } from "@capakraken/engine";
import { ShiftProjectSchema } from "@capakraken/shared";
import { z } from "zod";
import { getAnonymizationDirectory } from "../lib/anonymization.js";
import { controllerProcedure } from "../trpc.js";
import { previewTimelineProjectShift } from "./timeline-project-load-support.js";
import {
loadTimelineProjectContextDetailArtifacts,
} from "./timeline-project-context-support.js";
import {
loadTimelineProjectContext,
previewTimelineProjectShift,
} from "./timeline-project-load-support.js";
import {
buildTimelineProjectContextDetailResponse,
buildTimelineProjectContextResponse,
} from "./timeline-project-context-response-support.js";
import {
buildTimelineBudgetStatusAllocations,
buildTimelineBudgetStatusResponse,
buildTimelineShiftPreviewDetailResponse,
} from "./timeline-project-read-support.js";
import {
findTimelineProjectOrThrow,
timelineBudgetStatusProjectSelect,
timelineShiftPreviewProjectSelect,
} from "./timeline-project-query-support.js";
readTimelineProjectBudgetStatusResponse,
readTimelineProjectContextDetailResponse,
readTimelineProjectContextResponse,
readTimelineProjectShiftPreviewDetail,
} from "./timeline-project-procedure-support.js";
export const timelineProjectReadProcedures = {
getProjectContext: controllerProcedure
.input(z.object({ projectId: z.string() }))
.query(async ({ ctx, input }) => {
const {
project,
allocations,
demands,
assignments,
allResourceAllocations,
resourceIds,
} = await loadTimelineProjectContext(ctx.db, input.projectId);
const directory = await getAnonymizationDirectory(ctx.db);
return buildTimelineProjectContextResponse({
project,
allocations,
demands,
assignments,
allResourceAllocations,
resourceIds,
directory,
});
}),
.query(async ({ ctx, input }) => readTimelineProjectContextResponse(ctx.db, input.projectId)),
getProjectContextDetail: controllerProcedure
.input(
@@ -60,37 +23,7 @@ export const timelineProjectReadProcedures = {
durationDays: z.number().int().min(1).max(366).optional(),
}),
)
.query(async ({ ctx, input }) => {
const projectContext = await loadTimelineProjectContext(ctx.db, input.projectId);
const directory = await getAnonymizationDirectory(ctx.db);
const { period, holidayOverlays, assignmentConflicts } =
await loadTimelineProjectContextDetailArtifacts(ctx.db, {
projectId: input.projectId,
requestedStartDate: input.startDate,
requestedEndDate: input.endDate,
durationDays: input.durationDays,
projectStartDate: projectContext.project.startDate,
projectEndDate: projectContext.project.endDate,
firstAssignmentStartDate: projectContext.assignments[0]?.startDate,
firstDemandStartDate: projectContext.demands[0]?.startDate,
assignments: projectContext.assignments,
allResourceAllocations: projectContext.allResourceAllocations,
resourceIds: projectContext.resourceIds,
});
return buildTimelineProjectContextDetailResponse({
project: projectContext.project,
period,
allocations: projectContext.allocations,
demands: projectContext.demands,
assignments: projectContext.assignments,
allResourceAllocations: projectContext.allResourceAllocations,
resourceIds: projectContext.resourceIds,
assignmentConflicts,
holidayOverlays,
directory,
});
}),
.query(async ({ ctx, input }) => readTimelineProjectContextDetailResponse(ctx.db, input)),
previewShift: controllerProcedure
.input(ShiftProjectSchema)
@@ -98,49 +31,9 @@ export const timelineProjectReadProcedures = {
getShiftPreviewDetail: controllerProcedure
.input(ShiftProjectSchema)
.query(async ({ ctx, input }) => {
const [project, preview] = await Promise.all([
findTimelineProjectOrThrow(ctx.db, {
projectId: input.projectId,
select: timelineShiftPreviewProjectSelect,
}),
previewTimelineProjectShift(ctx.db, input),
]);
return buildTimelineShiftPreviewDetailResponse({
project,
requestedShift: {
newStartDate: input.newStartDate,
newEndDate: input.newEndDate,
},
preview,
});
}),
.query(async ({ ctx, input }) => readTimelineProjectShiftPreviewDetail(ctx.db, input)),
getBudgetStatus: controllerProcedure
.input(z.object({ projectId: z.string() }))
.query(async ({ ctx, input }) => {
const project = await findTimelineProjectOrThrow(ctx.db, {
projectId: input.projectId,
select: timelineBudgetStatusProjectSelect,
});
const bookings = await listAssignmentBookings(ctx.db, {
projectIds: [project.id],
});
const budgetStatus = computeBudgetStatus(
project.budgetCents,
project.winProbability,
buildTimelineBudgetStatusAllocations(bookings),
project.startDate,
project.endDate,
);
return buildTimelineBudgetStatusResponse({
project,
budgetStatus,
totalAllocations: bookings.length,
});
}),
.query(async ({ ctx, input }) => readTimelineProjectBudgetStatusResponse(ctx.db, input.projectId)),
};