Skip to content

Refactor workspace sidebar project child ordering#2161

Merged
Kitenite merged 4 commits into
superset-sh:mainfrom
Kitenite:kitenite/lilac-psychology
Mar 7, 2026
Merged

Refactor workspace sidebar project child ordering#2161
Kitenite merged 4 commits into
superset-sh:mainfrom
Kitenite:kitenite/lilac-psychology

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Mar 7, 2026

Summary

  • refactor desktop workspace sidebar ordering around a mixed top-level project child model
  • add shared top-level child ordering/reorder helpers plus a mixed reorder mutation and sidebar DnD wiring
  • expose ordered topLevelItems from grouped workspaces and keep optimistic sidebar caches in sync

Testing

  • bun test apps/desktop/src/renderer/stores/active-drag-item.test.ts apps/desktop/src/lib/trpc/routers/workspaces/utils/project-children-order.test.ts apps/desktop/src/lib/trpc/routers/workspaces/utils/visual-order.test.ts
  • bun run typecheck --filter=@superset/desktop

Summary by cubic

Refactored the desktop workspace sidebar to treat a project’s top‑level children (ungrouped workspaces + sections) as one ordered list. You can now drag sections and ungrouped workspaces together, and keyboard navigation follows the same order.

  • New Features

    • Mixed top‑level ordering with drag‑and‑drop across sections and ungrouped workspaces.
    • New reorderProjectChildren tRPC mutation and useReorderProjectChildren hook with optimistic cache updates.
    • getAllGrouped now exposes topLevelItems; shortcuts and next/previous navigation follow visual order.
  • Refactors

    • Unified tabOrder logic via getProjectChildItems and getMaxProjectChildTabOrder; section creation appends in mixed order.
    • Updated computeVisualOrder and ProjectSection/WorkspaceSection/WorkspaceListItem to render and reorder mixed items; in‑section workspace reordering unchanged.
    • Optimistic updates for close/delete and branch sync keep topLevelItems, sections, and workspaces in sync; added reorderProjectChildrenInCache with tests plus targeted tests for mixed ordering and visual order.

Written for commit 54026e2. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Sidebar now shows mixed top-level ordering of workspaces and sections and lets you reorder project children (workspaces + sections) together.
    • Drag-and-drop supports moving sections as top-level items alongside workspaces.
  • Bug Fixes
    • Improved optimistic updates for closing/deleting workspaces to keep sidebar state consistent.
  • Tests & Documentation
    • Added ordering tests and a planning doc outlining the rollout and acceptance criteria.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 7, 2026

📝 Walkthrough

Walkthrough

This PR implements mixed top-level ordering of project children (ungrouped workspaces and sections interleaved by tabOrder) across backend, utilities, queries, React hooks, cache mutation logic, and sidebar UI/DnD, and adds planning docs and tests to support the migration.

Changes

