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
@@ -3,7 +3,7 @@
import { useState, useEffect, useRef } from "react";
import { createPortal } from "react-dom";
import { trpc } from "~/lib/trpc/client.js";
import { useInvalidateTimeline } from "~/hooks/useInvalidatePlanningViews.js";
import { useInvalidatePlanningViews } from "~/hooks/useInvalidatePlanningViews.js";
interface InlineAllocationEditorProps {
allocationId: string;
@@ -34,12 +34,12 @@ export function InlineAllocationEditor({
const [hoursPerDay, setHoursPerDay] = useState(initialHoursPerDay);
const [error, setError] = useState<string | null>(null);
const panelRef = useRef<HTMLDivElement>(null);
const invalidateTimeline = useInvalidateTimeline();
const invalidatePlanningViews = useInvalidatePlanningViews();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const updateMutation = (trpc.allocation.update.useMutation as any)({
onSuccess: () => {
invalidateTimeline();
void invalidatePlanningViews();
onSaved();
},
onError: (err: { message: string }) => {
@@ -95,7 +95,9 @@ export function InlineAllocationEditor({
<div className="text-xs font-semibold text-gray-700 dark:text-gray-200">Edit Allocation</div>
<form onSubmit={handleSave} className="space-y-2">
<div>
<label className="block text-[11px] text-gray-500 dark:text-gray-400 mb-0.5">Start date</label>
<label className="block text-[11px] text-gray-500 dark:text-gray-400 mb-0.5">
Start date
</label>
<input
type="date"
value={startDate}
@@ -105,7 +107,9 @@ export function InlineAllocationEditor({
/>
</div>
<div>
<label className="block text-[11px] text-gray-500 dark:text-gray-400 mb-0.5">End date</label>
<label className="block text-[11px] text-gray-500 dark:text-gray-400 mb-0.5">
End date
</label>
<input
type="date"
value={endDate}
@@ -115,7 +119,9 @@ export function InlineAllocationEditor({
/>
</div>
<div>
<label className="block text-[11px] text-gray-500 dark:text-gray-400 mb-0.5">Hours / day</label>
<label className="block text-[11px] text-gray-500 dark:text-gray-400 mb-0.5">
Hours / day
</label>
<input
type="number"
value={hoursPerDay}