✨ Make AI Missions panel a flush-right drawer with Escape key#185
✨ Make AI Missions panel a flush-right drawer with Escape key#185clubanderson merged 1 commit intomainfrom
Conversation
- Panel now extends edge-to-edge on the right (right-0, top-16, bottom-0) instead of floating with margins like a card - Slides out to the right with 300ms ease-in-out transition - Escape key closes the sidebar (exits fullscreen first if active) - Left border only, no rounded corners on right side Co-Authored-By: Claude Opus 4.5 <[email protected]> Signed-off-by: Andrew Anderson <[email protected]>
|
[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. |
|
Welcome to KubeStellar! 🚀 Thank you for submitting this Pull Request. Before your PR can be merged, please ensure: ✅ DCO Sign-off - All commits must be signed off with ✅ PR Title - Must start with an emoji: ✨ (feature), 🐛 (bug fix), 📖 (docs), 🌱 (infra/tests), Getting Started with KubeStellar: Contributor Resources:
🌟 Help KubeStellar Grow - We Need Adopters! Our roadmap is driven entirely by adopter feedback. Whether you're using KubeStellar yourself or know someone who could benefit from multi-cluster Kubernetes: 📋 Take our Multi-Cluster Survey - Share your use cases and help shape our direction! A maintainer will review your PR soon. Feel free to ask questions in the comments or on Slack! |
|
🎉 Thank you for your contribution! Your PR has been successfully merged. 🌟 Help KubeStellar Grow - We Need Adopters! Our roadmap is driven entirely by adopter feedback - nothing else. Whether you're using KubeStellar yourself or know organizations that could benefit from multi-cluster Kubernetes, we need your help: 📋 Take our Multi-Cluster Survey - Share your use cases and help shape our direction! 🗣️ Spread the word - Tell colleagues, write blog posts, present at meetups 💬 Share feedback on Slack #kubestellar-dev Every adopter story helps us prioritize what matters most. Thank you for being part of the KubeStellar community! |
❌ Playwright Tests Failed📊 View Full ReportDownload the To view the report locally: # Download and extract playwright-report.zip
npx playwright show-report path/to/playwright-report |
- Panel now extends edge-to-edge on the right (right-0, top-16, bottom-0) instead of floating with margins like a card - Slides out to the right with 300ms ease-in-out transition - Escape key closes the sidebar (exits fullscreen first if active) - Left border only, no rounded corners on right side Signed-off-by: Andrew Anderson <[email protected]> Co-authored-by: Claude Opus 4.5 <[email protected]>
Two cascading issues caused infinite re-renders in cards with pagination (especially OperatorSubscriptions) when running in demo mode: 1. useCache returned unstable demoData references — getDemoX() functions create new arrays on every render, and useCache passed them through directly. This caused all downstream hooks to recalculate every render. Fix: stabilize demoData/initialData via useRef so identity is preserved. 2. useStablePageHeight had a useLayoutEffect with no dependency array that called setState on every render. Combined with useReportCardDataState (another useLayoutEffect that sets parent state), this created an infinite layout-effect cascade. Fix: add updatesInBatchRef convergence guard limiting one height setState per React commit batch. Also adds a nightly Playwright test (react-render-errors.spec.ts) that navigates all dashboard routes and fails on React-critical console errors like "Maximum update depth exceeded", ensuring this class of bug is caught before it reaches production. Signed-off-by: Andrew Anderson <[email protected]>
…2197) Two cascading issues caused infinite re-renders in cards with pagination (especially OperatorSubscriptions) when running in demo mode: 1. useCache returned unstable demoData references — getDemoX() functions create new arrays on every render, and useCache passed them through directly. This caused all downstream hooks to recalculate every render. Fix: stabilize demoData/initialData via useRef so identity is preserved. 2. useStablePageHeight had a useLayoutEffect with no dependency array that called setState on every render. Combined with useReportCardDataState (another useLayoutEffect that sets parent state), this created an infinite layout-effect cascade. Fix: add updatesInBatchRef convergence guard limiting one height setState per React commit batch. Also adds a nightly Playwright test (react-render-errors.spec.ts) that navigates all dashboard routes and fails on React-critical console errors like "Maximum update depth exceeded", ensuring this class of bug is caught before it reaches production. Signed-off-by: Andrew Anderson <[email protected]>
Fix 5 categories of errors identified via GA4 error tracking: 1. crypto.subtle.digest unhandled rejection (13 events): - crypto.subtle is unavailable in insecure contexts (HTTP) - Add FNV-1a hash fallback in analytics.ts for user ID hashing - Add Math.random fallback in useActiveUsers.ts for session IDs 2. React error #185 on /operators page (18 events): - useOperatorSubscriptions had infinite re-render loop - Demo mode path didn't set fetchInProgressRef guard - clusterCount state changes cascaded into synchronous state updates - Fix: set fetchInProgressRef immediately, use ref for clusterCount, trigger refetch via fetchVersion instead of dependency array 3. Undefined .includes() / .toLowerCase() crashes (9 events): - getStatusSeverity/getStatusColors crashed on undefined status - PodIssues card accessed issue.issues without null guard - Fix: accept null/undefined in status functions, guard array access 4. Maximum call stack exceeded (28 events): - Likely caused by the operator infinite loop cascading - Fixed by the same re-entrancy guard in fix #2 5. Chunk load errors (46 events): - Already handled by ChunkErrorBoundary auto-reload mechanism - 146 successful recoveries tracked in same period Signed-off-by: Andrew Anderson <[email protected]>
Fix 5 categories of errors identified via GA4 error tracking: 1. crypto.subtle.digest unhandled rejection (13 events): - crypto.subtle is unavailable in insecure contexts (HTTP) - Add FNV-1a hash fallback in analytics.ts for user ID hashing - Add Math.random fallback in useActiveUsers.ts for session IDs 2. React error #185 on /operators page (18 events): - useOperatorSubscriptions had infinite re-render loop - Demo mode path didn't set fetchInProgressRef guard - clusterCount state changes cascaded into synchronous state updates - Fix: set fetchInProgressRef immediately, use ref for clusterCount, trigger refetch via fetchVersion instead of dependency array 3. Undefined .includes() / .toLowerCase() crashes (9 events): - getStatusSeverity/getStatusColors crashed on undefined status - PodIssues card accessed issue.issues without null guard - Fix: accept null/undefined in status functions, guard array access 4. Maximum call stack exceeded (28 events): - Likely caused by the operator infinite loop cascading - Fixed by the same re-entrancy guard in fix #2 5. Chunk load errors (46 events): - Already handled by ChunkErrorBoundary auto-reload mechanism - 146 successful recoveries tracked in same period Signed-off-by: Andrew Anderson <[email protected]>
Reverts #5126. Removing useMemo/useCallback caused React error #185 (Maximum update depth exceeded) across all cards. Callbacks recreated on every render trigger useEffect dependency loops in child components. React Compiler's auto-memoization doesn't cover all cases — hooks that return callbacks used as useEffect deps in consumers still need explicit useCallback. Signed-off-by: Andrew Anderson <[email protected]>
…5134) Reverts #5126. Removing useMemo/useCallback caused React error #185 (Maximum update depth exceeded) across all cards. Callbacks recreated on every render trigger useEffect dependency loops in child components. React Compiler's auto-memoization doesn't cover all cases — hooks that return callbacks used as useEffect deps in consumers still need explicit useCallback. Signed-off-by: Andrew Anderson <[email protected]>
Wrap reportCallback in useCallback and reportCtx in useMemo to prevent new function/object references on every render. Without stable references, useLayoutEffect in CardDataContext re-fires on every render, causing React error #185 (Maximum update depth). Signed-off-by: Andrew Anderson <[email protected]>
Wrap reportCallback in useCallback and reportCtx in useMemo to prevent new function/object references on every render. Without stable references, useLayoutEffect in CardDataContext re-fires on every render, causing React error #185 (Maximum update depth). Signed-off-by: Andrew Anderson <[email protected]>
- Disable babel-plugin-react-compiler: it strips useCallback/useMemo that are load-bearing for useLayoutEffect deps in CardDataContext, causing infinite re-render loops (React error #185) in production - Upgrade @types/react from 18.x to 19.x to match React 19 runtime (fixes 80 TS errors for use() hook and RefObject types) Signed-off-by: Andrew Anderson <[email protected]>
…ble references Agent-Logs-Url: https://github.com/kubestellar/console/sessions/8f1b7408-f5d9-40bd-913f-2236348f9411 Co-authored-by: clubanderson <[email protected]>
GA4 reported React error #185 today on /storage for `default-pv_status-2` — "Maximum update depth exceeded". UnifiedCard was recomputing mergedConfig as a fresh object on every render when instanceConfig was provided, which in turn gave dataSource/filters/ stats fresh identities every render. Any downstream useEffect that depended on one of those (and the unified subtree has several) would fire on every render, triggering a setState → render → effect chain. Memoize against (config, instanceConfig) identity. Both come from static modules or stable parent state, so cache hits are the common case. This matches the same pattern we fixed in DrasiReactiveGraph earlier today. Signed-off-by: Andrew Anderson <[email protected]>
GA4 reported React error #185 today on /storage for `default-pv_status-2` — "Maximum update depth exceeded". UnifiedCard was recomputing mergedConfig as a fresh object on every render when instanceConfig was provided, which in turn gave dataSource/filters/ stats fresh identities every render. Any downstream useEffect that depended on one of those (and the unified subtree has several) would fire on every render, triggering a setState → render → effect chain. Memoize against (config, instanceConfig) identity. Both come from static modules or stable parent state, so cache hits are the common case. This matches the same pattern we fixed in DrasiReactiveGraph earlier today. Signed-off-by: Andrew Anderson <[email protected]>
) Move clearAlertError useState to the top of the component with all other state declarations. It was previously declared at line 337, after four useEffect calls, violating React rules of hooks and causing GA4 error #185 ('Rendered more hooks than during the previous render') on the home dashboard. Convert sortedAlerts from an IIFE to useMemo with stable deps [filteredAlerts, sortField, sortDirection]. The IIFE returned a new array on every render; the unstable length fed into totalPages → currentTotalPages → the pagination useEffect, which could loop and produce 'Maximum update depth exceeded' (also seen in GA4). Fixes #4086 Signed-off-by: Andrew Anderson <[email protected]>
Each dashboard config included a self-referencing card (e.g. hipaa_dashboard inside hipaa config) that caused UnifiedDashboard to recursively render itself infinitely, triggering 'Maximum update depth exceeded'. Fix: Remove self-referencing *_dashboard cards from all 15 configs and render the Content component directly in the default export alongside UnifiedDashboard. Signed-off-by: Andrew Anderson <[email protected]>
…ponses (#9749) GA4 reports 3 hits of "Cannot read properties of undefined (reading 'map')" on /enterprise/data-residency in the last 7 days, caused by four .map()/.join() calls on properties of API response objects that the backend may return as null or missing. Fix by applying `(arr || [])` guards to: - DataResidency.tsx: rule.allowed_regions.map() and v.allowed_regions.map()/join() - NISTDashboard.tsx: m.resources.map(), m.namespaces.join(), m.clusters.join() - SIEMDashboard.tsx: summary.top_sources.map() - SegregationOfDuties.tsx: p.roles.map() and p.clusters.join() These guards follow the project rule: NEVER call .map()/.join() on values that might be undefined (see CLAUDE.md §Array Safety). GA4 data: 10 hits of React error #185 on / (7 days), 3 hits of undefined.map on /enterprise/data-residency. The #185 root causes (unconditional setState in ComplianceFrameworks.tsx useMemo and missing useCallback in SegregationOfDuties fetchData) were fixed in PRs #9739 and are confirmed resolved in current code. Signed-off-by: Andy Anderson <[email protected]>
) * ✨ Epic 7: Enterprise Risk Management — Risk Matrix, Register, Appetite dashboards - Risk Matrix: Interactive 5×5 heat map, severity summary, top risks table, trend sparkline - Risk Register: Filterable risk table with detail panel, mitigation plans, controls - Risk Appetite: Appetite vs exposure bars, KRI monitoring, breach alerts, quarterly trends - 3 dashboard configs, 3 summary cards, 9 MSW mock endpoints - Enterprise portal updated: ERM vertical now active (6 of 7) - Nav badges removed (no longer 'Soon') - Card registry: 3 summary + 3 dashboard content cards - Card catalog: 6 new entries in Enterprise Compliance Signed-off-by: Andrew Anderson <[email protected]> * 🐛 Fix React #185 infinite loop in useStablePageHeight The useLayoutEffect with no dependency array was calling setState (setStableMinHeight) on every render when scrollHeight oscillated due to minHeight changes, creating an infinite re-render loop. Root cause: setting minHeight to measured scrollHeight caused the next measurement to return a different value, which exceeded maxHeightRef.current and triggered another setState, ad infinitum. Fix: add a hasMeasuredRef flag that stops re-measuring after the first successful measurement. The ref resets when pageSize changes or pagination is no longer needed, maintaining correct behavior for page changes. Signed-off-by: Andrew Anderson <[email protected]> --------- Signed-off-by: Andrew Anderson <[email protected]>
✅ Post-Merge Verification: passedCommit: |
Two root causes of React error #185 on /enterprise/frameworks: 1. ComplianceFrameworksContent called useClusters() just for cluster names. That heavyweight hook subscribes to the full shared cluster cache (health checks, WS pings, UI indicators) and re-renders on every cache update. Replace with a lightweight useClusterNames() hook using useSyncExternalStore that only re-renders when the set of cluster names actually changes. Also wrap the component in React.memo to prevent parent-triggered re-renders from VersionCheckProvider cascading through EnterpriseLayout. 2. VersionCheckProvider created a new context value object on every render (14 useState variables). Any internal state change (e.g. isChecking toggle, auto-update poll) forced ALL consumers to re-render, amplifying through useClusters/useDashboardHealth hooks in the sidebar and dashboard grid. Memoize the provider value against individual fields so consumers only re-render when a field they read actually changes. Fixes #9769 Signed-off-by: Andy Anderson <[email protected]>
…#185) (kubestellar#9738) Each dashboard config included a self-referencing card (e.g. hipaa_dashboard inside hipaa config) that caused UnifiedDashboard to recursively render itself infinitely, triggering 'Maximum update depth exceeded'. Fix: Remove self-referencing *_dashboard cards from all 15 configs and render the Content component directly in the default export alongside UnifiedDashboard. Signed-off-by: Andrew Anderson <[email protected]> Signed-off-by: lightyagami2109 <[email protected]>
…ponses (kubestellar#9749) GA4 reports 3 hits of "Cannot read properties of undefined (reading 'map')" on /enterprise/data-residency in the last 7 days, caused by four .map()/.join() calls on properties of API response objects that the backend may return as null or missing. Fix by applying `(arr || [])` guards to: - DataResidency.tsx: rule.allowed_regions.map() and v.allowed_regions.map()/join() - NISTDashboard.tsx: m.resources.map(), m.namespaces.join(), m.clusters.join() - SIEMDashboard.tsx: summary.top_sources.map() - SegregationOfDuties.tsx: p.roles.map() and p.clusters.join() These guards follow the project rule: NEVER call .map()/.join() on values that might be undefined (see CLAUDE.md §Array Safety). GA4 data: 10 hits of React error kubestellar#185 on / (7 days), 3 hits of undefined.map on /enterprise/data-residency. The kubestellar#185 root causes (unconditional setState in ComplianceFrameworks.tsx useMemo and missing useCallback in SegregationOfDuties fetchData) were fixed in PRs kubestellar#9739 and are confirmed resolved in current code. Signed-off-by: Andy Anderson <[email protected]> Signed-off-by: lightyagami2109 <[email protected]>
…hboards (kubestellar#9755) * ✨ Epic 7: Enterprise Risk Management — Risk Matrix, Register, Appetite dashboards - Risk Matrix: Interactive 5×5 heat map, severity summary, top risks table, trend sparkline - Risk Register: Filterable risk table with detail panel, mitigation plans, controls - Risk Appetite: Appetite vs exposure bars, KRI monitoring, breach alerts, quarterly trends - 3 dashboard configs, 3 summary cards, 9 MSW mock endpoints - Enterprise portal updated: ERM vertical now active (6 of 7) - Nav badges removed (no longer 'Soon') - Card registry: 3 summary + 3 dashboard content cards - Card catalog: 6 new entries in Enterprise Compliance Signed-off-by: Andrew Anderson <[email protected]> * 🐛 Fix React kubestellar#185 infinite loop in useStablePageHeight The useLayoutEffect with no dependency array was calling setState (setStableMinHeight) on every render when scrollHeight oscillated due to minHeight changes, creating an infinite re-render loop. Root cause: setting minHeight to measured scrollHeight caused the next measurement to return a different value, which exceeded maxHeightRef.current and triggered another setState, ad infinitum. Fix: add a hasMeasuredRef flag that stops re-measuring after the first successful measurement. The ref resets when pageSize changes or pagination is no longer needed, maintaining correct behavior for page changes. Signed-off-by: Andrew Anderson <[email protected]> --------- Signed-off-by: Andrew Anderson <[email protected]> Signed-off-by: lightyagami2109 <[email protected]>
…ar#9772) Two root causes of React error kubestellar#185 on /enterprise/frameworks: 1. ComplianceFrameworksContent called useClusters() just for cluster names. That heavyweight hook subscribes to the full shared cluster cache (health checks, WS pings, UI indicators) and re-renders on every cache update. Replace with a lightweight useClusterNames() hook using useSyncExternalStore that only re-renders when the set of cluster names actually changes. Also wrap the component in React.memo to prevent parent-triggered re-renders from VersionCheckProvider cascading through EnterpriseLayout. 2. VersionCheckProvider created a new context value object on every render (14 useState variables). Any internal state change (e.g. isChecking toggle, auto-update poll) forced ALL consumers to re-render, amplifying through useClusters/useDashboardHealth hooks in the sidebar and dashboard grid. Memoize the provider value against individual fields so consumers only re-render when a field they read actually changes. Fixes kubestellar#9769 Signed-off-by: Andy Anderson <[email protected]> Signed-off-by: lightyagami2109 <[email protected]>
Summary
right-0 top-16 bottom-0) instead of floating with margins like a cardFollows up on #184 which added the slide animation — this fixes the positioning to be a proper drawer.
Test plan
🤖 Generated with Claude Code