diff --git a/src/components/NamespacesListView.test.tsx b/src/components/NamespacesListView.test.tsx
index d0bbc46..ea96211 100644
--- a/src/components/NamespacesListView.test.tsx
+++ b/src/components/NamespacesListView.test.tsx
@@ -117,7 +117,7 @@ describe('NamespacesListView', () => {
expect(screen.getByText('No Polaris audit results found.')).toBeInTheDocument();
});
- it('renders namespace rows with correct scores and links', () => {
+ it('renders namespace rows with correct scores and buttons', () => {
const data = makeAuditData([
makeResult({
Name: 'deploy-a',
@@ -157,12 +157,14 @@ describe('NamespacesListView', () => {
renderWithRouter();
- // Namespace links
- const alphaLink = screen.getByText('alpha');
- expect(alphaLink.closest('a')).toHaveAttribute('href', '/polaris/ns/alpha');
+ // Namespace buttons (now buttons instead of links for drawer)
+ const alphaButton = screen.getByText('alpha');
+ expect(alphaButton).toBeInTheDocument();
+ expect(alphaButton.tagName).toBe('BUTTON');
- const betaLink = screen.getByText('beta');
- expect(betaLink.closest('a')).toHaveAttribute('href', '/polaris/ns/beta');
+ const betaButton = screen.getByText('beta');
+ expect(betaButton).toBeInTheDocument();
+ expect(betaButton.tagName).toBe('BUTTON');
});
it('uses correct scoreStatus: >=80 success, >=50 warning, <50 error', () => {
diff --git a/src/components/NamespacesListView.tsx b/src/components/NamespacesListView.tsx
index 2dfbe3f..6f38dae 100644
--- a/src/components/NamespacesListView.tsx
+++ b/src/components/NamespacesListView.tsx
@@ -1,4 +1,3 @@
-import { Router } from '@kinvolk/headlamp-plugin/lib';
import {
Loader,
NameValueTable,
@@ -7,13 +6,15 @@ import {
SimpleTable,
StatusLabel,
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
-import React from 'react';
-import { Link } from 'react-router-dom';
+import React, { useState } from 'react';
import {
computeScore,
countResultsForItems,
filterResultsByNamespace,
getNamespaces,
+ POLARIS_DASHBOARD_PROXY,
+ Result,
+ ResultCounts,
} from '../api/polaris';
import { usePolarisDataContext } from '../api/PolarisDataContext';
@@ -32,8 +33,188 @@ interface NamespaceRow {
skipped: number;
}
+function resourceCounts(result: Result): ResultCounts {
+ return countResultsForItems([result]);
+}
+
+interface NamespaceDetailPanelProps {
+ namespace: string;
+ onClose: () => void;
+}
+
+function NamespaceDetailPanel({ namespace, onClose }: NamespaceDetailPanelProps) {
+ 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);
+ }
+
+ 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 { data, loading, error } = usePolarisDataContext();
+ const [selectedNamespace, setSelectedNamespace] = useState(null);
if (loading) {
return ;
@@ -92,13 +273,20 @@ export default function NamespacesListView() {
{
label: 'Namespace',
getter: (row: NamespaceRow) => (
- setSelectedNamespace(row.namespace)}
+ style={{
+ border: 'none',
+ background: 'transparent',
+ color: 'var(--link-color, #1976d2)',
+ cursor: 'pointer',
+ textDecoration: 'underline',
+ padding: 0,
+ font: 'inherit',
+ }}
>
{row.namespace}
-
+
),
},
{
@@ -130,6 +318,28 @@ export default function NamespacesListView() {
emptyMessage="No namespaces found in Polaris audit data."
/>
+
+ {selectedNamespace && (
+ <>
+ setSelectedNamespace(null)}
+ style={{
+ position: 'fixed',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ zIndex: 1100,
+ }}
+ aria-label="Close panel backdrop"
+ />
+
setSelectedNamespace(null)}
+ />
+ >
+ )}
>
);
}
diff --git a/src/index.tsx b/src/index.tsx
index 8c6a538..1b91add 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -6,7 +6,6 @@ import {
import React from 'react';
import { PolarisDataProvider } from './api/PolarisDataContext';
import DashboardView from './components/DashboardView';
-import NamespaceDetailView from './components/NamespaceDetailView';
import NamespacesListView from './components/NamespacesListView';
import PolarisSettings from './components/PolarisSettings';
@@ -62,16 +61,4 @@ registerRoute({
),
});
-registerRoute({
- path: '/polaris/ns/:namespace',
- sidebar: 'polaris-namespaces',
- name: 'polaris-namespace',
- exact: true,
- component: () => (
-
-
-
- ),
-});
-
registerPluginSettings('polaris', PolarisSettings, true);