feat(ui): new resources side drawer with redesigned detail panel (#10673)

This commit is contained in:
Alejandro Bailo authored and GitHub committed 2026-04-14 17:20:19 +02:00
1 parent f3a042933f
commit 89d72cf8fd
20 files changed
+637 -502

No files matched your search

-1
View File
@@ -1,5 +1,4 @@
export * from "./date-with-time";
export * from "./entity-info";
export * from "./get-provider-logo";
export * from "./info-field";
export * from "./scan-status";
-79
View File
@@ -1,79 +0,0 @@
import { Tooltip } from "@heroui/tooltip";
import clsx from "clsx";
import { InfoIcon } from "lucide-react";
interface InfoFieldProps {
label: string;
children: React.ReactNode;
variant?: "default" | "simple" | "transparent";
className?: string;
tooltipContent?: string;
inline?: boolean;
}
<Tooltip
className="text-xs"
content="Download a ZIP file that includes the JSON (OCSF), CSV, and HTML scan reports, along with the compliance report."
>
<div className="flex items-center gap-2">
<InfoIcon className="text-primary mb-1" size={12} />
</div>
</Tooltip>;
export const InfoField = ({
label,
children,
variant = "default",
tooltipContent,
className,
inline = false,
}: InfoFieldProps) => {
if (inline) {
return (
<div className={clsx("flex items-center gap-2", className)}>
<span className="text-text-neutral-tertiary text-xs font-bold">
<span className="flex items-center gap-1">
{label}:
{tooltipContent && (
<Tooltip className="text-xs" content={tooltipContent}>
<div className="flex cursor-pointer items-center gap-2">
<InfoIcon className="text-bg-data-info mb-1" size={12} />
</div>
</Tooltip>
)}
</span>
</span>
<div className="text-text-neutral-primary text-sm">{children}</div>
</div>
);
}
return (
<div className={clsx("flex flex-col gap-1", className)}>
<span className="text-text-neutral-tertiary text-xs font-bold">
<span className="flex items-center gap-1">
{label}
{tooltipContent && (
<Tooltip className="text-xs" content={tooltipContent}>
<div className="flex cursor-pointer items-center gap-2">
<InfoIcon className="text-bg-data-info mb-1" size={12} />
</div>
</Tooltip>
)}
</span>
</span>
{variant === "simple" ? (
<div className="text-text-neutral-primary text-sm break-all">
{children}
</div>
) : variant === "transparent" ? (
<div className="text-text-neutral-primary text-sm">{children}</div>
) : (
<div className="border-border-neutral-tertiary bg-bg-neutral-tertiary text-text-neutral-primary rounded-lg border px-3 py-2 text-sm">
{children}
</div>
)}
</div>
);
};
@@ -166,7 +166,7 @@ describe("DataTableFilterCustom — batch vs instant mode", () => {
render(<DataTableFilterCustom filters={[severityFilter]} />);
// Then — renders without crashing
expect(screen.getByText("Severity")).toBeInTheDocument();
expect(screen.getByText("All Severity")).toBeInTheDocument();
});
});
@@ -185,7 +185,9 @@ export const DataTableFilterCustom = ({
onValuesChange={(values) => pushDropdownFilter(filter, values)}
>
<MultiSelectTrigger size="default">
<MultiSelectValue placeholder={filter.labelCheckboxGroup} />
<MultiSelectValue
placeholder={`All ${filter.labelCheckboxGroup}`}
/>
</MultiSelectTrigger>
<MultiSelectContent search={false}>
<MultiSelectSelectAll>Select All</MultiSelectSelectAll>
+22 -1
View File
@@ -108,6 +108,8 @@ interface DataTableProviderProps<TData, TValue> {
renderAfterRow?: (row: Row<TData>) => ReactNode;
/** Badge shown inside the search input (e.g., active drill-down group) */
searchBadge?: { label: string; onDismiss: () => void };
/** Optional click handler for top-level rows. */
onRowClick?: (row: Row<TData>) => void;
}
export function DataTable<TData, TValue>({
@@ -137,6 +139,7 @@ export function DataTable<TData, TValue>({
searchPlaceholder,
renderAfterRow,
searchBadge,
onRowClick,
}: DataTableProviderProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
@@ -213,6 +216,18 @@ export function DataTable<TData, TValue>({
const rows = table.getRowModel().rows;
const handleRowClick = (row: Row<TData>, target: HTMLElement | null) => {
if (!onRowClick) {
return;
}
if (target?.closest("a, button, input, [role=menuitem]")) {
return;
}
onRowClick(row);
};
return (
<div
className={cn(
@@ -282,7 +297,13 @@ export function DataTable<TData, TValue>({
/>
) : (
<Fragment key={row.id}>
<TableRow data-state={row.getIsSelected() && "selected"}>
<TableRow
data-state={row.getIsSelected() && "selected"}
className={cn(onRowClick && "cursor-pointer")}
onClick={(event) =>
handleRowClick(row, event.target as HTMLElement)
}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(