Skip to content

✨ Merge Project Selector and Filter Panel into single dropdown#4369

Merged
clubanderson merged 1 commit intomainfrom
feat/merge-project-filter
Apr 3, 2026
Merged

✨ Merge Project Selector and Filter Panel into single dropdown#4369
clubanderson merged 1 commit intomainfrom
feat/merge-project-filter

Conversation

@clubanderson
Copy link
Copy Markdown
Collaborator

@clubanderson clubanderson commented Apr 3, 2026

  • Fix DEFAULT_GROUPS reference mutation (useGlobalFilters.tsx:151)
  • Fix existingNames not updated in migration loop (useGlobalFilters.tsx:173)
  • Fix localStorage removal only when data is a valid array (useGlobalFilters.tsx:183)
  • Add aria-label + aria-pressed + type="button" to color swatches (ClusterFilterPanel.tsx:342)
  • Build + lint pass (no new errors in modified files)

The navbar had two overlapping filter dropdowns — "All Projects" and the
funnel filter — that confused users. The Project Selector let users
create named cluster groupings with colors, but no card components
actually consumed its filter (useProjectFilter was a dead code path).

This merges both into a single unified filter dropdown:

- Projects section (with color dots) at the top of the filter dropdown,
  replacing the old Cluster Groups section
- Color picker in the create form (6 colors)
- Active group detection — trigger button shows the group's color dot
  and name when a saved grouping matches the current cluster selection
- One-time migration of legacy projects:definitions localStorage data
  into globalFilter:clusterGroups
- Removes ProjectSelector, useProjectFilter, and projects.ts (all dead)
- Removes ProjectFilterProvider from App.tsx provider tree

All existing filter behavior (severity, status, text search, individual
clusters) is preserved. The 80+ card components that consume
useGlobalFilters are unaffected.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Copilot AI review requested due to automatic review settings April 3, 2026 02:21
@kubestellar-prow kubestellar-prow bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Apr 3, 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 mikespreitzer 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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

👋 Hey @clubanderson — thanks for opening this PR!

🤖 This project is developed exclusively using AI coding assistants.

Please do not attempt to code anything for this project manually.
All contributions should be authored using an AI coding tool such as:

This ensures consistency in code style, architecture patterns, test coverage,
and commit quality across the entire codebase.


This is an automated message.

@kubestellar-prow kubestellar-prow bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Apr 3, 2026
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 3, 2026

Deploy Preview for kubestellarconsole ready!

Name Link
🔨 Latest commit 2742440
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/69cf2437d8e7510008cd3b10
😎 Deploy Preview https://deploy-preview-4369.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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates the previously separate (and partially unused) “All Projects” selector and the existing filter panel into a single unified navbar dropdown, using the existing cluster group mechanism as the “Projects” concept and migrating legacy project definitions from localStorage.

Changes:

  • Removes the unused useProjectFilter + ProjectSelector stack and its provider wiring from the app.
  • Extends the existing ClusterFilterPanel to display/manage “Projects” (cluster groups) including color selection and active-group detection in the trigger.
  • Adds a one-time migration that converts legacy projects:definitions localStorage data into globalFilter:clusterGroups.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
web/src/lib/projects.ts Deletes legacy project model/constants and storage keys.
web/src/hooks/useProjectFilter.tsx Removes the unused project filter context/provider/hook.
web/src/hooks/useGlobalFilters.tsx Adds legacy project → cluster group migration during initialization.
web/src/components/layout/navbar/ProjectSelector.tsx Deletes the old “All Projects” navbar dropdown component.
web/src/components/layout/navbar/Navbar.tsx Removes the extra navbar dropdown, leaving the unified filter panel.
web/src/components/layout/navbar/ClusterFilterPanel.tsx Adds “Projects” section, color picker, and active-group trigger label/dot.
web/src/App.tsx Removes ProjectFilterProvider from the provider tree.

