refactor(ssr): compose the bulk-code form entirely in Selmer

Each bulk-code route now ends in a single sel/render call; all composition
(modal chrome, body, account grid, rows, footer, errors) happens in the
templates via {% extends %}/{% block %}/{% include %}/{% with %}, reading one
nested view-model (form-ctx). No HTML is stitched together in Clojure.

- Add components/modal-card.html: a base chrome with head/body/footer blocks;
  bulk-code/card.html extends it. (Transaction Edit keeps its string-slot
  edit-modal.html for now.)
- New top-level templates: open.html, form.html, card.html, body.html; rework
  account-grid/account-row/footer/head to pull the shared component partials in
  via {% include %}+{% with %} instead of hardcoding class strings or receiving
  pre-rendered HTML strings.
- render-form / open-handler collapse to one sel/render of form.html / open.html.
  bulk-code-body*, footer*, form-errors-html, account-grid*, the *errors* dynamic
  var and ferr are gone; field errors are read straight from :form-errors.
- Extract sc/{select,button,a-button,a-icon-button}-ctx so templates can include
  those partials with computed context (the render wrappers now call the -ctx fns).

Verified: rendered output is DOM-identical to the prior version across empty /
populated / error scenarios (whitespace-normalized token compare), plus browser
QA (open, vendor auto-populate, add/remove row, typeahead, per-row location swap,
percentage validation, submit); no JS errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 19:44:52 -07:00
parent a760d15509
commit ee558a34e9
14 changed files with 208 additions and 204 deletions

View File

@@ -0,0 +1,15 @@
{# Base modal-card chrome (single-step: header / optional side panel / body / footer).
A child template extends this and fills the head / side_panel / body / footer blocks,
so the whole card renders from one shared context in a single render call. Enter
triggers the footer save button via $refs.next. Mirrors transaction-edit/edit-modal
(the string-slot version still used by Transaction Edit). #}
<div class="modal-card bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col max-h-screen max-w-screen md:w-[950px] md:h-[650px] w-full h-full last-modal-step transition duration-150"
@keydown.enter.prevent.stop="if ($refs.next) {$refs.next.click()}"
x-data="">
<div class="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0">{% block head %}{% endblock %}</div>
<div class="flex shrink overflow-auto grow">
{% block side_panel %}{% endblock %}
<div class="px-6 py-2 space-y-6 overflow-y-scroll w-full shrink grow">{% block body %}{% endblock %}</div>
</div>
<div class="p-4 border-t">{% block footer %}{% endblock %}</div>
</div>