Skip to content

feat(ui): Redesign the workload overview - #627

Merged
mattdjenkinson merged 4 commits into
mainfrom
feat/workload-overview-redesign
Nov 7, 2026
Merged

mattdjenkinson merged 4 commits into
mainfrom
feat/workload-overview-redesign

Conversation

@mattdjenkinson

@mattdjenkinson mattdjenkinson commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Redesigns the compute plugin's workload Overview, following the "hairline sections" concept explored in Pencil.

  • Summary card in the ALB overview's style: status, when the workload last changed, and the last hour's instances, CPU, memory, network and requests.
  • Instances card: a table matching the ALB Backends card, with portal-style status badges (failure reasons appear in a tooltip), per-instance network traffic, and Logs, Shell and Metrics actions.
  • Load balancers card: each load balancer that sends traffic to the workload, with its state, hostname and the routes that reach it, e.g. / → :8080 (http), plus the workload's share when a rule splits traffic. Route reading comes from @kevwilliams's feat/workload-overview-polish branch.
  • Hostnames stay with their load balancer. The URL and Visit button by the title, the endpoint line on the summary card and the Load balancer row in Configuration are gone. With two load balancers, showing the first one's hostname made it look like the workload's own URL.
  • Loading states match what they stand in for:
    • Each stat in the summary card shows a placeholder until its own data arrives, instead of reading as zero.
    • The Instances and Load balancers cards load as their tables, headers included.
    • The page and workloads-list skeletons use the real tables' markup.
  • datum-ui 3.8.0 in both plugins, the version cloud-portal runs.
  • What changed and Configuration: recent activity replaces the recent-logs block, which was usually empty. Configuration gets its own card.
  • Deploy states replace the step-by-step bar. A deploy takes about 10–15s on staging, faster than the page polls, so the bar never showed its steps. The card now shows:
    • deploying: elapsed time and a live instance count;
    • deployed: "Deployed in 13s" with a Created → Placed → Started → Ready timeline rebuilt from condition timestamps;
    • stalled: names the cause the API reports. A missing Secret or ConfigMap is read from the workload's deployments, the only place it's reported; quota waits come from the instances;
    • failed: the image can't be pulled, or the configuration was rejected.
  • Polling stays at 3s until instances are ready, not just until the workload reports Available (which happens as soon as a placement exists).
  • Instance page: a problem callout explains failures in plain words, with what to try.
  • Metrics tab: network in and out per instance, replacing the "Not collected yet" stub.
  • Workloads list: the sparklines drop their value text.
Screen.Recording.2026-11-06.at.16.51.43.mov

Data the design wants that the API doesn't expose yet (the asleep state and wake-ups, structured crash details, image digest and revision, image pull progress) is left out for now and tracked in #626.

Test plan

  • pnpm typecheck, pnpm test (408 tests), pnpm build
  • Rendered every state against cloud-portal's compiled CSS with sample data, at desktop and phone widths; checked the utility classes used compile in the host
  • Network queries checked against the staging metrics API
  • Locally against cloud-portal#1800: Add to a load balancer on cpu-mem-wave (which had no service) opens the dialog with it chosen
  • Check in the portal against the compute-ui-state-test workloads on staging: state-missing-secret (stalled), state-bad-image (failed), state-crashloop (crash band), plus a fresh demo deploy (deploying, then deployed)

mattdjenkinson and others added 4 commits November 6, 2026 13:25
The overview now leads with a summary card in the ALB overview's style:
status, endpoint, when it last changed, and the last hour's instances,
CPU, memory, network and requests. Instances and configuration move into
cards of their own, and recent activity replaces the recent-logs block
that was usually empty.

A deploy takes seconds, faster than the page polls, so the card no longer
shows a step-by-step progress bar. It says what it can say truthfully:
deploying, deployed (with a timeline rebuilt from condition timestamps),
stalled with the cause the API reports (a missing Secret or ConfigMap,
quota), or failed. Pages poll every 3s until instances are actually
ready, not just until the workload reports Available.

Instance pages explain a failure in plain words with what to try, and the
instances table shows per-instance network traffic and status badges in
the portal's style. The workload Metrics tab gains network in and out.
Tailwind's animate-pulse only faded the bar in place. The host compiles no
keyframes the plugin could use, so the bar is animated with the Web
Animations API instead, and stays still under prefers-reduced-motion.
…place

A Load balancers card on the workload Overview lists each load balancer
whose rules send traffic to the workload: its state, hostname and the
routes that reach it ("/ → :8080 (http)", with the workload's share when
a rule splits traffic). Route reading comes from Kevin's
feat/workload-overview-polish branch. With no load balancer, "Add to a
load balancer" makes the workload's NetworkService if it has none (the
same object `datumctl compute url` makes) and opens the portal's create
dialog with the workload chosen.

A hostname belongs to its load balancer, so the page no longer presents
one as the workload's URL: the URL and Visit button by the title, the
endpoint line on the overview card and the Load balancer row in
Configuration are gone.

