diff --git a/apps/web/src/app/(app)/timeline/page.tsx b/apps/web/src/app/(app)/timeline/page.tsx index 22bd9d5..6f8e683 100644 --- a/apps/web/src/app/(app)/timeline/page.tsx +++ b/apps/web/src/app/(app)/timeline/page.tsx @@ -15,7 +15,7 @@ const TimelineView = dynamic( export default function TimelinePage() { return ( -
+

Timeline

diff --git a/apps/web/src/components/layout/AppShell.tsx b/apps/web/src/components/layout/AppShell.tsx index 4093936..01f07b4 100644 --- a/apps/web/src/components/layout/AppShell.tsx +++ b/apps/web/src/components/layout/AppShell.tsx @@ -295,15 +295,16 @@ function SidebarContent({ sidebarCollapsed, onToggleCollapse, onNavClick, + onPrefsOpen, }: { userRole: string; onChatOpen: () => void; sidebarCollapsed: boolean; onToggleCollapse: () => void; onNavClick?: () => void; + onPrefsOpen: () => void; }) { const pathname = usePathname(); - const [prefsOpen, setPrefsOpen] = useState(false); const activeHrefSet = useMemo(() => { const set = new Set(); @@ -594,7 +595,7 @@ function SidebarContent({
- {prefsOpen && setPrefsOpen(false)} />} ); } @@ -660,11 +660,13 @@ function DesktopSidebar({ onChatOpen, sidebarCollapsed, onToggleCollapse, + onPrefsOpen, }: { userRole: string; onChatOpen: () => void; sidebarCollapsed: boolean; onToggleCollapse: () => void; + onPrefsOpen: () => void; }) { return ( ); @@ -693,11 +696,13 @@ function MobileSidebar({ onClose, userRole, onChatOpen, + onPrefsOpen, }: { open: boolean; onClose: () => void; userRole: string; onChatOpen: () => void; + onPrefsOpen: () => void; }) { return ( @@ -738,6 +743,10 @@ function MobileSidebar({ sidebarCollapsed={false} onToggleCollapse={() => {}} onNavClick={onClose} + onPrefsOpen={() => { + onPrefsOpen(); + onClose(); + }} /> @@ -754,6 +763,7 @@ export function AppShell({ children, userRole = "USER" }: { children: React.Reac const [chatOpen, setChatOpen] = useState(false); const [mobileOpen, setMobileOpen] = useState(false); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); + const [prefsOpen, setPrefsOpen] = useState(false); const pathname = usePathname(); const contentRef = useRef(null); @@ -802,6 +812,7 @@ export function AppShell({ children, userRole = "USER" }: { children: React.Reac onChatOpen={() => setChatOpen(true)} sidebarCollapsed={sidebarCollapsed} onToggleCollapse={handleToggleCollapse} + onPrefsOpen={() => setPrefsOpen(true)} /> {/* Mobile sidebar overlay */} @@ -810,6 +821,7 @@ export function AppShell({ children, userRole = "USER" }: { children: React.Reac onClose={() => setMobileOpen(false)} userRole={userRole} onChatOpen={() => setChatOpen(true)} + onPrefsOpen={() => setPrefsOpen(true)} /> {/* Main content area */} @@ -844,6 +856,7 @@ export function AppShell({ children, userRole = "USER" }: { children: React.Reac )} + {prefsOpen && setPrefsOpen(false)} />} ); } diff --git a/apps/web/src/components/timeline/TimelineView.tsx b/apps/web/src/components/timeline/TimelineView.tsx index f46417a..88881f3 100644 --- a/apps/web/src/components/timeline/TimelineView.tsx +++ b/apps/web/src/components/timeline/TimelineView.tsx @@ -135,8 +135,8 @@ export function TimelineView() { return { ...prev, isSelecting: false, selectedAllocationIds: [...ids] }; }); }, - onMultiDragComplete: (daysDelta, mode) => { - const ids = multiSelectState.selectedAllocationIds; + onMultiDragComplete: (daysDelta, mode, selectedIds) => { + const ids = selectedIds ?? multiSelectState.selectedAllocationIds; if (ids.length > 0 && daysDelta !== 0) { pushBatchHistoryRef.current(ids, daysDelta, mode); batchShiftMutationOuter.mutate({ allocationIds: ids, daysDelta, mode }); @@ -547,7 +547,7 @@ function TimelineViewContent({ resourceHoverTimerRef.current = null; } }; - }, [resourceHover?.resourceId]); // eslint-disable-line react-hooks/exhaustive-deps + }, [resourceHover?.resourceId, isInitialLoading]); // eslint-disable-line react-hooks/exhaustive-deps // ─── Lazy-extend date range on scroll ───────────────────────────────────── function handleContainerScroll() { diff --git a/apps/web/src/hooks/useTimelineDrag.ts b/apps/web/src/hooks/useTimelineDrag.ts index 034f383..8fcfb3b 100644 --- a/apps/web/src/hooks/useTimelineDrag.ts +++ b/apps/web/src/hooks/useTimelineDrag.ts @@ -178,7 +178,7 @@ export function useTimelineDrag({ onRangeSelected?: (info: RangeSelectedInfo) => void; onAllocationMoved?: (snapshot: AllocationMovedSnapshot) => void; onShiftClickAlloc?: (allocationId: string) => void; - onMultiDragComplete?: (daysDelta: number, mode: AllocDragMode) => void; + onMultiDragComplete?: (daysDelta: number, mode: AllocDragMode, selectedIds?: string[]) => void; }) { const [dragState, setDragState] = useState(INITIAL_DRAG_STATE); const [allocDragState, setAllocDragState] = useState(INITIAL_ALLOC_DRAG); @@ -394,7 +394,9 @@ export function useTimelineDrag({ multiSelectRef.current = { ...multiSelectRef.current, isMultiDragging: false, multiDragDaysDelta: 0 }; if (finalDelta !== 0) { - onMultiDragCompleteRef.current?.(finalDelta, dragMode); + // Pass IDs from ref to avoid stale closure in the callback + const ids = multiSelectRef.current.selectedAllocationIds; + onMultiDragCompleteRef.current?.(finalDelta, dragMode, ids); } }