feat(timeline): add pulse animation for in-flight drag mutations

Allocation bars that have active optimistic overrides (post-drag,
awaiting server confirmation) now pulse subtly via animate-pulse.
The pending set is derived from the existing optimisticAllocations
map keys, requiring no additional state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 13:28:46 +02:00
parent 7a5e98e2e9
commit 1df208dbcc
386 changed files with 657 additions and 81650 deletions
@@ -222,8 +222,9 @@ function TimelineProjectPanelInner({
toWidth,
CELL_WIDTH,
totalCanvasWidth,
filters.showWeekends,
),
[CELL_WIDTH, filters.showVacations, toLeft, toWidth, totalCanvasWidth, vacationsByResource],
[CELL_WIDTH, filters.showVacations, filters.showWeekends, toLeft, toWidth, totalCanvasWidth, vacationsByResource],
);
const projectRowMetrics = useMemo(() => {
@@ -699,6 +700,7 @@ function renderOpenDemandRow(
allocDragState: AllocDragState,
suppressHoverInteractions: boolean,
) {
const selectedAllocationSet = new Set(multiSelectState.selectedAllocationIds);
const { visibleOpenDemands, laneMap, rowHeight } = layout;
if (visibleOpenDemands.length === 0) return null;
@@ -750,7 +752,7 @@ function renderOpenDemandRow(
// Multi-drag visual offset
const isMultiDragTarget =
multiSelectState.isMultiDragging &&
multiSelectState.selectedAllocationIds.includes(alloc.id);
selectedAllocationSet.has(alloc.id);
const multiDragPx = isMultiDragTarget ? multiSelectState.multiDragDaysDelta * CELL_WIDTH : 0;
const multiDragMode = multiSelectState.multiDragMode;
@@ -825,7 +827,7 @@ function renderOpenDemandRow(
isAllocDragged
? "ring-2 ring-amber-500 z-20"
: "hover:ring-2 hover:ring-amber-400 hover:ring-offset-1",
multiSelectState.selectedAllocationIds.includes(alloc.id) && "ring-2 ring-sky-500 ring-offset-1 z-20",
selectedAllocationSet.has(alloc.id) && "ring-2 ring-sky-500 ring-offset-1 z-20",
)}
style={{
left: left + 2,
@@ -1042,6 +1044,7 @@ function renderProjectDragHandles(
multiSelectState: MultiSelectState,
suppressHoverInteractions: boolean,
) {
const selectedAllocationSet = new Set(multiSelectState.selectedAllocationIds);
return allocs.map((alloc) => {
const allocStart = new Date(alloc.startDate);
const allocEnd = new Date(alloc.endDate);
@@ -1079,7 +1082,7 @@ function renderProjectDragHandles(
// Multi-drag visual offset
const isMultiDragTarget =
multiSelectState.isMultiDragging &&
multiSelectState.selectedAllocationIds.includes(alloc.id);
selectedAllocationSet.has(alloc.id);
const multiDragPx = isMultiDragTarget ? multiSelectState.multiDragDaysDelta * CELL_WIDTH : 0;
const multiDragMode = multiSelectState.multiDragMode;
@@ -1118,7 +1121,7 @@ function renderProjectDragHandles(
isAllocDragged
? "ring-2 ring-brand-400 z-20"
: "hover:ring-1 hover:ring-brand-300/70 z-[15]",
multiSelectState.selectedAllocationIds.includes(alloc.id) && "ring-2 ring-sky-500 ring-offset-1 z-20",
selectedAllocationSet.has(alloc.id) && "ring-2 ring-sky-500 ring-offset-1 z-20",
)}
style={{
left: left + 2,