refactor(ssr): render the bulk-code modal fully through Selmer
Move all markup in the Transaction Bulk Code modal out of Clojure and into
Selmer templates so bulk_code.clj only assembles data.
- Replace the inline sel/raw HTML strings and one Hiccup [:p] with templates:
head, form-errors, footer, account-entries, success-body.
- Render the expense-account grid from a {% for %} template (account-grid.html
+ account-row.html) driven by a per-row view-model (account-row-vm); the row
reuses the shared components/typeahead.html via a {% with %} include (no fork).
- Extract behaviour-preserving data-prep helpers reused by the view-model:
sc/typeahead-ctx, sc/money-input-attrs, sc/validated-field-classes,
sc/errors-str, edit/account-typeahead-ctx, edit/location-select-ctx.
Verified: REPL render parity + browser QA (add/remove row, typeahead select,
per-row location swap, percentage validation, submit, vendor auto-populate);
no JS errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -72,14 +72,19 @@
|
||||
(update :class #(str % (inputs/use-size size))))]
|
||||
(render "templates/components/text-input.html" {:attrs (attrs->str attrs)})))
|
||||
|
||||
(defn money-input [{:keys [size] :as params}]
|
||||
(let [attrs (-> params
|
||||
(defn money-input-attrs
|
||||
"The serialized attribute string for a money input. Split out so a template-driven
|
||||
grid can inline `<input {{ attrs|safe }}>` without re-deriving the class logic."
|
||||
[{:keys [size] :as params}]
|
||||
(attrs->str (-> params
|
||||
(dissoc :size)
|
||||
(update :class (fnil hh/add-class "") inputs/default-input-classes)
|
||||
(update :class hh/add-class "appearance-none text-right")
|
||||
(update :class #(str % (inputs/use-size size)))
|
||||
(assoc :type "number" :step "0.01"))]
|
||||
(render "templates/components/money-input.html" {:attrs (attrs->str attrs)})))
|
||||
(assoc :type "number" :step "0.01"))))
|
||||
|
||||
(defn money-input [params]
|
||||
(render "templates/components/money-input.html" {:attrs (money-input-attrs params)}))
|
||||
|
||||
(defn select
|
||||
"Generic <select> rendered from a Selmer partial (the location-select.html shape,
|
||||
@@ -101,23 +106,34 @@
|
||||
|
||||
;; --- field wrapper ---------------------------------------------------------------
|
||||
|
||||
(defn validated-field-classes
|
||||
"The wrapping-div class string for a validated field (group + optional has-error +
|
||||
caller class). Split out so a template-driven row can stamp the same classes."
|
||||
[{:keys [errors] :as params}]
|
||||
(cond-> (or (:class params) "")
|
||||
(sequential? errors) (hh/add-class "has-error")
|
||||
:always (hh/add-class "group")))
|
||||
|
||||
(defn errors-str
|
||||
"Comma-join the string errors at a field (nil/empty -> empty string), matching the
|
||||
validated-field error <p>."
|
||||
[errors]
|
||||
(or (when (sequential? errors)
|
||||
(str/join ", " (filter string? errors)))
|
||||
""))
|
||||
|
||||
(defn validated-field
|
||||
"Selmer port of com/validated-field (the errors- variant of field-): label + body +
|
||||
an always-present error <p>. Pass-through attrs land on the wrapping div (the account
|
||||
row's location cell hangs its swap wiring here)."
|
||||
[{:keys [label errors] :as params} & body]
|
||||
(let [classes (cond-> (or (:class params) "")
|
||||
(sequential? errors) (hh/add-class "has-error")
|
||||
:always (hh/add-class "group"))
|
||||
attrs (dissoc params :label :errors :error-source :error-key :class)
|
||||
errors-str (when (sequential? errors)
|
||||
(str/join ", " (filter string? errors)))]
|
||||
(let [attrs (dissoc params :label :errors :error-source :error-key :class)]
|
||||
(render "templates/components/validated-field.html"
|
||||
{:label label
|
||||
:classes classes
|
||||
:classes (validated-field-classes params)
|
||||
:attrs (attrs->str attrs)
|
||||
:body (body->html body)
|
||||
:errors_str (or errors-str "")})))
|
||||
:errors_str (errors-str errors)})))
|
||||
|
||||
;; --- buttons / badges / links ----------------------------------------------------
|
||||
|
||||
@@ -274,10 +290,12 @@
|
||||
:attrs (attrs->str (dissoc params :handle-unexpected-error? :class))
|
||||
:body (body->html children)}))
|
||||
|
||||
(defn typeahead
|
||||
"Selmer port of com/typeahead. Resolves the initial {value,label} server-side via
|
||||
value-fn/content-fn (DB lookups), builds the Alpine x-data, and serializes the
|
||||
hidden posting-input attributes. Preserves every tippy?. null-guard."
|
||||
(defn typeahead-ctx
|
||||
"Build the plain-data context map for templates/components/typeahead.html. Resolves the
|
||||
initial {value,label} server-side via value-fn/content-fn (DB lookups), builds the
|
||||
Alpine x-data, and serializes the hidden posting-input attributes. Split out from
|
||||
`typeahead` so a fully template-driven grid can feed the same partial per row (via
|
||||
{% with %}) without re-deriving any of this logic. Every value is a string/boolean."
|
||||
[{:keys [value value-fn content-fn x-model x-init id class placeholder disabled url]
|
||||
:as params}]
|
||||
(let [vf (or value-fn identity)
|
||||
@@ -298,13 +316,17 @@
|
||||
(dissoc :class :value-fn :content-fn :placeholder :x-model)
|
||||
(assoc "x-ref" "hidden" :type "hidden" ":value" "value.value"
|
||||
:x-init "$watch('value', v => { $el.value = (v && v.value != null) ? v.value : ''; $nextTick(() => $dispatch('change')); }); "))]
|
||||
(render "templates/components/typeahead.html"
|
||||
{:x_data x-data
|
||||
:x_model x-model
|
||||
:key (when id (str id "--" vval))
|
||||
:disabled disabled
|
||||
:a_class a-class
|
||||
:a_xinit a-xinit
|
||||
:search_class search-class
|
||||
:placeholder placeholder
|
||||
:hidden_attrs (attrs->str hidden-attrs)})))
|
||||
{:x_data x-data
|
||||
:x_model x-model
|
||||
:key (when id (str id "--" vval))
|
||||
:disabled disabled
|
||||
:a_class a-class
|
||||
:a_xinit a-xinit
|
||||
:search_class search-class
|
||||
:placeholder placeholder
|
||||
:hidden_attrs (attrs->str hidden-attrs)}))
|
||||
|
||||
(defn typeahead
|
||||
"Selmer port of com/typeahead. Preserves every tippy?. null-guard. See typeahead-ctx."
|
||||
[params]
|
||||
(render "templates/components/typeahead.html" (typeahead-ctx params)))
|
||||
|
||||
Reference in New Issue
Block a user