feat: detail view for scans has been implemented

This commit is contained in:
Pablo Lara
2024-10-14 17:30:32 +02:00
parent da9429351f
commit d0b54d1950
6 changed files with 129 additions and 154 deletions
@@ -1,7 +1,6 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { add } from "date-fns";
import { DateWithTime, SnippetId } from "@/components/ui/entities";
import { DataTableColumnHeader, StatusBadge } from "@/components/ui/table";
@@ -15,10 +14,10 @@ const getProviderData = (row: { original: ProviderProps }) => {
};
export const ColumnProviders: ColumnDef<ProviderProps>[] = [
{
header: " ",
cell: ({ row }) => <p className="text-medium">{row.index + 1}</p>,
},
// {
// header: " ",
// cell: ({ row }) => <p className="text-medium">{row.index + 1}</p>,
// },
{
accessorKey: "account",
header: ({ column }) => (
@@ -73,27 +72,6 @@ export const ColumnProviders: ColumnDef<ProviderProps>[] = [
return <DateWithTime dateTime={updated_at} />;
},
},
{
accessorKey: "nextScan",
header: "Next Scan",
cell: ({ row }) => {
const {
attributes: { updated_at },
} = getProviderData(row);
const nextDay = add(new Date(updated_at), {
hours: 24,
});
return <DateWithTime dateTime={nextDay.toISOString()} />;
},
},
{
accessorKey: "resources",
header: "Resources",
cell: () => {
// Temporarily overwriting the value until the API is functional.
return <p className="font-medium">{288}</p>;
},
},
{
accessorKey: "added",
header: ({ column }) => (
+108 -76
View File
@@ -1,85 +1,98 @@
"use client";
import { Card, CardBody, CardHeader, Chip, Divider } from "@nextui-org/react";
import { Card, CardBody, CardHeader, Divider } from "@nextui-org/react";
import { DateWithTime, SnippetId } from "@/components/ui/entities";
import { StatusBadge } from "@/components/ui/table/status-badge";
import { ScanProps } from "@/types";
export const ScanDetail = ({ scanDetails }: { scanDetails: ScanProps }) => {
const scanOnDemand = scanDetails.attributes;
return (
<div className="space-y-4">
<Card className="relative w-full border-small border-default-100 p-3 shadow-lg">
<CardHeader className="flex items-center justify-between py-2">
<h2 className="text-2xl font-bold">Scan Details</h2>
<Chip
color={
scanDetails.attributes.state === "failed" ? "danger" : "success"
}
variant="faded"
>
{scanDetails.attributes.state}
</Chip>
</CardHeader>
<div className="flex items-center justify-between">
<div className="flex flex-col items-baseline md:flex-row md:gap-x-4">
<h2 className="text-lg font-black uppercase">Scan Details - </h2>
<p>{scanOnDemand.name}</p>
</div>
<Divider />
<CardBody className="p-4">
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-4">
<DetailItem label="ID" value={scanDetails.id} />
<DetailItem label="Name" value={scanDetails.attributes.name} />
<DetailItem
label="Trigger"
value={scanDetails.attributes.trigger}
/>
<DetailItem
label="Resource Count"
value={scanDetails.attributes.unique_resource_count.toString()}
/>
<DetailItem
label="Progress"
value={`${scanDetails.attributes.progress}%`}
/>
<DetailItem
label="Duration"
value={`${scanDetails.attributes.duration} seconds`}
/>
</div>
<div className="space-y-4">
<DetailItem
label="Started At"
value={new Date(
scanDetails.attributes.started_at,
).toLocaleString()}
/>
<DetailItem
label="Completed At"
value={new Date(
scanDetails.attributes.completed_at,
).toLocaleString()}
/>
<DetailItem
label="Scheduled At"
value={
scanDetails.attributes.scheduled_at
? new Date(
scanDetails.attributes.scheduled_at,
).toLocaleString()
: "Not Scheduled"
}
/>
<DetailItem
label="Provider ID"
value={scanDetails.relationships.provider.data.id}
/>
<DetailItem
label="Task ID"
value={scanDetails.relationships.task.data.id}
/>
</div>
<StatusBadge size="lg" status={scanOnDemand.state} />
</div>
<Divider />
<div className="relative z-0 flex w-full flex-col justify-between gap-4 overflow-auto rounded-large bg-content1 p-4 shadow-small">
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-4">
<DetailItem
label="ID"
value={<SnippetId label="Type" entityId={scanDetails.id} />}
/>
<DetailItem label="Trigger" value={scanOnDemand.trigger} />
<DetailItem
label="Resource Count"
value={scanOnDemand.unique_resource_count.toString()}
/>
<DetailItem label="Progress" value={`${scanOnDemand.progress}%`} />
<DetailItem
label="Duration"
value={`${scanOnDemand.duration} seconds`}
/>
</div>
</CardBody>
</Card>
<div className="space-y-4">
<DateItem
label="Started At"
value={
scanOnDemand.started_at ? (
<DateWithTime dateTime={scanOnDemand.started_at.toString()} />
) : (
"Not Started"
)
}
/>
<DateItem
label="Completed At"
value={
scanOnDemand.completed_at ? (
<DateWithTime
dateTime={scanOnDemand.completed_at.toString()}
/>
) : (
"Not Started"
)
}
/>
<DateItem
label="Scheduled At"
value={
scanOnDemand.scheduled_at ? (
<DateWithTime
dateTime={scanOnDemand.scheduled_at.toString()}
/>
) : (
"Not Scheduled"
)
}
/>
<DetailItem
label="Provider ID"
value={
<SnippetId
label="Provider ID"
entityId={scanDetails.relationships.provider.data.id}
/>
}
/>
<DetailItem
label="Task ID"
value={
<SnippetId
label="Task ID"
entityId={scanDetails.relationships.task.data.id}
/>
}
/>
</div>
</div>
</div>
<Card className="relative w-full border-small border-default-100 p-3 shadow-lg">
<CardHeader className="py-2">
<h2 className="text-2xl font-bold">Scan Arguments</h2>
@@ -91,9 +104,9 @@ export const ScanDetail = ({ scanDetails }: { scanDetails: ScanProps }) => {
<DetailItem
label="Checks"
value={
(
scanDetails.attributes.scanner_args as any
)?.checks_to_execute?.join(", ") || "N/A"
(scanOnDemand.scanner_args as any)?.checks_to_execute?.join(
", ",
) || "N/A"
}
/>
</CardBody>
@@ -102,7 +115,26 @@ export const ScanDetail = ({ scanDetails }: { scanDetails: ScanProps }) => {
);
};
const DetailItem = ({ label, value }: { label: string; value: string }) => (
const DateItem = ({
label,
value,
}: {
label: string;
value: React.ReactNode;
}) => (
<div className="flex items-center justify-between">
<span className="font-semibold text-default-500">{label}:</span>
<span className="text-default-700">{value}</span>
</div>
);
const DetailItem = ({
label,
value,
}: {
label: string;
value: React.ReactNode;
}) => (
<div className="flex items-center justify-between">
<span className="font-semibold text-default-500">{label}:</span>
<span className="text-default-700">{value}</span>
@@ -31,7 +31,6 @@ export function DataTableRowActions<ScanProps>({
row,
}: DataTableRowActionsProps<ScanProps>) {
const [isEditOpen, setIsEditOpen] = useState(false);
// const [isDeleteOpen, setIsDeleteOpen] = useState(false);
const scanId = (row.original as { id: string }).id;
const scanName = (row.original as any).attributes?.name;
return (
@@ -49,16 +48,6 @@ export function DataTableRowActions<ScanProps>({
/>
</CustomAlertModal>
{/* <CustomAlertModal
isOpen={isDeleteOpen}
onOpenChange={setIsDeleteOpen}
title="Are you absolutely sure?"
description="This action cannot be undone. This will permanently delete your scan."
> */}
{/* <p>Hello</p> */}
{/* <DeleteForm providerId={providerId} setIsOpen={setIsDeleteOpen} /> */}
{/* </CustomAlertModal> */}
<div className="relative flex items-center justify-end gap-2">
<Dropdown className="shadow-xl" placement="bottom">
<DropdownTrigger>
@@ -83,23 +72,6 @@ export function DataTableRowActions<ScanProps>({
Edit Scan
</DropdownItem>
</DropdownSection>
{/* <DropdownSection title="Danger zone">
<DropdownItem
key="delete"
className="text-danger"
color="danger"
description="Delete the scan permanently"
textValue="Delete Scan"
startContent={
<DeleteDocumentBulkIcon
className={clsx(iconClasses, "!text-danger")}
/>
}
onClick={() => setIsDeleteOpen(true)}
>
Delete Scan
</DropdownItem>
</DropdownSection> */}
</DropdownMenu>
</Dropdown>
</div>
@@ -42,37 +42,22 @@ export const ColumnGetScansSchedule: ColumnDef<ScanProps>[] = [
},
},
{
accessorKey: "started_at",
accessorKey: "scheduled_at",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title={"Started at"}
param="started_at"
title={"Scheduled at"}
param="scheduled_at"
/>
),
cell: ({ row }) => {
const {
attributes: { started_at },
attributes: { scheduled_at },
} = getScanData(row);
return <DateWithTime dateTime={started_at} />;
},
},
{
accessorKey: "lastScan",
header: ({ column }) => (
<DataTableColumnHeader
column={column}
title={"Completed At"}
param="completed_at"
/>
),
cell: ({ row }) => {
const {
attributes: { completed_at },
} = getScanData(row);
return <DateWithTime dateTime={completed_at} />;
return <DateWithTime dateTime={scheduled_at} />;
},
},
{
id: "moreInfo",
header: "Details",
+1 -1
View File
@@ -23,7 +23,7 @@ export function TriggerSheet({
return (
<Sheet>
<SheetTrigger>{triggerComponent}</SheetTrigger>
<SheetContent className="max-w-[95vw] pt-10 md:max-w-[55vw]">
<SheetContent className="max-w-[95vw] pt-10 md:max-w-[45vw]">
<SheetHeader>
<SheetTitle className="sr-only">{title}</SheetTitle>
<SheetDescription className="sr-only">{description}</SheetDescription>
+10 -2
View File
@@ -21,15 +21,23 @@ const statusColorMap: Record<
cancelled: "danger",
};
export const StatusBadge = ({ status }: { status: Status }) => {
export const StatusBadge = ({
status,
size = "sm",
...props
}: {
status: Status;
size?: "sm" | "md" | "lg";
}) => {
const color = statusColorMap[status as keyof typeof statusColorMap];
return (
<Chip
className="gap-1 border-none capitalize text-default-600"
size="sm"
size={size}
variant="flat"
color={color}
{...props}
>
{status}
</Chip>