refactor(ssr): shared component partials own their CSS classes

Bake the Tailwind class base into the shared Selmer component partials so the
partials own their markup and callers pass only data + a small variant
(width / size / color). Applies across all four modals that share them
(bulk-code, invoices, sales-summaries, transaction-edit).

- typeahead / select / location-select / money-input / validated-field /
  button / a-button / a-icon-button: the class base, the validated-field
  has-error toggle, and the button color ladders now live in the .html. The
  sc/*-ctx fns pass width / variant / extra / color plus the non-class attrs
  (computed exactly as before, so every non-class attribute is unchanged).
- bulk-code templates updated to the new partial contracts; account-row pulls
  money-input and a-icon-button in via includes.

Verified: every component's class SET is identical to before across all
variants (14/14 oracle match -- buttons reorder/dedupe classes, CSS is
order-independent); bulk-code full render is DOM-equivalent to the pre-sweep
baseline (class-set + attr-order normalized) for empty / populated / error;
browser QA of bulk-code (full flow) and transaction-edit (open + render) clean,
no JS errors; invoices + sales-summaries compile and render through the same
sc/* fns.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 22:23:57 -07:00
parent ee558a34e9
commit f16c52d70b
15 changed files with 69 additions and 88 deletions

View File

@@ -15,7 +15,7 @@
<tbody>
{% for row in accounts.rows %}{% include "templates/transaction-bulk-code/account-row.html" %}{% endfor %}
<tr class="new-row border-b dark:border-gray-600 group hover:bg-gray-100 dark:hover:bg-gray-700">
<td class="px-4 py-2" colspan="4">{% with classes=accounts.new_account.classes attrs=accounts.new_account.attrs indicator=accounts.new_account.indicator body=accounts.new_account.body %}{% include "templates/components/a-button.html" %}{% endwith %}</td>
<td class="px-4 py-2" colspan="4">{% with color=accounts.new_account.color extra=accounts.new_account.extra attrs=accounts.new_account.attrs indicator=accounts.new_account.indicator body=accounts.new_account.body %}{% include "templates/components/a-button.html" %}{% endwith %}</td>
</tr>
</tbody>
</table>

View File

@@ -7,21 +7,21 @@
<input type="hidden" name="{{ row.db_id_name }}"{% if row.db_id_value %} value="{{ row.db_id_value }}"{% endif %}>
<td class="px-4 py-2">
<div class="{{ row.account_field_classes }}">
<div class="flex flex-col">{% with x_data=row.account.x_data x_model=row.account.x_model key=row.account.key disabled=row.account.disabled a_class=row.account.a_class a_xinit=row.account.a_xinit search_class=row.account.search_class placeholder=row.account.placeholder hidden_attrs=row.account.hidden_attrs %}{% include "templates/components/typeahead.html" %}{% endwith %}</div>
<div class="flex flex-col">{% with width=row.account.width x_data=row.account.x_data x_model=row.account.x_model key=row.account.key disabled=row.account.disabled a_xinit=row.account.a_xinit placeholder=row.account.placeholder hidden_attrs=row.account.hidden_attrs %}{% include "templates/components/typeahead.html" %}{% endwith %}</div>
<p class="mt-2 text-xs text-red-600 dark:text-red-500 h-4">{{ row.account_error }}</p>
</div>
</td>
<td class="px-4 py-2" id="{{ row.location_cell_id }}">
<div class="{{ row.location_field_classes }}"{{ row.location_field_attrs|safe }}>
{% with name=row.location.name classes=row.location.classes options=row.location.options %}{% include "templates/components/location-select.html" %}{% endwith %}
{% with name=row.location.name variant=row.location.variant options=row.location.options %}{% include "templates/components/location-select.html" %}{% endwith %}
<p class="mt-2 text-xs text-red-600 dark:text-red-500 h-4">{{ row.location_error }}</p>
</div>
</td>
<td class="px-4 py-2">
<div class="{{ row.pct_field_classes }}">
<input {{ row.pct_attrs|safe }}>
{% with variant=row.pct.variant attrs=row.pct.attrs %}{% include "templates/components/money-input.html" %}{% endwith %}
<p class="mt-2 text-xs text-red-600 dark:text-red-500 h-4">{{ row.pct_error }}</p>
</div>
</td>
<td class="px-4 py-2 align-top"><a class="{{ row.remove.classes }}"{{ row.remove.attrs|safe }}><div class="h-4 w-4">{% include "templates/components/svg-x.html" %}</div></a></td>
<td class="px-4 py-2 align-top">{% with extra=row.remove.extra attrs=row.remove.attrs body=row.remove.body %}{% include "templates/components/a-icon-button.html" %}{% endwith %}</td>
</tr>

View File

@@ -7,14 +7,14 @@
<div {{ vendor.changed_attrs|safe }}>
<div class="{{ vendor.field_classes }}">
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Vendor</label>
{% with x_data=vendor.ta.x_data x_model=vendor.ta.x_model key=vendor.ta.key disabled=vendor.ta.disabled a_class=vendor.ta.a_class a_xinit=vendor.ta.a_xinit search_class=vendor.ta.search_class placeholder=vendor.ta.placeholder hidden_attrs=vendor.ta.hidden_attrs %}{% include "templates/components/typeahead.html" %}{% endwith %}
{% with width=vendor.ta.width x_data=vendor.ta.x_data x_model=vendor.ta.x_model key=vendor.ta.key disabled=vendor.ta.disabled a_xinit=vendor.ta.a_xinit placeholder=vendor.ta.placeholder hidden_attrs=vendor.ta.hidden_attrs %}{% include "templates/components/typeahead.html" %}{% endwith %}
<p class="mt-2 text-xs text-red-600 dark:text-red-500 h-4">{{ vendor.error }}</p>
</div>
</div>
<div>
<div class="{{ status.field_classes }}">
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Status</label>
{% with name=status.name classes=status.classes attrs=status.attrs options=status.options %}{% include "templates/components/select.html" %}{% endwith %}
{% with name=status.name variant=status.variant attrs=status.attrs options=status.options %}{% include "templates/components/select.html" %}{% endwith %}
<p class="mt-2 text-xs text-red-600 dark:text-red-500 h-4">{{ status.error }}</p>
</div>
</div>

View File

@@ -1,5 +1,5 @@
{# Modal footer: the form-errors sink on the left, the Save button on the right. Both
pull from the shared view-model; the button reuses components/button.html. #}
<div class="flex justify-end">
<div class="flex items-baseline gap-x-4">{% include "templates/transaction-bulk-code/form-errors.html" %}{% with classes=save.classes attrs=save.attrs loading_label=save.loading_label body=save.body %}{% include "templates/components/button.html" %}{% endwith %}</div>
<div class="flex items-baseline gap-x-4">{% include "templates/transaction-bulk-code/form-errors.html" %}{% with color=save.color extra=save.extra attrs=save.attrs loading_label=save.loading_label body=save.body %}{% include "templates/components/button.html" %}{% endwith %}</div>
</div>