refactor: consolidate duplicated code across web and API packages

- Extract shared render helpers (vacation blocks, range overlay, overbooking blink) into renderHelpers.tsx
- Centralize status badge styles and vacation color maps into status-styles.ts
- Extract dragMath.ts utility from useTimelineDrag for reuse
- Split useInvalidatePlanningViews into useInvalidateTimeline (4 queries) + useInvalidatePlanningViews (8 queries)
- Adopt findUniqueOrThrow() and Prisma select constants across API routers
- Add shared fmtEur() helper for API-side money formatting
- Wrap TimelineResourcePanel and TimelineProjectPanel with React.memo
- Fix pre-existing TS2589 deep type errors in TeamCalendar and VacationModal
- 38 files changed, reducing ~400 lines of duplicated code

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-19 00:10:08 +01:00
parent ddec3a927a
commit e7b74f13bd
38 changed files with 637 additions and 652 deletions
+4 -20
View File
@@ -1,6 +1,7 @@
"use client";
import { useCallback, useRef, useState } from "react";
import { trpc } from "~/lib/trpc/client.js";
import { useInvalidateTimeline } from "./useInvalidatePlanningViews.js";
import type { AllocationMovedSnapshot } from "./useTimelineDrag.js";
export type { AllocationMovedSnapshot };
@@ -18,7 +19,7 @@ export function useAllocationHistory() {
const past = useRef<HistoryEntry[]>([]);
const future = useRef<HistoryEntry[]>([]);
const utils = trpc.useUtils();
const invalidateTimeline = useInvalidateTimeline();
// Configurable max steps from system settings
const { data: settings } = trpc.settings.getSystemSettings.useQuery(undefined, {
@@ -27,21 +28,11 @@ export function useAllocationHistory() {
const maxHistory = settings?.timelineUndoMaxSteps ?? DEFAULT_MAX_HISTORY;
const updateMutation = trpc.timeline.updateAllocationInline.useMutation({
onSuccess: () => {
void utils.timeline.getEntries.invalidate();
void utils.timeline.getEntriesView.invalidate();
void utils.timeline.getProjectContext.invalidate();
void utils.timeline.getBudgetStatus.invalidate();
},
onSuccess: invalidateTimeline,
});
const batchShiftMutation = trpc.timeline.batchShiftAllocations.useMutation({
onSuccess: () => {
void utils.timeline.getEntries.invalidate();
void utils.timeline.getEntriesView.invalidate();
void utils.timeline.getProjectContext.invalidate();
void utils.timeline.getBudgetStatus.invalidate();
},
onSuccess: invalidateTimeline,
});
const push = useCallback((snapshot: AllocationMovedSnapshot) => {
@@ -58,13 +49,6 @@ export function useAllocationHistory() {
setCanRedo(false);
}, [maxHistory]);
const invalidateAll = useCallback(() => {
void utils.timeline.getEntries.invalidate();
void utils.timeline.getEntriesView.invalidate();
void utils.timeline.getProjectContext.invalidate();
void utils.timeline.getBudgetStatus.invalidate();
}, [utils]);
const undo = useCallback(async () => {
const last = past.current[past.current.length - 1];
if (!last) return;