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:
@@ -6,7 +6,7 @@ import { useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { AllocationStatus } from "@capakraken/shared";
|
||||
import { trpc } from "~/lib/trpc/client.js";
|
||||
import { useInvalidateTimeline } from "~/hooks/useInvalidatePlanningViews.js";
|
||||
import { useInvalidatePlanningViews } from "~/hooks/useInvalidatePlanningViews.js";
|
||||
import { useViewportPopover } from "~/hooks/useViewportPopover.js";
|
||||
import { DateInput } from "~/components/ui/DateInput.js";
|
||||
import { ProjectCombobox } from "~/components/ui/ProjectCombobox.js";
|
||||
@@ -50,7 +50,7 @@ export function NewAllocationPopover({
|
||||
ignoreSelectors: ["[data-entity-combobox-overlay='true']"],
|
||||
...(ignoreScrollContainers ? { ignoreScrollContainers } : {}),
|
||||
});
|
||||
const invalidateTimeline = useInvalidateTimeline();
|
||||
const invalidatePlanningViews = useInvalidatePlanningViews();
|
||||
|
||||
const [selectedProjectId, setSelectedProjectId] = useState<string | null>(
|
||||
suggestedProjectId ?? null,
|
||||
@@ -62,7 +62,7 @@ export function NewAllocationPopover({
|
||||
|
||||
const createMutation = trpc.timeline.quickAssign.useMutation({
|
||||
onSuccess: () => {
|
||||
invalidateTimeline();
|
||||
void invalidatePlanningViews();
|
||||
onCreated();
|
||||
onClose();
|
||||
},
|
||||
@@ -91,15 +91,24 @@ export function NewAllocationPopover({
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-4 py-3 bg-gray-50 dark:bg-gray-900 border-b border-gray-100 dark:border-gray-700">
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">Assign to Project</span>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 text-lg leading-none">×</button>
|
||||
<span className="text-sm font-semibold text-gray-700 dark:text-gray-200">
|
||||
Assign to Project
|
||||
</span>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 text-lg leading-none"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 overflow-y-auto p-4">
|
||||
{/* Date range */}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Start</label>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">
|
||||
Start
|
||||
</label>
|
||||
<DateInput
|
||||
value={start}
|
||||
onChange={setStart}
|
||||
@@ -107,7 +116,9 @@ export function NewAllocationPopover({
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">End</label>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">
|
||||
End
|
||||
</label>
|
||||
<DateInput
|
||||
value={end}
|
||||
onChange={setEnd}
|
||||
@@ -119,7 +130,9 @@ export function NewAllocationPopover({
|
||||
|
||||
{/* Project picker */}
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Project</label>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">
|
||||
Project
|
||||
</label>
|
||||
<ProjectCombobox
|
||||
value={selectedProjectId}
|
||||
onChange={setSelectedProjectId}
|
||||
@@ -130,7 +143,9 @@ export function NewAllocationPopover({
|
||||
|
||||
{/* Role */}
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Role</label>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">
|
||||
Role
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={role}
|
||||
@@ -141,7 +156,9 @@ export function NewAllocationPopover({
|
||||
|
||||
{/* Hours per day */}
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Hours / day</label>
|
||||
<label className="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">
|
||||
Hours / day
|
||||
</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="number"
|
||||
|
||||
Reference in New Issue
Block a user