Skip to content

feat(settings): add defaultSort user preference for resource table - #1428

Merged
hisco merged 2 commits into
mainfrom
radar-529-default-sort
Sep 24, 2026
Merged

hisco merged 2 commits into
mainfrom
radar-529-default-sort

Conversation

@hisco

@hisco hisco commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

The resource table drops its sort on every kind switch. Sort Pods by Restarts, open Deployments, come back, and Pods is in its built-in order again. Reproduce on main: open Resources, click any column header, click another kind in the sidebar, click back.

The table already remembers which columns show, their widths and any custom columns, per kind, in the browser. Sort was the one thing it forgot.

Continues #529 by @7adityaraj. The first commit is theirs and adds one global sort preference. The second reworks it into per-table memory, so the Settings section and server field it added are removed again there.

What this does

Sort gets its own per-kind record, stored beside the column settings. A header click is remembered for that table and restored when the user comes back to it, across kind switches and reloads. Cycling a sort off clears it. Nothing carries between tables: sorting Nodes by Status leaves Pods alone.

There is no setting to find and nothing new in Settings. It behaves the way column visibility already does.

Where to look hardest

effectiveSortColumn in ResourcesView. State holds the sort that was chosen or restored. Whether it is in force is decided at render time: it applies only while this table can currently sort by that column. A saved column can name one the table no longer renders (a custom column since removed, a CRD whose printer columns changed) or one that has not loaded yet. Applying it blind would order rows by an absent value with no header arrow to undo it from. Deciding at render time also means a column that arrives later takes effect on its own, with no reset and no second pass.

Why sort is not inside the column record. The existence of a column record is read elsewhere as "the user chose columns": GPU auto-show stands down behind it, and the stale-defaults migration skips it. A sort is not that choice, so it lives in a sibling radar-sort-<kind> key with the same per-kind scoping. It is written at the click in handleSort, the only place it changes, rather than through the column-settings save effect, so a kind switch cannot write one kind's sort under another kind's key.

The shortcut guard in handleSort. The N/A/S shortcuts fire on every kind, including ones with no such column. Without the guard they would save a sort the table cannot show.

Deliberately not in scope

  • No sync across browsers or devices. Same as the column settings this sits beside.
  • No global "sort everything by X". One preference for every table meant a click on one table changed all of them.
  • Radar Hub gets this through the shared component with no change on its side, as it does for column visibility.

What it looks like

  1. Pods, nothing sorted, built-in order.
01-pods-fresh
  1. Pods sorted by Restarts.
02-pods-sorted-restarts
  1. Deployments, unaffected by the Pods sort.
03-deployments-untouched
  1. Deployments sorted by Age, newest first.
04-deployments-sorted-age
  1. Back on Pods: still Restarts.
05-pods-still-restarts
  1. Back on Deployments: still Age.
06-deployments-still-age
  1. After a full reload: still sorted.
07-deployments-after-reload
  1. Restarts cycled off on Pods: forgotten.
08-pods-cycled-off
  1. A user-defined label column, remembered like any other.
09-custom-column-sorted
  1. That column removed: no arrow and no hidden ordering.
10-custom-column-removed

Tests

  • packages/k8s-ui: the saved-sort sanitizer at the localStorage boundary.
  • Driven in a browser against cmd/testserver: two kinds keep separate sorts, a sort alone writes no column record, both survive a reload, cycling off clears the record, a shortcut on a kind without the column saves nothing, a user-defined column is remembered, a removed column produces no invisible sort and an order identical to no sort at all, a corrupt record is ignored.

Closes #529.

Co-authored-by: 7adityaraj 17121540+7adityaraj@users.noreply.github.com


Note

Low Risk
Client-only localStorage and render-time guards; no auth or API changes, with bounded impact if saved sort data is corrupt.

Overview
The resource table remembers column sort per kind in localStorage (separate radar-sort-* keys from column visibility), so switching kinds or reloading no longer clears a header sort.

sanitizeSavedSort validates stored sort blobs; loadSavedSort / saveSavedSort read and write on kind load, header clicks, and column reset. Sort state is not cleared on kind change anymore (filters still are). effectiveSortColumn only applies a restored sort while that column is currently sortable; canSortBy blocks shortcuts and saves for non-sortable columns. Sorting and header arrows use the effective column so removed or not-yet-loaded columns cannot impose invisible ordering.

Adds unit tests for sanitizeSavedSort.

Reviewed by Cursor Bugbot for commit de75f7c. Bugbot is set up for automated code reviews on this repo. Configure here.

@hisco
hisco requested a review from nadaverell as a code owner August 12, 2026 14:03

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread packages/k8s-ui/src/components/resources/ResourcesView.tsx Outdated
Comment thread web/e2e/settings.spec.ts Outdated
Comment thread web/src/hooks/useDefaultSort.ts Outdated
The resource table resets to its built-in order on every kind switch, so
anyone who prefers age-first (or any other column) has to re-click the
header each time they navigate.

Add a `defaultSort` user preference — a column plus a direction — that
persists in ~/.radar/settings.json alongside theme and pinnedKinds, with
the same localStorage cache, PUT /api/settings sync, and cloud-mode
strip/reject. It applies on load, on every kind switch, and immediately
when edited; sorting a table from its column header writes it back, so
the two stay in sync.

The preference names one column, but column sets are per-kind: a kind
whose table has no such column (Status on ConfigMaps, Namespace on
cluster-scoped Nodes) keeps its built-in order rather than sorting by an
absent value with no header arrow to undo it from.

