job detailed view #58

Open
opened 2026-04-28 05:30:56 +00:00 by despiegk · 2 comments
Owner

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

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
Author
Owner

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

  1. Inside #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.
  2. The detail view has two horizontal panes:
    • Left pane (~40 % width, min 360 px) — job metadata: id, action, service, run, status/phase, type (process/one-shot), attempt / max attempts, exit code, error, timestamps (created / started / finished), duration, PID, CPU/memory live stats (running jobs only), tags, description, a collapsible env preview, the resolved script.
    • Right pane (flex-grow) — live-tailing log container, auto-scroll on new lines, with a header showing live-state, line-count, "pause/resume", "clear", and "open in Logs tab" controls.
  3. Cross-links in the left pane:
    • Service: chip → switches to Services tab and opens that service detail (navigateTo('services', svcName)).
    • Run: chip → switches to Runs tab and opens that run (navigateTo('runs', runId)).
    • Action: chip → switches to Actions tab and opens that action (navigateTo('actions', actionName)).
    • Logs (full): button → navigateToJobLogs(id) (existing) — jumps to Logs tab filtered by this job id.
    • Terminal: button (only when action has TTY and job is running) → navigateToTerminalJob(id).
  4. Action buttons in the header: Back to jobs, Cancel (running), Retry (failed), Kill, Delete.
  5. Back behaviour:
    • Clicking "Back to jobs" returns to the list (#jobs, preserves prior search/phase/service/run filters).
    • Pressing Esc while in the detail view triggers "Back to jobs".
    • Browser Back does the same — popstate/hashchange handler reads #jobs and exits the detail view.
  6. Hash routing (no new schema — reuse #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.
  7. Live tail: replace the current 2 s polling pattern with the same poll-driven model (calls job.logs every ~1.5 s) but tied to the detail view's lifecycle. Stop the timer when leaving the detail view, switching tabs, or unloading.
  8. Theme parity: every new style must work in both [data-theme=light] and [data-theme=dark] (the dashboard uses the existing --bg-primary/--bg-secondary/--bg-tertiary/--border-color/--text-* tokens).
  9. Accessibility: the Back button is a real <button> with aria-label="Back to jobs", the layout is keyboard-reachable, and the ESC handler is registered with addEventListener (not stomping the existing modal handler at line 3887).

Files to modify

File Why
crates/hero_proc_ui/templates/index.html Replace #tab-jobs body to add a sibling #jobs-detail-view container next to the list, plus the two-pane skeleton. Lines 38–81.
crates/hero_proc_ui/static/js/dashboard.js Rewrite viewJob (line 1367) to render into the new container instead of the side panel, add showJobsDetailView/hideJobsDetailView helpers, update closePanel('jobs') (line 3970), update the hashchange handler (line 5375 for jobs/...) 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 Add styles for .jobs-detail-view, .jobs-detail-back, .jobs-detail-split, .jobs-detail-left, .jobs-detail-right, .jobs-detail-logs and the responsive collapse.

No backend / Rust changes required — job.get, job.logs, job.process_stats, job.cancel, job.retry, job.delete already provide everything the view needs (see crates/hero_proc_server/src/rpc/job.rs and the Job struct at crates/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.html

Inside <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-split with .jobs-detail-left (id=jobs-detail-left) and .jobs-detail-right (logs toolbar + #jobs-detail-log-container). The legacy #jobs-detail panel 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-color tokens for theme parity. Add a @media (max-width: 900px) rule that flips the split to vertical.

Dependencies: Step 1.

Step 3 — showJobsDetailView / hideJobsDetailView helpers + ESC handler

Add _jobsDetailOpen, _jobsDetailLogPaused state vars and showJobsDetailView(), hideJobsDetailView() functions that toggle the hidden attribute on #jobs-list-view / #jobs-detail-view, stop log + proc-stats pollers on hide, and replaceRoute('jobs') so the URL no longer contains /<id>. Register a keydown listener for Escape that calls hideJobsDetailView() only when the detail view is open and no modal is active.

Dependencies: Step 1.

Step 4 — Rewrite viewJob to render into the new container

Replace the body of viewJob (around line 1367 in dashboard.js) so it: fetches the job via job.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 via navigateTo, 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, calls showJobsDetailView(), and setRoute('jobs/' + id).

Dependencies: Steps 1, 2, 3.

Step 5 — Update live-log polling helpers

Update fetchJobLogs() and startJobLogPolling() to write into #jobs-detail-log-container and the #jobs-detail-log-dot indicator. Add toggleJobsDetailLogPause() and clearJobsDetailLog() helpers. Skip rendering when _jobsDetailLogPaused is true. Update the line-count display.

Dependencies: Step 4.

Step 6 — Hash-routing wiring

In the hashchange handler (line ~5427), when the hash starts with jobs/ keep the list-preload + viewJob(id) call, but ensure the detail view opens. In the plain #jobs branch (~line 5464), call hideJobsDetailView() first when the detail view is open. In switchTab(tabName) (~line 510), when leaving the Jobs tab also call hideJobsDetailView() to stop pollers.

Dependencies: Steps 3, 4.

Step 7 — Update closePanel('jobs') for symmetry

Modify closePanel('jobs') (line ~3970) to also call hideJobsDetailView(). 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_use flow and drive the UI with a headless browser.

Dependencies: Steps 1–7.

Acceptance criteria

  • Clicking a row in the Jobs table hides the table+toolbar and shows a full-pane two-column detail view inside #tab-jobs.
  • URL becomes #jobs/<id> after open; pasting that URL into a fresh tab opens the same view directly.
  • Back to jobs button restores the list with the prior search/phase/service/run filters intact.
  • Esc while the detail view is open returns to the list.
  • Browser Back also returns to the list (and Forward re-opens the same job).
  • Left pane shows: action, service, run, action chip-links that switch to the proper tab and open the related entity.
  • Right pane streams logs every ~1.5 s; the live-dot animates; pause and clear work; line-count updates.
  • Process stats (CPU, memory) update for running jobs.
  • Cancel / Retry / Kill / Delete / Terminal / Logs buttons work from the detail-view header.
  • Layout is correct in both light and dark themes.
  • At viewport widths < 900 px the two panes stack vertically (left on top, logs below).
  • Switching to another top-level tab cleanly exits the detail view and stops both pollers.
  • No regression in the list view: filters, search, the "Run #N" pill, bulk actions, the New Job form, the existing #jobs?service=…&run=… deep-link all still work.
  • No console errors on open, close, or rapid back-forward.

Notes

  • Polling vs SSE: the dashboard already uses interval polling (_liveTailTimer, _jobLogPollTimer). Reusing that avoids new server endpoints and keeps the change UI-only.
  • Hash conflict: #jobs/<id> is already wired in two places (DOMContentLoaded init and hashchange handler) and both call viewJob(id). Rewriting viewJob makes routing "just work".
  • Legacy #jobs-detail panel: still used by showJobForm() for the New Job form — keep it.
  • Esc priority: the existing keydown listener for closeJobModal/closeRunModal must keep priority — the new handler bails out early when one of those modal overlays is present.
  • Auto-scroll vs pause: when paused, do not append new lines. Resume snaps back to the tail.
  • Run chip: render only when j.run_id is a positive integer (many jobs are runless).
  • Max attempts: pull from the cached action spec (retry_policy.max_attempts) when available; otherwise show only the current attempt.
# 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 1. Inside `#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. 2. The detail view has two horizontal panes: - **Left pane (~40 % width, min 360 px)** — job metadata: id, action, service, run, status/phase, type (process/one-shot), attempt / max attempts, exit code, error, timestamps (created / started / finished), duration, PID, CPU/memory live stats (running jobs only), tags, description, a collapsible env preview, the resolved script. - **Right pane (flex-grow)** — live-tailing log container, auto-scroll on new lines, with a header showing live-state, line-count, "pause/resume", "clear", and "open in Logs tab" controls. 3. Cross-links in the left pane: - **Service**: chip → switches to Services tab and opens that service detail (`navigateTo('services', svcName)`). - **Run**: chip → switches to Runs tab and opens that run (`navigateTo('runs', runId)`). - **Action**: chip → switches to Actions tab and opens that action (`navigateTo('actions', actionName)`). - **Logs (full)**: button → `navigateToJobLogs(id)` (existing) — jumps to Logs tab filtered by this job id. - **Terminal**: button (only when action has TTY and job is running) → `navigateToTerminalJob(id)`. 4. Action buttons in the header: **Back to jobs**, **Cancel** (running), **Retry** (failed), **Kill**, **Delete**. 5. **Back behaviour**: - Clicking "Back to jobs" returns to the list (`#jobs`, preserves prior search/phase/service/run filters). - Pressing **Esc** while in the detail view triggers "Back to jobs". - Browser **Back** does the same — `popstate`/`hashchange` handler reads `#jobs` and exits the detail view. 6. **Hash routing** (no new schema — reuse `#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. 7. **Live tail**: replace the current 2 s polling pattern with the same poll-driven model (calls `job.logs` every ~1.5 s) but tied to the detail view's lifecycle. Stop the timer when leaving the detail view, switching tabs, or unloading. 8. **Theme parity**: every new style must work in both `[data-theme=light]` and `[data-theme=dark]` (the dashboard uses the existing `--bg-primary`/`--bg-secondary`/`--bg-tertiary`/`--border-color`/`--text-*` tokens). 9. **Accessibility**: the Back button is a real `<button>` with `aria-label="Back to jobs"`, the layout is keyboard-reachable, and the ESC handler is registered with `addEventListener` (not stomping the existing modal handler at line 3887). ## Files to modify | File | Why | |---|---| | `crates/hero_proc_ui/templates/index.html` | Replace `#tab-jobs` body to add a sibling `#jobs-detail-view` container next to the list, plus the two-pane skeleton. Lines 38–81. | | `crates/hero_proc_ui/static/js/dashboard.js` | Rewrite `viewJob` (line 1367) to render into the new container instead of the side panel, add `showJobsDetailView`/`hideJobsDetailView` helpers, update `closePanel('jobs')` (line 3970), update the hashchange handler (line 5375 for `jobs/...`) 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` | Add styles for `.jobs-detail-view`, `.jobs-detail-back`, `.jobs-detail-split`, `.jobs-detail-left`, `.jobs-detail-right`, `.jobs-detail-logs` and the responsive collapse. | No backend / Rust changes required — `job.get`, `job.logs`, `job.process_stats`, `job.cancel`, `job.retry`, `job.delete` already provide everything the view needs (see `crates/hero_proc_server/src/rpc/job.rs` and the `Job` struct at `crates/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.html` Inside `<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-split` with `.jobs-detail-left` (id=`jobs-detail-left`) and `.jobs-detail-right` (logs toolbar + `#jobs-detail-log-container`). The legacy `#jobs-detail` panel 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-color` tokens for theme parity. Add a `@media (max-width: 900px)` rule that flips the split to vertical. Dependencies: Step 1. ### Step 3 — `showJobsDetailView` / `hideJobsDetailView` helpers + ESC handler Add `_jobsDetailOpen`, `_jobsDetailLogPaused` state vars and `showJobsDetailView()`, `hideJobsDetailView()` functions that toggle the `hidden` attribute on `#jobs-list-view` / `#jobs-detail-view`, stop log + proc-stats pollers on hide, and `replaceRoute('jobs')` so the URL no longer contains `/<id>`. Register a `keydown` listener for `Escape` that calls `hideJobsDetailView()` only when the detail view is open and no modal is active. Dependencies: Step 1. ### Step 4 — Rewrite `viewJob` to render into the new container Replace the body of `viewJob` (around line 1367 in dashboard.js) so it: fetches the job via `job.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 via `navigateTo`, 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, calls `showJobsDetailView()`, and `setRoute('jobs/' + id)`. Dependencies: Steps 1, 2, 3. ### Step 5 — Update live-log polling helpers Update `fetchJobLogs()` and `startJobLogPolling()` to write into `#jobs-detail-log-container` and the `#jobs-detail-log-dot` indicator. Add `toggleJobsDetailLogPause()` and `clearJobsDetailLog()` helpers. Skip rendering when `_jobsDetailLogPaused` is true. Update the line-count display. Dependencies: Step 4. ### Step 6 — Hash-routing wiring In the `hashchange` handler (line ~5427), when the hash starts with `jobs/` keep the list-preload + `viewJob(id)` call, but ensure the detail view opens. In the plain `#jobs` branch (~line 5464), call `hideJobsDetailView()` first when the detail view is open. In `switchTab(tabName)` (~line 510), when leaving the Jobs tab also call `hideJobsDetailView()` to stop pollers. Dependencies: Steps 3, 4. ### Step 7 — Update `closePanel('jobs')` for symmetry Modify `closePanel('jobs')` (line ~3970) to also call `hideJobsDetailView()`. 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_use` flow and drive the UI with a headless browser. Dependencies: Steps 1–7. ## Acceptance criteria - [ ] Clicking a row in the Jobs table hides the table+toolbar and shows a full-pane two-column detail view inside `#tab-jobs`. - [ ] URL becomes `#jobs/<id>` after open; pasting that URL into a fresh tab opens the same view directly. - [ ] **Back to jobs** button restores the list with the prior search/phase/service/run filters intact. - [ ] **Esc** while the detail view is open returns to the list. - [ ] Browser **Back** also returns to the list (and **Forward** re-opens the same job). - [ ] Left pane shows: action, service, run, action chip-links that switch to the proper tab and open the related entity. - [ ] Right pane streams logs every ~1.5 s; the live-dot animates; pause and clear work; line-count updates. - [ ] Process stats (CPU, memory) update for running jobs. - [ ] Cancel / Retry / Kill / Delete / Terminal / Logs buttons work from the detail-view header. - [ ] Layout is correct in both light and dark themes. - [ ] At viewport widths < 900 px the two panes stack vertically (left on top, logs below). - [ ] Switching to another top-level tab cleanly exits the detail view and stops both pollers. - [ ] No regression in the list view: filters, search, the "Run #N" pill, bulk actions, the New Job form, the existing `#jobs?service=…&run=…` deep-link all still work. - [ ] No console errors on open, close, or rapid back-forward. ## Notes - **Polling vs SSE**: the dashboard already uses interval polling (`_liveTailTimer`, `_jobLogPollTimer`). Reusing that avoids new server endpoints and keeps the change UI-only. - **Hash conflict**: `#jobs/<id>` is already wired in two places (DOMContentLoaded init and `hashchange` handler) and both call `viewJob(id)`. Rewriting `viewJob` makes routing "just work". - **Legacy `#jobs-detail` panel**: still used by `showJobForm()` for the New Job form — keep it. - **Esc priority**: the existing `keydown` listener for `closeJobModal`/`closeRunModal` must keep priority — the new handler bails out early when one of those modal overlays is present. - **Auto-scroll vs pause**: when paused, do not append new lines. Resume snaps back to the tail. - **Run chip**: render only when `j.run_id` is a positive integer (many jobs are runless). - **Max attempts**: pull from the cached action spec (`retry_policy.max_attempts`) when available; otherwise show only the current attempt.
Author
Owner

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

File Lines
crates/hero_proc_ui/templates/index.html +75 / -34
crates/hero_proc_ui/static/js/dashboard.js +132 / -26
crates/hero_proc_ui/static/css/dashboard.css +122 / -0

No backend / Rust changes. The legacy #jobs-detail panel was preserved — showJobForm() (the New Job creation form) still uses it.

Test matrix (all pass)

Acceptance criterion Result
Click row hides list, shows two-pane detail view pass
URL becomes #jobs/<id> on open pass
Pasting #jobs/853 deep-links straight to detail pass
Back-to-jobs restores list with prior row count pass
Esc dismisses the detail view pass
Browser Back returns to list pass
Service / Run / Action chip-links cross-navigate pass (Service chip jumped to #services/hero_proc_ui and reset _jobsDetailOpen=false, _jobLogPollTimer=null)
Live polling: status text, line count, pause toggle pass (Live 3 linesPaused 3 lines)
Process Stats live for running jobs (CPU / mem) pass (0.4% / 15 MB)
Cancel / Kill / Logs / Delete header actions render pass
Responsive collapse below 900 px pass (split flips to flex-direction: column, panes stack)
Switching tabs exits the detail view + clears pollers pass
List filters (phase + search) survive Back pass (1 row matches phase=running + search=hero_proc)
New Job form (legacy #jobs-detail) still opens pass
No console errors pass (empty console_messages)

Notes

  • Theme parity: the new view uses the same --bg-primary / --bg-secondary / --bg-tertiary / --text-* / --border-color tokens that the rest of the dashboard uses, so it follows the existing dark/light toggle automatically.
  • Polling cadence reduced from 2 s to 1.5 s (per spec) and is tied to the detail-view lifecycle: stopJobLogPolling() runs from both hideJobsDetailView() and switchTab(non-jobs), so no leaked intervals.
  • _jobsDetailLogPaused short-circuits fetchJobLogs() when paused; the line-count display and live-dot reflect that.
  • The hash route #jobs/<id> was already wired in two places (DOMContentLoaded init + hashchange handler); both already called viewJob(id), so rewriting viewJob was sufficient — no additions to the routing table.
  • closePanel('jobs') now also calls hideJobsDetailView() 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.

## 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 | File | Lines | |---|---| | `crates/hero_proc_ui/templates/index.html` | +75 / -34 | | `crates/hero_proc_ui/static/js/dashboard.js` | +132 / -26 | | `crates/hero_proc_ui/static/css/dashboard.css` | +122 / -0 | No backend / Rust changes. The legacy `#jobs-detail` panel was preserved — `showJobForm()` (the New Job creation form) still uses it. ### Test matrix (all pass) | Acceptance criterion | Result | |---|---| | Click row hides list, shows two-pane detail view | pass | | URL becomes `#jobs/<id>` on open | pass | | Pasting `#jobs/853` deep-links straight to detail | pass | | Back-to-jobs restores list with prior row count | pass | | Esc dismisses the detail view | pass | | Browser Back returns to list | pass | | Service / Run / Action chip-links cross-navigate | pass (Service chip jumped to `#services/hero_proc_ui` and reset `_jobsDetailOpen=false`, `_jobLogPollTimer=null`) | | Live polling: status text, line count, pause toggle | pass (`Live 3 lines` → `Paused 3 lines`) | | Process Stats live for running jobs (CPU / mem) | pass (0.4% / 15 MB) | | Cancel / Kill / Logs / Delete header actions render | pass | | Responsive collapse below 900 px | pass (split flips to `flex-direction: column`, panes stack) | | Switching tabs exits the detail view + clears pollers | pass | | List filters (phase + search) survive Back | pass (1 row matches `phase=running` + `search=hero_proc`) | | New Job form (legacy `#jobs-detail`) still opens | pass | | No console errors | pass (empty `console_messages`) | ### Notes - Theme parity: the new view uses the same `--bg-primary` / `--bg-secondary` / `--bg-tertiary` / `--text-*` / `--border-color` tokens that the rest of the dashboard uses, so it follows the existing dark/light toggle automatically. - Polling cadence reduced from 2 s to 1.5 s (per spec) and is tied to the detail-view lifecycle: `stopJobLogPolling()` runs from both `hideJobsDetailView()` and `switchTab(non-jobs)`, so no leaked intervals. - `_jobsDetailLogPaused` short-circuits `fetchJobLogs()` when paused; the line-count display and live-dot reflect that. - The hash route `#jobs/<id>` was already wired in two places (DOMContentLoaded init + hashchange handler); both already called `viewJob(id)`, so rewriting `viewJob` was sufficient — no additions to the routing table. - `closePanel('jobs')` now also calls `hideJobsDetailView()` 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.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_proc#58
No description provided.