feat(ui): integrate threat map with regions API endpoint (#9324)

Co-authored-by: alejandrobailo <alejandrobailo94@gmail.com>
This commit is contained in:
Alan Buscaglia
2025-11-26 16:12:31 +01:00
committed by GitHub
parent c8d9f37e70
commit 28d5b2bb6c
26 changed files with 721 additions and 475 deletions

View File

@@ -4,32 +4,12 @@ import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { Rectangle, ResponsiveContainer, Sankey, Tooltip } from "recharts";
import {
AWSProviderBadge,
AzureProviderBadge,
GCPProviderBadge,
GitHubProviderBadge,
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
OracleCloudProviderBadge,
} from "@/components/icons/providers-badge";
import { IconSvgProps } from "@/types";
import { PROVIDER_ICONS } from "@/components/icons/providers-badge";
import { initializeChartColors } from "@/lib/charts/colors";
import { SEVERITY_FILTER_MAP } from "@/types/severities";
import { ChartTooltip } from "./shared/chart-tooltip";
// Map node names to their corresponding provider icon components
const PROVIDER_ICONS: Record<string, React.FC<IconSvgProps>> = {
AWS: AWSProviderBadge,
Azure: AzureProviderBadge,
"Google Cloud": GCPProviderBadge,
Kubernetes: KS8ProviderBadge,
"Microsoft 365": M365ProviderBadge,
GitHub: GitHubProviderBadge,
"Infrastructure as Code": IacProviderBadge,
"Oracle Cloud Infrastructure": OracleCloudProviderBadge,
};
interface SankeyNode {
name: string;
newFindings?: number;
@@ -74,77 +54,6 @@ interface NodeTooltipState {
const TOOLTIP_OFFSET_PX = 10;
const MIN_LINK_WIDTH = 4;
// Map severity node names to their filter values for the findings page
const SEVERITY_FILTER_MAP: Record<string, string> = {
Critical: "critical",
High: "high",
Medium: "medium",
Low: "low",
Informational: "informational",
};
// Map color names to CSS variable names defined in globals.css
const COLOR_MAP: Record<string, string> = {
// Status colors
Success: "--color-bg-pass",
Pass: "--color-bg-pass",
Fail: "--color-bg-fail",
// Provider colors
AWS: "--color-bg-data-aws",
Azure: "--color-bg-data-azure",
"Google Cloud": "--color-bg-data-gcp",
Kubernetes: "--color-bg-data-kubernetes",
"Microsoft 365": "--color-bg-data-m365",
GitHub: "--color-bg-data-github",
"Infrastructure as Code": "--color-bg-data-muted",
"Oracle Cloud Infrastructure": "--color-bg-data-muted",
// Severity colors
Critical: "--color-bg-data-critical",
High: "--color-bg-data-high",
Medium: "--color-bg-data-medium",
Low: "--color-bg-data-low",
Info: "--color-bg-data-info",
Informational: "--color-bg-data-info",
};
/**
* Compute color value from CSS variable name at runtime.
* SVG fill attributes cannot directly resolve CSS variables,
* so we extract computed values from globals.css CSS variables.
* Falls back to black (#000000) if variable not found or access fails.
*
* @param colorName - Key in COLOR_MAP (e.g., "AWS", "Fail")
* @returns Computed CSS variable value or fallback color
*/
const getColorVariable = (colorName: string): string => {
const varName = COLOR_MAP[colorName];
if (!varName) return "#000000";
try {
if (typeof document === "undefined") {
// SSR context - return fallback
return "#000000";
}
return (
getComputedStyle(document.documentElement)
.getPropertyValue(varName)
.trim() || "#000000"
);
} catch (error: unknown) {
// CSS variables not loaded or access failed - return fallback
return "#000000";
}
};
// Initialize all color variables from CSS
const initializeColors = (): Record<string, string> => {
const colors: Record<string, string> = {};
for (const [colorName] of Object.entries(COLOR_MAP)) {
colors[colorName] = getColorVariable(colorName);
}
return colors;
};
interface TooltipPayload {
payload: {
source?: { name: string };
@@ -476,7 +385,7 @@ export function SankeyChart({ data, height = 400 }: SankeyChartProps) {
// Initialize colors from CSS variables on mount
useEffect(() => {
setColors(initializeColors());
setColors(initializeChartColors());
}, []);
const handleLinkHover = (

View File

@@ -512,23 +512,20 @@ export function ThreatMap({
position={tooltipPosition}
/>
)}
</div>
<div
className="mt-3 flex items-center gap-2"
role="status"
aria-label={`${filteredLocations.length} threat locations on map`}
>
<div
aria-hidden="true"
className="h-3 w-3 rounded-full"
style={{ backgroundColor: "var(--bg-data-critical)" }}
/>
<span
className="text-sm"
style={{ color: "var(--text-neutral-tertiary)" }}
className="border-border-neutral-primary bg-bg-neutral-secondary absolute bottom-4 left-4 flex items-center gap-2 rounded-full border px-3 py-1.5"
role="status"
aria-label={`${filteredLocations.length} threat locations on map`}
>
{filteredLocations.length} Locations
</span>
<div
aria-hidden="true"
className="h-3 w-3 rounded"
style={{ backgroundColor: "var(--bg-data-critical)" }}
/>
<span className="text-text-neutral-primary text-sm font-medium">
{filteredLocations.length} Locations
</span>
</div>
</div>
</>
)}
@@ -542,9 +539,13 @@ export function ThreatMap({
<div className="flex w-full flex-col">
<div className="mb-4">
<div
className="mb-1 flex items-center gap-2"
className="mb-1 flex items-center"
aria-label={`Selected location: ${selectedLocation.name}`}
>
<MapPin
size={21}
style={{ color: "var(--color-text-text-error)" }}
/>
<div
aria-hidden="true"
className="bg-pass-primary h-2 w-2 rounded-full"

View File

@@ -1,8 +1,33 @@
export * from "./aws-provider-badge";
export * from "./azure-provider-badge";
export * from "./gcp-provider-badge";
export * from "./github-provider-badge";
export * from "./iac-provider-badge";
export * from "./ks8-provider-badge";
export * from "./m365-provider-badge";
export * from "./oraclecloud-provider-badge";
import { IconSvgProps } from "@/types";
import { AWSProviderBadge } from "./aws-provider-badge";
import { AzureProviderBadge } from "./azure-provider-badge";
import { GCPProviderBadge } from "./gcp-provider-badge";
import { GitHubProviderBadge } from "./github-provider-badge";
import { IacProviderBadge } from "./iac-provider-badge";
import { KS8ProviderBadge } from "./ks8-provider-badge";
import { M365ProviderBadge } from "./m365-provider-badge";
import { OracleCloudProviderBadge } from "./oraclecloud-provider-badge";
export {
AWSProviderBadge,
AzureProviderBadge,
GCPProviderBadge,
GitHubProviderBadge,
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
OracleCloudProviderBadge,
};
// Map provider display names to their icon components
export const PROVIDER_ICONS: Record<string, React.FC<IconSvgProps>> = {
AWS: AWSProviderBadge,
Azure: AzureProviderBadge,
"Google Cloud": GCPProviderBadge,
Kubernetes: KS8ProviderBadge,
"Microsoft 365": M365ProviderBadge,
GitHub: GitHubProviderBadge,
"Infrastructure as Code": IacProviderBadge,
"Oracle Cloud Infrastructure": OracleCloudProviderBadge,
};