feat: timeline UI overhaul with project/resource panel redesign, quick filters, and API improvements
Redesigned timeline project and resource panels with expanded detail views, added quick filter toolbar, improved drag handling, and enhanced vacation/entitlement router logic. Includes e2e test updates and minor API fixes. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -248,19 +248,35 @@ function TimelineViewContent({
|
||||
const dragTooltipRef = useRef<HTMLDivElement>(null);
|
||||
const allocTooltipRef = useRef<HTMLDivElement>(null);
|
||||
const rangeHintRef = useRef<HTMLDivElement>(null);
|
||||
const heatmapTooltipRef = useRef<HTMLDivElement>(null);
|
||||
const vacationTooltipRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [openDemandToAssign, setOpenDemandToAssign] = useState<OpenDemandAssignment | null>(null);
|
||||
|
||||
const { CELL_WIDTH, dates, totalCanvasWidth, toLeft, toWidth, gridLines, monthGroups, xToDate } =
|
||||
useTimelineLayout(viewStart, viewDays, filters.zoom, filters.showWeekends, today);
|
||||
const hasActivePointerOverlay =
|
||||
dragState.isDragging || allocDragState.isActive || rangeState.isSelecting;
|
||||
|
||||
function openAllocationPopoverAt(
|
||||
info: {
|
||||
allocationId: string;
|
||||
projectId: string;
|
||||
},
|
||||
anchorX: number,
|
||||
anchorY: number,
|
||||
) {
|
||||
setPopover({
|
||||
allocationId: info.allocationId,
|
||||
projectId: info.projectId,
|
||||
x: anchorX,
|
||||
y: anchorY,
|
||||
});
|
||||
}
|
||||
|
||||
// Keep cellWidthRef in sync so the drag hook uses the correct value.
|
||||
cellWidthRef.current = CELL_WIDTH;
|
||||
|
||||
// ─── Native mousemove listener — updates tooltips without React state ─────
|
||||
useEffect(() => {
|
||||
if (!hasActivePointerOverlay) return;
|
||||
const el = canvasRef.current;
|
||||
if (!el) return;
|
||||
const handler = (e: MouseEvent) => {
|
||||
@@ -279,18 +295,10 @@ function TimelineViewContent({
|
||||
rangeHintRef.current.style.left = `${x + 12}px`;
|
||||
rangeHintRef.current.style.top = `${y - 28}px`;
|
||||
}
|
||||
if (heatmapTooltipRef.current) {
|
||||
heatmapTooltipRef.current.style.left = `${x + 16}px`;
|
||||
heatmapTooltipRef.current.style.top = `${y - 52}px`;
|
||||
}
|
||||
if (vacationTooltipRef.current) {
|
||||
vacationTooltipRef.current.style.left = `${x + 14}px`;
|
||||
vacationTooltipRef.current.style.top = `${y - 8}px`;
|
||||
}
|
||||
};
|
||||
el.addEventListener("mousemove", handler, { passive: true });
|
||||
return () => el.removeEventListener("mousemove", handler);
|
||||
}, [isLoading, mousePosRef]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [hasActivePointerOverlay, isLoading, mousePosRef]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// ─── Shift+wheel → horizontal scroll ──────────────────────────────────────
|
||||
useEffect(() => {
|
||||
@@ -336,6 +344,7 @@ function TimelineViewContent({
|
||||
}
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent) => {
|
||||
if (!hasActivePointerOverlay) return;
|
||||
onCanvasMouseMove(e);
|
||||
};
|
||||
|
||||
@@ -396,10 +405,10 @@ function TimelineViewContent({
|
||||
onMouseUp={(e) => void onCanvasMouseUp(e)}
|
||||
onMouseLeave={onCanvasMouseLeave}
|
||||
onTouchMove={(e) => {
|
||||
if (!hasActivePointerOverlay) return;
|
||||
onCanvasTouchMove(e);
|
||||
}}
|
||||
onTouchEnd={(e) => void onCanvasTouchEnd(e)}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
className={clsx(
|
||||
(dragState.isDragging || allocDragState.isActive) && "cursor-grabbing select-none",
|
||||
rangeState.isSelecting && "cursor-crosshair select-none",
|
||||
@@ -417,6 +426,7 @@ function TimelineViewContent({
|
||||
onAllocTouchStart={onAllocTouchStart}
|
||||
onRowMouseDown={onRowMouseDown}
|
||||
onRowTouchStart={onRowTouchStart}
|
||||
onAllocationContextMenu={openAllocationPopoverAt}
|
||||
CELL_WIDTH={CELL_WIDTH}
|
||||
dates={dates}
|
||||
totalCanvasWidth={totalCanvasWidth}
|
||||
@@ -429,6 +439,7 @@ function TimelineViewContent({
|
||||
|
||||
{viewMode === "project" && (
|
||||
<TimelineProjectPanel
|
||||
scrollContainerRef={scrollContainerRef}
|
||||
dragState={dragState}
|
||||
allocDragState={allocDragState}
|
||||
rangeState={rangeState}
|
||||
@@ -440,6 +451,7 @@ function TimelineViewContent({
|
||||
onRowTouchStart={onRowTouchStart}
|
||||
onOpenPanel={setOpenPanelProjectId}
|
||||
onOpenDemandClick={setOpenDemandToAssign}
|
||||
onAllocationContextMenu={openAllocationPopoverAt}
|
||||
CELL_WIDTH={CELL_WIDTH}
|
||||
dates={dates}
|
||||
totalCanvasWidth={totalCanvasWidth}
|
||||
|
||||
Reference in New Issue
Block a user