Commit Graph

14 Commits

Author SHA1 Message Date
a01dfc197e refactor(ssr): full Selmer migration of Transaction Edit; remove the wizard
Migrate every part of the Transaction Edit modal's HTML to Selmer templates
(zero Hiccup in the render path) and delete the mm multi-modal "wizard"
abstraction entirely -- there was only ever one step.

- New auto-ap.ssr.components.selmer (sc) + ~22 shared component partials under
  resources/templates/components/ (typeahead, button-group, radio-card,
  data-grid, validated-field, modal, buttons, inputs, SVGs). Each wrapper renders
  its own partial; dynamic HTMX/Alpine attrs bridge via attrs->str -> {{attrs|safe}}.
- 15 modal templates under resources/templates/transaction-edit/.
- Delete EditWizard/LinksStep records + all mm/* usage. Plain handlers: flat
  wrap-decode-edit (fields renamed off step-params[...], stray keys stripped),
  flat wrap-derive-state, *errors*-based field errors, generic wrap-form-4xx-2.
- Drop the edit-wizard-navigate route (routes ~12 -> 5).
- Fix: stray `method` (tab button-group hidden) leaked into the upsert -> 500;
  strip decoded map to schema keys.
- e2e selectors updated (#wizard-form->#edit-form, #wizardmodal->#editmodal,
  step-params[...] field names). Parity: swap 6/6, edit 8/8, suite 38/1
  (1 pre-existing unrelated nav test).
- ssr-form-migration skill updated with the learnings (composition mechanics,
  sc/* library, drop-the-wizard recipe, scorecard row, 3 new gotchas).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 07:47:47 -07:00
d0fad63e24 refactor(ssr): remove the EDN snapshot round-trip; transaction edit is a plain form (heuristic 2)
The wizard serialized the whole accumulating form state into a `snapshot` hidden field
(pr-str EDN + custom readers), decoded it every request, and merged step-params back in.
For this single-step modal the snapshot is pure redundancy: every value is either in the
entity or the live posted form. Remove it:

- render: EditWizard.render-wizard renders a plain form -- no snapshot / edit-path /
  current-step hidden fields; a single `db/id` hidden rides in the form instead.
- middleware: wrap-derive-state rebuilds :multi-form-state per request from the entity
  (loaded by the db/id hidden) overlaid with the live step-params, replacing
  wrap-init-multi-form-state + wrap-entity. The ~34 :snapshot reads are unchanged --
  :snapshot is now a derived map, not a round-tripped blob.
- editable fields (accounts, vendor, memo, approval, action, mode, amount-mode) come ONLY
  from the posted form (absent = cleared) so removing all account rows doesn't resurrect
  the entity's persisted accounts; only entity-only fields (db/id, client, amount, ...)
  come from the entity.
- delete the dead initial-edit-wizard-state and render-account-grid-body.
- e2e: make removeAllAccounts re-query each iteration (whole-form swaps stale a captured
  row index) and restore the percentage test to type-then-add ordering.

Scorecard: snapshot EDN round-trip + custom readers + merge-multi-form-state -> gone
(snapshot-field renders 0). Verified on a fresh server: full suite 38 pass / 1 unrelated
fail, swap 6/6, transaction-edit 8/8 -- same green as before, snapshot removed.
2026-06-03 15:20:26 -07:00
0b5bfd9c84 fix(ssr): operation handlers read live step-params, not the stale snapshot (rewrite stage 1)
apply-new-account / apply-remove-account / apply-toggle-amount-mode rebuilt the account
rows from the decoded :snapshot, dropping any value the user typed but that had not yet
round-tripped into the snapshot (type 50%, click "New account" -> first row reverts,
giving a 66.67/33.33 split instead of 50/50). Read the live :step-params rows instead
(already schema-decoded by mm/wrap-wizard, so typed), falling back to snapshot only when
absent. First stage of removing the snapshot round-trip; fixes a real user-facing bug
(typed amounts lost on add/remove/$%-toggle).

Restore the percentage-split e2e to the realistic type-then-add ordering as a regression
guard. Modal stays green: swap 6/6, transaction-edit 8/8.
2026-06-03 08:18:58 -07:00
798b350c81 test(e2e): green the transaction-edit modal spec (8/8) + record snapshot-drop gotcha
Rewrite the percentage-split test and fix two pre-existing stale tests that were masked
behind it (the file is mode:serial, so the first failure hides the rest):

- Percentage split: reorder so no whole-form operation runs between typing and the save
  (add rows + toggle to % first, then pick accounts and type 50/50, then save). The old
  order typed an amount then added a row, and apply-new-account rebuilds rows from the
  stale snapshot -- dropping the typed value (66.67/33.33 instead of 50/50). Same observed
  behavior verified, just an ordering that doesn't trip the snapshot round-trip.
- Pre-populate-default-account: read the actual transaction total from the grid instead of
  hard-coding $400 for "row index 3" (same-date seed rows have no pinned order).
- openEditModalForTransaction: drop the removed multi-step "Transaction Actions" wizard
  navigation; the modal is single-page, action tabs are immediately available.

skill: gotchas.md records the snapshot-operations-drop-live-values bug (heuristic-2 work,
deferred to the wizard->plain-form rewrite) and the two stale-test traps; test-recipes.md
updates the baseline to 38 pass / 1 fail / 0 skip (transaction-edit 8/8, swap 6/6; the one
failure is the unrelated navigation date-range test).
2026-06-03 07:20:49 -07:00
0f5650b73e refactor(ssr): collapse 5 manual-coding operation routes into edit-form-changed (heuristic 6)
The 5 manual-coding operations (vendor change, simple/advanced toggle, add row, remove
row, $/% toggle) each had their own route + handler, all doing "mutate form state ->
render-full-form". Fold them into the single edit-form-changed endpoint, which now
dispatches on an `op` form-param to the relevant pure apply-* mutation fn (apply-vendor-
changed / apply-toggle-mode / apply-new-account / apply-remove-account /
apply-toggle-amount-mode) then re-renders. A missing/unknown op (a plain dependent-field
change, e.g. account->location or amount->totals) just re-renders, as before.

- edit.clj: 6 handlers -> 1 dispatcher + 5 pure apply-* fns; markup posts to
  edit-form-changed with :hx-vals {:op "..."}.
- routes/transactions.cljc: remove the 5 now-unused route keys.
- e2e specs: retarget the vendor selector by op (div[hx-vals*="vendor-changed"]) and
  point the toggle-amount-mode / vendor response waits at edit-form-changed, since the
  old per-op route names are gone. (Behavioral assertions unchanged.)

Scorecard: manual-coding routes ~10 -> ~5 (operations now one dispatcher). Parity held:
swap spec 6/6, full suite 32 pass (Shared Location green; no new regression).
2026-06-03 07:07:52 -07:00
69eed1f8a6 fix(ssr): strip UI-only :mode before transaction upsert (500 on advanced manual save)
The :manual save handler builds its tx-data from the wizard snapshot and stripped the
control fields :action and :amount-mode, but not :mode (simple/advanced) added by the
recent manual-coding work. manual-coding-section* emits step-params[mode] on every
render, so EVERY advanced manual save posted :mode "advanced" into :upsert-transaction
and 500'd with ":db.error/not-an-entity :mode". Strip :mode alongside :action so the
upsert only sees real schema attributes.

Also fix the e2e helper that masked this: selectAccountFromTypeahead poked the Alpine v2
internal `el.__x.$data`, which is undefined on Alpine v3 (this app loads alpinejs@3.x),
so it silently no-op'd and the account posted empty. Drive the typeahead via the real
Alpine v3 path (Alpine.$data + tippy dropdown + click), mirroring transaction-edit-swap.

Unmasks the previously-failing "Shared Location spread on save" test (was first in a
serial file, hiding 7 siblings). Verified: that test passes; transaction-edit-swap stays
6/6. Skill gotchas.md records the :mode-strip rule, the Alpine-v3 API requirement, and
the modal-won't-close diagnosis recipe.
2026-06-03 06:05:42 -07:00
cdb6bb6fe3 Whole-form htmx + Alpine morph for transaction edit
Re-render the entire #wizard-form on each field edit and swap with
hx-swap="morph" so the focused input keeps focus/caret/value while typing.

- Field-level routes return the full form and target #wizard-form
- Key state-owning wrappers (account rows, simple-mode wrapper, vendor
  typeahead) so server-driven value changes re-init across the morph
- Guard tippy/$refs access in typeahead against stale post-morph state
- Round-trip simple/advanced mode via step-params[mode]
- Add e2e/transaction-edit-morph.spec.ts covering focus/caret preservation,
  vendor->account population, and repeated vendor changes
- Seed a second vendor/account for test isolation

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 07:40:30 -07:00
e156d8bfd8 fixes vendor selection bug 2026-05-30 09:21:39 -07:00
4fca49bff0 fixes a number of issues 2026-05-26 23:20:31 -07:00
ae0788e6dd improvements 2026-05-26 11:18:52 -07:00
f239b114c3 Merge branch 'integreat-fix-errors' into staging 2026-05-24 21:54:54 -07:00
8e3aa13f4d fixes 2026-05-24 21:54:37 -07:00
3715910029 adds invoices dates 2026-05-23 12:28:21 -07:00
76c6eaddb9 improvements 2026-05-21 11:51:29 -07:00