Files
integreat/docs/plans/2026-06-27-001-fix-ssr-modal-regressions-plan.md
Bryce 74f1a49a10 fix(ssr): repair New Invoice + Transaction Edit 500s and broken modal sizes
Three regressions from the SSR rendering-modernization migration, all verified
live via agent-browser:

- BUG A — New Invoice: choosing a client 500'd from /invoice/new/due-date
  (ClassCastException: DateTime cannot be cast to java.util.Date). `due-date`
  and `scheduled-payment-date` called `coerce/from-date` on values already
  decoded to clj-time DateTimes. Drop the coerce; use the decoded dates.

- BUG C — Transaction Edit: any whole-form swap (mode toggle, vendor change,
  add/remove row) 500'd whenever the txn had >=1 autopay-invoice match
  (ClassCastException at links-body*: PersistentVector cannot be cast to Named).
  The autopay link-panel's hidden `action` input was missing `:form ""`, so it
  serialized alongside the main `action` hidden, producing a duplicate param
  that Ring collapsed to a vector. Add `:form ""` to match the unpaid/rule panels.

- Modal sizes: Vendor/Client/Invoice-Pay modals ballooned to full width because
  resources/public/output.css was missing their arbitrary Tailwind size classes.
  Root cause: tailwind.config.js `content` never scanned resources/templates/**/*.html
  (46 Selmer templates the migration introduced), so a rebuild also dropped
  template-only classes like md:w-[950px]. Add the templates glob and rebuild;
  all modal size classes now present, no working modal regressed.

Docs: add 2026-06-27 QA findings + resumable fix task list; cross-link from the
migration plan. Remaining (per the new plan): Vendor/Client inner step-body
overflow, wizard step animations, bulk-edit empty-selection 500, footer EDN leak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 13:11:09 -07:00

