Add the Usage page with Summary / Tokens / GPU Instances / Storage / Resource
Events tabs over the new metering endpoints: per-resource breakdowns with
date / scope / user / resource filters, trend charts, server-side sortable
tables (GPU-Hours, Instance-Hours, GB-Days, GB-Hours), Excel export with an
in-dialog preview, and KPI cards with help tooltips explaining each metric.
MaaS-only users (no Kubernetes cluster and no resource events) get a
tokens-only view with the tab bar dropped; GPU Service / the full page unlock
for admins, cluster owners, or anyone who has run a resource. Instance-type
rows reuse the GPU Instances list styling, and deleted users / instances /
volumes are flagged in breakdowns and filters.
GPU Service today only schedules on Kubernetes clusters; Docker / cloud
clusters can't host the CRDs. Without any awareness of that, Org
members whose Org has no K8s cluster (and no cluster_access grant on
one) saw a menu they couldn't use and a form that bottomed out with
backend errors.
Two changes lock the UX down:
- Boot probes the caller's cluster list once and stashes
hasKubernetesCluster in initialState. A new canSeeGpuService
predicate gates the menu — admins and Org owners always see it
(they can add the cluster); everyone else only sees it when a
reachable K8s cluster actually exists.
- The instances page filters its own cluster list to Kubernetes
before deciding what to show. With nothing reachable we render the
Deployments-style 'No clusters available. Add a Kubernetes cluster
to get started.' empty state and hide the create-instance CTA;
admins and Org owners additionally get the 'Add cluster' button
that jumps to cluster management.
Relax the dashboard route from canSeeAdmin (strict platform admin)
to canSeeOrgAdmin so the access seam can widen the audience past
the platform admin — by default platform admin, plus whatever an
access extension chooses to admit. The dashboard endpoint takes
care of scoping the response per caller.
Also move Dashboard under the canSeeOrgAdmin bullet in the
access.ts predicate notes.
Four purely-additive seams that let build-time tooling extend host
behaviour without forking files:
* `src/access.extensions.ts` — identity `applyAccessExtensions`
(mirrors `config/routes.extensions.ts`). `src/access.ts` runs the
resolved predicate object through it. Adds two new predicates,
`canSeeOrgAdmin` and `canManageCurrentOrg`, alongside the existing
`canSeeAdmin`. Resources, Models children, Cluster Management, and
Resources/Backends are retagged from `canSeeAdmin` to
`canSeeOrgAdmin`. Users and Dashboard stay strict.
* `src/request.extensions.ts` — identity-empty
`extraRequestInterceptors`. `src/request-config.tsx` spreads it
into the existing `requestInterceptors` list so extensions can
inject context-aware headers without forking the request config.
* Users page action column — renders
`getGPUStackPlugin()?.components?.UserRowActions` next to the
existing DropdownButtons inside a Space when a plugin component
is registered. If absent, the cell renders exactly as before.
* `src/components/plugin-extra-fields.tsx` — generic component-slot
helper. Renders `pluginManager.components.<name>` if registered,
forwarding a `context` payload. Used by create/edit forms to let
plugins inject extra `Form.Item` fields. Mounted in the relevant
create forms — API Keys, Cloud Credentials, Clusters, Model
Routes, Model Providers, and Inference Backends — under the slot
name `CreateOrgScopeField`. Resources whose org is implicit from a
parent (Models / Workers / Benchmarks / Worker Pools / Model Files
inherit from the chosen Cluster) deliberately don't mount the slot.