mirror of
https://github.com/we-promise/sure.git
synced 2026-09-09 08:34:26 +00:00
* feat(rules): support multiple tags in the set transaction tags action Fixes #3353. Reuses the existing DS::TagSelect multi-select tag picker (made generic via attribute:/show_label:) instead of a native <select multiple>, so the UX matches the rest of the app. Multiple tag ids are stored as a comma-separated string in the existing value column, keeping single-tag rows backward compatible with no migration. Also closes a read-modify-write race in SetTransactionTags#execute via with_lock. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(rules): address automated review findings on multi-tag actions - Fix data export/import: multi-tag actions were exported/imported as one opaque comma string, losing all but a bogus combined tag on restore. Each tag id is now resolved/reconstructed independently, with a backward-compatible scalar value_ref for single-tag actions. - Fix N+1 in Rule::Action#value_display (options queried once per tag). - Add aria-label to DS::TagSelect's trigger button when show_label is false, so the control keeps an accessible name. - Localize the "to" label in rule action rows (rules.actions.to_label). - Use a monotonic counter instead of Date.now() for nested form indices, closing a same-millisecond collision window. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(rules): batch tag lookups in multi-tag import resolution Avoids one find_by query per tag name when reconstructing multi-tag rule actions during import. * fix(rules): resolve jjmata review findings on multi-tag action - rules_controller.js: prefix the JS-side nested-form index counter with "new_" so it can never collide with the numeric indexes Rails assigns to already-persisted conditions/actions on an edit form. - data_exporter.rb: key the value_ref scalar/array decision off the number of tag ids on the action, not the number that still resolve, so a partially-orphaned multi-tag action keeps round-tripping as an array. - rule_import.rb: split comma-separated set_transaction_tags values into individual tag names during CSV rule import, matching the batched resolution already used by Family::DataImporter. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(rules): CSV-quote multi-tag names so commas don't split them CodeRabbit flagged that a tag name containing a comma (e.g. "Food, Dining") would be silently split into two tags when round-tripped through the comma-separated multi-tag value/CSV formats used by Family::DataExporter, Family::DataImporter, and RuleImport. Add Rule::Action.encode_multi_value_names/.decode_multi_value_names, backed by Ruby's CSV line quoting, and use them at all three call sites instead of a plain join(",")/split(","). A single name without a comma round-trips byte-identical to before, so existing exports and CSV rule templates are unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: GFR <248542187+gfr-free@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
65 lines
2.8 KiB
ERB
65 lines
2.8 KiB
ERB
<%# locals: (form:) %>
|
|
|
|
<% action = form.object %>
|
|
<% rule = action.rule %>
|
|
|
|
<li data-controller="rule--actions" data-rule--actions-action-executors-value="<%= rule.action_executors.to_json %>" class="flex items-center gap-3">
|
|
<%= form.hidden_field :_destroy, value: false, data: { rule__actions_target: "destroyField" } %>
|
|
|
|
<div class="grid grid-cols-1 gap-2 items-center h-full w-full">
|
|
<div class="grow">
|
|
<%= form.select :action_type, rule.action_executors.map { |executor| [ executor.label, executor.key ] }, {}, data: { action: "rule--actions#handleActionTypeChange" } %>
|
|
</div>
|
|
|
|
<%= tag.div class: class_names("min-w-1/2 flex items-center gap-2", { "hidden": action.executor.type == "function" }),
|
|
data: { rule__actions_target: "actionValue" } do %>
|
|
<%# Initial rendering based on the action executor type. %>
|
|
<%# Subsequent renders are injected by the Stimulus controller, which uses the templates from below. %>
|
|
<span class="font-medium text-primary uppercase text-xs"><%= t("rules.actions.to_label") %></span>
|
|
<% if action.executor.type == "select" %>
|
|
<%= form.select :value, action.options || [], {} %>
|
|
<% elsif action.executor.type == "multi_select" %>
|
|
<%= render DS::TagSelect.new(
|
|
form: form,
|
|
tags: rule.family.tags.alphabetically,
|
|
selected_ids: action.value.to_s.split(","),
|
|
attribute: :value,
|
|
show_label: false
|
|
) %>
|
|
<% elsif action.executor.type == "text" %>
|
|
<%= form.text_field :value, placeholder: t("rules.actions.value_placeholder") %>
|
|
<% end %>
|
|
<% end %>
|
|
</div>
|
|
|
|
<%= icon(
|
|
"trash-2",
|
|
size: "sm",
|
|
as_button: true,
|
|
data: { action: "rule--actions#remove", rule__actions_destroy_param: action.persisted? }) %>
|
|
|
|
<%# Templates for different input types - these will be cloned and used by the Stimulus controller %>
|
|
<template data-rule--actions-target="selectTemplate">
|
|
<span class="font-medium text-primary uppercase text-xs"><%= t("rules.actions.to_label") %></span>
|
|
<%= form.select :value, [], {} %>
|
|
</template>
|
|
|
|
<template data-rule--actions-target="multiSelectTemplate">
|
|
<span class="font-medium text-primary uppercase text-xs"><%= t("rules.actions.to_label") %></span>
|
|
<%= render DS::TagSelect.new(
|
|
form: form,
|
|
tags: rule.family.tags.alphabetically,
|
|
selected_ids: [],
|
|
attribute: :value,
|
|
show_label: false
|
|
) %>
|
|
</template>
|
|
|
|
<template data-rule--actions-target="textTemplate">
|
|
<span class="font-medium text-primary uppercase text-xs"><%= t("rules.actions.to_label") %></span>
|
|
<%= form.text_field :value, placeholder: t("rules.actions.value_placeholder") %>
|
|
</template>
|
|
|
|
<%# The function type doesn't need an input, so no template is required.%>
|
|
</li>
|