Skip to content

fix: implement sorting functionality for resource tree view#224

Open
jmischler72 wants to merge 6 commits intodarksworm:mainfrom
jmischler72:fix_sort_app_view
Open

fix: implement sorting functionality for resource tree view#224
jmischler72 wants to merge 6 commits intodarksworm:mainfrom
jmischler72:fix_sort_app_view

Conversation

@jmischler72
Copy link

@jmischler72 jmischler72 commented Feb 26, 2026

This pr Closes #220

It fixes by doing a sort on the resources (main tree nodes then sort childrens for each nodes)

In draft, because i am trying to generalize with the existing sort logic

Summary by CodeRabbit

  • New Features

    • Resource tree: sort siblings by health or sync (asc/desc); active sort persists across loaded trees and live updates for consistent ordering.
    • App list: improved semantic sorting for stable, predictable orderings.
  • Tests

    • Added unit and end-to-end tests validating tree sorting behavior and visual ordering in the UI.
  • Chores

    • Updated ignore rules to exclude E2E test snapshot files.

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Walkthrough

Introduces a generic Sortable-based sorting utility and a model SortKey, adds TreeView sorting support (SetSort, SortKey on nodes, re-sorting during tree upsert and status updates), propagates UI sort state from app input/model, and adds unit and e2e tests plus a .gitignore rule for e2e snapshots.

Changes

