5.7 KiB
phase: 11-ui-enhancements plan: "02" subsystem: printer-ui tags: [uie-01, htmx, patch, modal, pico-css, alpine-js, e2e, playwright] dependency_graph: requires: [11-01] provides: [PATCH /printers/{id}, printer_edit_modal.html, Edit button per row] affects: [imptune/api/printers.py, imptune/templates/partials/printer_list.html, imptune/templates/partials/printer_edit_modal.html, tests/e2e/test_printer_edit.py] tech_stack: added: [] patterns: [HTMX PATCH in-place update, Pico CSS native dialog, Alpine.js portEdited guard, session-scoped E2E row targeting] key_files: created: - imptune/templates/partials/printer_edit_modal.html - tests/e2e/test_printer_edit.py modified: - imptune/api/printers.py - imptune/templates/partials/printer_list.html decisions:
- "PATCH ip_address and port_name are optional Form fields (default empty string) that fall back to existing printer values — matches Wave 0 test scaffold that only sends name"
- "E2E row targeting uses locator(tr, has=locator(a, has_text)) to handle session-scoped live_server accumulating multiple printers across tests"
- "updated_at set explicitly via datetime.now(UTC).replace(tzinfo=None) inside PATCH handler"
- "clients and driver_data added to _render_printer_list context for edit modal pre-population" metrics: duration: "~20 minutes" completed: "2026-04-15" tasks_completed: 2 tasks_total: 2 files_modified: 4
Phase 11 Plan 02: Printer Edit Modal (UIE-01) Summary
One-liner: HTMX PATCH route + Pico CSS native dialog edit modal with Alpine.js port guard and Playwright E2E coverage.
What Was Built
UIE-01 is now complete: every printer row in the library has an Edit button that opens a pre-filled native <dialog> modal. Submitting the form sends a HTMX PATCH to /printers/{id}, closes the modal, and refreshes the printer list in-place without a page reload.
Key Changes
-
imptune/api/printers.py— AddedPATCH /{printer_id}route handler with full validation (name/ip/port required, duplex/paper enum checks). Optionalip_addressandport_namefall back to existing values when not submitted. Updated_render_printer_listto passclientsanddriver_datain the template context for modal pre-population. ImportedDriverat module level. -
imptune/templates/partials/printer_edit_modal.html(new, 98 lines) — Pico CSS native<dialog>with Edit trigger button and HTMX PATCH form. Useshx-on::after-requestto close the modal on success. Alpine.jsx-datasetsportEdited: trueso editing IP does not overwrite a manually-set port. Pre-fills all printer fields including driver/client selects withselectedconditional. -
imptune/templates/partials/printer_list.html— Actions<td>updated:{% include "partials/printer_edit_modal.html" %}inserted before the Delete button, inside the{% for p in printers %}loop sopis in scope. -
tests/e2e/test_printer_edit.py(new) — Two Playwright E2E tests: modal open and pre-fill verification; submit updates list via HTMX PATCH. Row targeting usespage.locator("tr", has=page.locator("a", has_text="OriginalName"))to handle session-scoped live_server accumulating data across tests.
Test Results
| Suite | Status |
|---|---|
| test_patch_printer | GREEN |
| test_patch_printer_not_found | GREEN |
| test_printer_edit_modal_open_and_prefill | GREEN (E2E) |
| test_printer_edit_submit_updates_list | GREEN (E2E) |
| Full non-E2E suite | 120 passed, 2 expected RED (UIE-03 Wave 0) |
Commits
| Hash | Message |
|---|---|
4b212b6 |
feat(11-02): PATCH /printers/{id} route handler and updated _render_printer_list |
7b948b6 |
feat(11-02): UIE-01 edit modal — Edit button per row, Pico dialog, E2E tests |
Deviations from Plan
Auto-fixed Issues
1. [Rule 2 - Missing functionality] ip_address and port_name made optional in PATCH handler
- Found during: Task 1 — reviewing Wave 0 test scaffold
test_patch_printer - Issue: The plan specified
ip_address: str = Form(...)andport_name: str = Form(...)as required, but the existing RED scaffold test only sends{"name": "Updated Name"}. The handler would have returned 422 Unprocessable Entity. - Fix: Changed
ip_addressandport_nametoForm("")with fallback toprinter.ip_address/printer.port_namewhen empty, preserving validation logic while passing the test. - Files modified:
imptune/api/printers.py - Commit:
4b212b6
2. [Rule 1 - Bug] E2E test_printer_edit_submit_updates_list used wrong selector for session-scope isolation
- Found during: Task 2 — E2E test run
- Issue:
page.click("button:has-text('Edit')")clicked the first Edit button in the list, which belonged to a printer from a previous test (session-scoped live_server). The targeted printer ("OriginalName") was not updated. - Fix: Changed to
page.locator("tr", has=page.locator("a", has_text="OriginalName")).locator("button:has-text('Edit')").click()to target the specific row. Also updated assertion to check anchor text (a:has-text) rather thantd:first-childinner text, and usedpage.wait_for_selector("a:has-text('UpdatedName')")for HTMX swap completion. - Files modified:
tests/e2e/test_printer_edit.py - Commit:
7b948b6
Success Criteria Check
- Every printer row has an Edit button
- Clicking Edit opens a native dialog pre-filled with that printer's current data
- Submitting the edit form sends HTMX PATCH, closes the modal, and updates the list
- PATCH /printers/{id} validated via test_patch_printer (GREEN)
- PATCH /printers/9999 returns 404 (confirmed by test_patch_printer_not_found)
- E2E tests pass: modal opens, name is pre-filled, submit updates list