Skip to content

pages: shared facet filters and one list toolbar on every list page - #276

Draft
jseifeddine wants to merge 6 commits into
sol1:mainfrom
jseifeddine:ux/shared-facet-filters-toolbars
Draft

jseifeddine wants to merge 6 commits into
sol1:mainfrom
jseifeddine:ux/shared-facet-filters-toolbars

Conversation

@jseifeddine

@jseifeddine jseifeddine commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Builds on #274 (feat/role-baked-pages-unified-nav) and #275 (ux/sessions-adhoc-modal); its own change is the last commit, the compare on the fork: jseifeddine/rustguac@ux/sessions-adhoc-modal...ux/shared-facet-filters-toolbars. The branch carries #271–#275 until they merge into main, so the Files tab shows them too.

Fixes #261.

static/filters.js: a dropdown per facet, multi-select with counts, All/None, find box, Clear, per-page persistence. One toolbar (search, facets, action on the right) on Sessions, Recordings, Reports and the Connections folder header, which becomes sticky. Sessions gains a search and a User facet; shared .type-badge everywhere; redundant page titles dropped.

Testing

  • Browser: facet-filters.spec.mjs.
  • Manual: Recordings, pick Type = rdp and type a user in the search: both apply; reload: the facet is still set.

…ion feedback