Comment on lines +151 to +155
let groups: ClusterGroup[] = DEFAULT_GROUPS
try {
const stored = localStorage.getItem(GROUPS_STORAGE_KEY)
if (stored) {
return JSON.parse(stored)
groups = JSON.parse(stored)
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

groups is initialized to DEFAULT_GROUPS by reference. If the migration below pushes into groups, it will mutate the module-level DEFAULT_GROUPS array. Initialize with a fresh array (e.g., a shallow copy) to avoid shared mutable state.

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +173
const existingNames = new Set(groups.map(g => g.name))
for (const p of projects) {
if (!existingNames.has(p.name)) {
groups.push({
id: `migrated-${p.id}`,
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the legacy projects → groups migration, existingNames is never updated when a group is added. If the legacy list contains duplicate names, multiple groups with the same name can be migrated. Add each migrated name to existingNames when you push.

Copilot uses AI. Check for mistakes.
Comment on lines +168 to +183
if (Array.isArray(projects) && projects.length > 0) {
const existingNames = new Set(groups.map(g => g.name))
for (const p of projects) {
if (!existingNames.has(p.name)) {
groups.push({
id: `migrated-${p.id}`,
name: p.name,
clusters: p.clusters || [],
color: p.color,
})
}
}
localStorage.setItem(GROUPS_STORAGE_KEY, JSON.stringify(groups))
}
localStorage.removeItem('projects:definitions')
localStorage.removeItem('projects:selected')
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These keys are removed whenever oldProjects exists, even if the parsed value wasn’t a valid non-empty array (i.e., nothing was migrated). Consider only removing projects:definitions / projects:selected after you’ve validated and/or successfully migrated the data to avoid accidental data loss.

Suggested change
if (Array.isArray(projects) && projects.length > 0) {
const existingNames = new Set(groups.map(g => g.name))
for (const p of projects) {
if (!existingNames.has(p.name)) {
groups.push({
id: `migrated-${p.id}`,
name: p.name,
clusters: p.clusters || [],
color: p.color,
})
}
}
localStorage.setItem(GROUPS_STORAGE_KEY, JSON.stringify(groups))
}
localStorage.removeItem('projects:definitions')
localStorage.removeItem('projects:selected')
if (Array.isArray(projects)) {
if (projects.length > 0) {
const existingNames = new Set(groups.map(g => g.name))
for (const p of projects) {
if (!existingNames.has(p.name)) {
groups.push({
id: `migrated-${p.id}`,
name: p.name,
clusters: p.clusters || [],
color: p.color,
})
}
}
localStorage.setItem(GROUPS_STORAGE_KEY, JSON.stringify(groups))
}
localStorage.removeItem('projects:definitions')
localStorage.removeItem('projects:selected')
}

Copilot uses AI. Check for mistakes.
Comment on lines +341 to +342
key={c}
onClick={() => setNewGroupColor(c)}
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The color-swatch buttons are purely visual and have no accessible name, which makes the color picker hard to use with screen readers. Add an aria-label (and optionally aria-pressed for the selected swatch) so the control is operable.

Suggested change
key={c}
onClick={() => setNewGroupColor(c)}
key={c}
type="button"
onClick={() => setNewGroupColor(c)}
aria-label={t('common:filters.selectColor', 'Select color {{color}}', { color: c })}
aria-pressed={newGroupColor === c}

Copilot uses AI. Check for mistakes.
@clubanderson
Copy link
Copy Markdown
Collaborator Author

🔄 Auto-Applying Copilot Code Review

Copilot code review found 2 code suggestion(s) and 2 general comment(s).

@copilot Please apply all of the following code review suggestions:

  • web/src/hooks/useGlobalFilters.tsx (line 183): if (Array.isArray(projects)) { if (projects.length > 0) { ...
  • web/src/components/layout/navbar/ClusterFilterPanel.tsx (line 342): key={c} type="button" onClic...

Also address these general comments:

  • web/src/hooks/useGlobalFilters.tsx (line 155): groups is initialized to DEFAULT_GROUPS by reference. If the migration below pushes into groups, it will mutate th
  • web/src/hooks/useGlobalFilters.tsx (line 173): In the legacy projects → groups migration, existingNames is never updated when a group is added. If the legacy list co

Push all fixes in a single commit. Run cd web && npm run build && npm run lint before committing.


Auto-generated by copilot-review-apply workflow.

@clubanderson clubanderson merged commit a429c70 into main Apr 3, 2026
27 of 29 checks passed
@kubestellar-prow kubestellar-prow bot deleted the feat/merge-project-filter branch April 3, 2026 02:28
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Thank you for your contribution! Your PR has been merged.

Check out what's new:

Stay connected: Slack #kubestellar-dev | Multi-Cluster Survey

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Post-merge build verification passed

Both Go and frontend builds compiled successfully against merge commit a429c7085ece89fcb373f7c5b9f0be5f1bb5d01d.

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 3, 2026

🔄 Auto-Applying Copilot Code Review

Copilot code review found 2 code suggestion(s) and 2 general comment(s)....

Applied all 4 suggestions in commit 0e62814:

  1. DEFAULT_GROUPS mutation — changed let groups: ClusterGroup[] = DEFAULT_GROUPS to [...DEFAULT_GROUPS] so migration pushes don't mutate the module-level constant.
  2. existingNames not updated in loop — added existingNames.add(p.name) after each push so duplicate names in legacy data are properly skipped.
  3. Premature localStorage removal — restructured the migration block so localStorage.removeItem('projects:definitions') and localStorage.removeItem('projects:selected') are now inside the if (Array.isArray(projects)) guard (cleaned up on any valid parse, including empty arrays), and never removed on a JSON parse error.
  4. Color swatch accessibility — added type="button", aria-label (e.g. "Select color #ef4444"), and aria-pressed (selected state) to each swatch button.

Build and lint both pass with no new errors.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

✅ Post-Merge Verification: passed

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

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/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants