jobs #9

Closed
opened 2026-04-01 09:57:00 +00:00 by despiegk · 4 comments
Owner

image

sort based on id (time)
also have selection, so we can delete all or some records

when I go in the log of a job (click on it)
when I open the job open a new tab

image

then I can't exit this page the 'X'. this should be exit me back to overview of jobs, by closing the open tab

![image](/attachments/55838b8f-846d-4275-acc9-fdae9fa19c4e) sort based on id (time) also have selection, so we can delete all or some records when I go in the log of a job (click on it) when I open the job open a new tab ![image](/attachments/a7731a02-1fa6-419f-9a79-192f34be0541) then I can't exit this page the 'X'. this should be exit me back to overview of jobs, by closing the open tab
Author
Owner

Implementation Spec — Issue #9: Jobs UX Improvements

Objective

Four targeted UX improvements to the Jobs tab:

  1. Sort jobs by ID descending (newest first)
  2. Multi-select with bulk delete
  3. Job log opens in a new browser tab (not an overlay)
  4. Fix the "X" close button — closes the tab and returns to jobs overview

Current State

  • loadJobs() in dashboard.js calls rpc('jobs.list') → server iterates two HashMaps in non-deterministic order → no sort applied
  • Clicking a job calls viewJobLogs() which creates a full-screen overlay injected into the DOM. The "X" close button removes a CSS class that has no effect — the overlay never closes (root bug)
  • No jobs.delete RPC method exists today

Files to Modify

File Purpose
crates/hero_slides_server/src/rpc.rs Add jobs.delete RPC handler
crates/hero_slides_server/openrpc.json Document new jobs.delete method
crates/hero_slides_ui/templates/index.html Jobs tab: checkbox column + Delete Selected button
crates/hero_slides_ui/static/js/dashboard.js Sort, checkbox state, bulk delete, new-tab nav, close-tab
crates/hero_slides_ui/static/css/dashboard.css Job log page styling

Implementation Plan

Step 1 — Server: add jobs.delete RPC

File: rpc.rs

  • New handler handle_jobs_delete(keys: Vec<String>) — removes matching entries from state.agent_jobs and state.gen_jobs
  • Register as "jobs.delete" in the method dispatch
  • Returns { "deleted": N }
  • Dependencies: none

Step 2 — API spec: document jobs.delete

File: openrpc.json

  • Add method entry after jobs.list with keys: string[] param and { deleted: integer } result
  • Dependencies: Step 1

Step 3 — Template: update Jobs tab markup

File: index.html

  • Add <input type="checkbox" id="jobs-select-all"> as first <th> in jobs table header
  • Add "Delete Selected" button (hidden by default, id="btn-delete-selected") in toolbar
  • Bump colspan on empty-state row from 6 → 7
  • Dependencies: none (parallel with Step 1)

Step 4 — JS: sort + checkbox + bulk delete + new-tab + close-tab

File: dashboard.js

  • 4a Sort: jobs.sort((a,b) => b.job_id - a.job_id) after fetching
  • 4b Render checkbox per row (with stopPropagation on click)
  • 4c Delegated change listener → update count + show/hide Delete Selected button
  • 4d toggleSelectAllJobs(cb) — checks/unchecks all rows
  • 4e deleteSelectedJobs() — collects keys, calls rpc('jobs.delete', {keys}), refreshes
  • 4f Replace viewJobLogs body: window.open('#job/<id>/<type>/<key>', '_blank')
  • 4g Add case 'job': in applyCurrentRoute() → decode params, call renderJobLogPage
  • 4h renderJobLogPage(jobId, type, key) — hides normal chrome, renders full-page log with "Close Tab" button → window.close()
  • Remove dead job-log-overlay DOM creation code
  • Dependencies: Steps 1 and 3

Step 5 — CSS: job log page styles

File: dashboard.css

  • Add .job-log-page, .job-log-topbar, .job-log-pre classes for the standalone log tab
  • Dependencies: Step 4

Acceptance Criteria

  • Jobs list renders newest job (highest job_id) at the top
  • Header checkbox selects/deselects all rows
  • Individual row checkboxes work independently; clicking the row (not checkbox) still opens the log
  • "Delete Selected" button visible only when ≥1 row is checked; deletes those records on click
  • Clicking a job row opens log in a new browser tab
  • New tab renders the full log for that job
  • Clicking the close/X button in the new tab calls window.close() successfully
  • The original Jobs tab remains open and unaffected

