refactor(api): extract timeline entry read builders

This commit is contained in:
2026-03-31 15:38:39 +02:00
parent ea10851fe4
commit 93e03e0f65
3 changed files with 282 additions and 32 deletions
@@ -44,6 +44,7 @@ export const TimelineWindowFiltersSchema = z.object({
type TimelineWindowFiltersInput = z.infer<typeof TimelineWindowFiltersSchema>;
type TimelineSelfServiceContext = Pick<TRPCContext, "db" | "dbUser">;
type TimelineAnonymizationDirectory = Awaited<ReturnType<typeof getAnonymizationDirectory>>;
export function getAssignmentResourceIds(
readModel: ReturnType<typeof buildSplitAllocationReadModel>,
@@ -142,6 +143,22 @@ export function createEmptyTimelineEntriesView() {
});
}
export function buildTimelineEntriesViewResponse<
TReadModel extends {
allocations: Array<{ resource?: { id: string } | null }>;
assignments: Array<{ resource?: { id: string } | null }>;
},
>(
readModel: TReadModel,
directory: TimelineAnonymizationDirectory,
): TReadModel {
return {
...readModel,
allocations: readModel.allocations.map((allocation) => anonymizeResourceOnEntry(allocation, directory)),
assignments: readModel.assignments.map((assignment) => anonymizeResourceOnEntry(assignment, directory)),
};
}
async function findOwnedTimelineResourceId(
ctx: TimelineSelfServiceContext,
): Promise<string | null> {
@@ -179,6 +196,31 @@ export async function buildSelfServiceTimelineInput(
};
}
export function buildTimelineEntriesDetailInput(input: {
startDate?: string | undefined;
endDate?: string | undefined;
durationDays?: number | undefined;
resourceIds?: string[] | undefined;
projectIds?: string[] | undefined;
clientIds?: string[] | undefined;
chapters?: string[] | undefined;
eids?: string[] | undefined;
countryCodes?: string[] | undefined;
}) {
const period = createTimelineDateRange(input);
const filters = createTimelineFilters(input);
return {
period,
filters,
timelineInput: {
...filters,
startDate: period.startDate,
endDate: period.endDate,
},
};
}
export function summarizeTimelineEntries(readModel: {
allocations: Array<{ projectId: string | null; resourceId: string | null }>;
demands: Array<{ projectId: string | null }>;
@@ -208,6 +250,44 @@ export function summarizeTimelineEntries(readModel: {
};
}
export function buildTimelineEntriesDetailResponse<
TReadModel extends {
allocations: Array<{ projectId: string | null; resourceId: string | null; resource?: { id: string } | null }>;
demands: Array<{ projectId: string | null }>;
assignments: Array<{ projectId: string | null; resourceId: string | null; resource?: { id: string } | null }>;
},
THolidayOverlay,
>(input: {
period: { startDate: Date; endDate: Date };
filters: Omit<TimelineEntriesFilters, "startDate" | "endDate">;
readModel: TReadModel;
directory: TimelineAnonymizationDirectory;
holidayOverlays: THolidayOverlay[];
holidaySummary: {
overlayCount: number;
holidayResourceCount: number;
byScope: Array<{ scope: string; count: number }>;
};
}) {
const view = buildTimelineEntriesViewResponse(input.readModel, input.directory);
return {
period: {
startDate: fmtDate(input.period.startDate),
endDate: fmtDate(input.period.endDate),
},
filters: input.filters,
summary: {
...summarizeTimelineEntries(input.readModel),
...input.holidaySummary,
},
allocations: view.allocations,
demands: input.readModel.demands,
assignments: view.assignments,
holidayOverlays: input.holidayOverlays,
};
}
export function rangesOverlap(
leftStart: Date,
leftEnd: Date,