job detailed view #58
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#58
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?
when we are looking at one job, we need different view
no longer show the grid
show the full job in pane
right show logs
left details of job
link back to service, run, actions... all which is relevant
and have a button to exit this view and go back to the tab
design a good ergonomic view of a job
Issue #58 — Job Detailed View — Implementation Spec
Objective
Replace the existing narrow side-panel job view with a full-pane, two-column "job detailed view" inside the Jobs tab. When a user opens a job, the table-grid is hidden and a focused workspace appears: left = structured job details with cross-links to Service / Run / Action, right = a live-tailing logs viewer. A clear "Back to jobs" affordance and ESC support let the user return to the list. The view is hash-routable at
#jobs/<id>, so deep-links and browser back/forward behave correctly.Requirements
#tab-jobs, hide the existing list (toolbar, table, bulk-bar, stats-bar) and show a new "job detail view" container when a job is opened. Do not open a separate page or tab.navigateTo('services', svcName)).navigateTo('runs', runId)).navigateTo('actions', actionName)).navigateToJobLogs(id)(existing) — jumps to Logs tab filtered by this job id.navigateToTerminalJob(id).#jobs, preserves prior search/phase/service/run filters).popstate/hashchangehandler reads#jobsand exits the detail view.#jobs/<id>): on first load with#jobs/<id>, the list is loaded in the background but the detail view is shown immediately on top. Tab badges still update.job.logsevery ~1.5 s) but tied to the detail view's lifecycle. Stop the timer when leaving the detail view, switching tabs, or unloading.[data-theme=light]and[data-theme=dark](the dashboard uses the existing--bg-primary/--bg-secondary/--bg-tertiary/--border-color/--text-*tokens).<button>witharia-label="Back to jobs", the layout is keyboard-reachable, and the ESC handler is registered withaddEventListener(not stomping the existing modal handler at line 3887).Files to modify
crates/hero_proc_ui/templates/index.html#tab-jobsbody to add a sibling#jobs-detail-viewcontainer next to the list, plus the two-pane skeleton. Lines 38–81.crates/hero_proc_ui/static/js/dashboard.jsviewJob(line 1367) to render into the new container instead of the side panel, addshowJobsDetailView/hideJobsDetailViewhelpers, updateclosePanel('jobs')(line 3970), update the hashchange handler (line 5375 forjobs/...) and the DOMContentLoaded init (line 4796) so deep-links open the detail view, add an Esc handler scoped to the detail view, and update the live-log polling functions (lines 1420–1482).crates/hero_proc_ui/static/css/dashboard.css.jobs-detail-view,.jobs-detail-back,.jobs-detail-split,.jobs-detail-left,.jobs-detail-right,.jobs-detail-logsand the responsive collapse.No backend / Rust changes required —
job.get,job.logs,job.process_stats,job.cancel,job.retry,job.deletealready provide everything the view needs (seecrates/hero_proc_server/src/rpc/job.rsand theJobstruct atcrates/hero_proc_lib/src/db/jobs/model.rs:95).Step-by-step implementation plan
Each step is self-contained and can be picked up independently as long as steps are completed in order.
Step 1 — Markup skeleton in
index.htmlInside
<div class="tab-pane" id="tab-jobs">, after the existing toolbar/bulk-bar/main-container blocks, wrap them in<div id="jobs-list-view" class="jobs-list-view">…</div>so they can be hidden together. Add a sibling<div id="jobs-detail-view" class="jobs-detail-view" hidden>…</div>containing a header (Back button, title, action bar) and a.jobs-detail-splitwith.jobs-detail-left(id=jobs-detail-left) and.jobs-detail-right(logs toolbar +#jobs-detail-log-container). The legacy#jobs-detailpanel stays in place for the New Job form.Dependencies: none.
Step 2 — CSS for the new layout
Append styles for
.jobs-list-view,.jobs-detail-view,.jobs-detail-header,.jobs-detail-title,.jobs-detail-actions,.jobs-detail-back,.jobs-detail-split,.jobs-detail-left(40% width, 360px min, 540px max, scrollable),.jobs-detail-right(flex column),.jobs-detail-logs-toolbar,.jobs-detail-logs(flex:1, no max-height). Use--bg-*,--text-*,--border-colortokens for theme parity. Add a@media (max-width: 900px)rule that flips the split to vertical.Dependencies: Step 1.
Step 3 —
showJobsDetailView/hideJobsDetailViewhelpers + ESC handlerAdd
_jobsDetailOpen,_jobsDetailLogPausedstate vars andshowJobsDetailView(),hideJobsDetailView()functions that toggle thehiddenattribute on#jobs-list-view/#jobs-detail-view, stop log + proc-stats pollers on hide, andreplaceRoute('jobs')so the URL no longer contains/<id>. Register akeydownlistener forEscapethat callshideJobsDetailView()only when the detail view is open and no modal is active.Dependencies: Step 1.
Step 4 — Rewrite
viewJobto render into the new containerReplace the body of
viewJob(around line 1367 in dashboard.js) so it: fetches the job viajob.get, sets#jobs-detail-title, renders header action buttons into#jobs-detail-actions, builds the left pane HTML in#jobs-detail-left(chips for Service/Run/Action vianavigateTo, attempt/max-attempts, exit code, error, timestamps, duration, PID, tags, description, env preview, script), starts log polling targeting the new container, starts proc-stats polling for running jobs, callsshowJobsDetailView(), andsetRoute('jobs/' + id).Dependencies: Steps 1, 2, 3.
Step 5 — Update live-log polling helpers
Update
fetchJobLogs()andstartJobLogPolling()to write into#jobs-detail-log-containerand the#jobs-detail-log-dotindicator. AddtoggleJobsDetailLogPause()andclearJobsDetailLog()helpers. Skip rendering when_jobsDetailLogPausedis true. Update the line-count display.Dependencies: Step 4.
Step 6 — Hash-routing wiring
In the
hashchangehandler (line ~5427), when the hash starts withjobs/keep the list-preload +viewJob(id)call, but ensure the detail view opens. In the plain#jobsbranch (~line 5464), callhideJobsDetailView()first when the detail view is open. InswitchTab(tabName)(~line 510), when leaving the Jobs tab also callhideJobsDetailView()to stop pollers.Dependencies: Steps 3, 4.
Step 7 — Update
closePanel('jobs')for symmetryModify
closePanel('jobs')(line ~3970) to also callhideJobsDetailView(). Verify that bulk-delete and stop-and-delete callers (lines 1520, 1576, 1675, 3965) leave the user on the refreshed list view, not on a stale detail pane.Dependencies: Step 3.
Step 8 — Smoke + acceptance test
Manual verification through the dashboard (see acceptance criteria below). Restart the service via the standard
nu_service_useflow and drive the UI with a headless browser.Dependencies: Steps 1–7.
Acceptance criteria
#tab-jobs.#jobs/<id>after open; pasting that URL into a fresh tab opens the same view directly.#jobs?service=…&run=…deep-link all still work.Notes
_liveTailTimer,_jobLogPollTimer). Reusing that avoids new server endpoints and keeps the change UI-only.#jobs/<id>is already wired in two places (DOMContentLoaded init andhashchangehandler) and both callviewJob(id). RewritingviewJobmakes routing "just work".#jobs-detailpanel: still used byshowJobForm()for the New Job form — keep it.keydownlistener forcloseJobModal/closeRunModalmust keep priority — the new handler bails out early when one of those modal overlays is present.j.run_idis a positive integer (many jobs are runless).retry_policy.max_attempts) when available; otherwise show only the current attempt.Implementation complete — browser test results
UI-only change, three files touched. 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.jscrates/hero_proc_ui/static/css/dashboard.cssNo backend / Rust changes. The legacy
#jobs-detailpanel was preserved —showJobForm()(the New Job creation form) still uses it.Test matrix (all pass)
#jobs/<id>on open#jobs/853deep-links straight to detail#services/hero_proc_uiand reset_jobsDetailOpen=false,_jobLogPollTimer=null)Live 3 lines→Paused 3 lines)flex-direction: column, panes stack)phase=running+search=hero_proc)#jobs-detail) still opensconsole_messages)Notes
--bg-primary/--bg-secondary/--bg-tertiary/--text-*/--border-colortokens that the rest of the dashboard uses, so it follows the existing dark/light toggle automatically.stopJobLogPolling()runs from bothhideJobsDetailView()andswitchTab(non-jobs), so no leaked intervals._jobsDetailLogPausedshort-circuitsfetchJobLogs()when paused; the line-count display and live-dot reflect that.#jobs/<id>was already wired in two places (DOMContentLoaded init + hashchange handler); both already calledviewJob(id), so rewritingviewJobwas sufficient — no additions to the routing table.closePanel('jobs')now also callshideJobsDetailView()so the destructive-action callers (bulk delete, stop-and-delete) leave the user on a refreshed list view rather than a stale detail pane.Implementation complete; ready to merge once reviewed.