Cohort / File(s) Summary
Planning & Documentation
apps/desktop/plans/20260306-2140-mixed-project-children-ordering.md
Adds a living ExecPlan describing goals, milestones, assumptions, decisions, and testing for transitioning to mixed top-level ordering.
Project-child ordering utils & tests
apps/desktop/src/lib/trpc/routers/workspaces/utils/project-children-order.ts, .../project-children-order.test.ts
New module providing getProjectChildItems, computeNextProjectChildTabOrder, reorderProjectChildItems plus tests covering mixed workspaces/sections and tabOrder normalization.
DB helper rename
apps/desktop/src/lib/trpc/routers/workspaces/utils/db-helpers.ts
Renames getMaxWorkspaceTabOrder → getMaxProjectChildTabOrder and changes implementation to account for both workspaces and sections.
TRPC procedures & query layer
apps/desktop/src/lib/trpc/routers/workspaces/procedures/query.ts, sections.ts, status.ts
getAllGrouped now computes and returns per-project topLevelItems; section creation uses new max-tab helper; adds reorderProjectChildren TRPC mutation to reorder mixed project children.
Visual ordering refactor & tests
apps/desktop/src/lib/trpc/routers/workspaces/utils/visual-order.ts, .../visual-order.test.ts
computeVisualOrder refactored to iterate getProjectChildItems topLevelItems (unified flow for workspaces/sections); tests updated for new ordering behavior.
Renderer hooks & react-query
apps/desktop/src/renderer/react-query/workspaces/useReorderProjectChildren.ts, index.ts, useWorkspaceShortcuts.ts
New useReorderProjectChildren hook wrapping TRPC mutation + invalidation; useWorkspaceShortcuts updated to flatten workspaces from group.topLevelItems.
Optimistic cache updates
apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts, useDeleteWorkspace.ts
Optimistic updates now remove workspaces from group.topLevelItems and ensure groups preserved/filtered based on combined workspace+section contents.
Sidebar — data flow & components
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectSection.tsx, WorkspaceSidebar.tsx
ProjectSection gains topLevelItems prop, computes discriminated topLevelChildren, and renders mixed items (workspaces or sections) in tabOrder; WorkspaceSidebar forwards topLevelItems.
Sidebar — DnD, sections & items
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSection/WorkspaceSection.tsx, .../WorkspaceListItem/useWorkspaceDnD.ts
DnD extended to support SECTION and WORKSPACE drag kinds (kind discriminator); replaces useReorderSections with useReorderProjectChildren; hover/drop and mutation logic updated to reorder mixed project children and use in-cache reordering helper.
Sidebar constants, types & cache util
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/constants.ts, types.ts, utils/reorderProjectChildrenInCache.ts, .../reorderProjectChildrenInCache.test.ts
Adds SECTION_DND_TYPE, augments drag item types with kind discriminant and optional section.projectId, and new reorderProjectChildrenInCache to reorder topLevelItems and normalize tabOrders (with tests).
Misc UI/store updates & tests
apps/desktop/src/renderer/stores/active-drag-item.test.ts, .../useBranchSyncInvalidation.ts
Test data updated to include drag item kind; branch-sync invalidation now propagates branch updates into nested section workspaces.
TRPC create/import flows
apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts
Replaces usages of old helper with getMaxProjectChildTabOrder to compute tabOrder when creating/importing workspaces.

Sequence Diagram

sequenceDiagram
    participant UI as UI Component<br/>(WorkspaceSection / Sidebar)
    participant Hook as useReorderProjectChildren<br/>Hook
    participant TRPC as TRPC Server<br/>(reorderProjectChildren)
    participant DB as Database
    participant Cache as React Query<br/>Cache

    UI->>Hook: mutate({ projectId, fromIndex, toIndex })
    Hook->>TRPC: call reorderProjectChildren mutation
    TRPC->>DB: fetch project workspaces & sections
    TRPC->>TRPC: getProjectChildItems(projectId, workspaces, sections)
    TRPC->>TRPC: reorderProjectChildItems(...) -> reordered list
    TRPC->>DB: update tabOrder on affected workspaces & sections
    TRPC->>Hook: return success
    Hook->>Cache: invalidateWorkspaceQueries()
    Cache->>UI: refetch grouped data (includes topLevelItems)
    UI->>UI: re-render with updated mixed top-level order
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Poem

🐰 I hopped through tabs and tiny trails,
Sections, workspaces weaving tails,
TabOrder led my nimble nose,
I nudged them where the order grows,
A mixed-top-level garden now unfolds.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main refactoring change: updating workspace sidebar project child ordering to support mixed top-level items.
Description check ✅ Passed The PR description includes a clear summary of changes, testing steps performed, and auto-generated context. However, it does not follow the provided template structure with sections like 'Related Issues' or explicitly marked 'Type of Change'.

✏️ 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.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 24 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectSection.tsx">

<violation number="1" location="apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectSection.tsx:315">
P1: This condition hides the ungrouped drop zone whenever sections exist, which prevents dragging workspaces out of a section in projects that currently have no top-level workspaces.</violation>
</file>

<file name="apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts">

<violation number="1" location="apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts:50">
P3: This optimistic grouped-cache transform duplicates `useDeleteWorkspace` logic. Extract it into a shared helper so close/delete stay behaviorally consistent as grouped sidebar data evolves.

