From 8814a0710aebc9e27e2b64024bba3d3d3d26dff1 Mon Sep 17 00:00:00 2001 From: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:52:38 +0200 Subject: [PATCH] fix(scans): detail drawer fails after dependencies migration (#8856) --- ui/components/scans/auto-refresh.tsx | 9 ++- .../scans/table/scans/column-get-scans.tsx | 33 +++++++++-- .../table/scans/data-table-row-details.tsx | 27 +-------- .../table/scans/skeleton-scan-detail.tsx | 58 +++++++++++++++++++ ui/components/ui/sheet/trigger-sheet.tsx | 4 +- 5 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 ui/components/scans/table/scans/skeleton-scan-detail.tsx diff --git a/ui/components/scans/auto-refresh.tsx b/ui/components/scans/auto-refresh.tsx index 218d5ccc32..fa2c93424f 100644 --- a/ui/components/scans/auto-refresh.tsx +++ b/ui/components/scans/auto-refresh.tsx @@ -1,6 +1,6 @@ "use client"; -import { useRouter } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import { useEffect } from "react"; interface AutoRefreshProps { @@ -9,16 +9,21 @@ interface AutoRefreshProps { export function AutoRefresh({ hasExecutingScan }: AutoRefreshProps) { const router = useRouter(); + const searchParams = useSearchParams(); useEffect(() => { if (!hasExecutingScan) return; + // Don't auto-refresh if scan details drawer is open + const scanId = searchParams.get("scanId"); + if (scanId) return; + const interval = setInterval(() => { router.refresh(); }, 5000); return () => clearInterval(interval); - }, [hasExecutingScan, router]); + }, [hasExecutingScan, router, searchParams]); return null; } diff --git a/ui/components/scans/table/scans/column-get-scans.tsx b/ui/components/scans/table/scans/column-get-scans.tsx index 918d2a80c1..f3f2e3ac62 100644 --- a/ui/components/scans/table/scans/column-get-scans.tsx +++ b/ui/components/scans/table/scans/column-get-scans.tsx @@ -2,7 +2,7 @@ import { Tooltip } from "@heroui/tooltip"; import { ColumnDef } from "@tanstack/react-table"; -import { useSearchParams } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import { InfoIcon } from "@/components/icons"; import { TableLink } from "@/components/ui/custom"; @@ -21,19 +21,44 @@ const getScanData = (row: { original: ScanProps }) => { }; const ScanDetailsCell = ({ row }: { row: any }) => { + const router = useRouter(); const searchParams = useSearchParams(); const scanId = searchParams.get("scanId"); const isOpen = scanId === row.original.id; + const scanState = row.original.attributes?.state; + const isExecuting = scanState === "executing" || scanState === "available"; + + const handleOpenChange = (open: boolean) => { + if (isExecuting) return; + + const params = new URLSearchParams(searchParams.toString()); + + if (open) { + params.set("scanId", row.original.id); + } else { + params.delete("scanId"); + } + + router.push(`?${params.toString()}`, { scroll: false }); + }; return (
} + triggerComponent={ + + } title="Scan Details" description="View the scan details" - defaultOpen={isOpen} + open={isOpen} + onOpenChange={handleOpenChange} > - + {isOpen && }
); diff --git a/ui/components/scans/table/scans/data-table-row-details.tsx b/ui/components/scans/table/scans/data-table-row-details.tsx index 307f7676f5..1b15a665ab 100644 --- a/ui/components/scans/table/scans/data-table-row-details.tsx +++ b/ui/components/scans/table/scans/data-table-row-details.tsx @@ -1,36 +1,20 @@ "use client"; -import { useRouter, useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; import { getProvider } from "@/actions/providers"; import { getScan } from "@/actions/scans"; import { getTask } from "@/actions/task"; import { ScanDetail } from "@/components/scans/table"; -import { Alert } from "@/components/ui/alert/Alert"; import { checkTaskStatus } from "@/lib"; import { ScanProps } from "@/types"; +import { SkeletonScanDetail } from "./skeleton-scan-detail"; + export const DataTableRowDetails = ({ entityId }: { entityId: string }) => { - const router = useRouter(); - const searchParams = useSearchParams(); const [scanDetails, setScanDetails] = useState(null); const [isLoading, setIsLoading] = useState(true); - useEffect(() => { - // Add scanId to URL - const params = new URLSearchParams(searchParams.toString()); - params.set("scanId", entityId); - router.push(`?${params.toString()}`, { scroll: false }); - - // Cleanup function: remove scanId from URL when component unmounts - return () => { - const newParams = new URLSearchParams(searchParams.toString()); - newParams.delete("scanId"); - router.push(`?${newParams.toString()}`, { scroll: false }); - }; - }, [entityId, router, searchParams]); - useEffect(() => { const fetchScanDetails = async () => { try { @@ -76,12 +60,7 @@ export const DataTableRowDetails = ({ entityId }: { entityId: string }) => { }, [entityId]); if (isLoading) { - return ( - - Scan details are loading and will be available once the scan is - completed. - - ); + return ; } if (!scanDetails) { diff --git a/ui/components/scans/table/scans/skeleton-scan-detail.tsx b/ui/components/scans/table/scans/skeleton-scan-detail.tsx new file mode 100644 index 0000000000..5e69becb48 --- /dev/null +++ b/ui/components/scans/table/scans/skeleton-scan-detail.tsx @@ -0,0 +1,58 @@ +export const SkeletonScanDetail = () => { + return ( +
+ {/* Header Skeleton */} +
+
+
+
+
+
+
+
+
+
+ + {/* Scan Details Section Skeleton */} +
+
+ + {/* First grid row */} +
+ {Array.from({ length: 3 }).map((_, index) => ( +
+
+
+
+ ))} +
+ + {/* Second grid row */} +
+ {Array.from({ length: 3 }).map((_, index) => ( +
+
+
+
+ ))} +
+ + {/* Scan ID field */} +
+
+
+
+ + {/* Third grid row */} +
+ {Array.from({ length: 3 }).map((_, index) => ( +
+
+
+
+ ))} +
+
+
+ ); +}; diff --git a/ui/components/ui/sheet/trigger-sheet.tsx b/ui/components/ui/sheet/trigger-sheet.tsx index dcb8e793f4..fb51a65c47 100644 --- a/ui/components/ui/sheet/trigger-sheet.tsx +++ b/ui/components/ui/sheet/trigger-sheet.tsx @@ -12,6 +12,7 @@ interface TriggerSheetProps { title: string; description: string; children: React.ReactNode; + open?: boolean; defaultOpen?: boolean; onOpenChange?: (open: boolean) => void; } @@ -21,11 +22,12 @@ export function TriggerSheet({ title, description, children, + open, defaultOpen = false, onOpenChange, }: TriggerSheetProps) { return ( - + {triggerComponent}