From fc8ce2633e58a77d914eb705b8ecc8238590002d Mon Sep 17 00:00:00 2001 From: Bryce Date: Sat, 27 Jun 2026 13:21:50 -0700 Subject: [PATCH] fix(ssr): bulk-edit empty-selection 500 + modal-footer EDN leak - BUG D: clicking "Bulk Edit" with no invoices selected 500'd. selected->ids returns nil with no selection, and all-ids-not-locked fed that nil into a Datomic `:in $ [?i ...]` query ("Unable to find data source"). Guard the body with (when (seq all-ids) ...) so an empty selection yields [] and the modal opens cleanly. Verified live: no "Oh, drat" toast, no 500. - BUG B: modal-footer- called (hx/alpine-appear ...) twice; the 2nd return value (an attribute map) landed in child position and rendered as literal EDN ({:x-show ...}) in the red error banner whenever unexpectedError flipped true. Delete the duplicate. Verified: rendered HTML now has one alpine-appear and no EDN-text child. (Pre-existing defect, also present on master.) Co-Authored-By: Claude Opus 4.8 --- ...6-27-001-fix-ssr-modal-regressions-plan.md | 18 ++++++++++------ src/clj/auto_ap/ssr/components/dialog.clj | 1 - src/clj/auto_ap/ssr/invoices.clj | 21 ++++++++++--------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/docs/plans/2026-06-27-001-fix-ssr-modal-regressions-plan.md b/docs/plans/2026-06-27-001-fix-ssr-modal-regressions-plan.md index 3677aeae..9ab4b653 100644 --- a/docs/plans/2026-06-27-001-fix-ssr-modal-regressions-plan.md +++ b/docs/plans/2026-06-27-001-fix-ssr-modal-regressions-plan.md @@ -1,7 +1,7 @@ # SSR Modal Regression Fixes — QA Findings & Resumable Task List -> **Status:** BUG A, BUG C, BUG E/F, and the CSS size root cause are **FIXED + verified live** -> (2026-06-27). Remaining: animations (§3), BUG D, BUG B. Resume from §4. +> **Status:** BUG A, BUG C, BUG D, BUG B, BUG E/F, and the CSS size root cause are +> **FIXED + verified** (2026-06-27). Remaining: modal step animations (§3). Resume from §4/§3. > **Owner:** Bryce > **Branch:** `integreat-execute-refactor` > **Date:** 2026-06-27 @@ -166,9 +166,13 @@ match.** (HIGH severity — breaks the flagship modal's core swap doctrine.) ### 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 D — Invoice Bulk Edit with no selection → 500** — **DONE & verified live.** With no +selection, `selected->ids` returns `nil`, which `all-ids-not-locked` fed straight into a Datomic +`:in $ [?i ...]` query → "Unable to find data source" exception → global "Oh, drat!" toast. +- File: `src/clj/auto_ap/ssr/invoices.clj` — wrapped `all-ids-not-locked`'s body in + `(when (seq all-ids) ...)`, so an empty/nil selection yields `[]` and the modal opens cleanly + (also protects every other caller). Live: Bulk Edit with nothing selected now opens the modal, + no toast, no 500. **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 @@ -176,7 +180,9 @@ visible whenever `unexpectedError` flips true (e.g. it showed during BUG A). Cau 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-`. +- **FIX — DONE & verified:** deleted the duplicate `(hx/alpine-appear ...)` line in `modal-footer-`. + Rendering the footer to HTML now shows a single `alpine-appear` (the legit attrs) and no literal + `{:x-show ...}` text child. **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 diff --git a/src/clj/auto_ap/ssr/components/dialog.clj b/src/clj/auto_ap/ssr/components/dialog.clj index e951ff40..2cd38ab6 100644 --- a/src/clj/auto_ap/ssr/components/dialog.clj +++ b/src/clj/auto_ap/ssr/components/dialog.clj @@ -55,7 +55,6 @@ (defn modal-footer- [params & children] [:div {:class "p-4 border-t"} [:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex - (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"}) (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"}) [:span {:class "w-2 h-2 bg-red-500 rounded-full"}] [:span.px-2.py-0.5 "An unexpected error has occured. Integreat staff have been notified."]] diff --git a/src/clj/auto_ap/ssr/invoices.clj b/src/clj/auto_ap/ssr/invoices.clj index e2126e49..d9d789cf 100644 --- a/src/clj/auto_ap/ssr/invoices.clj +++ b/src/clj/auto_ap/ssr/invoices.clj @@ -1486,16 +1486,17 @@ ;; TODO clientize (defn all-ids-not-locked [all-ids] - (->> all-ids - (dc/q '[:find ?i - :in $ [?i ...] - :where - [?i :invoice/client ?c] - [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu] - [?i :invoice/date ?d] - [(>= ?d ?lu)]] - (dc/db conn)) - (map first))) + (when (seq all-ids) + (->> all-ids + (dc/q '[:find ?i + :in $ [?i ...] + :where + [?i :invoice/client ?c] + [(get-else $ ?c :client/locked-until #inst "2000-01-01") ?lu] + [?i :invoice/date ?d] + [(>= ?d ?lu)]] + (dc/db conn)) + (map first)))) (defn- bulk-edit-account-row* "One expense-account row (no cursor). The location cell swaps just itself (#account-location-, Rule 2); the percentage swaps only #expense-totals