fix: use native Headlamp components for consistent styling
Replace inline-styled divs and native HTML elements with Headlamp's built-in NameValueTable, StatusLabel, and HeaderLabel components so the plugin matches the look and feel of native pages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,11 @@
|
|||||||
import { Loader, SectionBox, SectionHeader } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
import {
|
||||||
|
HeaderLabel,
|
||||||
|
Loader,
|
||||||
|
NameValueTable,
|
||||||
|
SectionBox,
|
||||||
|
SectionHeader,
|
||||||
|
StatusLabel,
|
||||||
|
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
AuditData,
|
AuditData,
|
||||||
@@ -17,94 +24,81 @@ const INTERVAL_OPTIONS = [
|
|||||||
{ label: '30 minutes', value: 1800 },
|
{ label: '30 minutes', value: 1800 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function scoreStatus(score: number): 'success' | 'warning' | 'error' {
|
||||||
|
if (score >= 80) return 'success';
|
||||||
|
if (score >= 50) return 'warning';
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
|
||||||
function RefreshSettings(props: { interval: number; onChange: (seconds: number) => void }) {
|
function RefreshSettings(props: { interval: number; onChange: (seconds: number) => void }) {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
<HeaderLabel
|
||||||
<label htmlFor="polaris-refresh-interval">Refresh interval:</label>
|
label="Refresh interval"
|
||||||
<select
|
value={INTERVAL_OPTIONS.find(o => o.value === props.interval)?.label ?? ''}
|
||||||
id="polaris-refresh-interval"
|
/>
|
||||||
value={props.interval}
|
|
||||||
onChange={e => props.onChange(Number(e.target.value))}
|
|
||||||
>
|
|
||||||
{INTERVAL_OPTIONS.map(opt => (
|
|
||||||
<option key={opt.value} value={opt.value}>
|
|
||||||
{opt.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function StatCard(props: { label: string; value: number; color?: string }) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
padding: '16px 24px',
|
|
||||||
textAlign: 'center',
|
|
||||||
minWidth: '120px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: '2rem',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: props.color,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.value}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: '0.875rem', opacity: 0.8 }}>{props.label}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ScoreBadge(props: { score: number }) {
|
|
||||||
const color = props.score >= 80 ? '#4caf50' : props.score >= 50 ? '#ff9800' : '#f44336';
|
|
||||||
return (
|
|
||||||
<div style={{ textAlign: 'center', marginBottom: '16px' }}>
|
|
||||||
<div style={{ fontSize: '3rem', fontWeight: 'bold', color }}>{props.score}%</div>
|
|
||||||
<div style={{ fontSize: '0.875rem', opacity: 0.8 }}>Cluster Score</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function OverviewSection(props: { data: AuditData; counts: ResultCounts }) {
|
function OverviewSection(props: { data: AuditData; counts: ResultCounts }) {
|
||||||
const score = computeScore(props.counts);
|
const score = computeScore(props.counts);
|
||||||
|
const status = scoreStatus(score);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SectionBox title="Score">
|
<SectionBox title="Score">
|
||||||
<ScoreBadge score={score} />
|
<NameValueTable
|
||||||
|
rows={[
|
||||||
|
{
|
||||||
|
name: 'Cluster Score',
|
||||||
|
value: (
|
||||||
|
<StatusLabel status={status}>
|
||||||
|
{score}%
|
||||||
|
</StatusLabel>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</SectionBox>
|
</SectionBox>
|
||||||
<SectionBox title="Check Summary">
|
<SectionBox title="Check Summary">
|
||||||
<div
|
<NameValueTable
|
||||||
style={{
|
rows={[
|
||||||
display: 'flex',
|
{ name: 'Total Checks', value: String(props.counts.total) },
|
||||||
justifyContent: 'center',
|
{
|
||||||
flexWrap: 'wrap',
|
name: 'Pass',
|
||||||
gap: '16px',
|
value: (
|
||||||
}}
|
<StatusLabel status="success">
|
||||||
>
|
{props.counts.pass}
|
||||||
<StatCard label="Total" value={props.counts.total} />
|
</StatusLabel>
|
||||||
<StatCard label="Pass" value={props.counts.pass} color="#4caf50" />
|
),
|
||||||
<StatCard label="Warning" value={props.counts.warning} color="#ff9800" />
|
},
|
||||||
<StatCard label="Danger" value={props.counts.danger} color="#f44336" />
|
{
|
||||||
</div>
|
name: 'Warning',
|
||||||
|
value: (
|
||||||
|
<StatusLabel status="warning">
|
||||||
|
{props.counts.warning}
|
||||||
|
</StatusLabel>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Danger',
|
||||||
|
value: (
|
||||||
|
<StatusLabel status="error">
|
||||||
|
{props.counts.danger}
|
||||||
|
</StatusLabel>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</SectionBox>
|
</SectionBox>
|
||||||
<SectionBox title="Cluster Info">
|
<SectionBox title="Cluster Info">
|
||||||
<div
|
<NameValueTable
|
||||||
style={{
|
rows={[
|
||||||
display: 'flex',
|
{ name: 'Nodes', value: String(props.data.ClusterInfo.Nodes) },
|
||||||
justifyContent: 'center',
|
{ name: 'Pods', value: String(props.data.ClusterInfo.Pods) },
|
||||||
flexWrap: 'wrap',
|
{ name: 'Namespaces', value: String(props.data.ClusterInfo.Namespaces) },
|
||||||
gap: '16px',
|
{ name: 'Controllers', value: String(props.data.ClusterInfo.Controllers) },
|
||||||
}}
|
]}
|
||||||
>
|
/>
|
||||||
<StatCard label="Nodes" value={props.data.ClusterInfo.Nodes} />
|
|
||||||
<StatCard label="Pods" value={props.data.ClusterInfo.Pods} />
|
|
||||||
<StatCard label="Namespaces" value={props.data.ClusterInfo.Namespaces} />
|
|
||||||
<StatCard label="Controllers" value={props.data.ClusterInfo.Controllers} />
|
|
||||||
</div>
|
|
||||||
</SectionBox>
|
</SectionBox>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -137,7 +131,18 @@ export default function PolarisView() {
|
|||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<SectionBox title="Error">
|
<SectionBox title="Error">
|
||||||
<div style={{ padding: '16px', color: '#f44336' }}>{error}</div>
|
<NameValueTable
|
||||||
|
rows={[
|
||||||
|
{
|
||||||
|
name: 'Status',
|
||||||
|
value: (
|
||||||
|
<StatusLabel status="error">
|
||||||
|
{error}
|
||||||
|
</StatusLabel>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</SectionBox>
|
</SectionBox>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -145,7 +150,14 @@ export default function PolarisView() {
|
|||||||
|
|
||||||
{!data && !error && (
|
{!data && !error && (
|
||||||
<SectionBox title="No Data">
|
<SectionBox title="No Data">
|
||||||
<div style={{ padding: '16px' }}>No Polaris audit results found.</div>
|
<NameValueTable
|
||||||
|
rows={[
|
||||||
|
{
|
||||||
|
name: 'Status',
|
||||||
|
value: 'No Polaris audit results found.',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</SectionBox>
|
</SectionBox>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user