feat(tree): Expand all / Collapse all for the space page tree #8
Closed
Ghost
wants to merge 2 commits from
feat/tree-expand-collapse-all into develop
pull from: feat/tree-expand-collapse-all
merge into: vvzvlad:develop
vvzvlad:main
vvzvlad:test/244-part-b
vvzvlad:fix/255-ws-redis-adapter-leak
vvzvlad:feat/251-intentional-clear
vvzvlad:fix/252-e2e-open-handles
vvzvlad:feat/184-autonomous-agent-runs
vvzvlad:feat/221-image-captions
vvzvlad:feat/git-sync
vvzvlad:refactor/193-tool-spec-registry
vvzvlad:fix/244-dataloss-bugs
vvzvlad:fix/embeddings-reindex-progress
vvzvlad:develop
vvzvlad:feature/offline-sync
vvzvlad:feat/229-catalog-yaml
vvzvlad:feat/243-blob-sandbox
vvzvlad:feat/228-inline-footnotes
vvzvlad:fix/qa-ui-bugs-216-218
vvzvlad:feature/agent-roles-catalog
vvzvlad:fix/share-alias-rename
vvzvlad:fix/ai-chat-empty-render
vvzvlad:feat/191-chat-doc-binding
vvzvlad:feat/201-temporary-notes
vvzvlad:feat/198-interrupt-agent
vvzvlad:feat/ai-chat-full-history
vvzvlad:feat/199-ai-generate-title
vvzvlad:feat/205-share-aliases
vvzvlad:batch/issues-189-187-170
vvzvlad:feat/170-mcp-test-button
vvzvlad:feat/189-context-badge
vvzvlad:feat/198-interrupt-agent-send-now
vvzvlad:fix/issues-190-159
vvzvlad:fix/ai-chat-new-chat-during-stream
vvzvlad:fix/ai-chat-stream-perf
vvzvlad:batch/issues-2026-06-25
vvzvlad:feat/ai-chat-persistent-history
vvzvlad:fix/ai-chat-copy-chat-wysiwyg
vvzvlad:fix/ai-stream-reset-resilience
vvzvlad:fix/ai-stream-undici-timeout
vvzvlad:fix/footnote-review-1227-followup
vvzvlad:fix/ai-chat-token-counter-realtime
vvzvlad:docs/manual-qa-test-plan
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9d18e20804 | docs: remove implemented tree-expand-collapse-all backlog plan | ||
|
|
0c7d67fe2a |
feat(tree): add Expand all / Collapse all to the space page tree
The space sidebar tree loaded children one level at a time, so there was
no way to expand the whole tree at once - doing it client-side would mean
recursively paging /pages/sidebar-pages hundreds of times. Add a server
endpoint that returns the whole space tree in one permission-filtered
request, and two menu items in the Space menu that drive it.
Server:
- POST /pages/tree (reuses SidebarPageDto, CASL Read gate) returns
{ items: [...] } - a flat list in the same shape as /pages/sidebar-pages
(id, slugId, title, icon, position, parentPageId, spaceId, hasChildren,
canEdit), sorted by position (collate 'C') then id.
- pageRepo.getSpaceDescendants(spaceId, opts): recursive CTE seeded from
space roots (parentPageId IS NULL). getSpaceDescendantsExcludingRestricted
is also added (space-rooted variant of getPageAnd*ExcludingRestricted)
but not used by the read endpoint - see below.
- pageService.getSidebarPagesTree: fetches the whole space tree WITHOUT
SQL restricted-pruning and lets filterAccessibleTreePages drop
inaccessible pages + their subtrees. This mirrors the stepwise
/pages/sidebar-pages behavior, so a restricted page the user HAS
access to stays visible in expand-all (an earlier draft pruned in SQL
and hid them - a behavioural regression). canEdit is per-page in
restricted spaces (filterAccessiblePageIdsWithPermissions) and
space-wide in open spaces. filterAccessibleTreePages is now public
and accepts rootPageId=null to treat every top-level page as a root.
- hasChildren is derived in JS (a page hasChildren iff some returned row
has it as parentPageId) - O(n), no N subqueries.
Client:
- SpaceTree is now a forwardRef exposing { expandAll, collapseAll,
isExpanding } via useImperativeHandle. expandAll fires one
getSpaceTree request, merges the full nested tree into treeDataAtom
(replace-and-merge so the authoritative server tree wins over stale
partially-loaded roots), opens every branch id of the current space,
and guards against space switches via spaceIdRef. Errors are logged
AND surfaced in a notification with the real reason - never a generic
string. collapseAll clears ONLY current-space ids from the shared
open-map (does not disturb other spaces).
- isExpanding is lifted to reactive state in SpaceSidebar and passed as
a prop to SpaceMenu so the Expand item's disabled state actually
updates (a ref mutation alone would not re-render).
- Two Menu.Items inside SpaceMenu (Expand all / Collapse all), gated
only by read access (not canManage) - these are view operations.
|