"No preference" is a reachable state, so the PUT merge keys off JSON key
presence — `{"defaultSort": null}` clears the setting, an absent key
leaves it alone — and an empty column or a direction other than
asc/desc is rejected at the boundary.

The Settings dialog gains a Preferences section: personal and
self-saving, like AI diagnose, rather than owner-gated.

Co-authored-by: Eyal <eyal@koalaops.com>
@hisco
hisco force-pushed the radar-529-default-sort branch from 2bb8a0b to 5c918fb Compare September 23, 2026 14:45

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread web/src/components/settings/SettingsDialog.tsx
Comment thread web/src/hooks/useDefaultSort.ts Outdated
@hisco
hisco force-pushed the radar-529-default-sort branch from 5c918fb to 2bba2c3 Compare September 24, 2026 03:56

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2bba2c3. Configure here.

Comment thread packages/k8s-ui/src/components/resources/ResourcesView.tsx Outdated
The resource table drops its sort on every kind switch. Sort Pods by Restarts,
open Deployments, come back, and Pods is in its built-in order again. The table
already remembers which columns show, their widths and any custom columns per
kind; sort was the one thing it forgot.

Sort now joins that per-kind record. A header click is remembered for that
table and restored when the user returns to it, across kind switches and
reloads. Cycling the sort off clears it. Nothing is shared between tables.

The restored value is applied only while this table can sort by it. A saved
column can name one the table no longer renders (a custom column since
removed, a CRD whose printer columns changed) or one that has not loaded yet,
and applying it blind would order rows by an absent value with no header arrow
to undo it from. State keeps what was chosen; effectiveSortColumn decides at
render time whether it is in force, so a column that arrives later takes
effect without a reset.

The N/A/S shortcuts fire on every kind, including ones without that column;
the guard keeps those from saving a sort the table cannot show.

This replaces the global defaultSort setting from the previous commit: one
preference for every table meant sorting Nodes by Status changed how Pods
opened, and the Settings section, server field and sync hook existed only to
carry that one value. Per-table memory needs none of them.
@hisco
hisco force-pushed the radar-529-default-sort branch from 2bba2c3 to de75f7c Compare September 24, 2026 04:04
@hisco
hisco merged commit 76315c4 into main Sep 24, 2026
9 checks passed
nadaverell added a commit that referenced this pull request Sep 27, 2026
Adds the v1.15.0 entry to the What's New catalog. This replaces #1877,
which was stacked on the closed #1876; the dialog landed on main in
#1902 instead. `scripts/check-whats-new.sh` (#1908) refuses to release
v1.15.0 without this entry, so this must merge before the tag.

## Highlights
A full-width lead card, then a 2x2 grid. Each secondary card has its own
icon color, so they read apart.

1. **AI investigations that show their work** (lead, no CTA; the
investigations workspace redirects home in run modes that can't host
local agents). This recaps the v1.14.1 rework (#1766, #1802) on purpose:
it's the first What's New dialog, and we want to put AI investigations
in front of people. OpenCode (#1265) is the new v1.15 part.
2. **Full manifest diffs for Argo CD resources** (#1622)
3. **Rightsizing and cost data in MCP**: new agent tools, and risks lead
before savings in the UI too (#1820, #1723)
4. **Deeper batch + AI/ML workloads support**: Kueue, JobSet and Ray
pages; Job and JobSet details show why a queued job has no Pods (#1874,
#1885, #1900, #1849, #1893, #1897)
5. **Canary and blue-green in Topology** (#1626)

## Also in this release
Eight one-liners, paired by length because the grid fills row by row:
table sort memory (#1428), OCI Helm installs (#1815), expiring TLS
certificates in Checks (#1829), Radar Desktop keeping preferences across
restarts (#1907), Pod vs template in Issues (#1848), PVC usage
availability (#1828), Strimzi and admission webhook failures in Issues
(#1840, #1830), `--listen-address` (#1845).

Left out: security fixes, dependency bumps, refactors and CI,
accessibility work (#1901, #1909), and narrow items (ALB action
annotations, INSTALL_DIR). No usage-stats line is needed, because
#1879's consent question already sits in this dialog.

## UI changes beyond the catalog
- The heading names the release line (`v1.15`, via `releaseLine`), since
patch releases show the same notes. The "You just updated from → to"
line keeps exact versions.
- An optional `tone` on highlights colors the icon tile (violet, teal,
emerald, indigo). It uses Radar's existing `bg-X-500/10 text-X-600
dark:text-X-400` pattern, and was checked in light and dark themes.

## Verification
- `scripts/check-whats-new.sh v1.15.0` → found notes
- `make tsc` clean; What's New and usage-data tests: 39 passed
- Previewed live with `VERSION=v1.15.0 make build`, opening
`/?whats-new`, with #1879's consent question in place

The matching public changelog is skyhook-dev/marketing#312.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Release-notes catalog and presentational What's New UI only; no auth,
API, or data-handling changes.
> 
> **Overview**
> Adds the **v1.15.0** What's New catalog entry (five highlights with
CTAs where appropriate, plus eight improvement one-liners) so
`check-whats-new` can ship the tag.
> 
> The dialog heading now shows the **minor release line** via new
`releaseLine()` (e.g. `v1.15` instead of `v1.15.0`), while the upgrade
subtitle still uses exact versions. Secondary highlight cards can set an
optional **`tone`** (violet, teal, emerald, indigo) for distinct icon
tiles; the lead card stays on the accent style.
> 
> Tests cover `releaseLine` and the preview dialog title expectation.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f0038c3. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants