mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-09 23:45:21 +02:00
Use HTML template in the ERB, clone and inject those templates from the stimulus controller
This commit is contained in:
parent
7256c44299
commit
7259ca4587
3 changed files with 72 additions and 62 deletions
|
@ -3,7 +3,13 @@ import { Controller } from "@hotwired/stimulus";
|
|||
// Connects to data-controller="rule--actions"
|
||||
export default class extends Controller {
|
||||
static values = { actionExecutors: Array };
|
||||
static targets = ["destroyField", "actionValue"];
|
||||
static targets = [
|
||||
"destroyField",
|
||||
"actionValue",
|
||||
"selectTemplate",
|
||||
"textTemplate",
|
||||
"toSpan"
|
||||
];
|
||||
|
||||
remove(e) {
|
||||
if (e.params.destroy) {
|
||||
|
@ -19,77 +25,69 @@ export default class extends Controller {
|
|||
(executor) => executor.key === e.target.value,
|
||||
);
|
||||
|
||||
if (!actionExecutor || actionExecutor.needs_value === false) {
|
||||
this.#hideActionValue();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear any existing input elements first
|
||||
this.#clearFormFields();
|
||||
|
||||
if (actionExecutor.type === "select") {
|
||||
this.#convertToSelect(actionExecutor);
|
||||
this.#showActionValue();
|
||||
this.#buildSelectFor(actionExecutor);
|
||||
} else if (actionExecutor.type === "text") {
|
||||
this.#convertToTextInput();
|
||||
this.#showActionValue();
|
||||
this.#buildTextInputFor();
|
||||
} else {
|
||||
// For any type that doesn't need a value (e.g. function)
|
||||
this.#hideActionValue();
|
||||
}
|
||||
}
|
||||
|
||||
get valueSelectEl() {
|
||||
return this.actionValueTarget.querySelector("select");
|
||||
}
|
||||
|
||||
get valueInputEl() {
|
||||
return this.actionValueTarget.querySelector("input");
|
||||
}
|
||||
|
||||
#showActionValue() {
|
||||
this.actionValueTarget.classList.remove("hidden");
|
||||
}
|
||||
|
||||
#hideActionValue() {
|
||||
this.actionValueTarget.classList.add("hidden");
|
||||
}
|
||||
|
||||
#clearFormFields() {
|
||||
const toRemove = [];
|
||||
|
||||
#convertToTextInput() {
|
||||
// If we already have a text input, do nothing
|
||||
if (this.valueInputEl && this.valueInputEl.type === "text") {
|
||||
return;
|
||||
}
|
||||
// Find all elements to remove, unless it's the "to" span
|
||||
Array.from(this.actionValueTarget.children).forEach(child => {
|
||||
if (child !== this.toSpanTarget) {
|
||||
toRemove.push(child);
|
||||
}
|
||||
});
|
||||
|
||||
// Convert select to text input
|
||||
const valueField = this.valueSelectEl || this.valueInputEl;
|
||||
|
||||
if (valueField) {
|
||||
const textInput = document.createElement("input");
|
||||
textInput.type = "text";
|
||||
textInput.name = valueField.name;
|
||||
textInput.id = valueField.id;
|
||||
textInput.placeholder = "Enter a value";
|
||||
textInput.className = "form-field__input";
|
||||
|
||||
valueField.replaceWith(textInput);
|
||||
}
|
||||
// Remove the elements
|
||||
toRemove.forEach(element => element.remove());
|
||||
}
|
||||
|
||||
// Converts a field to a select with new options based on the action executor
|
||||
// This includes a current select with different options
|
||||
#convertToSelect(actionExecutor) {
|
||||
const valueField = this.valueInputEl || this.valueSelectEl;
|
||||
#buildSelectFor(actionExecutor) {
|
||||
// Clone the select template
|
||||
const template = this.selectTemplateTarget.content.cloneNode(true);
|
||||
const selectEl = template.querySelector("select");
|
||||
|
||||
if (!valueField) {
|
||||
return;
|
||||
// Add options to the select element
|
||||
if (selectEl) {
|
||||
selectEl.innerHTML = "";
|
||||
for (const option of actionExecutor.options) {
|
||||
const optionEl = document.createElement("option");
|
||||
optionEl.value = option[1];
|
||||
optionEl.textContent = option[0];
|
||||
selectEl.appendChild(optionEl);
|
||||
}
|
||||
}
|
||||
|
||||
const selectInput = document.createElement("select");
|
||||
selectInput.name = valueField.name;
|
||||
selectInput.id = valueField.id;
|
||||
selectInput.className = "form-field__input";
|
||||
// Add the template content to the actionValue target and ensure it's visible
|
||||
this.actionValueTarget.appendChild(template);
|
||||
this.actionValueTarget.classList.remove("hidden");
|
||||
}
|
||||
|
||||
// Add options
|
||||
for (const option of actionExecutor.options) {
|
||||
const optionEl = document.createElement("option");
|
||||
optionEl.value = option[1];
|
||||
optionEl.textContent = option[0];
|
||||
selectInput.appendChild(optionEl);
|
||||
}
|
||||
#buildTextInputFor() {
|
||||
// Clone the text template
|
||||
const template = this.textTemplateTarget.content.cloneNode(true);
|
||||
|
||||
valueField.replaceWith(selectInput);
|
||||
// Add the template content to the actionValue target and ensure it's visible
|
||||
this.actionValueTarget.appendChild(template);
|
||||
this.actionValueTarget.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,9 @@ class Rule::Registry::TransactionResource < Rule::Registry
|
|||
Rule::ActionExecutor::SetTransactionName.new(rule)
|
||||
]
|
||||
|
||||
if ai_enabled?
|
||||
enabled_executors << Rule::ActionExecutor::AutoCategorize.new(rule)
|
||||
enabled_executors << Rule::ActionExecutor::AutoDetectMerchants.new(rule)
|
||||
end
|
||||
# TODO PUT BACK
|
||||
enabled_executors << Rule::ActionExecutor::AutoCategorize.new(rule)
|
||||
enabled_executors << Rule::ActionExecutor::AutoDetectMerchants.new(rule)
|
||||
|
||||
enabled_executors
|
||||
end
|
||||
|
|
|
@ -3,27 +3,31 @@
|
|||
<% action = form.object %>
|
||||
<% rule = action.rule %>
|
||||
<% action_type = action.executor.type %>
|
||||
<% needs_value = action.executor.type == "select" || action.executor.type == "text" %>
|
||||
|
||||
<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="grow flex gap-2 items-center h-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"),
|
||||
<%= tag.div class: class_names("min-w-1/2 flex items-center gap-2", "hidden" => !needs_value),
|
||||
data: { rule__actions_target: "actionValue" } do %>
|
||||
<span class="font-medium uppercase text-xs">to</span>
|
||||
<span class="font-medium uppercase text-xs" data-rule--actions-target="toSpan">to</span>
|
||||
<%# Initial rendering based on rule.action_executors.first from the form. %>
|
||||
<%# This is currently always SetTransactionCategory from app/models/rule/action_executor/transaction_resource.rb. %>
|
||||
<%# Only the first select option will render here, as subsequent renders are injected by the Stimulus controller, which uses the templates from below. %>
|
||||
<% case action_type
|
||||
when "select" %>
|
||||
<%= form.select :value, action.options || [], {} %>
|
||||
<% when "text" %>
|
||||
<%= form.text_field :value, placeholder: "Enter a value", class: "form-field__input" %>
|
||||
<%= form.text_field :value, placeholder: "Enter a value" %>
|
||||
<% when "function" %>
|
||||
<%# Function type doesn't need a value input %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
<%= icon(
|
||||
|
@ -31,4 +35,13 @@
|
|||
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">
|
||||
<%= form.select :value, [], {} %>
|
||||
</template>
|
||||
|
||||
<template data-rule--actions-target="textTemplate">
|
||||
<%= form.text_field :value, placeholder: "Enter a value" %>
|
||||
</template>
|
||||
</li>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue