Skip to content

✨ Make AI Missions panel a flush-right drawer with Escape key#185

Merged
clubanderson merged 1 commit intomainfrom
fix/mission-panel-drawer-style
Jan 29, 2026
Merged

✨ Make AI Missions panel a flush-right drawer with Escape key#185
clubanderson merged 1 commit intomainfrom
fix/mission-panel-drawer-style

Conversation

@clubanderson
Copy link
Copy Markdown
Collaborator

Summary

  • 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 when closing
  • Escape key closes the sidebar (exits fullscreen first if active)
  • Left border only, no rounded corners on right side

Follows up on #184 which added the slide animation — this fixes the positioning to be a proper drawer.

Test plan

  • Panel flush against right viewport edge, no gaps
  • Slide-out animation visible during close
  • Escape key closes panel
  • Escape exits fullscreen first, then closes on second press

🤖 Generated with Claude Code

- 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]>
@kubestellar-prow kubestellar-prow Bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Jan 29, 2026
@kubestellar-prow
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign clubanderson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@netlify
Copy link
Copy Markdown

netlify Bot commented Jan 29, 2026

Deploy Preview for kubestellarconsole ready!

Name Link
🔨 Latest commit 7ce85be
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/697b65b8d9cae20008c28934
😎 Deploy Preview https://deploy-preview-185.console-deploy-preview.kubestellar.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@kubestellar-prow kubestellar-prow Bot added the size/S Denotes a PR that changes 10-29 lines, ignoring generated files. label Jan 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

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 git commit -s to certify the Developer Certificate of Origin

PR Title - Must start with an emoji: ✨ (feature), 🐛 (bug fix), 📖 (docs), 🌱 (infra/tests), ⚠️ (breaking change)

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!

@clubanderson clubanderson merged commit 450c9ce into main Jan 29, 2026
24 of 29 checks passed
@kubestellar-prow kubestellar-prow Bot deleted the fix/mission-panel-drawer-style branch January 29, 2026 13:55
@github-actions
Copy link
Copy Markdown
Contributor

🎉 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!

@github-actions
Copy link
Copy Markdown
Contributor

❌ Playwright Tests Failed

📊 View Full Report

Download the playwright-report artifact from the workflow run for screenshots and detailed traces.

To view the report locally:

# Download and extract playwright-report.zip
npx playwright show-report path/to/playwright-report

clubanderson added a commit that referenced this pull request Feb 13, 2026
- 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]>
clubanderson added a commit that referenced this pull request Mar 11, 2026
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]>
clubanderson added a commit that referenced this pull request Mar 11, 2026
…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]>
clubanderson added a commit that referenced this pull request Mar 13, 2026
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]>
clubanderson added a commit that referenced this pull request Mar 13, 2026
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]>
clubanderson added a commit that referenced this pull request Apr 7, 2026
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]>
clubanderson added a commit that referenced this pull request Apr 7, 2026
…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]>
clubanderson added a commit that referenced this pull request Apr 7, 2026
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]>
clubanderson added a commit that referenced this pull request Apr 7, 2026
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]>
clubanderson added a commit that referenced this pull request Apr 7, 2026
- 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]>
clubanderson added a commit that referenced this pull request Apr 15, 2026
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]>
clubanderson added a commit that referenced this pull request Apr 15, 2026
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]>
clubanderson added a commit that referenced this pull request Apr 21, 2026
)

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]>
clubanderson added a commit that referenced this pull request Apr 23, 2026
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]>
clubanderson added a commit that referenced this pull request Apr 23, 2026
…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]>
clubanderson added a commit that referenced this pull request Apr 23, 2026
)

* ✨ 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]>
@github-actions
Copy link
Copy Markdown
Contributor

✅ Post-Merge Verification: passed

Commit: 6e8a23dab6612baf87e60fa40b9dbf7740bd4b8d
Specs run: navbar-responsive.spec.ts smoke.spec.ts
Report: https://github.com/kubestellar/console/actions/runs/24857877176

clubanderson added a commit that referenced this pull request Apr 23, 2026
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]>
lightyagami2109 pushed a commit to lightyagami2109/kubestellar_console_OJT that referenced this pull request May 3, 2026
…#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]>
lightyagami2109 pushed a commit to lightyagami2109/kubestellar_console_OJT that referenced this pull request May 3, 2026
…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]>
lightyagami2109 pushed a commit to lightyagami2109/kubestellar_console_OJT that referenced this pull request May 3, 2026
…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]>
lightyagami2109 pushed a commit to lightyagami2109/kubestellar_console_OJT that referenced this pull request May 3, 2026
…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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. size/S Denotes a PR that changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant