Skip to content

UI β€” Resource DashboardΒΆ

A Kubernetes-style operator console hosted by CPA. Each resource kind has a table view; selecting an instance opens a slide-over detail panel. The model behind it is the TimedResource tree (resource-model.md); the design decisions are in ADR-048.

GoalsΒΆ

  • One console for every resource kind: Session, SessionPart, PodInstance, Host/Worker, and catalog definitions.
  • Make desired vs actual state and lifecycle phases first-class in the UI.
  • Keep the existing Bootstrap 5 + vanilla web components + SSE stack; grow shared components in lcm-core (lcm_ui).

Component viewΒΆ

C4Component
    title Component View β€” Unified Resource Dashboard (in CPA UI subapp)

    Person(operator, "Operator")

    Container_Boundary(ui, "CPA UI subapp (Bootstrap 5 + web components)") {
        Component(nav, "Resource-type nav", "web component", "Lists kinds: Sessions, Parts, Pods, Hosts, Definitions")
        Component(table, "Resource table", "shared lcm-core component", "Per-type list, status badges, drill-down")
        Component(panel, "Detail slide-over", "shared lcm-core component", "Tabs: overview/lifecycle/history/related/yaml/reports/logs")
        Component(actions, "Action bar", "web component", "Declarative (set desired) + imperative (start/stop/grade)")
        Component(sse, "SSE client", "web component", "Live status/phase updates")
        Component(iframe, "SE report widget", "iframe", "Embeds SE job outputs / reports")
    }

    Container(api, "CPA API subapp", "FastAPI", "Resource REST + /events/stream (SSE)")
    Container(se, "Scenario Engine UI", "FastAPI", "Job outputs / reports")

    Rel(operator, nav, "Browses")
    Rel(nav, table, "Selects kind")
    Rel(table, panel, "Opens instance")
    Rel(panel, actions, "Hosts")
    Rel(table, api, "GET resources", "HTTPS")
    Rel(actions, api, "Set desired / commands", "HTTPS")
    Rel(sse, api, "Subscribe", "SSE")
    Rel(iframe, se, "Embeds report UI", "HTTPS")

    UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1")

A left nav lists resource kinds. Each opens a table; rows drill down to owned resources (Session β†’ Parts β†’ Pods β†’ Host) via breadcrumbs.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ RESOURCES    β”‚  Sessions                                   [+ New]  [⟳ Sync]   β”‚
β”‚              β”‚  ───────────────────────────────────────────────────────────── β”‚
β”‚ β–Έ Sessions   β”‚  NAME           PROFILE      STATUS    DESIRED   PARTS  AGE      β”‚
β”‚   Parts      β”‚  ───────────────────────────────────────────────────────────── β”‚
β”‚   Pods       β”‚  ccie-ent-042   ExpertExam   ● Active  Active    3      01:12    β”‚
β”‚   Hosts      β”‚  lab-1.1.1-x9   Lablet       ◐ Provis. Ready     1      00:03    β”‚
β”‚ ─────────    β”‚  ccde-des-room  DesignExpert ● Active  Active    4      00:48    β”‚
β”‚ CATALOG      β”‚  prac-vmw-7     PracticeLab  β—‹ Sched.  Ready     1      β€”        β”‚
β”‚   SessionDef β”‚                                                                 β”‚
β”‚   PartDef    β”‚  status legend:  ● ready/active  ◐ transitioning  β—‹ scheduled   β”‚
β”‚   PodDef     β”‚                  βœ– failed                                       β”‚
β”‚ ─────────    β”‚                                                                 β”‚
β”‚ INFRA        β”‚                                                                 β”‚
β”‚   Workers    β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Detail: slide-over side panelΒΆ

Selecting a row slides a panel in from the right (the table stays visible). Tabs expose the full resource. Declarative + imperative actions live in the header.

                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   Sessions table  β—€     β”‚  ccie-ent-042            ● Active   [Set desired β–Ύ]β”‚
   (dimmed)              β”‚  Session Β· ExpertExam                 [Reconcile]  β”‚
                         β”‚  ────────────────────────────────────────────────│
                         β”‚  Overview | Lifecycle | History | Related | YAML  β”‚
                         β”‚           | Reports | Logs                        β”‚
                         β”‚  ────────────────────────────────────────────────│
                         β”‚  SPEC (desired)            STATUS (actual)        β”‚
                         β”‚  desired_status: Active    status: Active         β”‚
                         β”‚  timeslot.start: 09:00     started_at: 09:00      β”‚
                         β”‚  parts: 3                  current_part: DOO       β”‚
                         β”‚  ────────────────────────────────────────────────│
                         β”‚  PARTS (drill-down)                               β”‚
                         β”‚   1 ● DES    (web, no pod)        completed       β”‚
                         β”‚   2 ● DOO    pod: hw-rack-3        active         β”‚
                         β”‚   3 β—‹ AI-DOO pod: cml-aws (JIT)    scheduled      β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Lifecycle tab (desired vs actual phases)ΒΆ

  Lifecycle β€” SessionPart "DOO"        engine legend:  [P] pipeline  [W] workflow
  ──────────────────────────────────────────────────────────────────────────────
   βœ” instantiate [P]  ──  βœ” ready [P]  ──  β–Ά grade [W]  ──  β—‹ teardown [P]
                                            β”” SE job: grade-lab@0.1.0 (running)
   desired_status: Active        retries: 0/3        last transition: 10:42 by system

History tab (state transitions)ΒΆ

  State history β€” newest first
  ──────────────────────────────────────────────────────────────────
   10:42  ready      β†’ grading     event:student_submit   by: system
   09:05  provisioning β†’ ready     phase:ready complete   by: PartMgr
   08:55  scheduled  β†’ provisioning provision_at reached  by: scheduler

Tabs referenceΒΆ

Tab Shows
Overview desired_status vs status side by side; key timeslot/lifecycle fields.
Lifecycle & phases phase chain, current vs desired, engine per phase, progress, retries.
State history / events the state_history timeline (StateTransitions).
Related parent, children, and bound host (clickable drill-down).
Raw YAML/JSON the resource spec/status document.
Reports & artifacts SE outputs, embedded as an iframe widget.
Live logs streamed reconcile/automation logs.

ActionsΒΆ

  • Declarative (aligned with the reconciliation framework, ADR-047): set desired_status (e.g. Ready, Active, TornDown), retry a phase, force a reconcile.
  • Imperative (operator break-glass): start/stop/grade/teardown now.

Real-timeΒΆ

The dashboard subscribes to CPA's /events/stream (SSE β€” ADR-013) for live status, phase progress, and transitions, with optional filtering by resource kind/ids.

Shared components in lcm-coreΒΆ

Promoted into lcm_ui for reuse across every service UI:

resource-table, resource-detail-panel, status-badge, lifecycle-phases, state-history-timeline, desired-status-editor, sse-client.