Cohort / File(s) Summary
Core sorting utility
pkg/sort/sort.go
Adds public Sortable interface and generic Sort[T Sortable] plus comparatorGeneric and updated status ordering; replaces previous type-specific SortApps.
Model sort types
pkg/model/sort.go, pkg/model/types.go
Adds model.SortKey type and App.SortKey() method to expose semantic keys (Health, Sync, Kind, Name) used by generic sorter.
Tree view implementation & tests
pkg/tui/treeview/treeview.go, pkg/tui/treeview/treeview_test.go
Adds sortConfig *model.SortConfig, SetSort(config), treeNode.SortKey() and sortNodeChildren(...); integrates sorting into UpsertAppTree and SetResourceStatuses; adds tests verifying health/sync/name ordering.
App layer propagation
cmd/app/input_components.go, cmd/app/model.go, cmd/app/view.go
Propagates UI sort config to active TreeView (calls m.treeView.SetSort when sort executed and on ResourceTreeLoadedMsg); switches apps sorting call to generic sort.Sort(...); minor formatting tweaks in view rendering.
E2E test and snapshots
e2e/sort_test.go, .gitignore
Adds TestSortInTreeView with mock resource-tree endpoint and snapshot-based order assertions; adds linePosition helper; ignores e2e/test-snapshots/*.screen in .gitignore.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as Input Handler
    participant Model
    participant TreeView
    participant Renderer

    User->>UI: Issue sort command (e.g., health asc)
    UI->>Model: Update m.state.UI.Sort
    UI->>TreeView: Call SetSort(sortConfig)
    alt Tree view active
        TreeView->>TreeView: Set sortConfig and call sortNodeChildren on roots & descendants
        TreeView->>TreeView: Rebuild DFS/display order
        TreeView->>Renderer: Trigger UI refresh
    end
    Renderer->>User: Rendered sorted tree

    Note over Model,TreeView: On ResourceTreeLoadedMsg, Model applies m.state.UI.Sort to TreeView
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Tests ⚠️ Warning Generic Sort[T Sortable] function and Sortable interface lack dedicated unit tests; comparator logic tested only indirectly through UI integration. Add unit tests in pkg/sort/sort_test.go for sort.Sort[model.App], sort.Sort[*treeNode], comparator logic, and edge cases like empty strings and unknown status handling.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing sorting functionality for the resource tree view, which is the primary objective of this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jmischler72 jmischler72 marked this pull request as ready for review February 27, 2026 08:19
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/tui/treeview/treeview.go (1)

318-328: Consider re-sorting when health status changes too.

SetResourceStatuses only triggers re-sort when sorting by sync status. However, if health status can also be updated through this path (or a similar path in the future), sorting by health would not reflect those updates. Currently this appears correct since SetResourceStatuses only updates sync status, but this could be a source of bugs if health updates are added later.

📝 Consider future-proofing

If health updates might come through a similar path in the future, consider:

-	// If sorting by sync status, re-sort to reflect updated values
-	if v.sortConfig != nil && v.sortConfig.Field == model.SortFieldSync {
+	// Re-sort if sorting by a status field that may have changed
+	if v.sortConfig != nil && (v.sortConfig.Field == model.SortFieldSync || v.sortConfig.Field == model.SortFieldHealth) {

Or add a comment explaining why only sync is handled here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/tui/treeview/treeview.go` around lines 318 - 328, The code in
SetResourceStatuses only re-sorts when v.sortConfig.Field ==
model.SortFieldSync, which misses updates if health status becomes part of this
path; update the condition in SetResourceStatuses to also trigger when
v.sortConfig.Field == model.SortFieldHealth (or the equivalent enum) so that
v.sortNodeChildren(roots), iterating nodesByUID children and v.rebuildOrder()
run for health-based sorting too, or alternatively add a clear comment on
SetResourceStatuses explaining why only sync is handled to future-proof the
code; locate this logic around the SetResourceStatuses method and the symbols
v.sortConfig.Field, model.SortFieldSync, sortNodeChildren, nodesByUID, roots and
rebuildOrder.
pkg/sort/sort.go (1)

114-133: Consider handling SortFieldName explicitly for clarity.

When config.Field is SortFieldName, the switch statement doesn't match any case, leaving cmp = 0. This causes the function to fall through to the tiebreak logic (kind, then name), which is correct behavior. However, explicit handling would improve readability and make the intent clearer.

♻️ Suggested clarification
 	less := func(a, b T) bool {
 		var cmp int
 		switch config.Field {
 		case model.SortFieldHealth:
 			cmp = compareHealthStatus(a.NodeHealth(), b.NodeHealth())
 		case model.SortFieldSync:
 			cmp = compareSyncStatus(a.NodeSync(), b.NodeSync())
+		case model.SortFieldName:
+			// Fall through to tiebreak by (kind, name)
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/sort/sort.go` around lines 114 - 133, The comparator "less" currently
omits an explicit case for model.SortFieldName causing reliance on tiebreak
logic; add a case for model.SortFieldName in the switch that sets cmp by
comparing a.NodeName() and b.NodeName() (e.g., lexicographic comparison) so the
name-based sort is explicit, then keep the existing Direction check
(config.Direction == model.SortDesc) to invert ordering when needed and preserve
the existing kind/name tiebreak fallback when cmp == 0.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/sort/sort.go`:
- Around line 128-132: The tiebreak currently always compares a.NodeKind() and
a.NodeName() in ascending order; update the comparator that handles ties (where
cmp == 0) to honor config.Direction by inverting the kind/name comparison when
config.Direction indicates descending (e.g., apply the same direction multiplier
or flip the comparison operands), referencing the existing config.Direction,
NodeKind(), and NodeName() symbols so ties follow the overall sort direction;
alternatively, if you choose stability over direction, add a clear
comment/docstring next to the tie-break logic stating that kind/name tiebreaks
are intentionally always ascending.

---

Nitpick comments:
In `@pkg/sort/sort.go`:
- Around line 114-133: The comparator "less" currently omits an explicit case
for model.SortFieldName causing reliance on tiebreak logic; add a case for
model.SortFieldName in the switch that sets cmp by comparing a.NodeName() and
b.NodeName() (e.g., lexicographic comparison) so the name-based sort is
explicit, then keep the existing Direction check (config.Direction ==
model.SortDesc) to invert ordering when needed and preserve the existing
kind/name tiebreak fallback when cmp == 0.

In `@pkg/tui/treeview/treeview.go`:
- Around line 318-328: The code in SetResourceStatuses only re-sorts when
v.sortConfig.Field == model.SortFieldSync, which misses updates if health status
becomes part of this path; update the condition in SetResourceStatuses to also
trigger when v.sortConfig.Field == model.SortFieldHealth (or the equivalent
enum) so that v.sortNodeChildren(roots), iterating nodesByUID children and
v.rebuildOrder() run for health-based sorting too, or alternatively add a clear
comment on SetResourceStatuses explaining why only sync is handled to
future-proof the code; locate this logic around the SetResourceStatuses method
and the symbols v.sortConfig.Field, model.SortFieldSync, sortNodeChildren,
nodesByUID, roots and rebuildOrder.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80a1185 and ab50e85.

📒 Files selected for processing (7)
  • .gitignore
  • cmd/app/input_components.go
  • cmd/app/model.go
  • e2e/sort_test.go
  • pkg/sort/sort.go
  • pkg/tui/treeview/treeview.go
  • pkg/tui/treeview/treeview_test.go

@greptile-apps
Copy link

greptile-apps bot commented Feb 27, 2026

Greptile Summary

Extends sorting functionality to resource tree view by refactoring sort logic to use a generic Sortable interface. Resources in the tree can now be sorted by health or sync status (ascending/descending), with automatic re-sorting when statuses are updated. The implementation applies semantic ordering (problems-first for ascending) and tiebreaks by kind and name.

Key changes:

  • Introduced generic Sort[T Sortable] function replacing SortApps
  • Added SetSort() method to TreeView that sorts all sibling groups in the tree
  • Sort configuration propagates to tree view via :sort command and persists across tree loads
  • Re-sorts tree automatically when resource statuses update (for health/sync sorting)
  • Added comprehensive E2E and unit tests validating sort behavior

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • Well-structured refactoring with comprehensive test coverage (both E2E and unit tests). The generic sorting implementation is clean and maintains backward compatibility. Logic for re-sorting on status updates is correct and handles edge cases properly.
  • No files require special attention

Important Files Changed

Filename Overview
pkg/sort/sort.go Refactored to use generic Sortable interface with Sort[T] function, replacing SortApps. Added case-insensitive status matching fallback.
pkg/tui/treeview/treeview.go Added sorting support via SetSort and sortNodeChildren. Re-sorts tree on status updates when sorting by health/sync fields.
pkg/model/types.go Added SortKey() method to App struct to implement Sortable interface for generic sorting.
pkg/model/sort.go Added SortKey struct to hold health, sync, kind, and name values used for semantic ordering.
e2e/sort_test.go Added TestSortInTreeView E2E test verifying sort behavior (health/sync asc/desc) in resource tree view.
pkg/tui/treeview/treeview_test.go Added unit tests for SetSort with health, sync, and name fields in ascending and descending order.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class Sortable {
        <<interface>>
        +SortKey() SortKey
    }
    
    class SortKey {
        +Health string
        +Sync string
        +Kind string
        +Name string
    }
    
    class App {
        +Name string
        +Health string
        +Sync string
        +SortKey() SortKey
    }
    
    class treeNode {
        +name string
        +kind string
        +health string
        +status string
        +SortKey() SortKey
    }
    
    class TreeView {
        -sortConfig *SortConfig
        -roots []*treeNode
        -nodesByUID map
        +SetSort(config SortConfig)
        +sortNodeChildren(list []*treeNode)
    }
    
    class Sort~T~ {
        <<generic function>>
        +Sort(items []T, config SortConfig)
    }
    
    Sortable <|.. App : implements
    Sortable <|.. treeNode : implements
    App ..> SortKey : returns
    treeNode ..> SortKey : returns
    TreeView --> treeNode : contains
    TreeView --> SortConfig : uses
    Sort~T~ ..> Sortable : constraint
Loading

Last reviewed commit: 46e8749

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

7 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/tui/treeview/treeview.go (1)

102-110: Misleading comment about default sort order.

The comment states "default (kind, name) ascending order" but the code sets Field: model.SortFieldName. Based on the SortTreeNodes implementation (from pkg/sort/sort.go), the primary sort is by name, with kind/name as tiebreakers. The comment should be updated to accurately reflect this behavior.

📝 Suggested comment fix
-// sortNodeChildren sorts a sibling list using the current sort config via 	sort.SortTreeNodes.
-// A nil sortConfig resolves to the default (kind, name) ascending order.
+// sortNodeChildren sorts a sibling list using the current sort config via pkgsort.SortTreeNodes.
+// A nil sortConfig resolves to the default (name ascending) order.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/tui/treeview/treeview.go` around lines 102 - 110, Update the misleading
comment above sortNodeChildren: it currently claims the default is "(kind, name)
ascending" but the code (and pkgsort.SortTreeNodes behavior) uses name as the
primary key with kind/name as tiebreakers; change the comment to state that a
nil sortConfig resolves to the default of sorting by name ascending, using kind
(and then name as necessary) as tiebreakers (or otherwise reflect the exact
tiebreak order), and keep references to TreeView.sortNodeChildren,
model.SortConfig/model.SortFieldName, and pkgsort.SortTreeNodes so reviewers can
verify the behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/tui/treeview/treeview.go`:
- Around line 318-328: The re-sort conditional in SetResourceStatuses
incorrectly includes model.SortFieldHealth even though this method only updates
node.status (see SetResourceStatuses and the node.status assignment) and not
health; remove model.SortFieldHealth from the condition so it only checks
v.sortConfig.Field == model.SortFieldSync before calling
v.sortNodeChildren(v.roots), iterating v.nodesByUID to
v.sortNodeChildren(node.children), and then v.rebuildOrder; this avoids
unnecessary re-sorting when the sort key is health which isn’t changed here.

---

Nitpick comments:
In `@pkg/tui/treeview/treeview.go`:
- Around line 102-110: Update the misleading comment above sortNodeChildren: it
currently claims the default is "(kind, name) ascending" but the code (and
pkgsort.SortTreeNodes behavior) uses name as the primary key with kind/name as
tiebreakers; change the comment to state that a nil sortConfig resolves to the
default of sorting by name ascending, using kind (and then name as necessary) as
tiebreakers (or otherwise reflect the exact tiebreak order), and keep references
to TreeView.sortNodeChildren, model.SortConfig/model.SortFieldName, and
pkgsort.SortTreeNodes so reviewers can verify the behavior.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab50e85 and 5fd2887.

📒 Files selected for processing (1)
  • pkg/tui/treeview/treeview.go

@jmischler72 jmischler72 marked this pull request as draft February 27, 2026 14:19
@jmischler72 jmischler72 marked this pull request as ready for review February 28, 2026 15:17
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
pkg/tui/treeview/treeview.go (1)

318-327: ⚠️ Potential issue | 🟡 Minor

Re-sort trigger should not include health in SetResourceStatuses.

SetResourceStatuses updates sync status (node.status) only. Including SortFieldHealth at Line 319 causes unnecessary full-tree re-sorts when health values did not change.

🛠️ Suggested fix
-	// If sorting by sync or health status, re-sort to reflect updated values
-	if v.sortConfig != nil && (v.sortConfig.Field == model.SortFieldSync || v.sortConfig.Field == model.SortFieldHealth)   {
+	// If sorting by sync status, re-sort to reflect updated values
+	if v.sortConfig != nil && v.sortConfig.Field == model.SortFieldSync {
 		v.sortNodeChildren(v.roots)
 		for _, node := range v.nodesByUID {
 			if len(node.children) > 0 {
 				v.sortNodeChildren(node.children)
 			}
 		}
 		v.rebuildOrder()
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/tui/treeview/treeview.go` around lines 318 - 327, The conditional in
SetResourceStatuses currently triggers a full re-sort when v.sortConfig.Field is
model.SortFieldHealth even though SetResourceStatuses only updates sync status;
remove model.SortFieldHealth from the check so the branch only runs for
model.SortFieldSync (i.e., change the conditional to check only for
SortFieldSync), leaving the calls to v.sortNodeChildren(...) and
v.rebuildOrder() intact so sorts still occur when sync status changes.
🧹 Nitpick comments (1)
pkg/sort/sort.go (1)

31-33: Comparator docs should match actual precedence chain.

Current comments imply direct kind/name tiebreaking, but for health/sync ties you first compare by name (Lines 47-50). Consider updating comments/docstrings to reflect the real order to avoid confusion.

Also applies to: 47-50, 59-63, 105-107

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/sort/sort.go` around lines 31 - 33, Update the misleading comment on
comparatorGeneric to reflect the actual precedence chain: it applies semantic
health/sync ordering and, on ties, tiebreaks by name first and then by kind (not
kind then name); find and correct the same phrasing in the nearby comparator/doc
comments in the same file (the other comparator functions and comment blocks
that reference "tiebreaks by kind then name") so all docs match the implemented
comparison order.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/tui/treeview/treeview.go`:
- Around line 318-327: The conditional in SetResourceStatuses currently triggers
a full re-sort when v.sortConfig.Field is model.SortFieldHealth even though
SetResourceStatuses only updates sync status; remove model.SortFieldHealth from
the check so the branch only runs for model.SortFieldSync (i.e., change the
conditional to check only for SortFieldSync), leaving the calls to
v.sortNodeChildren(...) and v.rebuildOrder() intact so sorts still occur when
sync status changes.

---

Nitpick comments:
In `@pkg/sort/sort.go`:
- Around line 31-33: Update the misleading comment on comparatorGeneric to
reflect the actual precedence chain: it applies semantic health/sync ordering
and, on ties, tiebreaks by name first and then by kind (not kind then name);
find and correct the same phrasing in the nearby comparator/doc comments in the
same file (the other comparator functions and comment blocks that reference
"tiebreaks by kind then name") so all docs match the implemented comparison
order.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5fd2887 and 46e8749.

📒 Files selected for processing (5)
  • cmd/app/view.go
  • pkg/model/sort.go
  • pkg/model/types.go
  • pkg/sort/sort.go
  • pkg/tui/treeview/treeview.go

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.

Bug: Issue using sort by health or sync in application view not working properly

1 participant