fix(web): make invalidation hooks async with Promise.all and fix cross-view staleness

- useInvalidateTimeline and useInvalidatePlanningViews now return
  Promise.all instead of fire-and-forget void calls
- Timeline mutations now use useInvalidatePlanningViews to also
  invalidate allocation list views, preventing stale data
- AllocationsClient sequential awaits replaced with single
  invalidatePlanningViews() call (parallel invalidation)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-11 08:24:33 +02:00
parent f18777c365
commit f3fa902773
11 changed files with 372 additions and 229 deletions
@@ -9,7 +9,7 @@ import { useProjectDragContext } from "~/hooks/useProjectDragContext.js";
import { useTimelineDrag } from "~/hooks/useTimelineDrag.js";
import { useTimelineLayout } from "~/hooks/useTimelineLayout.js";
import { trpc } from "~/lib/trpc/client.js";
import { useInvalidateTimeline } from "~/hooks/useInvalidatePlanningViews.js";
import { useInvalidatePlanningViews } from "~/hooks/useInvalidatePlanningViews.js";
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
import { FillOpenDemandModal } from "~/components/allocations/FillOpenDemandModal.js";
import { AllocationPopover } from "./AllocationPopover.js";
@@ -95,9 +95,9 @@ export function TimelineView() {
// We start with 40 (day zoom default) and update via a ref.
const cellWidthRef = useRef(40);
const invalidateTimeline = useInvalidateTimeline();
const invalidatePlanningViews = useInvalidatePlanningViews();
const batchShiftMutationOuter = trpc.timeline.batchShiftAllocations.useMutation({
onSuccess: invalidateTimeline,
onSuccess: () => void invalidatePlanningViews(),
});
const [dragErrorToast, setDragErrorToast] = useState<string | null>(null);
@@ -389,10 +389,10 @@ function TimelineViewContent({
const resourceHoverTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const previousViewModeRef = useRef(viewMode);
const invalidateTimelineInner = useInvalidateTimeline();
const invalidatePlanningViewsInner = useInvalidatePlanningViews();
const batchDeleteMutation = trpc.allocation.batchDelete.useMutation({
onSuccess: () => {
invalidateTimelineInner();
void invalidatePlanningViewsInner();
clearMultiSelect();
},
});