feat(ui): add a grid view to installed services - #3868
Merged
Conversation
The Installed services page renders a grid of service tiles as well as the table, chosen by a toggle in the page header and remembered in the ui node of patch-db so the choice follows the user across browsers. Below the mobile breakpoint the table loses its headers and re-grids every row, so the grid is forced there and the toggle is hidden; the row's mobile styles go with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MattDHill
force-pushed
the
feat/services-grid-view
branch
from
September 3, 2026 02:48
93faf6b to
c816d5d
Compare
dr-bonez
approved these changes
Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3740.
The Installed services page has had exactly one layout: a table. That is the right shape for a handful of services and the wrong one for a wall of them — five columns of mostly-repeated text where someone scanning for "is Nextcloud up" wants an icon and a status. This adds a grid of service tiles beside it, on the same layout as the marketplace grid.
The toggle
Two icon buttons in a
tui-segmentedat the top right of the page pick the renderer. The choice lands in theuinode of patch-db (servicesView) rather thanlocalStorage, alongsideregistries,startosRegistryandhiddenUpdates, so it follows the user to any browser pointed at the server. Bothpatchdb-ui-seed.jsonandpatchdb-ui-seed.beta.jsonseed it.The field is optional on
UIDataand defaults in the UI, so a server upgraded from a version without it behaves exactly as the seed does and nostart-coremigration is needed.Sorting
Dropping the column headers would drop sort control with them, so the grid carries an A–Z / Z–A toggle, persisted next to the layout. Both renderers import the same
byNamecomparator from a newsorters.ts— lifted out oftable.component.ts— so they can never disagree about what sorted-by-name means. Sorting by status stays a table-only affordance, driven by its headers and not remembered.Mobile is grid-only
Below the app's
_mobilebreakpoint the table already stopped being a table:.g-tablehidestheadand the row re-grids itself into an icon-plus-stacked-text block. The columns and click-to-sort headers that justify the list view are gone there, so the toggle was offering a choice between a card and a worse card. The grid is now forced below that breakpoint and the toggle is hidden — a control that cannot change anything is worse than no control — which let ~55 lines of mobile row CSS and the markup it propped up (thegrid-areabindings, the desktop-hiddenUptime:label) go.The layout preference is named
desktopLayoutto say so; the sort direction is not desktop-scoped and applies everywhere.Two things worth a look
appearance="floating"resolves tobackground: var(--tui-background-elevation-1), which this theme defines as the same colour asg-page's own ground, and Taiga force-disables the card shadow in dark mode. Tiles were invisible. The marketplace only escapes this because its shell paints a lighter ground behind the same cards. The tile repoints that token at the surface value the header chrome uses rather than special-casing the page background.The preference is seeded once from patch-db (
take(1)) instead of being reconciled continuously. A write's echo arriving after a newer local change would otherwise reset the view to the older value; writes are also chained so rapid clicks reach the server in click order. The cost is that a second tab's change is not picked up until reload, which seems right for a view preference.Fixtures
mockPatchDatagains five more installed services covering running, health-loading, stopped, health-failure and error states, so the grid is actually a grid under mocks. The mocksetDbValuealso now applies its patch tomockPatchData.uiand persists it tosessionStorage, andsubscribeToPatchDBmerges it back — previouslymockRevisiononly pushed a patch to the client stream, so no patch-db write survived a refresh under mocks. That gap applied tohiddenUpdatesandstartosRegistrytoo.🤖 Generated with Claude Code