Compare commits

...

4 Commits

Author SHA1 Message Date
github-actions[bot] 17495d4883 ci: update artifact hub metadata for v0.3.2 2026-02-12 03:19:50 +00:00
Chris Farhood 01eed82efc chore: bump version to 0.3.2 2026-02-11 22:19:07 -05:00
Chris Farhood 5dab426fe8 Merge pull request #3 from cpfarhood/fix/remove-mui-dependencies
fix: remove all MUI dependencies causing plugin load failure
2026-02-11 22:18:43 -05:00
Chris Farhood 5eaa6603f1 fix: remove all MUI dependencies causing plugin load failure
Replace all @mui/material and @mui/icons-material imports with standard
HTML elements and inline styles. This fixes the browser error:
"TypeError: undefined is not an object (evaluating 'q.createSvgIcon')"

The Headlamp plugin environment doesn't provide the full MUI library,
so plugins must use only Headlamp CommonComponents or standard HTML.

Changes:
- AppBarScoreBadge: Replace Chip and ShieldIcon with button and emoji
- DashboardView: Replace Button and RefreshIcon with button and emoji
- ExemptionManager: Replace all MUI form components with HTML equivalents
- PolarisSettings: Replace Button with HTML button

All tests passing (50/50), TypeScript compilation clean, build successful.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-11 22:18:20 -05:00
6 changed files with 137 additions and 57 deletions
+3 -3
View File
@@ -1,4 +1,4 @@
version: 0.3.1
version: 0.3.2
name: headlamp-polaris-plugin
displayName: Polaris
createdAt: "2026-02-05T19:00:00Z"
@@ -28,7 +28,7 @@ maintainers:
- name: cpfarhood
email: "chris@farhood.org"
annotations:
headlamp/plugin/archive-url: "https://github.com/cpfarhood/headlamp-polaris-plugin/releases/download/v0.3.1/headlamp-polaris-plugin-0.3.1.tar.gz"
headlamp/plugin/archive-url: "https://github.com/cpfarhood/headlamp-polaris-plugin/releases/download/v0.3.2/headlamp-polaris-plugin-0.3.2.tar.gz"
headlamp/plugin/version-compat: ">=0.26"
headlamp/plugin/archive-checksum: sha256:36a79050e920ee26c9371763182fc8bc2a4ddcac5086e108a7828f0467b111c8
headlamp/plugin/archive-checksum: sha256:66d51513a6bf73b6f67af10d2dc55dabea7340d551faf3d59a9cd34b232ca868
headlamp/plugin/distro-compat: in-cluster
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "headlamp-polaris-plugin",
"version": "0.3.1",
"version": "0.3.2",
"description": "Headlamp plugin for Fairwinds Polaris audit results",
"scripts": {
"start": "headlamp-plugin start",
+24 -13
View File
@@ -1,5 +1,3 @@
import { Chip } from '@mui/material';
import { Shield as ShieldIcon } from '@mui/icons-material';
import React from 'react';
import { useHistory } from 'react-router-dom';
import { usePolarisDataContext } from '../api/PolarisDataContext';
@@ -21,10 +19,10 @@ export default function AppBarScoreBadge() {
const score = computeScore(counts);
// Color based on score
const getColor = (score: number): 'success' | 'warning' | 'error' => {
if (score >= 80) return 'success';
if (score >= 50) return 'warning';
return 'error';
const getColor = (score: number): string => {
if (score >= 80) return '#4caf50'; // green
if (score >= 50) return '#ff9800'; // orange
return '#f44336'; // red
};
const handleClick = () => {
@@ -32,13 +30,26 @@ export default function AppBarScoreBadge() {
};
return (
<Chip
icon={<ShieldIcon />}
label={`Polaris: ${score}%`}
color={getColor(score)}
size="small"
<button
onClick={handleClick}
style={{ cursor: 'pointer', marginRight: '8px' }}
/>
style={{
cursor: 'pointer',
marginRight: '8px',
padding: '4px 12px',
borderRadius: '16px',
border: 'none',
backgroundColor: getColor(score),
color: 'white',
fontSize: '13px',
fontWeight: 500,
display: 'inline-flex',
alignItems: 'center',
gap: '4px',
}}
aria-label={`Polaris cluster score: ${score}%`}
>
<span>🛡</span>
<span>Polaris: {score}%</span>
</button>
);
}
+19 -5
View File
@@ -8,8 +8,6 @@ import {
SimpleTable,
StatusLabel,
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import { Button } from '@mui/material';
import { Refresh as RefreshIcon } from '@mui/icons-material';
import React from 'react';
import { AuditData, computeScore, countResults, ResultCounts } from '../api/polaris';
import { usePolarisDataContext } from '../api/PolarisDataContext';
@@ -114,9 +112,25 @@ export default function DashboardView() {
<span style={{ fontSize: '14px', color: 'var(--mui-palette-text-secondary, #666)' }}>
Last updated: {formatAuditTime(data.AuditTime)}
</span>
<Button variant="outlined" startIcon={<RefreshIcon />} onClick={refresh} size="small">
Refresh
</Button>
<button
onClick={refresh}
style={{
padding: '6px 16px',
backgroundColor: 'transparent',
color: '#1976d2',
border: '1px solid #1976d2',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '13px',
fontWeight: 500,
display: 'inline-flex',
alignItems: 'center',
gap: '4px',
}}
>
<span>🔄</span>
<span>Refresh</span>
</button>
</div>
)}
</div>
+78 -30
View File
@@ -1,6 +1,5 @@
import { NameValueTable, SectionBox, Dialog } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import { ApiProxy } from '@kinvolk/headlamp-plugin/lib';
import { Button, Checkbox, FormControlLabel, FormGroup } from '@mui/material';
import React from 'react';
import { Result } from '../api/polaris';
import { getCheckName } from '../api/checkMapping';
@@ -147,16 +146,23 @@ export default function ExemptionManager({
rows={currentExemptions.map(exemption => ({
name: exemption,
value: (
<Button
size="small"
color="error"
<button
style={{
padding: '4px 12px',
backgroundColor: '#f44336',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '12px',
}}
onClick={() => {
// Remove exemption logic
alert('Remove exemption: ' + exemption);
}}
>
Remove
</Button>
</button>
),
}))}
/>
@@ -164,58 +170,100 @@ export default function ExemptionManager({
<p>No exemptions configured</p>
)}
<Button
variant="outlined"
<button
onClick={() => setDialogOpen(true)}
disabled={failingChecks.length === 0}
style={{ marginTop: '8px' }}
style={{
marginTop: '8px',
padding: '6px 16px',
backgroundColor: failingChecks.length === 0 ? '#ccc' : 'transparent',
color: failingChecks.length === 0 ? '#999' : '#1976d2',
border: '1px solid',
borderColor: failingChecks.length === 0 ? '#ccc' : '#1976d2',
borderRadius: '4px',
cursor: failingChecks.length === 0 ? 'not-allowed' : 'pointer',
fontSize: '13px',
}}
>
Add Exemption
</Button>
</button>
</SectionBox>
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} title="Add Exemptions">
<div style={{ padding: '16px', minWidth: '400px' }}>
<FormControlLabel
control={
<Checkbox checked={exemptAll} onChange={e => setExemptAll(e.target.checked)} />
}
label="Exempt from all checks"
/>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
<input
type="checkbox"
checked={exemptAll}
onChange={e => setExemptAll(e.target.checked)}
/>
<span>Exempt from all checks</span>
</label>
{!exemptAll && (
<>
<div style={{ marginTop: '16px', marginBottom: '8px', fontWeight: 600 }}>
Select checks to exempt:
</div>
<FormGroup>
<div>
{failingChecks.map(check => (
<FormControlLabel
<label
key={check.checkId}
control={
<Checkbox
checked={selectedChecks.has(check.checkId)}
onChange={() => handleCheckToggle(check.checkId)}
/>
}
label={check.checkName}
/>
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
marginBottom: '8px',
cursor: 'pointer',
}}
>
<input
type="checkbox"
checked={selectedChecks.has(check.checkId)}
onChange={() => handleCheckToggle(check.checkId)}
/>
<span>{check.checkName}</span>
</label>
))}
</FormGroup>
</div>
</>
)}
<div
style={{ marginTop: '16px', display: 'flex', gap: '8px', justifyContent: 'flex-end' }}
>
<Button onClick={() => setDialogOpen(false)}>Cancel</Button>
<Button
variant="contained"
<button
onClick={() => setDialogOpen(false)}
style={{
padding: '6px 16px',
backgroundColor: 'transparent',
color: '#1976d2',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '13px',
}}
>
Cancel
</button>
<button
onClick={applyExemptions}
disabled={applying || (!exemptAll && selectedChecks.size === 0)}
style={{
padding: '6px 16px',
backgroundColor:
applying || (!exemptAll && selectedChecks.size === 0) ? '#ccc' : '#1976d2',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor:
applying || (!exemptAll && selectedChecks.size === 0) ? 'not-allowed' : 'pointer',
fontSize: '13px',
fontWeight: 500,
}}
>
{applying ? 'Applying...' : 'Apply'}
</Button>
</button>
</div>
</div>
</Dialog>
+12 -5
View File
@@ -4,7 +4,6 @@ import {
StatusLabel,
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import { ApiProxy } from '@kinvolk/headlamp-plugin/lib';
import { Button } from '@mui/material';
import React from 'react';
import {
getDashboardUrl,
@@ -124,14 +123,22 @@ export default function PolarisSettings(props: PluginSettingsProps) {
name: 'Connection Test',
value: (
<div>
<Button
variant="contained"
<button
onClick={testConnection}
disabled={testing}
size="small"
style={{
padding: '6px 16px',
backgroundColor: testing ? '#ccc' : '#1976d2',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: testing ? 'not-allowed' : 'pointer',
fontSize: '13px',
fontWeight: 500,
}}
>
{testing ? 'Testing...' : 'Test Connection'}
</Button>
</button>
{testResult && (
<div style={{ marginTop: '8px' }}>
<StatusLabel status={testResult.success ? 'success' : 'error'}>