Repository navigation
feat: commercial dashboard, sortable tables, multi-filters, user management - #4
Merged
Merged
Conversation
…gement Dashboard Drops the greeting and replaces it with the operational picture: revenue taken, value awaiting payment, outstanding shipments and their value, average order, then catalogue and stock, an order-mix breakdown and the value of cancelled and refunded orders. Restocking stays, since it was the part already earning its place. Every figure is derived from the orders list because the API exposes no stats endpoint. That has a limit worth knowing, so the page says it: the figures cover the most recent 100 orders rather than implying all time. Tables Rows are now activatable - products opens the editor, orders opens the detail, users opens the role change. Done as a directive that adds the button role, a tab stop and Enter/Space handling, because a row that only answers to a mouse is invisible to keyboard and screen-reader users. Clicks that start on a nested button are ignored, so Delete never also opens the editor. Column headings sort. Clicking the active column flips direction and clicking another moves to it ascending, which is what people expect; having it in one helper stops each table inventing its own rule. Missing values sort last in both directions - absent is not "smallest", and burying real data under blanks on one direction is not useful. Filtering A shared filter bar combining free-text search with any number of multi-select facets, all applied together. Multi-select because the real questions are compound - "paid or ready to ship", "low or out of stock" - and one-at-a-time filtering makes the operator look twice and hold the result in their head. Selections show as removable chips so what is being filtered is never hidden in a collapsed control. Orders filtering moved client-side: the API's status parameter accepts only one value, which cannot express the questions above. Users New section listing Keycloak accounts with their role, able to promote a customer to admin, demote an admin, and delete an account. It calls Keycloak's admin API directly rather than proxying through FastAPI. Keycloak owns accounts and roles, so going straight there means Keycloak enforces permission - the admin's token carries manage-users and view-realm through the composite admin role, and a customer's token gets 403. Re-implementing that check in the API would put a privilege-escalation bug one mistake away. The interceptor's allowlist now covers that host explicitly rather than sending the token anywhere. Two guards stop the worst mistake: self-actions are refused, and demoting the last remaining administrator is blocked, because a realm with no admin is only recoverable from the Keycloak console. Verified against the live stack: promote, demote and delete all round-trip through real Keycloak with no failed calls; sorting sets aria-sort; selecting two order statuses narrows 16 rows to 4 with two chips shown; clicking a product row opens the editor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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 #3.
Dashboard
Greeting gone. In its place, the operational picture: revenue taken, value awaiting
payment, outstanding shipments and their value, average order, then catalogue and
stock, an order-mix breakdown, and the value of cancelled and refunded orders.
Restocking stays — it was the part already earning its place.
Every figure is derived from the orders list, because the API has no stats endpoint. That
carries a limit worth knowing, so the page states it rather than implying all-time totals:
"Money, fulfilment and stock across the most recent 100 orders."
Clickable rows
Products opens the editor, orders the detail, users the role change.
Implemented as a directive that adds the button role, a tab stop and Enter/Space handling —
a row that answers only to a mouse is invisible to keyboard and screen-reader users. Clicks
originating on a nested control are ignored, so Delete never also opens the editor.
Sorting
Clicking the active column flips direction; clicking another moves to it ascending. In one
helper, so no table invents its own rule, and
aria-sortis set so the ordering isannounced rather than being visual-only.
Missing values sort last in both directions. Absent is not "smallest", and burying real
data under blanks on one direction is not what anyone wants.
Multi-filters
A shared bar combining free-text search with any number of multi-select facets, applied
together. Multi-select because the real questions are compound — "paid or ready to ship",
"low or out of stock" — and one-at-a-time filtering makes the operator look twice and hold
the result in their head. Selections appear as removable chips, so what is filtered is never
hidden inside a collapsed control.
Orders filtering moved client-side: the API's
statusparameter takes a single value andcannot express those questions.
Users
Lists Keycloak accounts with their role; promote, demote, delete.
It calls Keycloak's admin API directly rather than proxying through FastAPI. Keycloak
owns accounts and roles, so going straight there means Keycloak enforces permission — the
admin's token carries
manage-usersandview-realmthrough the compositeadminrole,and a customer's token gets 403 without our code being involved. Re-implementing that check
in the API would put a privilege-escalation bug one mistake away. The interceptor's
allowlist now names that host explicitly rather than widening to "anything".
Two guards prevent the damaging mistake:
with no administrator is only recoverable from the Keycloak console.
Both are shown as disabled buttons with the reason in the tooltip, rather than a dead
control.
Verification
Driven against the live stack — real Keycloak, real API:
New tests cover the sort helper (including the missing-value rule and non-mutation) and the
two user-management guards.
Not included
Users are listed with
max=200and roles resolved per account, since Keycloak has noendpoint returning both. Fine at this size; a realm with thousands of accounts should move
to a paged view resolving roles lazily. Noted in the service.