196 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SSR Modal Regression Fixes — QA Findings & Resumable Task List
> **Status:** BUG A, BUG C, and the CSS size root cause are **FIXED + verified live** (2026-06-27).
> Remaining: BUG E/F (wizard inner layout), animations (§3), BUG D/B. Resume from §4.
> **Owner:** Bryce
> **Branch:** `integreat-execute-refactor`
> **Date:** 2026-06-27
> **Context:** Follow-up to the SSR rendering-modernization migration
> (`2026-06-02-001-refactor-ssr-rendering-modernization-plan.md`, Phases 211 complete).
> The migration to simplified route rendering + whole-form/wizard swaps introduced
> modal **size**, **animation**, and a set of **HTTP 500** regressions. This doc records a
> full browser QA pass (agent-browser, 9 migrated modals) with root causes and fixes.
## 0. How to resume / reproduce
- App is running on the port in `.http-port` (was `42987`); nREPL in `nrepl-port` (was `35689`).
- Browser session: `agent-browser --session integreat ...`. Log in via
`http://localhost:<port>/dev-login` → "Continue to dashboard".
- **Test at a realistic viewport:** `agent-browser --session integreat set viewport 1440 900`
(a short viewport exaggerates modal overflow and gives false positives).
- Screenshots from this pass are in `./tmp/` (gitignored): `02..19-*.png`. Raw running notes:
`./tmp/findings.md`.
- After rebuilding CSS, the browser caches the old `output.css`; bust it by swapping the
`<link>` href (`?v=<n>`) or hard-reloading, or you'll keep seeing the old sizes.
## 1. Test matrix (9 migrated modals)
| Modal | File | Type | Result |
|-------|------|------|--------|
| Transaction Edit | `transaction/edit.clj` | form | Opens OK; **BUG C** — whole-form swap 500 |
| Transaction Bulk Code | `transaction/bulk_code.clj` | form | ✅ Works |
| Sales Summary Edit | `pos/sales_summaries.clj` | form | ✅ Works |
| Invoice Bulk Edit | `invoices.clj` | form | ✅ Works (empty-selection 500 = **BUG D**) |
| Transaction Rule | `admin/transaction_rules.clj` | wizard | ✅ Works |
| Invoice Pay | `invoices.clj` | wizard | ✅ Works (narrow until CSS rebuild — see §2) |
| New Invoice | `invoice/new_invoice_wizard.clj` | wizard | **BUG A** — choose-client 500 |
| Vendor | `admin/vendors.clj` | wizard | Size broken (§2) + **BUG E** inner overflow |
| Client | `admin/clients.clj` | wizard | Size broken (§2) + **BUG F** inner overflow |
Cross-cutting: **§2** (stale CSS → sizes), **BUG B** (footer EDN leak), **animations** (§3).
## 2. ★ ROOT CAUSE of broken modal SIZES — stale compiled Tailwind CSS
`resources/public/output.css` (committed, last built **Jun 24**) is **missing the migration's
newer arbitrary size classes**. Tailwind only compiles classes present in source at build time,
so any size value added/changed after Jun 24 isn't in the CSS and the element falls back to its
`w-full h-full` sibling class → the modal balloons to full width.
- **Present** (these modals size correctly today): `md:w-[750px]` (New Invoice), `md:w-[950px]`
(Transaction Edit / Bulk Code / Sales Summary), `md:h-[600px]`, `md:h-[650px]`, `w-[850px]`
(Transaction Rule).
- **Missing** (these balloon / mis-size): `md:w-[760px]` (Vendor), `md:w-[820px]` (Client),
`md:h-[520px]`, `md:h-[560px]`, `w-[50em]` (Invoice Pay).
**Verified:** rebuilding to a temp file generates all missing classes; after rebuild + cache-bust,
the Vendor card went **1257px → 760×520** and Client → **820×560** (exactly as declared).
**DEEPER ROOT CAUSE found while fixing (2026-06-27):** `tailwind.config.js` `content` only scanned
`./src/**/*.{cljs,clj,cljc}` — it **never scanned `resources/templates/**/*.html`** (the 46 Selmer
templates the migration introduced). So a naive rebuild *drops* every template-only class
(e.g. `md:w-[950px]` / `md:h-[650px]`, used only in `templates/transaction-edit/edit-modal.html`,
which would have re-broken Transaction Edit / Bulk Code / Sales Summary). The durable fix is to add
the templates glob to the config, then rebuild.
**FIX — DONE & verified:**
1. Added `"./resources/templates/**/*.html"` to `tailwind.config.js` `content`.
2. `npx tailwindcss -i resources/input.css -o resources/public/output.css` (kept unminified to
match the committed file; add `--minify` only if the prod pipeline minifies).
- Verified: all modal size classes now present (`md:w-[760px]`, `md:w-[820px]`, `md:h-[520px]`,
`md:h-[560px]`, `w-[50em]` **and** the template-sourced `md:w-[950px]`/`md:h-[650px]`). Class-diff
vs the old CSS shows the only removed classes are orphaned (the deleted `mm/*` modal-stack
`forward`/`backward`/`group/transition`/`htmx-*:translate-x-2/3` animation set + the unused
`lg:w-[900px]` sales size) — none still referenced in src/ or templates/. Vendor modal confirmed
live at **760×520** (was 1257px).
- **Still TODO:** confirm the prod/CI build runs this tailwind step (so output.css can't drift
again) — ideally wire it into `lein build` / buildspec.
> Note: during QA I rebuilt `output.css` to verify, then reverted it so the working tree is clean
> for review. The verified rebuild is preserved at `tmp/output-rebuilt.css`; backup at
> `tmp/output.css.bak`.
## 3. Modal ANIMATIONS
The new wizard step cards swap via `hx-target "this" hx-swap "outerHTML"` but their
`modal-card-advanced` carries **no transition classes**, so step→step and modal enter/leave do not
animate:
- New Invoice steps (`render-basic-details`, `render-accounts`): card class is only
`md:w-[750px] md:h-[600px] w-full h-full` — no `htmx-swapping:`/`htmx-added:` variants.
- `next-steps-modal` (new_invoice_wizard.clj ~678): only static `scale-100 translate-x-0
opacity-100` — missing the `htmx-swapping:`/`htmx-added:` swap variants.
- By contrast, `dialog/success-modal-` and the Invoice Pay card (`last-modal-step transition
duration-150`) keep transitions, so the intended pattern still exists to copy from.
**FIX:** add the swap-transition classes to the wizard step cards (mirror `success-modal-`'s
`transition duration-300 ease-in-out htmx-swapping:... htmx-added:...` string, or the lighter
`last-modal-step transition duration-150` used by the transaction-edit card). Confirm the
`htmx-swapping:`/`htmx-added:` custom variants survive the §2 CSS rebuild.
## 4. Bug list + fixes (prioritized task list)
### P0 — functional 500s
**BUG A — New Invoice: choosing a client → 500 from `/invoice/new/due-date`.**
- File: `src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj`, fn `due-date` (and same latent bug in
`scheduled-payment-date`).
- Exception: `ClassCastException: org.joda.time.DateTime cannot be cast to java.util.Date`.
- Cause: `form-vendor-client` decodes `:invoice/date`/`:invoice/due` to **clj-time `DateTime`**
(via `clj-date-schema`), but `due-date` then calls `(some-> date coerce/from-date)` —
`coerce/from-date` expects a `java.util.Date`. `scheduled-payment-date` has the same
`(some-> due coerce/from-date)` but is masked because `due` is nil-guarded by a `when`.
- Repro: open New Invoice, pick any client (vendor still empty) → red "unexpected error" banner;
network shows PUT `/invoice/new/due-date` = 500. REPL-confirmed the decoded date is already a
`DateTime` and `time/plus` works on it directly.
- **FIX — DONE & verified live:** dropped the `coerce/from-date` calls — `due-date` now uses the
decoded `date`/`due` `DateTime`s directly (removed the `date (some-> date coerce/from-date)`
rebind and the `due` coerce); same for `scheduled-payment-date`. Live: selecting a client now
fires PUT `/invoice/new/due-date` + `/scheduled-payment-date` → **200** (was 500).
**BUG C — Transaction Edit: any whole-form swap (mode toggle, vendor change, add/remove row, $/%
toggle) → 500 from `/transaction2/edit-form-changed`, whenever the txn has ≥1 autopay-invoice
match.** (HIGH severity — breaks the flagship modal's core swap doctrine.)
- File: `src/clj/auto_ap/ssr/transaction/edit.clj`.
- Exception: `ClassCastException: PersistentVector cannot be cast to Named` at **edit.clj:849**
(`links-body*` does `(name (:action step-params))`).
- Cause: the link-panels each render a hidden `<input name="action">`. The **unpaid** panel
(line 690) and **rule** panel (line 730) add `:form ""` to exclude that hidden from form
serialization. The **autopay** panel (**line 661**) is **missing `:form ""`**, so its
`action="link-autopay-invoices"` hidden is serialized alongside the main `action` hidden (line
862, value "manual"). Ring collapses the duplicate `action` params into a vector → `(name
vector)` throws.
- Repro: edit a transaction whose "Link to autopay invoices" tab shows a badge count ≥1, click
"Switch to advanced mode". REPL-confirmed: single `action` → 200; `action` as a vector → the
exact 500. (Txns with 0 autopay matches render `panel-empty*` with no action-hidden and swap
fine — hence intermittent.)
- **FIX — DONE & verified live:** added `:form ""` to the autopay panel's action-hidden
(**edit.clj:661**) to match the unpaid/rule panels. Live: on a txn with an autopay match,
"Switch to advanced mode" now POSTs `edit-form-changed` → **200** with a single `action=manual`
param (the duplicate `action=link-autopay-invoices` is gone) and the mode toggles correctly.
- (Optional hardening, not applied: make `links-body*` coerce a vector action via
`(some-> (:action step-params) (as-> a (if (coll? a) (last a) a)) name)`.)
### P1 — sizing / layout (after §2 CSS rebuild)
**§2 CSS rebuild** — do first; fixes Vendor/Client/Invoice-Pay widths.
**BUG E — Vendor wizard: inner step bodies overflow the card.** After the CSS rebuild the outer
card is correct (760×520), but the migrated step bodies keep the old fixed `w-[600px] h-[350px]`
wrappers (from when each step was its own modal card), causing a horizontal scrollbar, a clipped
"Name" field, a detached "Basic Info" header box, and a large empty left region.
- File: `src/clj/auto_ap/ssr/admin/vendors.clj` — step bodies at ~393/495/528/555/583.
- **FIX:** drop the per-step `w-[600px] h-[350px]` wrappers; let the step body fill the engine's
outer card (responsive `w-full` + flex column, like the New Invoice steps).
**BUG F — Client wizard: same inner-layout overflow** (worse — step progress bar + fields clipped,
horizontal scroll, detached "Info" header). Outer card correct (820×560) after rebuild.
- File: `src/clj/auto_ap/ssr/admin/clients.clj` — step bodies (info step ~1426 etc.).
- **FIX:** same as BUG E — rework migrated step bodies to fill the outer card.
### P2 — robustness / cosmetic
**BUG D — Invoice Bulk Edit with no selection → 500** (global "Oh, drat!" toast). Works fine with
a selection. Should no-op or show a friendly "select invoices first" message.
- File: `src/clj/auto_ap/ssr/invoices.clj` (bulk-edit open handler).
**BUG B — Footer leaks a raw Hiccup attr-map as text** in the red "unexpected error" banner
(`{:x-show "unexpectedError", ...}`). **Pre-existing in master** (NOT a migration regression), but
visible whenever `unexpectedError` flips true (e.g. it showed during BUG A). Cause: `modal-footer-`
calls `(hx/alpine-appear {...})` **twice** — the 2nd return value lands in child position and
renders as literal EDN.
- File: `src/clj/auto_ap/ssr/components/dialog.clj` ~line 59.
- **FIX:** delete the duplicate `(hx/alpine-appear ...)` line in `modal-footer-`.
**Cosmetic — over-tall empty modals.** Invoice/Transaction Bulk Edit and similar use fixed
`md:h-[650px]`; with little content they show a large empty lower region. Consider letting height
hug content (cap with `max-h`) rather than a fixed height. Low priority.
## 5. Suggested order of work
1. **§2 CSS rebuild** (unblocks all width checks; commit `output.css`; verify prod build does this).
2. **BUG A** + **BUG C** (the two functional 500s; one-liners each, REPL-verified).
3. **BUG E / BUG F** (wizard step-body layout rework; needs visual iteration per step).
4. **§3 animations** (add swap-transition classes to wizard step cards).
5. **BUG D**, **BUG B** (robustness / cosmetic).
6. Re-run the full agent-browser pass (all 9 modals) + the Playwright e2e suite as the parity gate.
## 6. What was verified vs. inferred
- **Verified in browser + REPL:** BUG A (500 + exact exception + fix direction), BUG C (500 +
exact exception + vector repro + line 661 cause), §2 CSS root cause (missing classes + rebuild
fixes Vendor 1257→760 and Client→820), BUG D (500 on empty selection), Vendor/Client inner
overflow (screenshots 16/17), and that Bulk Code / Sales Summary / Transaction Rule / Invoice
Pay / Transaction Edit-open all render without errors.
- **Inferred (static analysis, not yet visually A/B'd against master):** §3 animation regression
(step cards lack `htmx-swapping:`/`htmx-added:` classes) and BUG B being pre-existing.