From 8b319c0c8a8c5fa4248e1039c21c5de92e71c828 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Fri, 6 Feb 2026 21:49:17 -0500 Subject: [PATCH 1/5] 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 --- src/components/PolarisView.tsx | 166 ++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 77 deletions(-) diff --git a/src/components/PolarisView.tsx b/src/components/PolarisView.tsx index 7a7313f..16f0127 100644 --- a/src/components/PolarisView.tsx +++ b/src/components/PolarisView.tsx @@ -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 { AuditData, @@ -17,94 +24,81 @@ const INTERVAL_OPTIONS = [ { 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 }) { return ( -
- - -
- ); -} - -function StatCard(props: { label: string; value: number; color?: string }) { - return ( -
-
- {props.value} -
-
{props.label}
-
- ); -} - -function ScoreBadge(props: { score: number }) { - const color = props.score >= 80 ? '#4caf50' : props.score >= 50 ? '#ff9800' : '#f44336'; - return ( -
-
{props.score}%
-
Cluster Score
-
+ o.value === props.interval)?.label ?? ''} + /> ); } function OverviewSection(props: { data: AuditData; counts: ResultCounts }) { const score = computeScore(props.counts); + const status = scoreStatus(score); + return ( <> - + + {score}% + + ), + }, + ]} + /> -
- - - - -
+ + {props.counts.pass} + + ), + }, + { + name: 'Warning', + value: ( + + {props.counts.warning} + + ), + }, + { + name: 'Danger', + value: ( + + {props.counts.danger} + + ), + }, + ]} + />
-
- - - - -
+
); @@ -137,7 +131,18 @@ export default function PolarisView() { {error && ( -
{error}
+ + {error} + + ), + }, + ]} + />
)} @@ -145,7 +150,14 @@ export default function PolarisView() { {!data && !error && ( -
No Polaris audit results found.
+
)} From b10d09fd41e461a880d821fe90a0c6941e4f2e26 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Fri, 6 Feb 2026 21:55:13 -0500 Subject: [PATCH 2/5] feat: move refresh interval to plugin settings Register plugin settings via registerPluginSettings so the refresh interval is configurable from Headlamp's plugin config page instead of being embedded in the main view header. Co-Authored-By: Claude Opus 4.6 --- src/api/polaris.ts | 7 ++++++ src/components/PolarisSettings.tsx | 40 ++++++++++++++++++++++++++++++ src/components/PolarisView.tsx | 33 ++---------------------- src/index.tsx | 9 ++++++- 4 files changed, 57 insertions(+), 32 deletions(-) create mode 100644 src/components/PolarisSettings.tsx diff --git a/src/api/polaris.ts b/src/api/polaris.ts index 808b3d8..f40d5d5 100644 --- a/src/api/polaris.ts +++ b/src/api/polaris.ts @@ -93,6 +93,13 @@ export function countResults(data: AuditData): ResultCounts { // --- Settings --- +export const INTERVAL_OPTIONS = [ + { label: '1 minute', value: 60 }, + { label: '5 minutes', value: 300 }, + { label: '10 minutes', value: 600 }, + { label: '30 minutes', value: 1800 }, +]; + const STORAGE_KEY = 'polaris-plugin-refresh-interval'; const DEFAULT_INTERVAL_SECONDS = 300; // 5 minutes diff --git a/src/components/PolarisSettings.tsx b/src/components/PolarisSettings.tsx new file mode 100644 index 0000000..be80a22 --- /dev/null +++ b/src/components/PolarisSettings.tsx @@ -0,0 +1,40 @@ +import { NameValueTable, SectionBox } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; +import React from 'react'; +import { getRefreshInterval, INTERVAL_OPTIONS, setRefreshInterval } from '../api/polaris'; + +interface PluginSettingsProps { + data?: { [key: string]: string | number | boolean }; + onDataChange?: (data: { [key: string]: string | number | boolean }) => void; +} + +export default function PolarisSettings(props: PluginSettingsProps) { + const { data, onDataChange } = props; + const currentInterval = (data?.refreshInterval as number) ?? getRefreshInterval(); + + function handleChange(e: React.ChangeEvent) { + const seconds = Number(e.target.value); + setRefreshInterval(seconds); + onDataChange?.({ ...data, refreshInterval: seconds }); + } + + return ( + + + {INTERVAL_OPTIONS.map(opt => ( + + ))} + + ), + }, + ]} + /> + + ); +} diff --git a/src/components/PolarisView.tsx b/src/components/PolarisView.tsx index 16f0127..9e4efd2 100644 --- a/src/components/PolarisView.tsx +++ b/src/components/PolarisView.tsx @@ -1,5 +1,4 @@ import { - HeaderLabel, Loader, NameValueTable, SectionBox, @@ -13,32 +12,15 @@ import { countResults, getRefreshInterval, ResultCounts, - setRefreshInterval, usePolarisData, } from '../api/polaris'; -const INTERVAL_OPTIONS = [ - { label: '1 minute', value: 60 }, - { label: '5 minutes', value: 300 }, - { label: '10 minutes', value: 600 }, - { 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 }) { - return ( - o.value === props.interval)?.label ?? ''} - /> - ); -} - function OverviewSection(props: { data: AuditData; counts: ResultCounts }) { const score = computeScore(props.counts); const status = scoreStatus(score); @@ -105,13 +87,7 @@ function OverviewSection(props: { data: AuditData; counts: ResultCounts }) { } export default function PolarisView() { - const [interval, setInterval] = React.useState(getRefreshInterval); - - function handleIntervalChange(seconds: number) { - setInterval(seconds); - setRefreshInterval(seconds); - } - + const interval = getRefreshInterval(); const { data, loading, error } = usePolarisData(interval); if (loading) { @@ -122,12 +98,7 @@ export default function PolarisView() { return ( <> - , - ]} - /> + {error && ( diff --git a/src/index.tsx b/src/index.tsx index 3b30f3f..fdce70e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,10 @@ -import { registerRoute, registerSidebarEntry } from '@kinvolk/headlamp-plugin/lib'; +import { + registerPluginSettings, + registerRoute, + registerSidebarEntry, +} from '@kinvolk/headlamp-plugin/lib'; import React from 'react'; +import PolarisSettings from './components/PolarisSettings'; import PolarisView from './components/PolarisView'; registerSidebarEntry({ @@ -17,3 +22,5 @@ registerRoute({ exact: true, component: () => , }); + +registerPluginSettings('polaris-headlamp-plugin', PolarisSettings, true); From 672caec903e24baaadc14e41b72b6986ad3dd20e Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Fri, 6 Feb 2026 21:56:13 -0500 Subject: [PATCH 3/5] ci: add build step to CI workflow Co-Authored-By: Claude Opus 4.6 --- .gitea/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 2bd7dda..4f9b8cd 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -17,6 +17,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Build + run: npx @kinvolk/headlamp-plugin build + - name: Lint run: npx eslint --ext .ts,.tsx src/ From 1f110a2846b44364dd7b83cc25c360adc2603edc Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Fri, 6 Feb 2026 21:59:16 -0500 Subject: [PATCH 4/5] style: fix prettier formatting Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 4 ++-- polaris-headlamp-plugin-0.0.5.tar.gz | Bin 0 -> 2331 bytes src/components/PolarisView.tsx | 30 +++++---------------------- 3 files changed, 7 insertions(+), 27 deletions(-) create mode 100644 polaris-headlamp-plugin-0.0.5.tar.gz diff --git a/package-lock.json b/package-lock.json index 5a20917..75e4df1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "polaris-headlamp-plugin", - "version": "0.0.3", + "version": "0.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "polaris-headlamp-plugin", - "version": "0.0.3", + "version": "0.0.5", "devDependencies": { "@kinvolk/headlamp-plugin": "^0.13.0" } diff --git a/polaris-headlamp-plugin-0.0.5.tar.gz b/polaris-headlamp-plugin-0.0.5.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..2a4014cdde7b48c9cae3f181d853ed8decd8b92c GIT binary patch literal 2331 zcmV+$3FP)4iwFP!000006YW@gbKAHP&)@kJG}>m!Gel9gWV`e+KF4w9(n(!UoSAgS z79ab>Zx@RN^NbV0WM@K2!pSu6zk_?&Wqll*Y6GXfN}pt&^tT~4xJZ+=X7G*D5N3+T#54$ z5tA%SexaUfBGvVOV6-u9-?vA;CH_;wQvZv57C!)wgMNQY{CmCr;oe&O_j?}h^=ck0Bz{1$oeo5xT+JV?q%Sh~tLia)TS|4}RX%hlg;>t*}iUpI^&NqgSXa}}7tD*?w054{B{ zBz2?`3|u{qvs5Z4i(DGLrghk9I~5QhND8hHOC5Sb!-ahgDHs`g$CU-=&ZU@pi%}+A zqgFVe>u;qV|CY|BOFeI4NF<`y;^vt9DpQ2-?qcF!6>&_ZJmG#$q}<&N;T653f~k1~ z{@fFhvUH4qZf_s4BqSYE0WNdj-0to!m$iE{FmJ8%Xi?e?1FUVq#j1omBN+zE6@ohzzGm@^jsWtNy{Z=q@RV~eY$kZ6T%uCpvvP=l$cY}~n|CAt(;6(V)` z(#X#vRF!fQ`i`I@LFJ^wQbolb;Rs(w`$5n|=e=9ao<=;23BOXAAY4XW1f*j*E@o#5Sa**rCma&(e;9pA>|?JIa8Jv3ie4*QPA)pSmKH{ zuryf_q1H6iUa(F+Xp{FJk$Xo#ziHOnd8-a6Sd262qJ-TcypU?n^&Z4z&dEGPBTi=s z6VAq|t}8M`G1aq1cuevT9py8$#PNjjM9?(6E|s~cbOf|b)Re!2>DSftaT%>;(MLmb zDq4t^9<~Z$AJ@AjUa;gTWoEY2uijwT$0vHj2|4V)e)Z&D6w@IUE^LoPKz;U0InQgH zIrXO`cR|S7&+@YK!3$%78;ORW`{k6Em{+=%-p#UAdDE{Uo|B}^7}Pt+XU<+9fW?Z= z)TxHPK^+47nI&ohe0#xW@K@B1VB@=D|Dqw<6LgBOe*R3^c%niy%yaWrYsnIOkYz^q2a!Sw&xME?uBfa>)hNR``Nb7>*BWYtu@#@-k{;F>8{rm zn0~pDH`#A8;pr&Y)Onwdvfnfa7g<7Exau9!x6Wzb{F1&Y99i;|m?m$Kx~Ln|r%cvN z8#L%?iwv3<@sx@dmkq|s+We_H4qU^bZDZH@zIC({?gaXH_`zrJV_F5%N|3sh7}c#r zq}yeEP#Agbl@4j}0eB%vzD9aYRmLF7ZO$mj06B@grR^ zYt2F1P*P>Ze-7JswYSq|Ylck+*{vLD5OD$r zmCaAM0^TgZFb0Ml;MjTEt&7Zg3v&OJW9>>0@~dI8MgT7lNn?|h^7e(<%Ch5bYfkFmNS0H{SaZR}>2%WUe9CH59CiXtqf*}i89H{y zR}#bj%#kAQwmHKQn{DUrbb!1f13*nlsXvG9OfhJKI-PsL6y3lUntHd&T)s2Rz`L~3 zxqC}m5sQs=&nZ{yqeHolG3@o#XL$JGEL000Vd BnL_{o literal 0 HcmV?d00001 diff --git a/src/components/PolarisView.tsx b/src/components/PolarisView.tsx index 9e4efd2..c7b79ff 100644 --- a/src/components/PolarisView.tsx +++ b/src/components/PolarisView.tsx @@ -32,11 +32,7 @@ function OverviewSection(props: { data: AuditData; counts: ResultCounts }) { rows={[ { name: 'Cluster Score', - value: ( - - {score}% - - ), + value: {score}%, }, ]} /> @@ -47,27 +43,15 @@ function OverviewSection(props: { data: AuditData; counts: ResultCounts }) { { name: 'Total Checks', value: String(props.counts.total) }, { name: 'Pass', - value: ( - - {props.counts.pass} - - ), + value: {props.counts.pass}, }, { name: 'Warning', - value: ( - - {props.counts.warning} - - ), + value: {props.counts.warning}, }, { name: 'Danger', - value: ( - - {props.counts.danger} - - ), + value: {props.counts.danger}, }, ]} /> @@ -106,11 +90,7 @@ export default function PolarisView() { rows={[ { name: 'Status', - value: ( - - {error} - - ), + value: {error}, }, ]} /> From 40544429f43d6c702e3f09ba53039113854c03f9 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Fri, 6 Feb 2026 22:00:15 -0500 Subject: [PATCH 5/5] fix: remove tarball from repo and gitignore *.tar.gz Co-Authored-By: Claude Opus 4.6 --- .gitignore | 1 + polaris-headlamp-plugin-0.0.5.tar.gz | Bin 2331 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 polaris-headlamp-plugin-0.0.5.tar.gz diff --git a/.gitignore b/.gitignore index 70def5f..5df0edb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ dist/ .headlamp-plugin/ .mcp.json +*.tar.gz diff --git a/polaris-headlamp-plugin-0.0.5.tar.gz b/polaris-headlamp-plugin-0.0.5.tar.gz deleted file mode 100644 index 2a4014cdde7b48c9cae3f181d853ed8decd8b92c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2331 zcmV+$3FP)4iwFP!000006YW@gbKAHP&)@kJG}>m!Gel9gWV`e+KF4w9(n(!UoSAgS z79ab>Zx@RN^NbV0WM@K2!pSu6zk_?&Wqll*Y6GXfN}pt&^tT~4xJZ+=X7G*D5N3+T#54$ z5tA%SexaUfBGvVOV6-u9-?vA;CH_;wQvZv57C!)wgMNQY{CmCr;oe&O_j?}h^=ck0Bz{1$oeo5xT+JV?q%Sh~tLia)TS|4}RX%hlg;>t*}iUpI^&NqgSXa}}7tD*?w054{B{ zBz2?`3|u{qvs5Z4i(DGLrghk9I~5QhND8hHOC5Sb!-ahgDHs`g$CU-=&ZU@pi%}+A zqgFVe>u;qV|CY|BOFeI4NF<`y;^vt9DpQ2-?qcF!6>&_ZJmG#$q}<&N;T653f~k1~ z{@fFhvUH4qZf_s4BqSYE0WNdj-0to!m$iE{FmJ8%Xi?e?1FUVq#j1omBN+zE6@ohzzGm@^jsWtNy{Z=q@RV~eY$kZ6T%uCpvvP=l$cY}~n|CAt(;6(V)` z(#X#vRF!fQ`i`I@LFJ^wQbolb;Rs(w`$5n|=e=9ao<=;23BOXAAY4XW1f*j*E@o#5Sa**rCma&(e;9pA>|?JIa8Jv3ie4*QPA)pSmKH{ zuryf_q1H6iUa(F+Xp{FJk$Xo#ziHOnd8-a6Sd262qJ-TcypU?n^&Z4z&dEGPBTi=s z6VAq|t}8M`G1aq1cuevT9py8$#PNjjM9?(6E|s~cbOf|b)Re!2>DSftaT%>;(MLmb zDq4t^9<~Z$AJ@AjUa;gTWoEY2uijwT$0vHj2|4V)e)Z&D6w@IUE^LoPKz;U0InQgH zIrXO`cR|S7&+@YK!3$%78;ORW`{k6Em{+=%-p#UAdDE{Uo|B}^7}Pt+XU<+9fW?Z= z)TxHPK^+47nI&ohe0#xW@K@B1VB@=D|Dqw<6LgBOe*R3^c%niy%yaWrYsnIOkYz^q2a!Sw&xME?uBfa>)hNR``Nb7>*BWYtu@#@-k{;F>8{rm zn0~pDH`#A8;pr&Y)Onwdvfnfa7g<7Exau9!x6Wzb{F1&Y99i;|m?m$Kx~Ln|r%cvN z8#L%?iwv3<@sx@dmkq|s+We_H4qU^bZDZH@zIC({?gaXH_`zrJV_F5%N|3sh7}c#r zq}yeEP#Agbl@4j}0eB%vzD9aYRmLF7ZO$mj06B@grR^ zYt2F1P*P>Ze-7JswYSq|Ylck+*{vLD5OD$r zmCaAM0^TgZFb0Ml;MjTEt&7Zg3v&OJW9>>0@~dI8MgT7lNn?|h^7e(<%Ch5bYfkFmNS0H{SaZR}>2%WUe9CH59CiXtqf*}i89H{y zR}#bj%#kAQwmHKQn{DUrbb!1f13*nlsXvG9OfhJKI-PsL6y3lUntHd&T)s2Rz`L~3 zxqC}m5sQs=&nZ{yqeHolG3@o#XL$JGEL000Vd BnL_{o