diff --git a/app/javascript/controllers/rule/conditions_controller.js b/app/javascript/controllers/rule/conditions_controller.js index 1cffa119..d0c12941 100644 --- a/app/javascript/controllers/rule/conditions_controller.js +++ b/app/javascript/controllers/rule/conditions_controller.js @@ -21,12 +21,23 @@ export default class extends Controller { } remove(e) { + // Find the parent rules controller before removing the condition + const rulesEl = this.element.closest('[data-controller~="rules"]'); + if (e.params.destroy) { this.destroyFieldTarget.value = true; this.element.classList.add("hidden"); } else { this.element.remove(); } + + // Update the prefixes of all conditions from the parent rules controller + if (rulesEl) { + const rulesController = this.application.getControllerForElementAndIdentifier(rulesEl, "rules"); + if (rulesController && typeof rulesController.updateConditionPrefixes === "function") { + rulesController.updateConditionPrefixes(); + } + } } handleConditionTypeChange(e) { diff --git a/app/javascript/controllers/rules_controller.js b/app/javascript/controllers/rules_controller.js index 0db0e67a..3618acc6 100644 --- a/app/javascript/controllers/rules_controller.js +++ b/app/javascript/controllers/rules_controller.js @@ -11,11 +11,17 @@ export default class extends Controller { "effectiveDateInput", ]; + connect() { + // Update condition prefixes on first connection (form render on edit) + this.updateConditionPrefixes(); + } + addConditionGroup() { this.#appendTemplate( this.conditionGroupTemplateTarget, this.conditionsListTarget, ); + this.updateConditionPrefixes(); } addCondition() { @@ -23,6 +29,7 @@ export default class extends Controller { this.conditionTemplateTarget, this.conditionsListTarget, ); + this.updateConditionPrefixes(); } addAction() { @@ -45,4 +52,27 @@ export default class extends Controller { #uniqueKey() { return Date.now(); } + + // Updates the prefix visibility of all conditions and condition groups + // This is also called by the rule/conditions_controller when a subcondition is removed + updateConditionPrefixes() { + const conditions = Array.from(this.conditionsListTarget.children); + let conditionIndex = 0; + + conditions.forEach((condition) => { + // Only process visible conditions, this prevents conditions that are marked for removal and hidden + // from being added to the index. This is important when editing a rule. + if (!condition.classList.contains('hidden')) { + const prefixEl = condition.querySelector('[data-condition-prefix]'); + if (prefixEl) { + if (conditionIndex === 0) { + prefixEl.classList.add('hidden'); + } else { + prefixEl.classList.remove('hidden'); + } + conditionIndex++; + } + } + }); + } } diff --git a/app/models/family.rb b/app/models/family.rb index f565d54d..a3a73eec 100644 --- a/app/models/family.rb +++ b/app/models/family.rb @@ -74,11 +74,6 @@ class Family < ApplicationRecord account.sync_later(start_date: start_date, parent_sync: sync) end - Rails.logger.info("Syncing plaid items for family #{id}") - plaid_items.each do |plaid_item| - plaid_item.sync_later(start_date: start_date, parent_sync: sync) - end - Rails.logger.info("Applying rules for family #{id}") rules.each do |rule| rule.apply_later diff --git a/app/models/rule.rb b/app/models/rule.rb index 93ed47f4..b0d405c2 100644 --- a/app/models/rule.rb +++ b/app/models/rule.rb @@ -49,6 +49,18 @@ class Rule < ApplicationRecord RuleJob.perform_later(self, ignore_attribute_locks: ignore_attribute_locks) end + def primary_condition_title + return "No conditions" if conditions.none? + + first_condition = conditions.first + if first_condition.compound? && first_condition.sub_conditions.any? + first_sub_condition = first_condition.sub_conditions.first + "If #{first_sub_condition.filter.label.downcase} #{first_sub_condition.operator} #{first_sub_condition.value_display}" + else + "If #{first_condition.filter.label.downcase} #{first_condition.operator} #{first_condition.value_display}" + end + end + private def matching_resources_scope scope = registry.resource_scope diff --git a/app/models/sync.rb b/app/models/sync.rb index 826899ce..4d923f12 100644 --- a/app/models/sync.rb +++ b/app/models/sync.rb @@ -21,58 +21,17 @@ class Sync < ApplicationRecord begin syncable.sync_data(self, start_date: start_date) - unless has_pending_child_syncs? - complete! - Rails.logger.info("Sync completed, starting post-sync") - syncable.post_sync(self) - Rails.logger.info("Post-sync completed") - end + complete! + Rails.logger.info("Sync completed, starting post-sync") + syncable.post_sync(self) + Rails.logger.info("Post-sync completed") rescue StandardError => error fail! error, report_error: true - ensure - notify_parent_of_completion! if has_parent? - end - end - end - - def handle_child_completion_event - Sync.transaction do - # We need this to ensure 2 child syncs don't update the parent at the exact same time with different results - # and cause the sync to hang in "syncing" status indefinitely - self.lock! - - unless has_pending_child_syncs? - if has_failed_child_syncs? - fail!(Error.new("One or more child syncs failed")) - else - complete! - end - - # If this sync is both a child and a parent, we need to notify the parent of completion - notify_parent_of_completion! if has_parent? - - syncable.post_sync(self) end end end private - def has_pending_child_syncs? - children.where(status: [ :pending, :syncing ]).any? - end - - def has_failed_child_syncs? - children.where(status: :failed).any? - end - - def has_parent? - parent_id.present? - end - - def notify_parent_of_completion! - parent.handle_child_completion_event - end - def start! Rails.logger.info("Starting sync") update! status: :syncing diff --git a/app/views/accounts/index.html.erb b/app/views/accounts/index.html.erb index 4893e44c..b4c78332 100644 --- a/app/views/accounts/index.html.erb +++ b/app/views/accounts/index.html.erb @@ -2,15 +2,16 @@
match
<%= form.select :operator, [["all", "and"], ["any", "or"]], { container_class: "w-fit" }, data: { rules_target: "operatorField" } %>of the following conditions
@@ -21,16 +20,16 @@ <%= icon( "trash-2", - size: "sm", as_button: true, - data: { action: "element-removal#remove" } + size: "sm", + data: { action: "rule--conditions#remove" } ) %>