fix: nodes stat drill-down crash and error page UX (#5902)#5909
fix: nodes stat drill-down crash and error page UX (#5902)#5909clubanderson merged 1 commit intomainfrom
Conversation
Clicking the "nodes" stat block on the My Clusters dashboard navigated
to /compute and immediately hit the AppErrorBoundary with
"can't access property 'replace', cardType is undefined".
Root cause: ensureCardInDashboard() in Compute.tsx (and Security.tsx)
injected cards into saved layouts using the legacy `cardType` field,
but DashboardCard uses the snake_case `card_type` field. When the
renderer called formatCardTitle(card.card_type) the value was undefined
and crashed the whole dashboard.
Fixes:
- Compute.tsx / Security.tsx: pass card_type (snake_case) to
ensureCardInDashboard so injected cards match the DashboardCard shape.
- migrateStorageKey.ts: normalize stored layouts on read — any entries
written by older builds with the legacy cardType field are repaired
to card_type automatically, so users who already hit the crash
recover without clearing localStorage. The helper still accepts
cardType on the injected-card argument for back-compat.
- formatCardTitle.ts: defensive guard against null/undefined input so
a single bad card can never crash the whole dashboard again.
- AppErrorBoundary.tsx:
- "Try again" now bumps a resetKey that the children subtree uses as
a React key, forcing a full remount instead of re-rendering with
the same broken props. Previously the button appeared unresponsive
because the same error re-threw immediately.
- Added a distinct "Go to dashboard" button so "Try again" and
"Reload page" no longer look like synonyms.
- Error text uses break-words + whitespace-pre-wrap instead of
break-all, so long tokens like "undefined" stop wrapping mid-word
(the reported "d" on its own line).
- PageErrorBoundary.tsx: same break-words fix for consistency.
- Tests: new regression tests for the undefined guard in
formatCardTitle and for the legacy cardType normalization in
ensureCardInDashboard.
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 |
|
👋 Hey @clubanderson — thanks for opening this PR!
This is an automated message. |
✅ Deploy Preview for kubestellarconsole ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
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
Fixes a runtime crash when navigating to the Compute dashboard from the “nodes” stat block due to legacy dashboard layout entries using cardType instead of the canonical card_type, and improves the recovery UX of the app/page error boundaries.
Changes:
- Hardened
formatCardTitle()againstnull/undefinedcard types so a single corrupt card can’t crash the dashboard. - Updated
ensureCardInDashboard()to normalize stored layouts (cardType→card_type) and adjusted Compute/Security injected-card definitions to usecard_type. - Improved error boundary UX: “Try again” now forces a subtree remount, added “Go to dashboard”, and fixed long-token wrapping.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| web/src/lib/formatCardTitle.ts | Adds a safe fallback for missing/invalid card types to prevent crashes. |
| web/src/lib/dashboards/migrateStorageKey.ts | Normalizes legacy stored layouts and injected cards to the canonical card_type field. |
| web/src/lib/dashboards/tests/migrateStorageKey.test.ts | Adds regression coverage for legacy cardType normalization and back-compat injected cards. |
| web/src/lib/tests/formatCardTitle.test.ts | Adds regression tests for null/undefined guard behavior. |
| web/src/components/security/Security.tsx | Updates injected card shape to use card_type. |
| web/src/components/compute/Compute.tsx | Updates injected card shape to use card_type. |
| web/src/components/PageErrorBoundary.tsx | Fixes error-message wrapping to avoid mid-word breaks. |
| web/src/components/AppErrorBoundary.tsx | Improves recovery UX (remount on retry, adds “Go to dashboard”, fixes wrapping). |
❌ Post-Merge Verification: failedCommit: |
|
Post-merge build verification passed ✅ Both Go and frontend builds compiled successfully against merge commit |
Summary
Fixes #5902. Clicking the nodes stat block on the My Clusters dashboard
navigated to
/computeand immediately hit the AppErrorBoundary withcan't access property "replace", cardType is undefined, and the recoveryUI itself had three UX bugs.
Root cause
ensureCardInDashboard()inCompute.tsx(andSecurity.tsx) injected cardsinto saved layouts using the legacy
cardTypefield, butDashboardCardusesthe snake_case
card_typefield. When the renderer calledformatCardTitle(card.card_type)the value wasundefinedand crashed thewhole dashboard.
Fixes
card_type(snake_case) toensureCardInDashboardso injected cards match theDashboardCardshape.written by older builds with the legacy
cardTypefield are repaired tocard_typeautomatically so users who already hit the crash recoverwithout clearing localStorage. The helper still accepts
cardTypeon theinjected-card argument for back-compat.
null/undefinedinputso a single bad card can never crash the whole dashboard again.
resetKeyused as a Reactkeyon thechildren subtree, forcing a full remount instead of re-rendering with
the same broken props. Previously the button appeared unresponsive
because the same error re-threw immediately.
"Reload page" no longer look like synonyms.
break-words+whitespace-pre-wrapinstead ofbreak-all, so long tokens likeundefinedstop wrapping mid-word(the reported "d" on its own line).
break-wordsfix for consistency.formatCardTitleand for legacy-cardTypenormalization inensureCardInDashboard.Test plan
npm run buildpassesnpx vitest run src/lib/__tests__/formatCardTitle.test.ts src/lib/dashboards/__tests__/migrateStorageKey.test.ts- 33/33 passcardTypeload cleanly after the fix