(Based on your team's feedback about avoiding duplicated business logic in cache workflows.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/useWorkspaceDnD.ts">

<violation number="1" location="apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/useWorkspaceDnD.ts:59">
P2: Skip `reorderProjectChildren` when the section index did not change; the current code performs a no-op reorder mutation and unnecessary writes.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

sectionId={null}
sections={sections}
orderedWorkspaceIds={orderedWorkspaceIds}
{topLevelChildren.length === 0 && (
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Mar 7, 2026

Choose a reason for hiding this comment

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

P1: This condition hides the ungrouped drop zone whenever sections exist, which prevents dragging workspaces out of a section in projects that currently have no top-level workspaces.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectSection.tsx, line 315:

<comment>This condition hides the ungrouped drop zone whenever sections exist, which prevents dragging workspaces out of a section in projects that currently have no top-level workspaces.</comment>

<file context>
@@ -253,44 +312,52 @@ export function ProjectSection({
-									sectionId={null}
-									sections={sections}
-									orderedWorkspaceIds={orderedWorkspaceIds}
+							{topLevelChildren.length === 0 && (
+								<div
+									{...ungroupedDropZone.handlers}
</file context>
Suggested change
{topLevelChildren.length === 0 && (
{workspaces.length === 0 && (
Fix with Cubic

);
} else {
reorderWorkspaces.mutate(
reorderProjectChildren.mutate(
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Mar 7, 2026

Choose a reason for hiding this comment

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

P2: Skip reorderProjectChildren when the section index did not change; the current code performs a no-op reorder mutation and unnecessary writes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/useWorkspaceDnD.ts, line 59:

<comment>Skip `reorderProjectChildren` when the section index did not change; the current code performs a no-op reorder mutation and unnecessary writes.</comment>

<file context>
@@ -54,7 +56,7 @@ export function useWorkspaceDnD({
 				);
 			} else {
-				reorderWorkspaces.mutate(
+				reorderProjectChildren.mutate(
 					{
 						projectId: item.projectId,
</file context>
Fix with Cubic

}))
.filter((group) => group.workspaces.length > 0),
.map((group) => {
const isTopLevelWorkspace = group.workspaces.some(
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Mar 7, 2026

Choose a reason for hiding this comment

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

P3: This optimistic grouped-cache transform duplicates useDeleteWorkspace logic. Extract it into a shared helper so close/delete stay behaviorally consistent as grouped sidebar data evolves.

(Based on your team's feedback about avoiding duplicated business logic in cache workflows.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts, line 50:

<comment>This optimistic grouped-cache transform duplicates `useDeleteWorkspace` logic. Extract it into a shared helper so close/delete stay behaviorally consistent as grouped sidebar data evolves.

(Based on your team's feedback about avoiding duplicated business logic in cache workflows.) </comment>

<file context>
@@ -46,11 +46,34 @@ export function useCloseWorkspace(
-						}))
-						.filter((group) => group.workspaces.length > 0),
+						.map((group) => {
+							const isTopLevelWorkspace = group.workspaces.some(
+								(w) => w.id === id,
+							);
</file context>
Fix with Cubic

Copy link
Copy Markdown
Contributor

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts (1)

472-483: ⚠️ Potential issue | 🟠 Major

createBranchWorkspace still bypasses the mixed top-level ordering.

The new top-level workspace creation paths append via shared project-child order, but the branch-workspace path at Line 591 still hardcodes tabOrder: 0 and only shifts workspaces on Lines 601-619. If a section is already first, both children end up at 0, and the next getProjectChildItems pass will move the workspace ahead of the section via tie ordering rather than user intent. Please route createBranchWorkspace through the same helper, or renormalize sections there too.

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

In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts` around
lines 472 - 483, createBranchWorkspace currently hardcodes tabOrder and only
shifts workspaces, causing conflicts with the mixed top-level ordering; update
createBranchWorkspace to use the same ordering helper as other creation paths by
calling getMaxProjectChildTabOrder(input.projectId) and set tabOrder to max+1
when inserting into workspaces (or alternatively invoke the shared normalization
routine used by the other paths). Also ensure you adjust/renormalize section
items consistently by calling getProjectChildItems and applying the same
shift/normalize logic (the insertion in createBranchWorkspace and the shifting
block that currently operates on workspaces must mirror the shared behavior used
elsewhere) so no two items end up with the same tabOrder.
🧹 Nitpick comments (4)
apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts (1)

49-76: Consider extracting this grouped-cache mutation into a shared helper.

This block is effectively duplicated in apps/desktop/src/renderer/react-query/workspaces/useDeleteWorkspace.ts:58-90. Pulling it into one helper would reduce drift the next time the sidebar model changes.

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

In `@apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts` around
lines 49 - 76, The grouped-cache mutation that filters out a workspace (the
map/filter block in useCloseWorkspace) is duplicated in useDeleteWorkspace;
extract it into a shared helper (e.g., updateGroupsRemovingWorkspace(groups, id)
or normalizeGroupsAfterWorkspaceRemoval) that accepts the groups array and the
workspace id and returns the new groups array using the same map/filter logic
(preserve topLevelItems removal when isTopLevelWorkspace, remove workspace from
group.workspaces and section.workspaces, then filter out empty groups). Replace
the inlined code in useCloseWorkspace and useDeleteWorkspace with a call to this
helper, export/import it from a common module, and ensure TypeScript types for
Group/Section/Workspace and any tests or usage of mutate callbacks are updated
accordingly.
apps/desktop/src/lib/trpc/routers/workspaces/procedures/sections.ts (1)

98-127: Consider deprecating reorderSections in favor of reorderProjectChildren.

With the new mixed top-level ordering model, reorderSections operates only on sections and doesn't account for interleaved ungrouped workspaces. This could lead to inconsistent tabOrder values if used alongside the new reorderProjectChildren mutation.

If reorderSections is still needed for section-only reordering within the UI, consider adding a comment clarifying its intended use case versus the newer reorderProjectChildren.

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

In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/sections.ts` around
lines 98 - 127, The reorderSections mutation (reorderSections) only reorders
workspaceSections and can conflict with the newer reorderProjectChildren when
project-level mixed ordering exists; mark reorderSections as deprecated and add
a clear comment above the procedure explaining it only reorders sections within
a project (does not handle interleaved ungrouped workspaces), advise using
reorderProjectChildren for mixed top-level ordering, and keep the existing logic
(uses localDb, workspaceSections, reorderItems) for any UI flows that
specifically require section-only reordering.
apps/desktop/src/lib/trpc/routers/workspaces/procedures/status.ts (1)

64-97: Indices alone make this reorder mutation brittle.

The server rewrites whichever child currently sits at fromIndex. If a top-level child is inserted or removed while a drag is in flight, this can persist the wrong reorder. The drag payload already has the child's kind/id, so passing that through and verifying items[fromIndex] still matches would make the mutation deterministic.

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

In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/status.ts` around
lines 64 - 97, The reorderProjectChildren mutation is brittle because it blindly
reorders whatever is at items[fromIndex]; change the mutation to accept the
dragged child's identifier (e.g., kind and id) in the input, then verify that
items[fromIndex] matches that identifier before calling reorderProjectChildItems
in order to make the operation deterministic; if items[fromIndex] does not
match, locate the correct current index by matching kind/id in the items array
(or return an error) and use that resolved index as the source to call
reorderProjectChildItems with fromIndex and toIndex. Ensure updates touch the
input z.object schema, the mutation handler (reorderProjectChildren), and the
logic that computes and passes indices to reorderProjectChildItems.
apps/desktop/src/lib/trpc/routers/workspaces/utils/project-children-order.test.ts (1)

60-78: Please lock down no-op and bounds handling here too.

reorderProjectChildren now depends on reorderProjectChildItems for all index semantics, but this suite only exercises the happy path. Adding fromIndex === toIndex and out-of-range cases would make regressions much easier to catch.

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

In
`@apps/desktop/src/lib/trpc/routers/workspaces/utils/project-children-order.test.ts`
around lines 60 - 78, Add tests to cover no-op and bounds behavior for
reorderProjectChildItems (and mirror semantics expected from
reorderProjectChildren): add a test where fromIndex === toIndex to assert the
output is normalized (tabOrder reset to 0..n-1) but item order unchanged, and
add tests for out-of-range indices (negative and indices >= items.length) to
assert they are handled deterministically (e.g., treated as clamped to 0 or
items.length-1 or throw—match reorderProjectChildren behavior) so regressions
are caught; reference the functions reorderProjectChildItems and
reorderProjectChildren when adding these cases to
project-children-order.test.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/desktop/plans/20260306-2140-mixed-project-children-ordering.md`:
- Around line 41-54: The ExecPlan is stale: update the checklist to reflect
completed work (check the remaining box for running typecheck/QA or mark
appropriately) and fill in the decision logs (previously TBD) to reflect that
`topLevelItems` was added to `getAllGrouped` and `reorderProjectChildren` (and
that `computeVisualOrder` was moved onto the shared mixed top-level ordering
helper), and confirm how new items should receive `tabOrder`; ensure lines
referencing `topLevelItems`, `reorderProjectChildren`, `getAllGrouped`, and
`computeVisualOrder` are updated to the current state and remove or adjust any
outstanding TODOs before merging.

In `@apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts`:
- Around line 68-76: The empty-group filter currently sums group.workspaces and
section.workspaces to decide whether to keep a group; change it to check
group.topLevelItems (e.g., replace the whole predicate in the .filter callback)
so the filter keeps groups when group.topLevelItems.length > 0 — update the
.filter to use group.topLevelItems.length instead of the manual sum over
workspaces/sections to prevent removing groups that still have visible top-level
children.

---

Outside diff comments:
In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts`:
- Around line 472-483: createBranchWorkspace currently hardcodes tabOrder and
only shifts workspaces, causing conflicts with the mixed top-level ordering;
update createBranchWorkspace to use the same ordering helper as other creation
paths by calling getMaxProjectChildTabOrder(input.projectId) and set tabOrder to
max+1 when inserting into workspaces (or alternatively invoke the shared
normalization routine used by the other paths). Also ensure you
adjust/renormalize section items consistently by calling getProjectChildItems
and applying the same shift/normalize logic (the insertion in
createBranchWorkspace and the shifting block that currently operates on
workspaces must mirror the shared behavior used elsewhere) so no two items end
up with the same tabOrder.

---

Nitpick comments:
In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/sections.ts`:
- Around line 98-127: The reorderSections mutation (reorderSections) only
reorders workspaceSections and can conflict with the newer
reorderProjectChildren when project-level mixed ordering exists; mark
reorderSections as deprecated and add a clear comment above the procedure
explaining it only reorders sections within a project (does not handle
interleaved ungrouped workspaces), advise using reorderProjectChildren for mixed
top-level ordering, and keep the existing logic (uses localDb,
workspaceSections, reorderItems) for any UI flows that specifically require
section-only reordering.

In `@apps/desktop/src/lib/trpc/routers/workspaces/procedures/status.ts`:
- Around line 64-97: The reorderProjectChildren mutation is brittle because it
blindly reorders whatever is at items[fromIndex]; change the mutation to accept
the dragged child's identifier (e.g., kind and id) in the input, then verify
that items[fromIndex] matches that identifier before calling
reorderProjectChildItems in order to make the operation deterministic; if
items[fromIndex] does not match, locate the correct current index by matching
kind/id in the items array (or return an error) and use that resolved index as
the source to call reorderProjectChildItems with fromIndex and toIndex. Ensure
updates touch the input z.object schema, the mutation handler
(reorderProjectChildren), and the logic that computes and passes indices to
reorderProjectChildItems.

In
`@apps/desktop/src/lib/trpc/routers/workspaces/utils/project-children-order.test.ts`:
- Around line 60-78: Add tests to cover no-op and bounds behavior for
reorderProjectChildItems (and mirror semantics expected from
reorderProjectChildren): add a test where fromIndex === toIndex to assert the
output is normalized (tabOrder reset to 0..n-1) but item order unchanged, and
add tests for out-of-range indices (negative and indices >= items.length) to
assert they are handled deterministically (e.g., treated as clamped to 0 or
items.length-1 or throw—match reorderProjectChildren behavior) so regressions
are caught; reference the functions reorderProjectChildItems and
reorderProjectChildren when adding these cases to
project-children-order.test.ts.

In `@apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts`:
- Around line 49-76: The grouped-cache mutation that filters out a workspace
(the map/filter block in useCloseWorkspace) is duplicated in useDeleteWorkspace;
extract it into a shared helper (e.g., updateGroupsRemovingWorkspace(groups, id)
or normalizeGroupsAfterWorkspaceRemoval) that accepts the groups array and the
workspace id and returns the new groups array using the same map/filter logic
(preserve topLevelItems removal when isTopLevelWorkspace, remove workspace from
group.workspaces and section.workspaces, then filter out empty groups). Replace
the inlined code in useCloseWorkspace and useDeleteWorkspace with a call to this
helper, export/import it from a common module, and ensure TypeScript types for
Group/Section/Workspace and any tests or usage of mutate callbacks are updated
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: de92f204-80d9-428a-b1d7-3f50d6f51395

📥 Commits

Reviewing files that changed from the base of the PR and between ebda4b4 and 7a8d754.

📒 Files selected for processing (24)
  • apps/desktop/plans/20260306-2140-mixed-project-children-ordering.md
  • apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/procedures/query.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/procedures/sections.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/procedures/status.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/db-helpers.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/project-children-order.test.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/project-children-order.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/visual-order.test.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/visual-order.ts
  • apps/desktop/src/renderer/hooks/useWorkspaceShortcuts.ts
  • apps/desktop/src/renderer/react-query/workspaces/index.ts
  • apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts
  • apps/desktop/src/renderer/react-query/workspaces/useDeleteWorkspace.ts
  • apps/desktop/src/renderer/react-query/workspaces/useReorderProjectChildren.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/ProjectSection/ProjectSection.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/useWorkspaceDnD.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSection/WorkspaceSection.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebar.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/constants.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/types.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/utils/reorderProjectChildrenInCache.ts
  • apps/desktop/src/renderer/screens/main/hooks/useBranchSyncInvalidation/useBranchSyncInvalidation.ts
  • apps/desktop/src/renderer/stores/active-drag-item.test.ts

Comment on lines +41 to +54
## Progress

- [x] (2026-03-07 05:40Z) Draft ExecPlan
- [x] (2026-03-07 05:43Z) Inventory ordering assumptions across `getAllGrouped`, `computeVisualOrder`, sidebar DnD, and workspace shortcuts
- [x] (2026-03-07 05:45Z) Add shared server helper for mixed top-level project child ordering
- [x] (2026-03-07 05:45Z) Add backend tests for mixed workspace/section ordering
- [x] (2026-03-07 05:45Z) Route new top-level workspace/section creation through shared top-level child ordering
- [x] (2026-03-07 05:47Z) Move `computeVisualOrder` onto the shared mixed top-level ordering helper
- [x] (2026-03-07 06:19Z) Add `topLevelItems` to `getAllGrouped` and update keyboard shortcuts to use mixed top-level ordering
- [x] (2026-03-07 06:19Z) Replace sidebar rendering with one ordered top-level list per project
- [x] (2026-03-07 06:19Z) Replace section-only reorder mutation/UI flow with mixed top-level reorder flow
- [x] (2026-03-07 06:19Z) Update keyboard shortcut and next/previous workspace ordering
- [ ] Audit create/move paths so new items get correct top-level `tabOrder`
- [ ] Run `bun run typecheck`, targeted desktop tests, and manual sidebar DnD QA
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

This ExecPlan is already stale.

Line 54 is still unchecked, and Lines 68-75 leave both decision logs as TBD even though this PR already adds topLevelItems and reorderProjectChildren. Since Line 3 says these sections must stay current, please update the plan before merge.

Also applies to: 66-75

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

In `@apps/desktop/plans/20260306-2140-mixed-project-children-ordering.md` around
lines 41 - 54, The ExecPlan is stale: update the checklist to reflect completed
work (check the remaining box for running typecheck/QA or mark appropriately)
and fill in the decision logs (previously TBD) to reflect that `topLevelItems`
was added to `getAllGrouped` and `reorderProjectChildren` (and that
`computeVisualOrder` was moved onto the shared mixed top-level ordering helper),
and confirm how new items should receive `tabOrder`; ensure lines referencing
`topLevelItems`, `reorderProjectChildren`, `getAllGrouped`, and
`computeVisualOrder` are updated to the current state and remove or adjust any
outstanding TODOs before merging.

Comment on lines +68 to +76
.filter(
(group) =>
group.workspaces.length +
group.sections.reduce(
(sum, section) => sum + section.workspaces.length,
0,
) >
0,
),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use topLevelItems for the empty-group check.

This filter only counts remaining workspaces. If the last workspace in a project is closed but the project still has section items, the whole group disappears from the optimistic cache until refetch even though topLevelItems still has visible children.

💡 Proposed fix
-						.filter(
-							(group) =>
-								group.workspaces.length +
-									group.sections.reduce(
-										(sum, section) => sum + section.workspaces.length,
-										0,
-									) >
-								0,
-						),
+						.filter((group) => group.topLevelItems.length > 0),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.filter(
(group) =>
group.workspaces.length +
group.sections.reduce(
(sum, section) => sum + section.workspaces.length,
0,
) >
0,
),
.filter((group) => group.topLevelItems.length > 0),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/react-query/workspaces/useCloseWorkspace.ts` around
lines 68 - 76, The empty-group filter currently sums group.workspaces and
section.workspaces to decide whether to keep a group; change it to check
group.topLevelItems (e.g., replace the whole predicate in the .filter callback)
so the filter keeps groups when group.topLevelItems.length > 0 — update the
.filter to use group.topLevelItems.length instead of the manual sum over
workspaces/sections to prevent removing groups that still have visible top-level
children.

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/utils/reorderProjectChildrenInCache.test.ts (1)

56-73: Consider adding a test for undefined input.

The isolation test is valuable. However, looking at the real call site in WorkspaceSection.tsx, the function receives oldData from setData(undefined, (oldData) => ...) which could be undefined if the cache is empty.

Consider adding a test to verify the function handles undefined gracefully:

test("returns undefined when data is undefined", () => {
  expect(reorderProjectChildrenInCache(undefined, "p1", 0, 1)).toBeUndefined();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/utils/reorderProjectChildrenInCache.test.ts`
around lines 56 - 73, Add a unit test to ensure reorderProjectChildrenInCache
handles undefined input: add a test in reorderProjectChildrenInCache.test.ts
that calls reorderProjectChildrenInCache(undefined, "p1", 0, 1) and asserts the
result is undefined (e.g., expect(...).toBeUndefined()), since the real caller
in WorkspaceSection.tsx may pass oldData as undefined.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/utils/reorderProjectChildrenInCache.test.ts`:
- Around line 56-73: Add a unit test to ensure reorderProjectChildrenInCache
handles undefined input: add a test in reorderProjectChildrenInCache.test.ts
that calls reorderProjectChildrenInCache(undefined, "p1", 0, 1) and asserts the
result is undefined (e.g., expect(...).toBeUndefined()), since the real caller
in WorkspaceSection.tsx may pass oldData as undefined.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 87d904e7-4b65-4c0f-855d-3aea1de4757d

📥 Commits

Reviewing files that changed from the base of the PR and between 7a8d754 and 54026e2.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/utils/reorderProjectChildrenInCache.test.ts

@Kitenite Kitenite merged commit 0ca7016 into superset-sh:main Mar 7, 2026
7 checks passed
@Kitenite Kitenite deleted the kitenite/lilac-psychology branch March 7, 2026 06:51
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.

1 participant