fix(scans): detail drawer fails after dependencies migration (#8856)

This commit is contained in:
Alejandro Bailo
2025-10-06 17:52:38 +02:00
committed by GitHub
parent 76a55cdb54
commit 8814a0710a
5 changed files with 100 additions and 31 deletions
+7 -2
View File
@@ -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;
}
@@ -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 (
<div className="flex w-9 items-center justify-center">
<TriggerSheet
triggerComponent={<InfoIcon className="text-primary" size={16} />}
triggerComponent={
<InfoIcon
className={
isExecuting ? "cursor-default text-gray-400" : "text-primary"
}
size={16}
/>
}
title="Scan Details"
description="View the scan details"
defaultOpen={isOpen}
open={isOpen}
onOpenChange={handleOpenChange}
>
<DataTableRowDetails entityId={row.original.id} />
{isOpen && <DataTableRowDetails entityId={row.original.id} />}
</TriggerSheet>
</div>
);
@@ -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<ScanProps | null>(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 (
<Alert className="text-small text-center font-bold text-gray-500">
Scan details are loading and will be available once the scan is
completed.
</Alert>
);
return <SkeletonScanDetail />;
}
if (!scanDetails) {
@@ -0,0 +1,58 @@
export const SkeletonScanDetail = () => {
return (
<div className="flex flex-col gap-6 rounded-lg">
{/* Header Skeleton */}
<div className="flex items-center gap-4">
<div className="bg-default-200 h-8 w-24 animate-pulse rounded-full" />
<div className="flex items-center gap-2">
<div className="bg-default-200 relative h-8 w-8 animate-pulse rounded-full" />
<div className="flex flex-col gap-1">
<div className="bg-default-200 h-4 w-32 animate-pulse rounded" />
<div className="bg-default-200 h-3 w-24 animate-pulse rounded" />
</div>
</div>
</div>
{/* Scan Details Section Skeleton */}
<div className="dark:bg-prowler-blue-400 flex flex-col gap-4 rounded-lg p-4 shadow">
<div className="bg-default-200 h-5 w-32 animate-pulse rounded" />
{/* First grid row */}
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
{Array.from({ length: 3 }).map((_, index) => (
<div key={`grid1-${index}`} className="flex flex-col gap-2">
<div className="bg-default-200 h-4 w-24 animate-pulse rounded" />
<div className="bg-default-200 h-5 w-full animate-pulse rounded" />
</div>
))}
</div>
{/* Second grid row */}
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
{Array.from({ length: 3 }).map((_, index) => (
<div key={`grid2-${index}`} className="flex flex-col gap-2">
<div className="bg-default-200 h-4 w-20 animate-pulse rounded" />
<div className="bg-default-200 h-5 w-full animate-pulse rounded" />
</div>
))}
</div>
{/* Scan ID field */}
<div className="flex flex-col gap-2">
<div className="bg-default-200 h-4 w-20 animate-pulse rounded" />
<div className="bg-default-200 h-10 w-full animate-pulse rounded" />
</div>
{/* Third grid row */}
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
{Array.from({ length: 3 }).map((_, index) => (
<div key={`grid3-${index}`} className="flex flex-col gap-2">
<div className="bg-default-200 h-4 w-24 animate-pulse rounded" />
<div className="bg-default-200 h-5 w-full animate-pulse rounded" />
</div>
))}
</div>
</div>
</div>
);
};
+3 -1
View File
@@ -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 (
<Sheet defaultOpen={defaultOpen} onOpenChange={onOpenChange}>
<Sheet open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>
<SheetTrigger className="flex items-center gap-2">
{triggerComponent}
</SheetTrigger>