SSR modernization: ssr-form-migration skill + Transaction Edit plain-form/Selmer migration #14

Open
notid wants to merge 21 commits from integreat-execute-refactor into staging
2 changed files with 68 additions and 54 deletions
Showing only changes of commit 57f3b63b6a - Show all commits

View File

@@ -39,14 +39,15 @@ Each migration appends one row (after-numbers), referencing the before in the di
| Phase | Modal | LOC | Routes | no-cursor twins | faked roots | snapshot merges | OOB | mixed hx- | cookbook reused / added |
|-------|-------|-----|--------|-----------------|-------------|-----------------|-----|-----------|-------------------------|
| 1 (baseline) | Transaction Edit `transaction/edit.clj` | 1608 | ~12 | 1 | 2 | ~75 | 0 | 8 | — / seeded 7 entries |
| 2 (in progress) | Transaction Edit `transaction/edit.clj` | 1555 | ~12 | **0** | 2 | ~75 | 0 | 8 | — / 0 |
| 2 (in progress) | Transaction Edit `transaction/edit.clj` | 1570 | ~12 | **0** | **0** | ~75 | 0 | 8 | — / 0 |
> **Phase 2 progress (partial).** Achieved with parity held (swap spec 6/6 + Shared
> Location green): deleted the dead `*-no-cursor*` twin (no-cursor 1→0, 53 LOC) and fixed
> a real production bug (`:mode` leaking into the upsert → 500 on every advanced manual
> save). **Still open** for this modal — and intentionally *not* forced under parity risk:
> faked cursor roots (2 — de-faking needs the render-fn rewrite, see `gotchas.md`), the
> snapshot round-trip (~75 — removed by the wizard→plain-form reclassification), Selmer
> conversion of the render fns, and route collapse (~12 → ~3). These are the bulk of the
> modal migration and require restructuring the modal's rendering wholesale rather than
> isolated edits; track as the continuation of Phase 2.
> Location green, full suite 31 pass / no regression): deleted the dead `*-no-cursor*`
> twin (no-cursor 1→0), **de-faked the simple-mode cursor** (faked roots 2→0) by rendering
> the row from explicit data with explicit field names (`account-field-name`) + explicit
> error lookup — the render-fn rewrite the `with-field-default` shortcut couldn't do — and
> fixed a real production bug (`:mode` leaking into the upsert → 500 on every advanced
> manual save). **Still open** for this modal: the snapshot round-trip (~75 — removed by
> the wizard→plain-form reclassification), Selmer conversion of the render fns, and route
> collapse (~12 → ~3). These remain the bulk of the migration and need wholesale
> restructuring of the modal's rendering; track as the continuation of Phase 2.

View File

@@ -183,10 +183,32 @@
(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
client-id)))})])
(defn- account-field-name
"Explicit form-field name for account row `index`, field `field` -- the same string
the form cursor produces at path [:step-params :transaction/accounts index field]
(via path->name2), without faking a deep cursor to get there."
[index field]
(str "step-params[transaction/accounts][" index "]["
(if (keyword? field)
(str (when (namespace field) (str (namespace field) "/")) (name field))
field)
"]"))
(defn- account-field-errors
"Errors for account row `index`, field `field`, read straight from the form errors
at the same path the cursor would walk -- avoids re-rooting a cursor to look them up."
[index field]
(when (bound? #'fc/*form-errors*)
(get-in fc/*form-errors* [:step-params :transaction/accounts index field])))
(defn simple-mode-fields*
"Renders the simple-mode account + location row and the toggle-to-advanced link.
Must be called within a fc/start-form + fc/with-field :step-params context.
Caller must establish Alpine x-data with simpleAccountId in scope."
Caller must establish Alpine x-data with simpleAccountId in scope.
The single account row is rendered from explicit data with explicit field names
(account-field-name 0 ...) rather than faking a synthetic MapCursor rooted at
accounts[0] -- the row always lives at index 0 in simple mode."
[request]
(let [snapshot (-> request :multi-form-state :snapshot)
step-params (-> request :multi-form-state :step-params)
@@ -204,50 +226,41 @@
(:transaction/amount snapshot)
0.0))]
[:div
(fc/with-field :transaction/accounts
(fc/with-cursor (let [cur fc/*current*]
(if (sequential? @cur)
(nth cur 0 nil)
(auto_ap.cursor.MapCursor. {} (cursor/state cur) (conj (cursor/path cur) 0))))
[:span
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value row-id}))
[:div.flex.gap-2.mt-2
(fc/with-field :transaction-account/account
(com/validated-field
{:label "Account"
:errors (fc/field-errors)}
[:div.w-72
(account-typeahead* {:value account-val
:client-id client-id
:name (fc/field-name)
:x-model "simpleAccountId"})]))
(fc/with-field :transaction-account/location
;; Selecting the account only affects the valid Location options, so the
;; change swaps just this cell -- nothing else needs to re-render.
[:div {:id "simple-account-location"}
(com/validated-field
{:label "Location"
:errors (fc/field-errors)
:x-hx-val:account-id "simpleAccountId"
:hx-vals (hx/json (cond-> {:name (fc/field-name)}
client-id (assoc :client-id client-id)))
:x-dispatch:changed "simpleAccountId"
:hx-trigger "changed"
:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
:hx-target "#simple-account-location"
:hx-select "#simple-account-location"
:hx-swap "outerHTML"
:hx-include "closest form"}
(location-select*
{:name (fc/field-name)
:account-location (:account/location account-id)
:client-locations (pull-attr (dc/db conn) :client/locations client-id)
:value location-val}))])
(fc/with-field :transaction-account/amount
(com/hidden {:name (fc/field-name)
:value total}))]]))
[:span
(com/hidden {:name (account-field-name 0 :db/id)
:value row-id})
[:div.flex.gap-2.mt-2
(com/validated-field
{:label "Account"
:errors (account-field-errors 0 :transaction-account/account)}
[:div.w-72
(account-typeahead* {:value account-val
:client-id client-id
:name (account-field-name 0 :transaction-account/account)
:x-model "simpleAccountId"})])
;; Selecting the account only affects the valid Location options, so the
;; change swaps just this cell -- nothing else needs to re-render.
[:div {:id "simple-account-location"}
(com/validated-field
{:label "Location"
:errors (account-field-errors 0 :transaction-account/location)
:x-hx-val:account-id "simpleAccountId"
:hx-vals (hx/json (cond-> {:name (account-field-name 0 :transaction-account/location)}
client-id (assoc :client-id client-id)))
:x-dispatch:changed "simpleAccountId"
:hx-trigger "changed"
:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-form-changed)
:hx-target "#simple-account-location"
:hx-select "#simple-account-location"
:hx-swap "outerHTML"
:hx-include "closest form"}
(location-select*
{:name (account-field-name 0 :transaction-account/location)
:account-location (:account/location account-id)
:client-locations (pull-attr (dc/db conn) :client/locations client-id)
:value location-val}))]
(com/hidden {:name (account-field-name 0 :transaction-account/amount)
:value total})]]
[:div.mt-1
[:a.text-sm.text-blue-600.hover:underline.cursor-pointer
{:hx-post (bidi/path-for ssr-routes/only-routes ::route/edit-wizard-toggle-mode)