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>
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'}>
|
||||
|
||||
Reference in New Issue
Block a user