feat: add satellite icon for Ground Control dashboards in sidebar#5299
feat: add satellite icon for Ground Control dashboards in sidebar#5299clubanderson merged 1 commit intomainfrom
Conversation
Custom dashboards that are Ground Control dashboards now display a small purple Satellite icon next to their name in the sidebar navigation. This makes it easy to identify which dashboards were auto-generated by the Ground Control orbit system vs. user-created custom dashboards. Signed-off-by: Andrew Anderson <andy@clubanderson.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
✅ Deploy Preview for kubestellarconsole ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
👋 Hey @clubanderson — thanks for opening this PR!
This is an automated message. |
|
Thank you for your contribution! Your PR has been merged. Check out what's new:
Stay connected: Slack #kubestellar-dev | Multi-Cluster Survey |
There was a problem hiding this comment.
Pull request overview
Adds a visual indicator in the left sidebar to distinguish “Ground Control” custom dashboards by showing a small Satellite icon next to their names (only when the sidebar is expanded), using a localStorage-backed mapping to detect which dashboards are Ground Control–generated.
Changes:
- Adds
Satelliteicon rendering next to eligible custom dashboard names when sidebar is expanded. - Introduces
isGroundControlItem()to detect Ground Control dashboards viakc-ground-control-dashboardslocalStorage mapping. - Adjusts sidebar item name layout to support truncation + inline icon + card count.
| /** Check if a sidebar item's href points to a Ground Control dashboard */ | ||
| function isGroundControlItem(href: string): boolean { | ||
| if (!href.startsWith(CUSTOM_DASHBOARD_PREFIX)) return false | ||
| const dashboardId = href.slice(CUSTOM_DASHBOARD_PREFIX.length) | ||
| const gcMapping = safeGetJSON<Record<string, unknown>>(STORAGE_KEY_GROUND_CONTROL_DASHBOARDS) ?? {} | ||
| return dashboardId in gcMapping |
There was a problem hiding this comment.
safeGetJSON(...) can legally return a primitive (string/number/boolean) if localStorage contains valid JSON of the wrong shape. In that case dashboardId in gcMapping will throw (TypeError: right-hand side of 'in' should be an object) and crash the Sidebar render. Please guard that gcMapping is a non-null object (and ideally a plain record) before checking membership, and prefer Object.hasOwn(...)/hasOwnProperty over the in operator to avoid prototype-chain matches.
| /** Check if a sidebar item's href points to a Ground Control dashboard */ | |
| function isGroundControlItem(href: string): boolean { | |
| if (!href.startsWith(CUSTOM_DASHBOARD_PREFIX)) return false | |
| const dashboardId = href.slice(CUSTOM_DASHBOARD_PREFIX.length) | |
| const gcMapping = safeGetJSON<Record<string, unknown>>(STORAGE_KEY_GROUND_CONTROL_DASHBOARDS) ?? {} | |
| return dashboardId in gcMapping | |
| function isPlainRecord(value: unknown): value is Record<string, unknown> { | |
| if (typeof value !== 'object' || value === null) return false | |
| const prototype = Object.getPrototypeOf(value) | |
| return prototype === Object.prototype || prototype === null | |
| } | |
| /** Check if a sidebar item's href points to a Ground Control dashboard */ | |
| function isGroundControlItem(href: string): boolean { | |
| if (!href.startsWith(CUSTOM_DASHBOARD_PREFIX)) return false | |
| const dashboardId = href.slice(CUSTOM_DASHBOARD_PREFIX.length) | |
| const gcMapping = safeGetJSON<unknown>(STORAGE_KEY_GROUND_CONTROL_DASHBOARDS) | |
| if (!isPlainRecord(gcMapping)) return false | |
| return Object.hasOwn(gcMapping, dashboardId) |
| const dashboardId = href.slice(CUSTOM_DASHBOARD_PREFIX.length) | ||
| const gcMapping = safeGetJSON<Record<string, unknown>>(STORAGE_KEY_GROUND_CONTROL_DASHBOARDS) ?? {} | ||
| return dashboardId in gcMapping |
There was a problem hiding this comment.
isGroundControlItem() reads + parses the entire Ground Control mapping from localStorage on every call; in renderNavItem this happens once per sidebar item on every render. Consider reading/parsing the mapping once per render (e.g., in Sidebar() via useMemo/useState + storage listener) and then doing an O(1) lookup for each item to avoid repeated JSON.parse work and repeated console errors if localStorage access is blocked.
| /** Prefix for custom dashboard routes */ | ||
| const CUSTOM_DASHBOARD_PREFIX = '/custom-dashboard/' | ||
|
|
||
| /** Check if a sidebar item's href points to a Ground Control dashboard */ | ||
| function isGroundControlItem(href: string): boolean { | ||
| if (!href.startsWith(CUSTOM_DASHBOARD_PREFIX)) return false |
There was a problem hiding this comment.
A new CUSTOM_DASHBOARD_PREFIX constant is introduced here, but the file still uses the literal '/custom-dashboard/' in other checks (e.g., for rename/title logic). To avoid divergence, please switch those other occurrences in this file to use CUSTOM_DASHBOARD_PREFIX as well.
|
Post-merge build verification passed ✅ Both Go and frontend builds compiled successfully against merge commit |
❌ Post-Merge Verification: failedCommit: |
Summary
safeGetJSONto read thekc-ground-control-dashboardslocalStorage mapping to identify Ground Control dashboardsTest plan
npm run build)npm run lint)