service detailed view #59
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_proc#59
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
mirror the approach used for the per-job detailed view (issue #58), but for a single service.
when we are looking at one service, we need a different view:
design a good ergonomic view for a service that mirrors the layout/feel of the new job detail view.
Spec: Issue #59 — Service Detailed View
Objective
Mirror the per-job detail view shipped in issue #58 (commit
400cfcb) for individual services. Clicking a service row in#tab-serviceshides the table and opens a focused two-column workspace inside the same tab pane: left = service config, dependencies, runtime state and metadata; right = a live, run-scoped jobs table. The view is hash-routable at#services/<name>, dismissable via Back button / Esc / browser back, and collapses to a vertical stack below 900 px.Requirements
services-list-view(toolbar + table) and showservices-detail-view(header + split) when a service is opened.current_run_id(1.5 s polling, mirroring_jobLogPollTimerlifecycle). Each row clicks through to the per-job detail view.#services/<name>deep-links and persists across reload.#servicesand triggershideServicesDetailView()).if (tabName !== 'jobs') hideJobsDetailView()line).#services-detailside panel (still used byshowServiceForm()for the New / Edit Service form).Files to modify
crates/hero_proc_ui/templates/index.htmlcrates/hero_proc_ui/static/js/dashboard.jscrates/hero_proc_ui/static/css/dashboard.cssNo backend / Rust changes. All required RPCs (
service.get,service.status,job.list { filter: { run_id } }) already exist.Implementation plan
Step 1 — Restructure
#tab-servicesmarkupWrap the existing toolbar + bulk-bar + main-container (table + legacy
#services-detailside panel) in<div class="services-list-view" id="services-list-view">. Append a sibling<div class="services-detail-view" id="services-detail-view" hidden>withservices-detail-header(Back, title, actions) +services-detail-split(left pane + right pane with toolbar + jobs container). The legacy#services-detailpanel stays inside#services-list-view.Dependencies: none.
Step 2 — Add CSS for
services-detail-viewAppend rules for
.services-list-view,.services-detail-view,.services-detail-header,.services-detail-title,.services-detail-actions,.services-detail-back,.services-detail-split,.services-detail-left,.services-detail-right,.services-detail-jobs-toolbar,.services-detail-jobs,.services-detail-left .form-group,.services-detail-left .chip. Use the same--bg-*/--text-*/--border-colortokens as the jobs detail view. Add a 900 px media query that flips.services-detail-splittoflex-direction: column. Or extend the existing selectors as.jobs-list-view, .services-list-view { ... }etc.Dependencies: Step 1.
Step 3 — State variables and show/hide helpers
Add (near the existing
_jobsDetailOpenblock):hideServicesDetailView()clears the timer, resets state, restores the list view, andreplaceRoute('services')if the hash starts withservices/.Dependencies: Step 1.
Step 4 — Esc handler
Add a second
keydownlistener that callshideServicesDetailView()when_servicesDetailOpenis true and no modal overlay is visible (#job-modal-overlay,.confirm-modal-overlay,.modal.show).Dependencies: Step 3.
Step 5 —
switchTabintegrationIn
switchTab, addif (tabName !== 'services') hideServicesDetailView();next to the existing jobs-equivalent line.Dependencies: Step 3.
Step 6 — Rewrite
viewService(name)Fetch
service.getandservice.statusin parallel. Populate title, action buttons (state-gated Start vs Stop+Restart+Kill, plus Run/Edit/Logs/Jobs/Delete), and left-pane HTML (state badge, wanted status, class, critical flag, current-run chip, PID, restarts, dependency chips per category, action chips, jobs-summary chip, links to Logs/Terminal). Initialise the right pane with a "Loading…" placeholder and the toolbar label "Jobs in run #N" (or "No active run"). Then_servicesDetailName = name,_servicesDetailRunId = current_run_id,showServicesDetailView(),setRoute('services/' + encodeURIComponent(name)). Mark the row selected. StartstartServicesDetailJobsPolling(name).Dependencies: Steps 1, 3.
Step 7 — Right-pane jobs polling loop
Add
stopServicesDetailJobsPolling,fetchServicesDetailJobs,startServicesDetailJobsPolling. The fetch re-readsservice.statusevery tick to trackcurrent_run_idchanges (e.g. after Restart) and re-targetsjob.list { filter: { run_id, limit: 200 } }. Renders a<table class="data-table">with abbreviated columns (ID, Action, Phase, Attempt, Exit, Duration). Rowonclickopens the per-job detail view. 1.5 s interval. The poller updates the live-dot, the toolbar label, and the jobs-count.Dependencies: Step 6.
Step 8 — Toolbar wiring
Right-pane toolbar buttons:
toggleServicesDetailJobsPause().fetchServicesDetailJobs()(one-shot).navigateToServiceJobs(_servicesDetailName, _servicesDetailRunId).Dependencies: Steps 1, 7.
Step 9 — Hashchange + DOMContentLoaded + back-to-list cleanup
Tweak the
hashchangehandler's terminalelsebranch: whenhash === 'services',if (_servicesDetailOpen) hideServicesDetailView();. The existingelse if (hash.startsWith('services/'))branch already callsviewService(name). The DOMContentLoaded deep-link block routestype: 'service'throughviewService(value)— unchanged.Dependencies: Step 3.
Step 10 —
closePanel('services')cleanupIn
closePanel, whentab === 'services', callhideServicesDetailView()so legacy callers (bulk delete, save-then-close) drop the detail view if it's open.Dependencies: Step 3.
Step 11 — Row click rewires to the new detail view
The row's
onclick="viewService('<name>')"(line 2594) is unchanged — the rewrittenviewServicenow opens the full-pane view.Dependencies: Step 6.
Acceptance criteria
requires/after/wants/conflicts), and action chips. Each chip navigates correctly.current_run_id, refreshed every 1.5 s, with live dot, pause, refresh, and open-in-Jobs-tab controls.#tab-jobs.#services/<name>when opened, reverts to#serviceswhen closed.#services/<name>lands directly on the detail view for that service.#services-detailside panel still works for New / Edit Service forms._servicesDetailJobsTimeris null after closing).Notes & tradeoffs
4ca6ed0and gives instant per-job phase/exit-code visibility. Logs are one click away via the header "Logs" button. A future enhancement could add a Jobs/Logs toggle in the right-pane toolbar._pendingRunFilterinteraction. The detail view holds its own_servicesDetailRunIdsnapshot and does not touch the global_pendingRunFilteruntil the user explicitly clicks "Open in Jobs tab" (which callsnavigateToServiceJobs(name, runId)).current_run_idcan change while the view is open. The poller re-fetchesservice.statuson every tick so the right pane re-targets the new run automatically._jobLogPollTimer. Idle services still get polled so the view detects a new run starting.services-detail-view(plural prefix) intentionally mirrorsjobs-detail-view. The legacy#services-detailside-panel ID is unchanged.Implementation complete — browser test results
UI-only change, three files. cargo check passes for hero_proc_ui. Service was restarted via
service_proc start --reset; the dashboard was driven through the Hero Browser MCP at the running ui.sock.Files modified
crates/hero_proc_ui/templates/index.htmlcrates/hero_proc_ui/static/js/dashboard.jsviewServiceto render into the new container, newfetchServicesDetailJobs+startServicesDetailJobsPolling, hashchange branch + closePanel symmetrycrates/hero_proc_ui/static/css/dashboard.css.jobs-detail-*selector to also cover the.services-detail-*counterparts; added.services-detail-jobsoverflow + table hover rulesNo backend / Rust changes. The legacy
#services-detailpanel is preserved —showServiceForm()still uses it for New / Edit Service.Test matrix (all pass)
#services/<name>on open#tab-jobs#jobs/924,_jobsDetailOpen=true, services state cleared)#services/<name>opens the view directly_servicesDetailJobsTimer=null)#services-detailNew Service form still opensconsole_messages)Notes
4ca6ed0and gives instant per-job phase/exit-code visibility. Logs are one click away via the headerLogsbutton.service.statusevery tick so the right pane re-targets a newcurrent_run_idwhen the user clicks Restart while the view is open. The toolbar label updates in place._servicesDetailRunIdsnapshot and never writes to the global_pendingRunFilter/_activeRunFilter(those still drive the Jobs tab's own filter). The "Open in Jobs tab" button in the right-pane toolbar hands off throughnavigateToServiceJobs(name, runId)which sets the global filter as before.--bg-*/--text-*/--border-colortokens and follows the dashboard's existing dark/light toggle.Implementation complete; ready to merge once reviewed.