connections: sticky self-scrolling sidebar, flush-left folder rows (sol1#254)

With 20+ customer folders the sidebar shared the page scroll with the
entries table, so picking a folder far down the list changed content off
screen, and every leaf row was pushed ~90px in by a hidden chevron
placeholder while names were ellipsised.

- .sidebar is position: sticky with its own thin scrollbar and
  max-height tied to the viewport; .main keeps scrolling with the page.
- The FOLDERS header is wrapped in .sidebar-pin together with a new
  empty #sidebar-tools slot, both pinned at the top of the sidebar.
- buildFolderNode() renders a .tree-chevron only for folders with
  children; leaves get no placeholder (removed the .tree-chevron.leaf
  rule), so rows start right after the 3px accent bar.
- Name, entry-count pill (.folder-count, now just the number with a
  data-count attribute and "N entries" title) and a smaller scope glyph
  share one line; description sits beneath at --fz-xs; rows are ~40px.
- Nested .tree-children indent 14px behind the existing dashed guide.

All new CSS is in one block at the end of the page's <style>.

connections: selection feedback and in-place sidebar updates (sol1#254)

With a long folder list, clicking a folder appeared to do nothing: the
entries table was off screen, the only loading state was a text swap,
and nothing drew the eye to the changed title. loadEntries() also
called renderFolders() just to update one count badge, rebuilding the
whole sidebar on every click.

- Jump to the entries header when the shown folder changes (smooth,
  'auto' under prefers-reduced-motion); never on initial load, live
  refresh, forceRefresh after a mutation or search clear.
- .changed on #entries-header replays a 350ms title slide-in and an
  accent underline flash on the action bar; cleared on animationend.
- Skeleton table (4 shimmering rows, same geometry as the real table)
  while entries are fetched from the server; the cached path renders
  immediately with no skeleton.
- updateFolderCount() swaps the number in the existing .folder-count
  badge in place; renderFolders() only as a fallback when the badge
  does not exist yet.
- renderFolders() is wrapped (not edited) to preserve the sidebar
  scroll position across rebuilds and to reveal the selected row within
  the sidebar (nearest, no page scroll) when the selection changes or
  after loadFolders() rebuilds the tree.

tests: sidebar scrolls on its own, selected folder marked and kept in view (sol1#254)
connections: folder filter box, keyboard tree navigation and ARIA roles (sol1#254)

The sidebar lists one folder per customer and gets long. Add a live
filter box (name, path, description; loaded subfolders included, with
ancestors of a match kept visible) mounted in a new #sidebar-tools div,
persisted in sessionStorage so live refreshes keep it. Make the tree
keyboard-navigable (arrows, Home/End, Enter/Space, Left/Right to
collapse/expand) with role=tree/treeitem, aria-selected/expanded and a
roving tabindex, and add an 'f' shortcut that mirrors the '/' one.

Implemented by post-processing the DOM renderFolders() builds and by
wrapping that function; its body is unchanged.

tests: folder filter box, ARIA tree and keyboard navigation (sol1#254)
…sublines

The sticky sidebar was sized from the viewport rather than from where
it starts under the header, so at the top of the page its bottom hung
off screen and the page had to be scrolled before the list could be
scrolled to its end. The page is now an app shell: the header stays,
and the sidebar and the entries area are two independent scroll panes
that always fit the viewport. Selecting a folder scrolls the entries
pane, not the page.

The sidebar is a bordered panel: one pinned header band on a surface
tone with the Folders label, a compact new-folder button and the filter
box, then the list with hairline dividers, 46px rows and one 12px edge
for every band. "host (Tenant)" names read as the host over a quieter
tenant line in the entries and search tables, and a role-gate
reconciliation covers API-key users.

Part of sol1#254

connections: scroll only the entries pane on selection, clip the body

jumpToEntriesHeader() used scrollIntoView(), which scrolls every
scrollable ancestor, and a body with overflow hidden can still be
scrolled by script: selecting a folder pushed the brand row and the nav
off the top of the page. Scroll the entries pane to its top instead, and
make the body overflow: clip, which no script can scroll. The body's
border box now spans the full height so the page padding stays inside
the viewport.

Part of sol1#254

tests: app shell with two independent scroll panes and a clipped body (sol1#254)
…o the served page

pages: bake the caller's role into the served page, version the logo

The header flashed on every navigation: gated links (Sessions, Reports,
Tokens, Admin) were hidden in the markup and shown after /api/me
answered, the logo was revalidated on each page load, and the nav
differed between pages, so switching tabs reflowed the header.

Serve each page in five variants, one per role level, that differ only
in a data-role-level attribute on <html>; optional auth now runs on the
page routes so a logged-in request gets its variant with the navigation
already right on first paint. The ETag is per variant. The logo joins
scripts and stylesheets in content-addressed, immutable caching.

Part of sol1#254

pages: one navigation for every page, gated by role in CSS

Every page carries the same eight links in the same order, including
the Credentials entry (which opens the dialog on the connections page
and deep-links to it from elsewhere), with data-min-role on the gated
ones instead of inline display:none. rustguac.css shows them from the
data-role-level the server writes on <html>; the pages set that
attribute themselves from /api/me for API-key users, whose page GET
carries no credential. Admin-only controls follow the same gate, so
nothing pops in after the first paint. Recordings shows "host (Tenant)"
entry names as the host over a quieter tenant line.

Part of sol1#254

tests: one navigation on every page, gated by data-role-level
The ad-hoc session form sat in a collapsible framed box above the session
list. It now opens as a modal from a button on the right of the Sessions
heading, dropping the page's framed form box inside the dialog, with the
primary button first and Cancel at the same height as Connect, matching the
other modals. Section headings centre their heading and action button on
one line.

Part of sol1#254

tests: the ad-hoc session form opens as a modal
(cherry picked from commit 872cc61)
reports: show entry names as host over a tenant subline

Session history and the most-used table showed "host (Tenant)" names
wrapped across three lines; they now read as the host over a quieter
tenant line, as on Connections and Recordings.

Part of sol1#254

sessions, recordings: show the session type with the shared badge (sol1#254)

The sessions page printed the type as plain text and the recordings page
as a coloured word; connections and reports already used .type-badge
from rustguac.css. All four now use the same badge. The Proxmox badge
colour moves into the shared stylesheet with the others.

Shared facet filters (static/filters.js) on the sessions, recordings,
reports and connections pages: a dropdown per facet (Type, Folder, User,
Owner, Status as each page has them), multi-select with counts that narrow
as other facets apply, All/None, a find box for long lists, a Clear filters
link, and selections kept per page in sessionStorage. They sit beside the
existing text search and combine with it; the buttons are compacted to the
height and padding of the other controls and the dropdown reserves room for
its scrollbar so it no longer sits over the counts.

The list pages share one toolbar: a borderless row with the search box, the
facets and the page action on the right (New ad-hoc session, Export CSV),
placeholders standardised to 'Search <noun> (fields…)'. Sessions gains a
search (user, host, owner, ID, type) and a User facet; Reports moves from
its own .filter-bar to the shared one. The redundant page titles go — the
nav already names and marks the active page — and section headings centre
their heading and action button on one line.

Connections: the folder header is sticky at the top of the entries pane, so
it stays under the nav and the active-sessions strip while a long table
scrolls. All four pages show the session type with the shared .type-badge
(the Proxmox colour moves into rustguac.css), reports and recordings show
"host (Tenant)" names as the host over a quieter tenant line, and the key
glyph before Credentials in the nav is sized to the text.

Part of sol1#254

tests: shared facet filters and the list toolbar on every list page
@jseifeddine
jseifeddine force-pushed the ux/shared-facet-filters-toolbars branch from 2d34b97 to 4707d03 Compare October 13, 2026 07:04
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.

List pages: no facet filters, and each page has its own toolbar

1 participant