UI Specification: Orbital Management Console¶
Status: v1 · Stack: Next.js (App Router, TypeScript) + MUI (Material UI) + SWR
Replaces: the minimal single-file dashboard served by FastAPI at /.
1. Goals & principles¶
- A management console for the platform: everything the REST API offers, discoverable and safe to use — deploy, observe, configure, share, delete.
- Live by default: app states, logs, and build progress refresh automatically (SWR polling; no manual reload).
- Calm Material design: MUI components, light/dark mode, a single accent
color (Streamlit red
#e74c3cfamily), generous whitespace, no visual noise. - Non-blocking: long operations (build/deploy) are reflected as state chips + progress indicators; the UI never freezes on them.
- Destructive actions always require explicit confirmation.
2. Information architecture¶
/ Apps overview (home)
/apps/<id> App detail
├─ tab: Overview state, URL, repo info, current build, quick actions
├─ tab: Metrics CPU/memory usage charts vs. limits
├─ tab: Logs live runtime logs
├─ tab: Builds build history + per-build logs
├─ tab: Secrets TOML editor
├─ tab: Sharing public/private + allowed groups
└─ tab: Settings branch/main file/python, webhook, danger zone
/admin Reconciler logs (admins only)
Top-level layout: slim AppBar (product name, admin shield icon for admins,
theme toggle) + content container (max-width lg). No side nav — two levels
only.
3. Screens¶
3.1 Apps overview (/)¶
- Header row: "Apps" title + card/table view toggle + primary button New app.
- Stat row (admins only, above the filter bar): app count, running
count, total CPU consumption, total memory consumption — plain sums of
the latest per-app metrics-server samples, not against a platform
total: apps have per-app resource limits, not a shared/mutualized pool,
so an "of N allocated" figure would misrepresent capacity. Fed by
GET /api/v1/admin/overview, polled every 5s. - Filter bar: search-by-name text field + State and Owner multi-select
Autocompletes, options derived from the currently loaded apps (not a fixed list) so they never show a state/owner nothing matches. Filters apply identically to both views; "Clear filters" appears once any filter is active. Built for fleets in the hundreds — an admin can have far more apps than fit a single screen. - App grid (default view): one MUI
Cardper app: - Slug (title) + lock icon when private, app id (caption, monospace).
- StateChip (color-coded: running=green, building/deploying=amber with spinner, failed=red, sleeping/deleting=grey).
- Repo (shortened), branch · main file.
- Owner groups + CPU/memory (admins: live usage across every app; other roles: "—", no bulk metrics endpoint outside the admin role).
- Error preview line when failed (truncated, tooltip with full text).
- Actions: Open (external link, disabled unless running), Redeploy, overflow menu (Reboot, Delete).
- Card click → app detail.
- Table view: alternative to the grid — slug (link), state, owner groups, CPU, memory, last updated. Same admin/other-role data split as the card grid, same filters.
- Empty state: centered illustration-ish box + "Deploy your first app" CTA when there are no apps at all; "No apps match these filters" + Clear when filters exclude everything.
- Data:
GET /api/v1/apps, SWRrefreshInterval: 4s; admins additionally pollGET /api/v1/admin/overviewevery 5s.
3.2 New app dialog¶
MUI Dialog from the overview:
- Fields: slug, repo URL, branch (default main), main file (default
streamlit_app.py), Python version (Select, from GET /platform config —
v1: static "3.12"), visibility (public switch), allowed groups
(Autocomplete freeSolo multiple chips, shown only when private),
secrets TOML (optional, collapsible, monospace multiline).
- Client-side validation: slug pattern [a-z0-9-], required fields.
- Submit → POST /api/v1/apps → close + navigate to the new app's detail.
- API errors shown inline (409 slug taken, 422 validation).
3.3 App detail (/apps/<id>)¶
Header: slug + StateChip + private badge · URL as copyable link · action
buttons: Open, Redeploy, Reboot, overflow (Delete).
Below: MUI Tabs.
- Overview: definition list (repo, branch, main file, python, created,
updated) · current build card (id, commit sha, phase, started/finished,
link to logs) · error alert when
*_failedwith the stored message. - Metrics: two single-series area charts (CPU, memory — never dual-axis)
with current value and % of the platform limit, crosshair tooltips, and a
table view toggle; polls
GET .../metricsevery 10 s (last ~30 min of 15 s samples). Info alert when metrics-server is unavailable. Visible to all roles, viewers included. - Logs: monospace scrollable pane (
tail=500), auto-refresh 3 s toggle ("Follow"), manual refresh, download button. Auto-scroll to bottom when following. - Builds: table (id, commit, phase chip, started, duration); row click
opens build logs in a collapsible panel below.
GET .../builds,GET .../builds/<bid>/logs. - Secrets: monospace
TextField multilinewith the current TOML (GET .../secrets), Save →PUT .../secrets(202 → snackbar "saved — app restarting"), client-side hint that saving restarts the app. - Sharing: public/private
Switch+ groupsAutocomplete(chips) + Save. Explains semantics: "empty groups = any signed-in user".PATCH /api/v1/apps/<id>— applies live, no rebuild (mention in helper text). - Settings: form for branch / main file / python version, Save →
PATCH(warns: triggers rebuild) · Webhook section: full webhook URL with copy button + hint for GitHub/GitLab/Gitea · Danger zone: outlined red card with Delete (confirm dialog typing the slug).
3.4 Reconciler logs (/admin)¶
Admins only (403-style Alert for everyone else; the AppBar shield icon is
hidden for non-admins). LogPane fed by GET /api/v1/admin/logs?tail=500,
polled every 4 s — a simpler sibling of the per-app Logs tab (no
follow-toggle or download, always-on tail). Fleet-wide app status and
resource totals live on the home page's table view (§3.1), not here.
4. Components¶
| Component | Purpose |
|---|---|
StateChip |
app/build state → color, label, spinner for transient states |
AppCard |
overview grid item (owner + CPU/memory line included) |
AppsTable |
overview table-view rows (slug, state, owner, CPU, memory, updated) |
AppsFilterBar |
search + State/Owner Autocomplete filters, shared by both overview views |
CreateAppDialog |
new app form |
LogPane |
scrollable monospace log viewer w/ follow + download |
ConfirmDialog |
generic destructive-action confirmation |
CopyField |
read-only value + copy-to-clipboard button |
FleetLogsTab |
/admin body (reconciler log tail) |
useApps / useApp / useBuilds / useLogs / useAdminOverview / useAdminLogs |
SWR hooks in lib/api.ts |
5. Design system¶
- MUI default theme, customized: primary
#e74c3c, shape.borderRadius 10,CssBaseline; typography: default Roboto stack,JetBrains Mono, ui-monospacefor code/ids/logs. - Color scheme: light + dark via MUI CSS variables
(
colorSchemes: { light, dark }), toggle in AppBar, persisted. - State colors: running
success, building/deployingwarning, failederror, sleeping/deletingdefault, createdinfo. - Feedback: global
Snackbar(bottom-left) for action results; inlineAlertfor form/API errors.
6. Data layer & integration¶
- All calls go to relative
/api/v1/...; Next.jsrewrites()proxies to the control plane (CONTROL_PLANE_URL, defaulthttp://localhost:8000) — no CORS, works in dev and behind the VS Code tunnel. - SWR global config:
refreshInterval4 s on collection/detail, no retry on 4xx; mutations use optimistic revalidate. - Errors normalized to
{detail}(FastAPI convention).
7. Out of scope (v1)¶
Auth for the console itself (the API is currently unauthenticated), analytics charts, favorites, log websocket streaming (polling suffices), i18n.
8. Verification¶
npm run buildpasses (type-safe).- Manual flows against the live control plane on minikube: create app → watch build → open app; edit secrets; toggle sharing; delete app.
- Dev server behind VS Code tunnel: forward port 3000.