import { Loader, NameValueTable, SectionBox, SectionHeader, SimpleTable, StatusLabel, } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import React, { useEffect, useState } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; import { computeScore, countResultsForItems, filterResultsByNamespace, getNamespaces, getPolarisProxyUrl, Result, ResultCounts, } from '../api/polaris'; import { usePolarisDataContext } from '../api/PolarisDataContext'; function scoreStatus(score: number): 'success' | 'warning' | 'error' { if (score >= 80) return 'success'; if (score >= 50) return 'warning'; return 'error'; } interface NamespaceRow { namespace: string; score: number; pass: number; warning: number; danger: number; skipped: number; } function resourceCounts(result: Result): ResultCounts { return countResultsForItems([result]); } interface NamespaceDetailPanelProps { namespace: string; onClose: () => void; } function NamespaceDetailPanel({ namespace, onClose }: NamespaceDetailPanelProps) { const [isMaximized, setIsMaximized] = React.useState(false); const { data, loading, error } = usePolarisDataContext(); if (loading) { return (
); } if (error) { return (
{error}, }, ]} />
); } if (!data) { return (
); } const results = filterResultsByNamespace(data, namespace); const counts = countResultsForItems(results); const score = computeScore(counts); const status = scoreStatus(score); const countsPerResource = new Map(); for (const r of results) { countsPerResource.set(`${r.Namespace}/${r.Kind}/${r.Name}`, resourceCounts(r)); } function getResourceCounts(row: Result): ResultCounts { return countsPerResource.get(`${row.Namespace}/${row.Kind}/${row.Name}`) ?? resourceCounts(row); } // Generate a unique class name for this drawer to avoid conflicts const drawerClass = `polaris-namespace-drawer-${namespace}`; return ( <>

Polaris — {namespace}

View in Polaris Dashboard ), }, ]} /> {score}%, }, { name: 'Total Checks', value: String(counts.total) }, { name: 'Pass', value: {counts.pass}, }, { name: 'Warning', value: {counts.warning}, }, { name: 'Danger', value: {counts.danger}, }, { name: 'Skipped', value: ( {counts.skipped} ), }, ]} /> row.Name }, { label: 'Kind', getter: (row: Result) => row.Kind }, { label: 'Pass', getter: (row: Result) => ( {getResourceCounts(row).pass} ), }, { label: 'Warning', getter: (row: Result) => ( {getResourceCounts(row).warning} ), }, { label: 'Danger', getter: (row: Result) => ( {getResourceCounts(row).danger} ), }, ]} data={results} emptyMessage={`No resources found in namespace "${namespace}".`} />
); } export default function NamespacesListView() { const location = useLocation(); const history = useHistory(); const { data, loading, error } = usePolarisDataContext(); // Initialize from URL hash const [selectedNamespace, setSelectedNamespace] = useState( location.hash.slice(1) || null ); // Sync drawer state when URL hash changes (browser back/forward) useEffect(() => { const hashNs = location.hash.slice(1); setSelectedNamespace(hashNs || null); }, [location.hash]); const openNamespace = (ns: string) => { setSelectedNamespace(ns); history.push(`${location.pathname}#${ns}`); }; const closeNamespace = () => { setSelectedNamespace(null); history.push(location.pathname); }; // Handle keyboard navigation (Escape key closes drawer) useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && selectedNamespace) { closeNamespace(); } }; if (selectedNamespace) { window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedNamespace]); if (loading) { return ; } if (error) { return ( <> {error}, }, ]} /> ); } if (!data) { return ( <> ); } const namespaces = getNamespaces(data); const rows: NamespaceRow[] = namespaces.map(ns => { const results = filterResultsByNamespace(data, ns); const counts = countResultsForItems(results); const score = computeScore(counts); return { namespace: ns, score, pass: counts.pass, warning: counts.warning, danger: counts.danger, skipped: counts.skipped, }; }); return ( <> ( ), }, { label: 'Score', getter: (row: NamespaceRow) => ( {row.score}% ), }, { label: 'Pass', getter: (row: NamespaceRow) => {row.pass}, }, { label: 'Warning', getter: (row: NamespaceRow) => ( {row.warning} ), }, { label: 'Danger', getter: (row: NamespaceRow) => {row.danger}, }, { label: 'Skipped', getter: (row: NamespaceRow) => String(row.skipped), }, ]} data={rows} emptyMessage="No namespaces found in Polaris audit data." /> {selectedNamespace && ( <>
)} ); }