Notes

  • No new Axum routes needed — the new tab uses hash routing (#job/…) in the existing SPA
  • window.close() works because the tab is opened via window.open() from script
  • jobs.delete removes in-memory tracking records only; the underlying hero_proc job is not affected
  • Job keys may contain / — encode with encodeURIComponent and reassemble in the route parser
# Implementation Spec — Issue #9: Jobs UX Improvements ## Objective Four targeted UX improvements to the Jobs tab: 1. Sort jobs by ID descending (newest first) 2. Multi-select with bulk delete 3. Job log opens in a new browser tab (not an overlay) 4. Fix the "X" close button — closes the tab and returns to jobs overview ## Current State - `loadJobs()` in `dashboard.js` calls `rpc('jobs.list')` → server iterates two `HashMap`s in non-deterministic order → no sort applied - Clicking a job calls `viewJobLogs()` which creates a full-screen overlay injected into the DOM. The "X" close button removes a CSS class that has no effect — the overlay never closes (root bug) - No `jobs.delete` RPC method exists today ## Files to Modify | File | Purpose | |------|----------| | `crates/hero_slides_server/src/rpc.rs` | Add `jobs.delete` RPC handler | | `crates/hero_slides_server/openrpc.json` | Document new `jobs.delete` method | | `crates/hero_slides_ui/templates/index.html` | Jobs tab: checkbox column + Delete Selected button | | `crates/hero_slides_ui/static/js/dashboard.js` | Sort, checkbox state, bulk delete, new-tab nav, close-tab | | `crates/hero_slides_ui/static/css/dashboard.css` | Job log page styling | ## Implementation Plan ### Step 1 — Server: add `jobs.delete` RPC **File:** `rpc.rs` - New handler `handle_jobs_delete(keys: Vec<String>)` — removes matching entries from `state.agent_jobs` and `state.gen_jobs` - Register as `"jobs.delete"` in the method dispatch - Returns `{ "deleted": N }` - Dependencies: none ### Step 2 — API spec: document `jobs.delete` **File:** `openrpc.json` - Add method entry after `jobs.list` with `keys: string[]` param and `{ deleted: integer }` result - Dependencies: Step 1 ### Step 3 — Template: update Jobs tab markup **File:** `index.html` - Add `<input type="checkbox" id="jobs-select-all">` as first `<th>` in jobs table header - Add "Delete Selected" button (hidden by default, `id="btn-delete-selected"`) in toolbar - Bump `colspan` on empty-state row from 6 → 7 - Dependencies: none (parallel with Step 1) ### Step 4 — JS: sort + checkbox + bulk delete + new-tab + close-tab **File:** `dashboard.js` - **4a** Sort: `jobs.sort((a,b) => b.job_id - a.job_id)` after fetching - **4b** Render checkbox per row (with `stopPropagation` on click) - **4c** Delegated change listener → update count + show/hide Delete Selected button - **4d** `toggleSelectAllJobs(cb)` — checks/unchecks all rows - **4e** `deleteSelectedJobs()` — collects keys, calls `rpc('jobs.delete', {keys})`, refreshes - **4f** Replace `viewJobLogs` body: `window.open('#job/<id>/<type>/<key>', '_blank')` - **4g** Add `case 'job':` in `applyCurrentRoute()` → decode params, call `renderJobLogPage` - **4h** `renderJobLogPage(jobId, type, key)` — hides normal chrome, renders full-page log with "Close Tab" button → `window.close()` - Remove dead `job-log-overlay` DOM creation code - Dependencies: Steps 1 and 3 ### Step 5 — CSS: job log page styles **File:** `dashboard.css` - Add `.job-log-page`, `.job-log-topbar`, `.job-log-pre` classes for the standalone log tab - Dependencies: Step 4 ## Acceptance Criteria - [ ] Jobs list renders newest job (highest job_id) at the top - [ ] Header checkbox selects/deselects all rows - [ ] Individual row checkboxes work independently; clicking the row (not checkbox) still opens the log - [ ] "Delete Selected" button visible only when ≥1 row is checked; deletes those records on click - [ ] Clicking a job row opens log in a **new browser tab** - [ ] New tab renders the full log for that job - [ ] Clicking the close/X button in the new tab calls `window.close()` successfully - [ ] The original Jobs tab remains open and unaffected ## Notes - No new Axum routes needed — the new tab uses hash routing (`#job/…`) in the existing SPA - `window.close()` works because the tab is opened via `window.open()` from script - `jobs.delete` removes in-memory tracking records only; the underlying hero_proc job is not affected - Job keys may contain `/` — encode with `encodeURIComponent` and reassemble in the route parser
Author
Owner

Test Results

  • Status: PASS
  • Total: 45
  • Passed: 44
  • Failed: 0
  • Ignored: 1

Breakdown by crate

Crate Tests Result
hero_slides (bin) 0 ok
hero_slides_lib 31 ok
hero_slides_rhai 0 ok
integration_test 12 passed, 1 ignored ok
hero_slides_sdk 0 ok
hero_slides_server 0 ok
hero_slides_ui 0 ok
doc-tests hero_slides_lib 1 ok
doc-tests hero_slides_rhai 1 ok
doc-tests hero_slides_sdk 0 ok

Notes

  • 1 test ignored: test_generate_single_slide_ai (requires AI/network, skipped intentionally)
  • 2 compiler warnings (unused variable and dead code) — non-blocking
  • Build time: ~38 seconds
## Test Results - Status: PASS - Total: 45 - Passed: 44 - Failed: 0 - Ignored: 1 ### Breakdown by crate | Crate | Tests | Result | |-------|-------|--------| | hero_slides (bin) | 0 | ok | | hero_slides_lib | 31 | ok | | hero_slides_rhai | 0 | ok | | integration_test | 12 passed, 1 ignored | ok | | hero_slides_sdk | 0 | ok | | hero_slides_server | 0 | ok | | hero_slides_ui | 0 | ok | | doc-tests hero_slides_lib | 1 | ok | | doc-tests hero_slides_rhai | 1 | ok | | doc-tests hero_slides_sdk | 0 | ok | ### Notes - 1 test ignored: `test_generate_single_slide_ai` (requires AI/network, skipped intentionally) - 2 compiler warnings (unused variable and dead code) — non-blocking - Build time: ~38 seconds
Author
Owner

Implementation Complete

All four UX improvements from issue #9 have been implemented.

Changes Made

crates/hero_slides_server/src/rpc.rs

  • Added handle_jobs_delete async handler — accepts keys: Vec<String>, removes matching records from agent_jobs and gen_jobs state maps, returns { "deleted": N }
  • Registered as "jobs.delete" in the method dispatch

crates/hero_slides_server/openrpc.json

  • Documented new jobs.delete method with keys: string[] param and { deleted: integer } result schema

crates/hero_slides_ui/templates/index.html

  • Added Select All checkbox (#jobs-select-all) as first column in jobs table header
  • Added "Delete Selected" button (#btn-delete-selected, hidden by default) in jobs toolbar
  • Updated empty-state row colspan from 6 → 7

crates/hero_slides_ui/static/js/dashboard.js

  • Sort: jobs.sort((a, b) => b.job_id - a.job_id) — newest job at top
  • Checkbox per row with stopPropagation so row-click still works
  • updateJobsDeleteButton() — shows/hides Delete Selected button and count
  • toggleSelectAllJobs(cb) — checks/unchecks all rows
  • deleteSelectedJobs() — calls rpc('jobs.delete', { keys }), refreshes list
  • viewJobLogs() — replaced overlay creation with window.open('#job/...', '_blank')
  • applyCurrentRoute() — added case 'job': to handle hash-based job log routes
  • renderJobLogPage(jobId, type, key) — full-page log view with "Close Tab" → window.close()
  • Removed dead job-log-overlay DOM code

crates/hero_slides_ui/static/css/dashboard.css

  • Added body.job-log-page, .job-log-topbar, .job-log-title, .job-log-pre styles for the standalone job log tab

Test Results

  • 44 tests passed, 0 failed (1 ignored: AI/network test)
  • All hero_slides_lib unit tests and integration tests green
## Implementation Complete All four UX improvements from issue #9 have been implemented. ### Changes Made **`crates/hero_slides_server/src/rpc.rs`** - Added `handle_jobs_delete` async handler — accepts `keys: Vec<String>`, removes matching records from `agent_jobs` and `gen_jobs` state maps, returns `{ "deleted": N }` - Registered as `"jobs.delete"` in the method dispatch **`crates/hero_slides_server/openrpc.json`** - Documented new `jobs.delete` method with `keys: string[]` param and `{ deleted: integer }` result schema **`crates/hero_slides_ui/templates/index.html`** - Added Select All checkbox (`#jobs-select-all`) as first column in jobs table header - Added "Delete Selected" button (`#btn-delete-selected`, hidden by default) in jobs toolbar - Updated empty-state row colspan from 6 → 7 **`crates/hero_slides_ui/static/js/dashboard.js`** - Sort: `jobs.sort((a, b) => b.job_id - a.job_id)` — newest job at top - Checkbox per row with `stopPropagation` so row-click still works - `updateJobsDeleteButton()` — shows/hides Delete Selected button and count - `toggleSelectAllJobs(cb)` — checks/unchecks all rows - `deleteSelectedJobs()` — calls `rpc('jobs.delete', { keys })`, refreshes list - `viewJobLogs()` — replaced overlay creation with `window.open('#job/...', '_blank')` - `applyCurrentRoute()` — added `case 'job':` to handle hash-based job log routes - `renderJobLogPage(jobId, type, key)` — full-page log view with "Close Tab" → `window.close()` - Removed dead `job-log-overlay` DOM code **`crates/hero_slides_ui/static/css/dashboard.css`** - Added `body.job-log-page`, `.job-log-topbar`, `.job-log-title`, `.job-log-pre` styles for the standalone job log tab ### Test Results - **44 tests passed, 0 failed** (1 ignored: AI/network test) - All hero_slides_lib unit tests and integration tests green
Author
Owner

Implementation committed: 6c90ea2

Browse: 6c90ea2

All changes verified present in codebase (rpc.js.delete handler, JS sort/checkboxes/tabs/close, CSS styles, HTML template).

Implementation committed: `6c90ea2` Browse: https://forge.ourworld.tf/lhumina_code/hero_slides/commit/6c90ea2 All changes verified present in codebase (rpc.js.delete handler, JS sort/checkboxes/tabs/close, CSS styles, HTML template).
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_slides#9
No description provided.