Loading states now match what they stand in for. The stats strip holds
each value, hint and sparkline with a bone until its data is in, instead
of showing zero or a flat line. The Instances and Load balancers cards
load as their tables, headers included, and the page and workloads-list
skeletons use the live tables' markup.

Co-authored-by: Kevin Williams <kwilliams@datum.net>
@mattdjenkinson mattdjenkinson self-assigned this Nov 6, 2026
@mattdjenkinson
mattdjenkinson marked this pull request as ready for review November 6, 2026 16:55
@mattdjenkinson
mattdjenkinson requested a review from a team as a code owner November 6, 2026 16:55
mattdjenkinson added a commit to datum-cloud/cloud-portal that referenced this pull request Nov 6, 2026
The compute plugin's workload Overview now has an "Add to a load
balancer" button (datum-cloud/compute#627). It links to
`/project/:id/alb?action=create&workload=<name>`. This makes that link
open the new load balancer dialog with the workload already chosen.

**The dialog now opens from the link**

The ALB page read `?action=create` on its first render, before its
create-permission check had answered. While the check loads,
`useResourcePermissions` reports `canCreate` as false. The page deleted
the parameter without opening anything, so the dialog usually didn't
open from a link at all. The page now waits for the check to finish
before reading or clearing the parameter.

**It opens with the workload chosen**

With `&workload=<name>`, the dialog:
- opens on **Datum compute**, with that workload's NetworkService
chosen;
- chooses its port too, when it has only one;
- sets the display name to the workload's name;
- refetches the service list first, since the plugin may have just
created the service;
- says when the workload has no service, instead of leaving the picker
empty.

**Why the selection is set the way it is**

- **The select's own control:** the choice goes through each select's
own form control. A second `Form.useField` control on the same field
never reaches the select, because Conform keeps a separate value per
control.
- **A tick after render:** the port select remounts when the workload
changes, and a value set in the same render as that remount never
reaches the form.

## Test plan
- [x] `serviceForWorkload` unit tests (vitest), and `app/features/edge`
tests pass
- [x] New Cypress component test (`workload-service-fields.cy.tsx`)
covers four cases: the workload and its only port are chosen; a workload
with several ports gets only the workload; an outdated cached list is
refetched; a workload with no service gets the note
- [x] Typecheck and lint
- [x] Locally, with the compute plugin: "Add to a load balancer" on
`cpu-mem-wave` opens the dialog with it chosen

@ecv ecv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve.

Every new API and metrics path stays project-scoped and the project label is pinned and escaped; no tenant-isolation, query-bound or supply-chain problem found.

  • Tenant isolation holds. New reads (WorkloadDeployments, NetworkService/HTTPProxy lists, the ensureWorkloadService POST) all go through getProjectScopedBase(projectId) in ui/consumer/src/lib/api.ts. The workload UID goes into labelSelector via URLSearchParams, so it is encoded.
  • The new networkTrafficTotalQuery and networkThroughputQuery reuse networkTrafficLabels, which pins resourcemanager_datumapis_com_project_name and resource_namespace through escapePromQL, and escapes interface names with escapePromQLRegexp. The project comes from the route, the namespace and interface refs from the project's own Instance objects. A hostile project string cannot leave the quoted label.
  • The __name__=~"rx|tx" regex in networkThroughputQuery is two constants, with the same label pin beside it, so it cannot widen the selection.
  • Ranges and polling are bounded. Charts take useOverviewRange (default 1h) and the 5m rate window; the 3s fast poll is limited to settling workloads, and workloadSettling caps the "recently changed, short of replicas" case at 10 minutes.
  • Dependencies: only @datum-cloud/datum-ui 3.6.0 to 3.8.0 (consumer, pinned; provider, ^3.8.0), plus its own bumps (isomorphic-dompurify 3.23.0, tailwind-merge 3.7.0). Every new lock entry carries an integrity hash from the npm registry. The provider lock gains ai@7.0.163, @ai-sdk/*, @vercel/oidc and @datum-cloud/datum-ui-assistant-core because datum-ui 3.8.0 now declares ai as a peer; consumer already had ai. Say so in the PR body so the provider bundle growth is expected.
  • Tests are meaningful: exact-string query assertions, albRoutes and presenter tests, and api.test.ts covers the new helpers. No test feeds a hostile project or interface name through the new queries.
  • Versus network-services-operator#877: both build on the same project-label-pinned PromQL helpers. This PR adds a write (ensureWorkloadService creates a NetworkService, treating 409 as success), which #877 does not.

Follow-ups:

  • useWorkloadDeployments polls every 3s for as long as coming is true. A stalled deploy that stays Deploying polls indefinitely; give it the same 10-minute settle window as workloadSettling.
  • ensureWorkloadService only checks whether a service selects the workload, then POSTs; a 403 surfaces as a generic Request failed. Show the permission cause in the load balancer UI.
  • Add a hostile-value test (quotes, backslash, regex metacharacters in project and interface ref) for the two new query builders.

@mattdjenkinson
mattdjenkinson merged commit e924e03 into main Nov 7, 2026
14 checks passed
@mattdjenkinson
mattdjenkinson deleted the feat/workload-overview-redesign branch November 7, 2026 11:03
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.

2 participants