mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-02 02:11:33 +00:00
Merge pull request #13592 from rioug/13266-tag-variant-tag-rule
[Variant tags] Add tag rules for variant
This commit is contained in:
@@ -5,16 +5,19 @@ class TagListInputComponent < ViewComponent::Base
|
||||
placeholder: I18n.t("components.tag_list_input.default_placeholder"),
|
||||
only_one: false,
|
||||
aria_label: nil,
|
||||
hidden_field_data_options: {})
|
||||
hidden_field_data_options: {},
|
||||
autocomplete_url: "")
|
||||
@name = name
|
||||
@tags = tags
|
||||
@placeholder = placeholder
|
||||
@only_one = only_one
|
||||
@aria_label_option = aria_label ? { 'aria-label': aria_label } : {}
|
||||
@hidden_field_data_options = hidden_field_data_options
|
||||
@autocomplete_url = autocomplete_url
|
||||
end
|
||||
|
||||
attr_reader :name, :tags, :placeholder, :only_one, :aria_label_option, :hidden_field_data_options
|
||||
attr_reader :name, :tags, :placeholder, :only_one, :aria_label_option,
|
||||
:hidden_field_data_options, :autocomplete_url
|
||||
|
||||
private
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%div{ "data-controller": "tag-list-input", "data-tag-list-input-only-one-value": "#{only_one}" }
|
||||
%div{ "data-controller": "tag-list-input", "data-tag-list-input-only-one-value": "#{only_one}", "data-tag-list-input-url-value": autocomplete_url, "data-action": "autocomplete.change->tag-list-input#addTag" }
|
||||
.tags-input
|
||||
.tags
|
||||
- # We use display:none instead of hidden field, so changes to the value can be picked up by the bulkFormController
|
||||
@@ -16,4 +16,12 @@
|
||||
%span=tag
|
||||
%a.remove-button{ "data-action": "click->tag-list-input#removeTag" }
|
||||
×
|
||||
= text_field_tag "variant_add_tag", nil, class: "input", placeholder: placeholder, "data-action": "keydown.enter->tag-list-input#addTag keyup->tag-list-input#filterInput blur->tag-list-input#addTag", "data-tag-list-input-target": "newTag", **aria_label_option, style: "display: #{display};"
|
||||
= text_field_tag("variant_add_tag",
|
||||
nil,
|
||||
{ class: "input",
|
||||
placeholder: placeholder,
|
||||
"data-action": "keydown.enter->tag-list-input#keyboardAddTag keyup->tag-list-input#filterInput blur->tag-list-input#onBlur focus->tag-list-input#onInputChange",
|
||||
"data-tag-list-input-target": "input",
|
||||
**aria_label_option,
|
||||
style: "display: #{display};"})
|
||||
%ul.suggestion-list{ "data-tag-list-input-target": "results" , hidden: true }
|
||||
|
||||
@@ -67,4 +67,35 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
ul.suggestion-list {
|
||||
margin-top: 5px;
|
||||
padding: 5px 0;
|
||||
z-index: $tag-drop-down-z-index;
|
||||
width: fit-content;
|
||||
background-color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
box-shadow: $shadow-dropdown;
|
||||
list-style-type: none;
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
|
||||
li.suggestion-item {
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
width: stretch;
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: $color-link-visited;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import { Controller } from "stimulus";
|
||||
import { Autocomplete } from "stimulus-autocomplete";
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["tagList", "newTag", "template", "list"];
|
||||
// Extend the stimulus-autocomplete controller, so we can load tag with existing rules
|
||||
// The autocomplete functionality is only loaded if the url value is set
|
||||
// For more informatioon on "stimulus-autocomplete", see:
|
||||
// https://github.com/afcapel/stimulus-autocomplete/tree/main
|
||||
//
|
||||
export default class extends Autocomplete {
|
||||
static targets = ["tagList", "input", "template", "list"];
|
||||
static values = { onlyOne: Boolean };
|
||||
|
||||
addTag(event) {
|
||||
// prevent hotkey form submitting the form (default action for "enter" key)
|
||||
event.preventDefault();
|
||||
connect() {
|
||||
// Don't start autocomplete controller if we don't have an url
|
||||
if (this.urlValue.length == 0) return;
|
||||
|
||||
const newTagName = this.newTagTarget.value.trim().replaceAll(" ", "-");
|
||||
super.connect();
|
||||
}
|
||||
|
||||
addTag(event) {
|
||||
const newTagName = this.inputTarget.value.trim().replaceAll(" ", "-");
|
||||
if (newTagName.length == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -18,7 +27,7 @@ export default class extends Controller {
|
||||
const index = tags.indexOf(newTagName);
|
||||
if (index != -1) {
|
||||
// highlight the value in red
|
||||
this.newTagTarget.classList.add("tag-error");
|
||||
this.inputTarget.classList.add("tag-error");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -38,14 +47,23 @@ export default class extends Controller {
|
||||
this.listTarget.appendChild(newTagElement);
|
||||
|
||||
// Clear new tag value
|
||||
this.newTagTarget.value = "";
|
||||
this.inputTarget.value = "";
|
||||
|
||||
// hide tag input if limited to one tag
|
||||
if (this.tagListTarget.value.split(",").length == 1 && this.onlyOneValue == true) {
|
||||
this.newTagTarget.style.display = "none";
|
||||
this.inputTarget.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
keyboardAddTag(event) {
|
||||
// prevent hotkey form submitting the form (default action for "enter" key)
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
this.addTag();
|
||||
}
|
||||
|
||||
removeTag(event) {
|
||||
// Text to remove
|
||||
const tagName = event.srcElement.previousElementSibling.textContent;
|
||||
@@ -62,14 +80,14 @@ export default class extends Controller {
|
||||
|
||||
// Make sure the tag input is displayed
|
||||
if (this.tagListTarget.value.length == 0) {
|
||||
this.newTagTarget.style.display = "block";
|
||||
this.inputTarget.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
filterInput(event) {
|
||||
// clear error class if key is not enter
|
||||
if (event.key !== "Enter") {
|
||||
this.newTagTarget.classList.remove("tag-error");
|
||||
this.inputTarget.classList.remove("tag-error");
|
||||
}
|
||||
|
||||
// Strip comma from tag name
|
||||
@@ -77,4 +95,53 @@ export default class extends Controller {
|
||||
event.srcElement.value = event.srcElement.value.replace(",", "");
|
||||
}
|
||||
}
|
||||
|
||||
// Add tag if we don't have an autocomplete list open
|
||||
onBlur() {
|
||||
// check if we have any autocomplete results
|
||||
if (this.resultsTarget.childElementCount == 0) this.addTag();
|
||||
}
|
||||
|
||||
// Override original to add tag filtering
|
||||
replaceResults(html) {
|
||||
const filteredHtml = this.#filterResults(html);
|
||||
|
||||
// Don't show result if we don't have anything to show
|
||||
if (filteredHtml.length == 0) return;
|
||||
|
||||
super.replaceResults(filteredHtml);
|
||||
}
|
||||
|
||||
// Override original to all empty query, which will return all existing tags
|
||||
onInputChange = () => {
|
||||
if (this.urlValue.length == 0) return;
|
||||
|
||||
if (this.hasHiddenTarget) this.hiddenTarget.value = "";
|
||||
|
||||
const query = this.inputTarget.value.trim();
|
||||
if (query.length >= this.minLengthValue) {
|
||||
this.fetchResults(query);
|
||||
} else {
|
||||
this.hideAndRemoveOptions();
|
||||
}
|
||||
};
|
||||
|
||||
//private
|
||||
|
||||
#filterResults(html) {
|
||||
const existingTags = this.tagListTarget.value.split(",");
|
||||
// Parse the HTML
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, "text/html");
|
||||
const lis = doc.getElementsByTagName("li");
|
||||
// Filter
|
||||
let filteredHtml = "";
|
||||
for (let li of lis) {
|
||||
if (!existingTags.includes(li.dataset.autocompleteValue)) {
|
||||
filteredHtml += li.outerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
return filteredHtml;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,13 @@ class TagRuleFormComponent < ViewComponent::Base
|
||||
taggable: "variant",
|
||||
visibility_field: "preferred_matched_variants_visibility",
|
||||
}
|
||||
when "TagRule::FilterVariants"
|
||||
{
|
||||
text_top: t('components.tag_rule_form.tag_rules.variant_tagged_top'),
|
||||
text_bottom: t('components.tag_rule_form.tag_rules.variant_tagged_bottom'),
|
||||
taggable: "variant",
|
||||
visibility_field: "preferred_matched_variants_visibility",
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ module Admin
|
||||
|
||||
load_tag_rule_types
|
||||
|
||||
load_tag_rules
|
||||
return unless params[:stimulus]
|
||||
|
||||
@enterprise.is_primary_producer = params[:is_primary_producer]
|
||||
@@ -84,6 +85,7 @@ module Admin
|
||||
end
|
||||
else
|
||||
load_tag_rule_types
|
||||
load_tag_rules
|
||||
respond_with(@object) do |format|
|
||||
format.json {
|
||||
render json: { errors: @object.errors.messages }, status: :unprocessable_entity
|
||||
@@ -398,9 +400,26 @@ module Admin
|
||||
[t(".form.tag_rules.show_hide_order_cycles"), "FilterOrderCycles"]
|
||||
]
|
||||
|
||||
return unless helpers.feature?(:inventory, @object)
|
||||
if helpers.feature?(:variant_tag, @object)
|
||||
@tag_rule_types.prepend([t(".form.tag_rules.show_hide_variants"), "FilterVariants"])
|
||||
elsif helpers.feature?(:inventory, @object)
|
||||
@tag_rule_types.prepend([t(".form.tag_rules.show_hide_variants"), "FilterProducts"])
|
||||
end
|
||||
end
|
||||
|
||||
@tag_rule_types.prepend([t(".form.tag_rules.show_hide_variants"), "FilterProducts"])
|
||||
def load_tag_rules
|
||||
if helpers.feature?(:variant_tag, @object)
|
||||
@default_rules = @enterprise.tag_rules.exclude_inventory.select(&:is_default)
|
||||
@rules = @enterprise.tag_rules.exclude_inventory.prioritised.reject(&:is_default)
|
||||
elsif helpers.feature?(:inventory, @object)
|
||||
@default_rules = @enterprise.tag_rules.exclude_variant.select(&:is_default)
|
||||
@rules = @enterprise.tag_rules.exclude_variant.prioritised.reject(&:is_default)
|
||||
else
|
||||
@default_rules =
|
||||
@enterprise.tag_rules.exclude_inventory.exclude_variant.select(&:is_default)
|
||||
@rules =
|
||||
@enterprise.tag_rules.exclude_inventory.exclude_variant.prioritised.reject(&:is_default)
|
||||
end
|
||||
end
|
||||
|
||||
def setup_property
|
||||
|
||||
@@ -41,6 +41,7 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
# Used by the tag input autocomplete
|
||||
def map_by_tag
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
@@ -50,6 +51,26 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
# Use to populate autocomplete with available rule for the given tag/enterprise
|
||||
def variant_tag_rules
|
||||
tag_rules =
|
||||
TagRule.matching_variant_tag_rules_by_enterprises(params[:enterprise_id], params[:q])
|
||||
|
||||
@formatted_tag_rules = tag_rules.each_with_object({}) do |rule, mapping|
|
||||
rule.preferred_variant_tags.split(",").each do |tag|
|
||||
if mapping[tag]
|
||||
mapping[tag][:rules] += 1
|
||||
else
|
||||
mapping[tag] = { tag:, rules: 1 }
|
||||
end
|
||||
end
|
||||
end.values
|
||||
|
||||
respond_with do |format|
|
||||
format.html { render :variant_tag_rules, layout: false }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collection_actions
|
||||
@@ -78,7 +99,7 @@ module Admin
|
||||
end
|
||||
|
||||
def permitted_tag_rule_type
|
||||
%w{FilterOrderCycles FilterPaymentMethods FilterProducts FilterShippingMethods}
|
||||
%w{FilterOrderCycles FilterPaymentMethods FilterProducts FilterShippingMethods FilterVariants}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,7 +23,8 @@ module Api
|
||||
order_cycle,
|
||||
customer,
|
||||
search_params,
|
||||
inventory_enabled:
|
||||
inventory_enabled:,
|
||||
variant_tag_enabled:
|
||||
).products_json
|
||||
|
||||
render plain: products
|
||||
@@ -96,13 +97,17 @@ module Api
|
||||
|
||||
def distributed_products
|
||||
OrderCycles::DistributedProductsService.new(
|
||||
distributor, order_cycle, customer, inventory_enabled:
|
||||
distributor, order_cycle, customer, inventory_enabled:, variant_tag_enabled:,
|
||||
).products_relation.pluck(:id)
|
||||
end
|
||||
|
||||
def inventory_enabled
|
||||
OpenFoodNetwork::FeatureToggle.enabled?(:inventory, distributor)
|
||||
end
|
||||
|
||||
def variant_tag_enabled
|
||||
OpenFoodNetwork::FeatureToggle.enabled?(:variant_tag, distributor)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -143,7 +143,7 @@ module Spree
|
||||
can [:admin, :index, :read, :create, :edit, :update_positions, :destroy], ProducerProperty
|
||||
|
||||
can :new, TagRule
|
||||
can [:admin, :map_by_tag, :destroy], TagRule do |tag_rule|
|
||||
can [:admin, :map_by_tag, :destroy, :variant_tag_rules], TagRule do |tag_rule|
|
||||
user.enterprises.include? tag_rule.enterprise
|
||||
end
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ class TagRule < ApplicationRecord
|
||||
|
||||
scope :for, ->(enterprise) { where(enterprise_id: enterprise) }
|
||||
scope :prioritised, -> { order('priority ASC') }
|
||||
scope :exclude_inventory, -> { where.not(type: "TagRule::FilterProducts") }
|
||||
scope :exclude_variant, -> { where.not(type: "TagRule::FilterVariants") }
|
||||
|
||||
def self.mapping_for(enterprises)
|
||||
self.for(enterprises).each_with_object({}) do |rule, mapping|
|
||||
@@ -20,6 +22,14 @@ class TagRule < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def self.matching_variant_tag_rules_by_enterprises(enterprise_id, tag)
|
||||
rules = where(type: "TagRule::FilterVariants").for(enterprise_id)
|
||||
|
||||
return [] if rules.empty?
|
||||
|
||||
rules.select { |r| r.preferred_variant_tags =~ /#{tag}/ }
|
||||
end
|
||||
|
||||
# The following method must be overriden in a concrete tagRule
|
||||
def tags
|
||||
raise NotImplementedError, 'please use concrete TagRule'
|
||||
|
||||
22
app/models/tag_rule/filter_variants.rb
Normal file
22
app/models/tag_rule/filter_variants.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class TagRule
|
||||
class FilterVariants < TagRule
|
||||
preference :matched_variants_visibility, :string, default: "visible"
|
||||
preference :variant_tags, :string, default: ""
|
||||
|
||||
def tags_match?(variant)
|
||||
variant_tags = variant&.[]("tag_list") || []
|
||||
preferred_tags = preferred_variant_tags.split(",")
|
||||
variant_tags.intersect?(preferred_tags)
|
||||
end
|
||||
|
||||
def reject_matched?
|
||||
preferred_matched_variants_visibility != "visible"
|
||||
end
|
||||
|
||||
def tags
|
||||
preferred_variant_tags
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -118,13 +118,22 @@ module OrderCycles
|
||||
end
|
||||
|
||||
def variants
|
||||
options[:inventory_enabled] ? stocked_variants_and_overrides : stocked_variants
|
||||
return tag_rule_filtered_variants if options[:variant_tag_enabled]
|
||||
|
||||
return stocked_variants_and_overrides if options[:inventory_enabled]
|
||||
|
||||
stocked_variants
|
||||
end
|
||||
|
||||
def stocked_variants
|
||||
Spree::Variant.joins(:stock_items).where(query_stock)
|
||||
end
|
||||
|
||||
def tag_rule_filtered_variants
|
||||
VariantTagRulesFilterer.new(distributor:, customer:,
|
||||
variants_relation: stocked_variants).call
|
||||
end
|
||||
|
||||
def stocked_variants_and_overrides
|
||||
stocked_variants = Spree::Variant.
|
||||
joins("LEFT OUTER JOIN variant_overrides ON variant_overrides.variant_id = spree_variants.id
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# ( despite the use of `TagRule::FilterProducts.prioritised` ). It will apply the "show rule"
|
||||
# if any
|
||||
# * When there is no default rule, the order of customer related rules doesn't matter, it will
|
||||
# apply the "hide rule" if any
|
||||
# apply the "show rule" over any hide rule
|
||||
#
|
||||
class ProductTagRulesFilterer
|
||||
def initialize(distributor, customer, variants_relation)
|
||||
|
||||
109
app/services/variant_tag_rules_filterer.rb
Normal file
109
app/services/variant_tag_rules_filterer.rb
Normal file
@@ -0,0 +1,109 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Takes a Spree::Variant AR object and filters results based on applicable tag rules.
|
||||
# Tag rules exists in the context of enterprise, customer, and variants.
|
||||
# Returns a Spree::Variant AR object.
|
||||
|
||||
# The filtering is somewhat not intuitive when they are conflicting rules in play:
|
||||
# * When a variant is hidden by a default rule, It will apply the "show rule" if any
|
||||
# * When there is no default rule, it will apply the "show rule" over any "hide rule"
|
||||
#
|
||||
class VariantTagRulesFilterer
|
||||
def initialize(distributor:, customer:, variants_relation: )
|
||||
@distributor = distributor
|
||||
@customer = customer
|
||||
@variants_relation = variants_relation
|
||||
end
|
||||
|
||||
def call
|
||||
return variants_relation unless distributor_rules.any?
|
||||
|
||||
filter(variants_relation)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_accessor :distributor, :customer, :variants_relation
|
||||
|
||||
def distributor_rules
|
||||
@distributor_rules ||= TagRule::FilterVariants.for(distributor).all
|
||||
end
|
||||
|
||||
def filter(variants_relation)
|
||||
return variants_relation unless variants_to_hide.any?
|
||||
|
||||
variants_relation.where(query_with_tag_rules)
|
||||
end
|
||||
|
||||
def query_with_tag_rules
|
||||
"#{variant_not_hidden_by_rule} OR #{variant_shown_by_rule}"
|
||||
end
|
||||
|
||||
def variant_not_hidden_by_rule
|
||||
return "FALSE" unless variants_to_hide.any?
|
||||
|
||||
"spree_variants.id NOT IN (#{variants_to_hide.join(',')})"
|
||||
end
|
||||
|
||||
def variant_shown_by_rule
|
||||
return "FALSE" unless variants_to_show.any?
|
||||
|
||||
"spree_variants.id IN (#{variants_to_show.join(',')})"
|
||||
end
|
||||
|
||||
def variants_to_hide
|
||||
@variants_to_hide ||= Spree::Variant.where(supplier: distributor)
|
||||
.tagged_with(default_rule_tags + hide_rule_tags, any: true)
|
||||
.pluck(:id)
|
||||
end
|
||||
|
||||
def variants_to_show
|
||||
@variants_to_show ||= Spree::Variant.where(supplier: distributor)
|
||||
.tagged_with(show_rule_tags, any: true)
|
||||
.pluck(:id)
|
||||
end
|
||||
|
||||
def default_rule_tags
|
||||
default_rules.map(&:preferred_variant_tags)
|
||||
end
|
||||
|
||||
def hide_rule_tags
|
||||
hide_rules.map(&:preferred_variant_tags)
|
||||
end
|
||||
|
||||
def show_rule_tags
|
||||
show_rules.map(&:preferred_variant_tags)
|
||||
end
|
||||
|
||||
def default_rules
|
||||
# These rules hide a variant with tag X and apply to all customers
|
||||
distributor_rules.select(&:is_default?)
|
||||
end
|
||||
|
||||
def non_default_rules
|
||||
# These rules show or hide a variant with tag X for customer with tag Y
|
||||
distributor_rules.reject(&:is_default?)
|
||||
end
|
||||
|
||||
def customer_applicable_rules
|
||||
# Rules which apply specifically to the current customer
|
||||
@customer_applicable_rules ||= non_default_rules.select{ |rule| customer_tagged?(rule) }
|
||||
end
|
||||
|
||||
def hide_rules
|
||||
@hide_rules ||= customer_applicable_rules
|
||||
.select{ |rule| rule.preferred_matched_variants_visibility == 'hidden' }
|
||||
end
|
||||
|
||||
def show_rules
|
||||
customer_applicable_rules - hide_rules
|
||||
end
|
||||
|
||||
def customer_tagged?(rule)
|
||||
customer_tag_list.include? rule.preferred_customer_tags
|
||||
end
|
||||
|
||||
def customer_tag_list
|
||||
customer&.tag_list || []
|
||||
end
|
||||
end
|
||||
@@ -5,15 +5,14 @@
|
||||
- # We use a high enough index increment so that the default tag rule should not overlap with the tag rules
|
||||
- # Rails will deal with non continous numbered tag_rules_attributes just fine, it saves us from having to manage the index state in javascript
|
||||
- current_rule_index = 1000
|
||||
- rules = @enterprise.tag_rules.prioritised.reject(&:is_default)
|
||||
- if rules.empty?
|
||||
- if @rules.empty?
|
||||
.no_tags
|
||||
= t('.no_tags_yet')
|
||||
|
||||
= render 'admin/enterprises/form/tag_rules/default_rules', f:, current_rule_index:
|
||||
|
||||
#customer-tag-rule
|
||||
- tag_groups(rules).each_with_index do |group, group_index|
|
||||
- tag_groups(@rules).each_with_index do |group, group_index|
|
||||
- current_group_index = group_index + 1
|
||||
= render TagRuleGroupFormComponent.new(group:, index: group_index, customer_rule_index: current_rule_index, tag_rule_types: @tag_rule_types)
|
||||
- # Same as above, We use a high enough increcment so that the previous tag rule group does not overlap with the next tag rule group
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
= t('.by_default')
|
||||
%i.text-big.icon-question-sign{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "tag_rule_help_modal" }
|
||||
#default-tag-rule
|
||||
- default_rules = @enterprise.tag_rules.select(&:is_default)
|
||||
- current_rule_index = 0
|
||||
- if default_rules.empty?
|
||||
- if @default_rules.empty?
|
||||
.no_rules
|
||||
= t('.no_rules_yet')
|
||||
- else
|
||||
- default_rules.each_with_index do |default_rule, index|
|
||||
- @default_rules.each_with_index do |default_rule, index|
|
||||
- current_rule_index = index + 1
|
||||
= render TagRuleFormComponent.new(rule: default_rule, index: index)
|
||||
%hr
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
= error_message_on variant, :tax_category
|
||||
- if feature?(:variant_tag, spree_current_user)
|
||||
%td.col-tags.field.naked_inputs
|
||||
= render TagListInputComponent.new(name: f.field_name(:tag_list), tags: variant.tag_list, placeholder: t('.add_a_tag'), aria_label: t('admin.products_page.columns.tags'))
|
||||
= render TagListInputComponent.new(name: f.field_name(:tag_list), tags: variant.tag_list, autocomplete_url: variant_tag_rules_admin_tag_rules_path(enterprise_id: variant.supplier_id), placeholder: t('.add_a_tag'), aria_label: t('admin.products_page.columns.tags'))
|
||||
%td.col-inherits_properties.align-left
|
||||
-# empty
|
||||
%td.align-right
|
||||
|
||||
3
app/views/admin/tag_rules/variant_tag_rules.html.haml
Normal file
3
app/views/admin/tag_rules/variant_tag_rules.html.haml
Normal file
@@ -0,0 +1,3 @@
|
||||
- @formatted_tag_rules.each do |tag_rule|
|
||||
%li.suggestion-item{ role: "option", "data-autocomplete-value": tag_rule[:tag], "data-autocomplete-label": tag_rule[:tag] }
|
||||
= t("admin.products_v3.tag_rules.rules_per_tag", tag: tag_rule[:tag], count: tag_rule[:rules])
|
||||
@@ -7,6 +7,7 @@ import consumer from "../channels/consumer";
|
||||
import controller from "../controllers/application_controller";
|
||||
import CableReady from "cable_ready";
|
||||
import RailsNestedForm from "@stimulus-components/rails-nested-form/dist/stimulus-rails-nested-form.umd.js"; // the default module entry point is broken
|
||||
import { Autocomplete } from "stimulus-autocomplete";
|
||||
|
||||
const application = Application.start();
|
||||
const context = require.context("controllers", true, /_controller\.js$/);
|
||||
@@ -37,6 +38,7 @@ contextComponents.keys().forEach((path) => {
|
||||
});
|
||||
|
||||
application.register("nested-form", RailsNestedForm);
|
||||
application.register("autocomplete", Autocomplete);
|
||||
|
||||
application.consumer = consumer;
|
||||
StimulusReflex.initialize(application, { controller, isolate: true });
|
||||
|
||||
@@ -186,3 +186,4 @@ $btn-condensed-height: 26px !default;
|
||||
//--------------------------------------------------------------
|
||||
$tos-banner-z-index: 1001;
|
||||
$flash-message-z-index: 1000;
|
||||
$tag-drop-down-z-index: 999;
|
||||
|
||||
@@ -1026,6 +1026,10 @@ en:
|
||||
clone:
|
||||
success: Successfully cloned the product
|
||||
error: Unable to clone the product
|
||||
tag_rules:
|
||||
rules_per_tag:
|
||||
one: "%{tag} has 1 rule"
|
||||
other: "%{tag} has %{count} rules"
|
||||
product_import:
|
||||
title: Product Import
|
||||
file_not_found: File not found or could not be opened
|
||||
@@ -5077,6 +5081,8 @@ en:
|
||||
order_cycle_tagged_bottom: "are:"
|
||||
inventory_tagged_top: "Inventory variants tagged"
|
||||
inventory_tagged_bottom: "are:"
|
||||
variant_tagged_top: "Variants tagged"
|
||||
variant_tagged_bottom: "are:"
|
||||
visible: VISIBLE
|
||||
not_visible: NOT VISIBLE
|
||||
tag_rule_group_form:
|
||||
|
||||
@@ -91,8 +91,9 @@ Openfoodnetwork::Application.routes.draw do
|
||||
|
||||
resources :customers, only: [:index, :create, :update, :destroy, :show]
|
||||
|
||||
resources :tag_rules, only: [], format: :json do
|
||||
get :map_by_tag, on: :collection
|
||||
resources :tag_rules, only: [] do
|
||||
get :map_by_tag, on: :collection, format: :json
|
||||
get :variant_tag_rules, on: :collection
|
||||
end
|
||||
|
||||
resource :contents
|
||||
|
||||
@@ -66,7 +66,7 @@ module.exports = {
|
||||
// maxWorkers: "50%",
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
moduleDirectories: ["node_modules", "app/webpacker"],
|
||||
moduleDirectories: ["node_modules", "app/webpacker", "app/components"],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
@@ -173,7 +173,7 @@ module.exports = {
|
||||
// transform: undefined,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
transformIgnorePatterns: ["/node_modules/(?!(stimulus)/)"],
|
||||
transformIgnorePatterns: ["/node_modules/(?!(stimulus.+)/)"],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"select2": "^4.0.13",
|
||||
"shortcut-buttons-flatpickr": "^0.4.0",
|
||||
"stimulus": "^3.2.2",
|
||||
"stimulus-autocomplete": "^3.1.0",
|
||||
"stimulus-flatpickr": "^1.4.0",
|
||||
"stimulus_reflex": "3.5.5",
|
||||
"tom-select": "^2.4.3",
|
||||
@@ -37,6 +38,7 @@
|
||||
"webpack": "~4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "<10.0.0",
|
||||
"jasmine-core": "~5.12.1",
|
||||
"jest": "^27.4.7",
|
||||
"karma": "~6.4.4",
|
||||
|
||||
@@ -69,4 +69,53 @@ RSpec.describe Admin::TagRulesController do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#variant_tag_rules", feature: :variant_tag do
|
||||
render_views
|
||||
|
||||
let(:enterprise) { create(:distributor_enterprise) }
|
||||
let(:q) { "" }
|
||||
let!(:rule1) {
|
||||
create(:filter_variants_tag_rule, enterprise:, preferred_customer_tags: "Tag-1",
|
||||
preferred_variant_tags: "variant-tag-1" )
|
||||
}
|
||||
let!(:rule2) {
|
||||
create(:filter_variants_tag_rule, enterprise:, preferred_customer_tags: "Tag-1",
|
||||
preferred_variant_tags: "variant2-tag-1" )
|
||||
}
|
||||
let!(:rule3) {
|
||||
create(:filter_variants_tag_rule, enterprise:, preferred_customer_tags: "organic",
|
||||
preferred_variant_tags: "variant-organic" )
|
||||
}
|
||||
let!(:rule4) {
|
||||
create(:filter_variants_tag_rule, enterprise:, preferred_customer_tags: "organic",
|
||||
preferred_variant_tags: "variant-tag-1" )
|
||||
}
|
||||
|
||||
before do
|
||||
controller_login_as_enterprise_user [enterprise]
|
||||
end
|
||||
|
||||
it "returns a list of tag rules and number of assiciated rules" do
|
||||
spree_get(:variant_tag_rules, format: :html, enterprise_id: enterprise.id, q:)
|
||||
|
||||
expect(response).to render_template :variant_tag_rules
|
||||
expect(response.body).to include "variant-tag-1 has 2 rules"
|
||||
expect(response.body).to include "variant2-tag-1 has 1 rule"
|
||||
expect(response.body).to include "variant-organic has 1 rule"
|
||||
end
|
||||
|
||||
context "with search string" do
|
||||
let(:q) { "org" }
|
||||
|
||||
it "returns a list of tag rules matching the string" do
|
||||
spree_get(:variant_tag_rules, format: :html, enterprise_id: enterprise.id, q:)
|
||||
|
||||
expect(response).to render_template :variant_tag_rules
|
||||
expect(response.body).not_to include "variant-tag-1 has 2 rules"
|
||||
expect(response.body).not_to include "variant2-tag-1 has 1 rule"
|
||||
expect(response.body).to include "variant-organic has 1 rule"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -132,7 +132,7 @@ RSpec.describe Api::V0::OrderCyclesController do
|
||||
end
|
||||
end
|
||||
|
||||
context "when tag rules apply", feature: :inventory do
|
||||
context "when inventory tag rules apply", feature: :inventory do
|
||||
let!(:vo1) {
|
||||
create(:variant_override,
|
||||
hub: distributor,
|
||||
@@ -201,6 +201,63 @@ RSpec.describe Api::V0::OrderCyclesController do
|
||||
end
|
||||
end
|
||||
|
||||
context "when variant tag rules apply", feature: :variant_tag do
|
||||
let!(:variant1) { product1.variants.first.tap { |v| v.update(supplier: distributor) } }
|
||||
let!(:variant2) { product2.variants.first.tap { |v| v.update(supplier: distributor) } }
|
||||
let!(:variant3) { product3.variants.first.tap { |v| v.update(supplier: distributor) } }
|
||||
let(:default_hide_rule) {
|
||||
create(:filter_variants_tag_rule,
|
||||
enterprise: distributor,
|
||||
is_default: true,
|
||||
preferred_variant_tags: "hide_these_variants_from_everyone",
|
||||
preferred_matched_variants_visibility: "hidden")
|
||||
}
|
||||
let(:hide_rule) {
|
||||
create(:filter_variants_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_variant_tags: "hide_these_variants",
|
||||
preferred_customer_tags: "hide_from_these_customers",
|
||||
preferred_matched_variants_visibility: "hidden" )
|
||||
}
|
||||
let(:show_rule) {
|
||||
create(:filter_variants_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_variant_tags: "show_these_variants",
|
||||
preferred_customer_tags: "show_for_these_customers",
|
||||
preferred_matched_variants_visibility: "visible" )
|
||||
}
|
||||
|
||||
it "does not return variants hidden by general rules" do
|
||||
variant1.update_attribute(:tag_list, default_hide_rule.preferred_variant_tags)
|
||||
|
||||
api_get :products, id: order_cycle.id, distributor: distributor.id
|
||||
|
||||
expect(product_ids).not_to include product1.id
|
||||
end
|
||||
|
||||
it "does not return variants hidden for this specific customer" do
|
||||
variant2.update_attribute(:tag_list, hide_rule.preferred_variant_tags)
|
||||
customer.update_attribute(:tag_list, hide_rule.preferred_customer_tags)
|
||||
|
||||
api_get :products, id: order_cycle.id, distributor: distributor.id
|
||||
|
||||
expect(product_ids).not_to include product2.id
|
||||
end
|
||||
|
||||
it "returns hidden variants made visible for this specific customer" do
|
||||
variant1.update_attribute(:tag_list, default_hide_rule.preferred_variant_tags)
|
||||
variant3.update_attribute(:tag_list,
|
||||
"#{show_rule.preferred_variant_tags}," \
|
||||
"#{default_hide_rule.preferred_variant_tags}")
|
||||
customer.update_attribute(:tag_list, show_rule.preferred_customer_tags)
|
||||
|
||||
api_get :products, id: order_cycle.id, distributor: distributor.id
|
||||
|
||||
expect(product_ids).not_to include product1.id
|
||||
expect(product_ids).to include product3.id
|
||||
end
|
||||
end
|
||||
|
||||
context "when the order cycle is closed" do
|
||||
before do
|
||||
allow(controller).to receive(:order_cycle) { order_cycle }
|
||||
|
||||
@@ -13,6 +13,10 @@ FactoryBot.define do
|
||||
enterprise factory: :distributor_enterprise
|
||||
end
|
||||
|
||||
factory :filter_variants_tag_rule, class: TagRule::FilterVariants do
|
||||
enterprise factory: :distributor_enterprise
|
||||
end
|
||||
|
||||
factory :filter_payment_methods_tag_rule, class: TagRule::FilterPaymentMethods do
|
||||
enterprise factory: :distributor_enterprise
|
||||
end
|
||||
|
||||
@@ -3,23 +3,61 @@
|
||||
*/
|
||||
|
||||
import { Application } from "stimulus";
|
||||
import tag_list_input_controller from "../../../app/components/tag_list_input_component/tag_list_input_controller";
|
||||
import { screen } from "@testing-library/dom";
|
||||
|
||||
import tag_list_input_controller from "tag_list_input_component/tag_list_input_controller";
|
||||
|
||||
// Mock jest to return an autocomplete list
|
||||
global.fetch = jest.fn(() => {
|
||||
const html = `
|
||||
<li
|
||||
data-testid="item"
|
||||
class="suggestion-item"
|
||||
data-autocomplete-label="tag-1"
|
||||
data-autocomplete-value="tag-1"
|
||||
role="option"
|
||||
id="stimulus-autocomplete-option-4"
|
||||
>
|
||||
tag-1 has 1 rule
|
||||
</li>
|
||||
<li
|
||||
data-testid="item"
|
||||
class="suggestion-item"
|
||||
data-autocomplete-label="rule-2"
|
||||
data-autocomplete-value="rule-2"
|
||||
role="option"
|
||||
id="stimulus-autocomplete-option-5"
|
||||
>
|
||||
rule-2 has 2 rules
|
||||
</li>`;
|
||||
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
text: () => Promise.resolve(html),
|
||||
});
|
||||
});
|
||||
|
||||
describe("TagListInputController", () => {
|
||||
beforeAll(() => {
|
||||
const application = Application.start();
|
||||
application.register("tag-list-input", tag_list_input_controller);
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// Tag input with three existing tags
|
||||
document.body.innerHTML = `
|
||||
<div data-controller="tag-list-input">
|
||||
<div
|
||||
data-controller="tag-list-input"
|
||||
data-action="autocomplete.change->tag-list-input#addTag"
|
||||
data-tag-list-input-url-value="/admin/tag_rules/variant_tag_rules?enterprise_id=3"
|
||||
>
|
||||
<input
|
||||
value="tag-1,tag-2,tag-3"
|
||||
data-tag-list-input-target="tagList"
|
||||
type="hidden"
|
||||
name="variant_tag_list" id="variant_tag_list"
|
||||
name="variant_tag_list"
|
||||
id="variant_tag_list"
|
||||
>
|
||||
<div class="tags-input">
|
||||
<div class="tags">
|
||||
@@ -68,10 +106,12 @@ describe("TagListInputController", () => {
|
||||
name="variant_add_tag"
|
||||
id="variant_add_tag"
|
||||
placeholder="Add a tag"
|
||||
data-action="keydown.enter->tag-list-input#addTag keyup->tag-list-input#filterInput" data-tag-list-input-target="newTag"
|
||||
data-action="keydown.enter->tag-list-input#keyboardAddTag keyup->tag-list-input#filterInput focus->tag-list-input#onInputChange blur->tag-list-input#onBlur"
|
||||
data-tag-list-input-target="input"
|
||||
style="display: block;"
|
||||
>
|
||||
</div>
|
||||
<ul data-testid="suggestion-list" class="suggestion-list" data-tag-list-input-target="results" hidden></ul>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
@@ -162,6 +202,8 @@ describe("TagListInputController", () => {
|
||||
document.body.innerHTML = `
|
||||
<div
|
||||
data-controller="tag-list-input"
|
||||
data-action="autocomplete.change->tag-list-input#addTag"
|
||||
data-tag-list-input-url-value="/admin/tag_rules/variant_tag_rules?enterprise_id=3"
|
||||
data-tag-list-input-only-one-value="true"
|
||||
>
|
||||
<input
|
||||
@@ -190,10 +232,12 @@ describe("TagListInputController", () => {
|
||||
name="variant_add_tag"
|
||||
id="variant_add_tag"
|
||||
placeholder="Add a tag"
|
||||
data-action="keydown.enter->tag-list-input#addTag keyup->tag-list-input#filterInput" data-tag-list-input-target="newTag"
|
||||
data-action="keydown.enter->tag-list-input#keyboardAddTag keyup->tag-list-input#filterInput blur->tag-list-input#onBlur focus->tag-list-input#onInputChange"
|
||||
data-tag-list-input-target="input"
|
||||
style="display: block;"
|
||||
>
|
||||
</div>
|
||||
<ul class="suggestion-list" data-tag-list-input-target="results" hidden></ul>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
@@ -229,6 +273,8 @@ describe("TagListInputController", () => {
|
||||
document.body.innerHTML = `
|
||||
<div
|
||||
data-controller="tag-list-input"
|
||||
data-action="autocomplete.change->tag-list-input#addTag"
|
||||
data-tag-list-input-url-value="/admin/tag_rules/variant_tag_rules?enterprise_id=3"
|
||||
data-tag-list-input-only-one-value="true"
|
||||
>
|
||||
<input
|
||||
@@ -266,10 +312,12 @@ describe("TagListInputController", () => {
|
||||
name="variant_add_tag"
|
||||
id="variant_add_tag"
|
||||
placeholder="Add a tag"
|
||||
data-action="keydown.enter->tag-list-input#addTag keyup->tag-list-input#filterInput" data-tag-list-input-target="newTag"
|
||||
style="display: none;"
|
||||
data-action="keydown.enter->tag-list-input#keyboardAddTag keyup->tag-list-input#filterInput blur->tag-list-input#onBlur focus->tag-list-input#onInputChange"
|
||||
data-tag-list-input-target="input"
|
||||
style="display: block;"
|
||||
>
|
||||
</div>
|
||||
<ul class="suggestion-list" data-tag-list-input-target="results" hidden></ul>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
@@ -300,4 +348,130 @@ describe("TagListInputController", () => {
|
||||
expect(variant_add_tag.classList).not.toContain("tag-error");
|
||||
});
|
||||
});
|
||||
|
||||
describe("onBlur", () => {
|
||||
it("adds the tag", () => {
|
||||
variant_add_tag.value = "newer_tag";
|
||||
variant_add_tag.dispatchEvent(new FocusEvent("blur"));
|
||||
|
||||
expect(variant_tag_list.value).toBe("tag-1,tag-2,tag-3,newer_tag");
|
||||
});
|
||||
|
||||
describe("with autocomplete results", () => {
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
<div
|
||||
data-controller="tag-list-input"
|
||||
data-action="autocomplete.change->tag-list-input#addTag"
|
||||
data-tag-list-input-url-value="/admin/tag_rules/variant_tag_rules?enterprise_id=3"
|
||||
>
|
||||
<input
|
||||
value="tag-1,tag-2,tag-3"
|
||||
data-tag-list-input-target="tagList"
|
||||
type="hidden"
|
||||
name="variant_tag_list"
|
||||
id="variant_tag_list"
|
||||
>
|
||||
<div class="tags-input">
|
||||
<div class="tags">
|
||||
<ul class="tag-list" data-tag-list-input-target="list">
|
||||
<template data-tag-list-input-target="template">
|
||||
<li class="tag-item">
|
||||
<div class="tag-template">
|
||||
<span></span>
|
||||
<a
|
||||
class="remove-button"
|
||||
data-action="click->tag-list-input#removeTag"
|
||||
>✖</a>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<li class="tag-item">
|
||||
<div class="tag-template">
|
||||
<span>tag-1</span>
|
||||
<a
|
||||
class="remove-button"
|
||||
data-action="click->tag-list-input#removeTag"
|
||||
>✖</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="tag-item">
|
||||
<div class="tag-template">
|
||||
<span>tag-2</span>
|
||||
<a
|
||||
class="remove-button"
|
||||
data-action="click->tag-list-input#removeTag"
|
||||
>✖</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="tag-item">
|
||||
<div class="tag-template">
|
||||
<span>tag-3</span>
|
||||
<a
|
||||
class="remove-button"
|
||||
data-action="click->tag-list-input#removeTag"
|
||||
>✖</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<input
|
||||
type="text"
|
||||
name="variant_add_tag"
|
||||
id="variant_add_tag"
|
||||
placeholder="Add a tag"
|
||||
data-action="keydown.enter->tag-list-input#keyboardAddTag keyup->tag-list-input#filterInput blur->tag-list-input#onBlur focus->tag-list-input#onInputChange"
|
||||
data-tag-list-input-target="input"
|
||||
style="display: block;"
|
||||
>
|
||||
</div>
|
||||
<ul class="suggestion-list" data-tag-list-input-target="results">
|
||||
<li
|
||||
class="suggestion-item"
|
||||
data-autocomplete-label="rule-1"
|
||||
data-autocomplete-value="rule-1"
|
||||
role="option"
|
||||
id="stimulus-autocomplete-option-4"
|
||||
>
|
||||
rule-1 has 1 rule
|
||||
</li>
|
||||
<li
|
||||
class="suggestion-item"
|
||||
data-autocomplete-label="rule-2"
|
||||
data-autocomplete-value="rule-2"
|
||||
role="option"
|
||||
id="stimulus-autocomplete-option-5"
|
||||
>
|
||||
rule-2 has 2 rules
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
it("doesn't add the tag", () => {
|
||||
variant_add_tag.value = "newer_tag";
|
||||
variant_add_tag.dispatchEvent(new FocusEvent("blur"));
|
||||
|
||||
expect(variant_tag_list.value).toBe("tag-1,tag-2,tag-3");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("replaceResults", () => {
|
||||
beforeEach(() => {
|
||||
fetch.mockClear();
|
||||
});
|
||||
|
||||
it("filters out existing tags in the autocomplete dropdown", async () => {
|
||||
variant_add_tag.dispatchEvent(new FocusEvent("focus"));
|
||||
// onInputChange uses a debounce function implemented using setTimeout
|
||||
jest.runAllTimers();
|
||||
|
||||
// findAll* will wait for all promises to be finished before returning a result, this ensure
|
||||
// the dom has been updated with the autocomplete data
|
||||
const items = await screen.findAllByTestId("item");
|
||||
expect(items.length).toBe(1);
|
||||
expect(items[0].textContent.trim()).toBe("rule-2 has 2 rules");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,56 +3,69 @@
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe TagRule::FilterOrderCycles do
|
||||
let!(:tag_rule) { build_stubbed(:filter_order_cycles_tag_rule) }
|
||||
let(:tag_rule) {
|
||||
build(:filter_order_cycles_tag_rule, preferred_exchange_tags: order_cycle_tags, enterprise:)
|
||||
}
|
||||
let(:order_cycle_tags) { "" }
|
||||
let(:enterprise) { build(:enterprise) }
|
||||
|
||||
describe "#tags" do
|
||||
it "return the exchange tags" do
|
||||
tag_rule = create(:filter_order_cycles_tag_rule, preferred_exchange_tags: "my_tag")
|
||||
let(:order_cycle_tags) { "my_tag" }
|
||||
|
||||
it "return the exchange tags" do
|
||||
expect(tag_rule.tags).to eq("my_tag")
|
||||
end
|
||||
end
|
||||
|
||||
describe "determining whether tags match for a given exchange" do
|
||||
describe "#tags_match?" do
|
||||
context "when the exchange is nil" do
|
||||
before do
|
||||
allow(tag_rule).to receive(:exchange_for) { nil }
|
||||
end
|
||||
|
||||
it "returns false" do
|
||||
expect(tag_rule.__send__(:tags_match?, nil)).to be false
|
||||
expect(tag_rule.tags_match?(nil)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when the exchange is not nil" do
|
||||
let(:exchange_object) { double(:exchange, tag_list: ["member", "local", "volunteer"]) }
|
||||
let(:order_cycle) { create(:simple_order_cycle, distributors: [enterprise]) }
|
||||
|
||||
before do
|
||||
allow(tag_rule).to receive(:exchange_for) { exchange_object }
|
||||
exchange = order_cycle.exchanges.outgoing.first
|
||||
exchange.tag_list = "member,local,volunteer"
|
||||
exchange.save!
|
||||
end
|
||||
|
||||
context "when the rule has no preferred exchange tags specified" do
|
||||
before { allow(tag_rule).to receive(:preferred_exchange_tags) { "" } }
|
||||
it { expect(tag_rule.__send__(:tags_match?, exchange_object)).to be false }
|
||||
it { expect(tag_rule.tags_match?(order_cycle)).to be false }
|
||||
end
|
||||
|
||||
context "when the rule has preferred exchange tags specified that match ANY exchange tags" do
|
||||
before {
|
||||
allow(tag_rule).to receive(:preferred_exchange_tags) {
|
||||
"wholesale,some_tag,member"
|
||||
}
|
||||
}
|
||||
it { expect(tag_rule.__send__(:tags_match?, exchange_object)).to be true }
|
||||
let(:order_cycle_tags) { "wholesale,some_tag,member" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(order_cycle)).to be true }
|
||||
end
|
||||
|
||||
context "when the rule has preferred exchange tags specified that match NO exchange tags" do
|
||||
before {
|
||||
allow(tag_rule).to receive(:preferred_exchange_tags) {
|
||||
"wholesale,some_tag,some_other_tag"
|
||||
}
|
||||
}
|
||||
it { expect(tag_rule.__send__(:tags_match?, exchange_object)).to be false }
|
||||
let(:order_cycle_tags) { "wholesale,some_tag,some_other_tag" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(order_cycle)).to be false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#reject_matched?" do
|
||||
it "return false with default visibility (visible)" do
|
||||
expect(tag_rule.reject_matched?).to be false
|
||||
end
|
||||
|
||||
context "when visiblity is set to hidden" do
|
||||
let(:tag_rule) {
|
||||
build(:filter_order_cycles_tag_rule, preferred_matched_order_cycles_visibility: "hidden")
|
||||
}
|
||||
|
||||
it { expect(tag_rule.reject_matched?).to be true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,48 +3,59 @@
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe TagRule::FilterPaymentMethods do
|
||||
let!(:tag_rule) { build_stubbed(:filter_payment_methods_tag_rule) }
|
||||
let(:tag_rule) {
|
||||
build(:filter_payment_methods_tag_rule, preferred_payment_method_tags: payment_method_tags)
|
||||
}
|
||||
let(:payment_method_tags) { "" }
|
||||
|
||||
describe "#tags" do
|
||||
it "return the payment method tags" do
|
||||
tag_rule = create(:filter_payment_methods_tag_rule, preferred_payment_method_tags: "my_tag")
|
||||
let(:payment_method_tags) { "my_tag" }
|
||||
|
||||
it "return the payment method tags" do
|
||||
expect(tag_rule.tags).to eq("my_tag")
|
||||
end
|
||||
end
|
||||
|
||||
describe "determining whether tags match for a given payment method" do
|
||||
describe "#tags_match?" do
|
||||
context "when the payment method is nil" do
|
||||
it "returns false" do
|
||||
expect(tag_rule.__send__(:tags_match?, nil)).to be false
|
||||
expect(tag_rule.tags_match?(nil)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when the payment method is not nil" do
|
||||
let(:payment_method) { create(:payment_method, tag_list: ["member", "local", "volunteer"]) }
|
||||
let(:payment_method) { build(:payment_method, tag_list: ["member", "local", "volunteer"]) }
|
||||
|
||||
context "when the rule has no preferred payment method tags specified" do
|
||||
before { allow(tag_rule).to receive(:preferred_payment_method_tags) { "" } }
|
||||
it { expect(tag_rule.__send__(:tags_match?, payment_method)).to be false }
|
||||
it { expect(tag_rule.tags_match?(payment_method)).to be false }
|
||||
end
|
||||
|
||||
context "when the rule has preferred customer tags specified that match ANY customer tags" do
|
||||
before {
|
||||
allow(tag_rule).to receive(:preferred_payment_method_tags) {
|
||||
"wholesale,some_tag,member"
|
||||
}
|
||||
}
|
||||
it { expect(tag_rule.__send__(:tags_match?, payment_method)).to be true }
|
||||
let(:payment_method_tags) { "wholesale,some_tag,member" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(payment_method)).to be true }
|
||||
end
|
||||
|
||||
context "when the rule has preferred customer tags specified that match NO customer tags" do
|
||||
before {
|
||||
allow(tag_rule).to receive(:preferred_payment_method_tags) {
|
||||
"wholesale,some_tag,some_other_tag"
|
||||
}
|
||||
}
|
||||
it { expect(tag_rule.__send__(:tags_match?, payment_method)).to be false }
|
||||
let(:payment_method_tags) { "wholesale,some_tag,some_other_tag" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(payment_method)).to be false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#reject_matched?" do
|
||||
it "return false with default visibility (visible)" do
|
||||
expect(tag_rule.reject_matched?).to be false
|
||||
end
|
||||
|
||||
context "when visiblity is set to hidden" do
|
||||
let(:tag_rule) {
|
||||
build(:filter_payment_methods_tag_rule,
|
||||
preferred_matched_payment_methods_visibility: "hidden")
|
||||
}
|
||||
|
||||
it { expect(tag_rule.reject_matched?).to be true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,20 +3,23 @@
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe TagRule::FilterProducts do
|
||||
let!(:tag_rule) { build_stubbed(:filter_products_tag_rule, preferred_variant_tags: "my_tag") }
|
||||
let(:tag_rule) { build(:filter_products_tag_rule, preferred_variant_tags: variant_tags) }
|
||||
let(:variant_tags) { "" }
|
||||
|
||||
describe "#tags" do
|
||||
it "return the variants tags" do
|
||||
tag_rule = create(:filter_products_tag_rule, preferred_variant_tags: "my_tag")
|
||||
let(:variant_tags) { "my_tag" }
|
||||
|
||||
it "return the variants tags" do
|
||||
expect(tag_rule.tags).to eq("my_tag")
|
||||
end
|
||||
end
|
||||
|
||||
describe "determining whether tags match for a given variant" do
|
||||
describe "#tags_match?" do
|
||||
let(:variant_tags) { "my_tag" }
|
||||
|
||||
context "when the variant is nil" do
|
||||
it "returns false" do
|
||||
expect(tag_rule.__send__(:tags_match?, nil)).to be false
|
||||
expect(tag_rule.tags_match?(nil)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,27 +27,34 @@ RSpec.describe TagRule::FilterProducts do
|
||||
let(:variant_object) { { "tag_list" => ["member", "local", "volunteer"] } }
|
||||
|
||||
context "when the rule has no preferred variant tags specified" do
|
||||
before { allow(tag_rule).to receive(:preferred_variant_tags) { "" } }
|
||||
it { expect(tag_rule.__send__(:tags_match?, variant_object)).to be false }
|
||||
it { expect(tag_rule.tags_match?(variant_object)).to be false }
|
||||
end
|
||||
|
||||
context "when the rule has preferred variant tags specified that match ANY variant tags" do
|
||||
before {
|
||||
allow(tag_rule).to receive(:preferred_variant_tags) {
|
||||
"wholesale,some_tag,member"
|
||||
}
|
||||
}
|
||||
it { expect(tag_rule.__send__(:tags_match?, variant_object)).to be true }
|
||||
let(:variant_tags) { "wholesale,some_tag,member" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(variant_object)).to be true }
|
||||
end
|
||||
|
||||
context "when the rule has preferred variant tags specified that match NO variant tags" do
|
||||
before {
|
||||
allow(tag_rule).to receive(:preferred_variant_tags) {
|
||||
"wholesale,some_tag,some_other_tag"
|
||||
}
|
||||
}
|
||||
it { expect(tag_rule.__send__(:tags_match?, variant_object)).to be false }
|
||||
let(:variant_tags) { "wholesale,some_tag,some_other_tag" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(variant_object)).to be false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#reject_matched?" do
|
||||
it "return false with default visibility (visible)" do
|
||||
expect(tag_rule.reject_matched?).to be false
|
||||
end
|
||||
|
||||
context "when visiblity is set to hidden" do
|
||||
let(:tag_rule) {
|
||||
build(:filter_products_tag_rule, preferred_matched_variants_visibility: "hidden")
|
||||
}
|
||||
|
||||
it { expect(tag_rule.reject_matched?).to be true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,50 +3,61 @@
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe TagRule::FilterShippingMethods do
|
||||
let!(:tag_rule) { build_stubbed(:filter_shipping_methods_tag_rule) }
|
||||
let(:tag_rule) {
|
||||
build(:filter_shipping_methods_tag_rule, preferred_shipping_method_tags: shipping_method_tags)
|
||||
}
|
||||
let(:shipping_method_tags) { "" }
|
||||
|
||||
describe "#tags" do
|
||||
it "return the shipping method tags" do
|
||||
tag_rule = create(:filter_shipping_methods_tag_rule, preferred_shipping_method_tags: "my_tag")
|
||||
let(:shipping_method_tags) { "my_tag" }
|
||||
|
||||
it "return the shipping method tags" do
|
||||
expect(tag_rule.tags).to eq("my_tag")
|
||||
end
|
||||
end
|
||||
|
||||
describe "determining whether tags match for a given shipping method" do
|
||||
describe "#tags_match?" do
|
||||
context "when the shipping method is nil" do
|
||||
it "returns false" do
|
||||
expect(tag_rule.__send__(:tags_match?, nil)).to be false
|
||||
expect(tag_rule.tags_match?(nil)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when the shipping method is not nil" do
|
||||
let(:shipping_method) {
|
||||
build_stubbed(:shipping_method, tag_list: ["member", "local", "volunteer"])
|
||||
build(:shipping_method, tag_list: ["member", "local", "volunteer"])
|
||||
}
|
||||
|
||||
context "when the rule has no preferred shipping method tags specified" do
|
||||
before { allow(tag_rule).to receive(:preferred_shipping_method_tags) { "" } }
|
||||
it { expect(tag_rule.__send__(:tags_match?, shipping_method)).to be false }
|
||||
it { expect(tag_rule.tags_match?(shipping_method)).to be false }
|
||||
end
|
||||
|
||||
context "when rule has preferred customer tags specified that match ANY customer tags" do
|
||||
before {
|
||||
allow(tag_rule).to receive(:preferred_shipping_method_tags) {
|
||||
"wholesale,some_tag,member"
|
||||
}
|
||||
}
|
||||
it { expect(tag_rule.__send__(:tags_match?, shipping_method)).to be true }
|
||||
let(:shipping_method_tags) { "wholesale,some_tag,member" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(shipping_method)).to be true }
|
||||
end
|
||||
|
||||
context "when rule has preferred customer tags specified that match NO customer tags" do
|
||||
before {
|
||||
allow(tag_rule).to receive(:preferred_shipping_method_tags) {
|
||||
"wholesale,some_tag,some_other_tag"
|
||||
}
|
||||
}
|
||||
it { expect(tag_rule.__send__(:tags_match?, shipping_method)).to be false }
|
||||
let(:shipping_method_tags) { "wholesale,some_tag,some_other_tag" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(shipping_method)).to be false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#reject_matched?" do
|
||||
it "return false with default visibility (visible)" do
|
||||
expect(tag_rule.reject_matched?).to be false
|
||||
end
|
||||
|
||||
context "when visiblity is set to hidden" do
|
||||
let(:tag_rule) {
|
||||
build(:filter_shipping_methods_tag_rule,
|
||||
preferred_matched_shipping_methods_visibility: "hidden")
|
||||
}
|
||||
|
||||
it { expect(tag_rule.reject_matched?).to be true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
60
spec/models/tag_rule/filter_variants_spec.rb
Normal file
60
spec/models/tag_rule/filter_variants_spec.rb
Normal file
@@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe TagRule::FilterVariants do
|
||||
let(:tag_rule) { build(:filter_variants_tag_rule, preferred_variant_tags: variant_tags) }
|
||||
let(:variant_tags) { "" }
|
||||
|
||||
describe "#tags" do
|
||||
let(:variant_tags) { "my_tag" }
|
||||
|
||||
it "return the variants tags" do
|
||||
expect(tag_rule.tags).to eq("my_tag")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#tags_match?" do
|
||||
let(:variant_tags) { "my_tag" }
|
||||
|
||||
context "when the variant is nil" do
|
||||
it "returns false" do
|
||||
expect(tag_rule.tags_match?(nil)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when the variant is not nil" do
|
||||
let(:variant_object) { { "tag_list" => ["member", "local", "volunteer"] } }
|
||||
|
||||
context "when the rule has no preferred variant tags specified" do
|
||||
it { expect(tag_rule.tags_match?(variant_object)).to be false }
|
||||
end
|
||||
|
||||
context "when the rule has preferred variant tags specified that match ANY variant tags" do
|
||||
let(:variant_tags) { "wholesale,some_tag,member" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(variant_object)).to be true }
|
||||
end
|
||||
|
||||
context "when the rule has preferred variant tags specified that match NO variant tags" do
|
||||
let(:variant_tags) { "wholesale,some_tag,some_other_tag" }
|
||||
|
||||
it { expect(tag_rule.tags_match?(variant_object)).to be false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#reject_matched?" do
|
||||
it "return false with default visibility (visible)" do
|
||||
expect(tag_rule.reject_matched?).to be false
|
||||
end
|
||||
|
||||
context "when visiblity is set to hidden" do
|
||||
let(:tag_rule) {
|
||||
build(:filter_variants_tag_rule, preferred_matched_variants_visibility: "hidden")
|
||||
}
|
||||
|
||||
it { expect(tag_rule.reject_matched?).to be true }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -9,6 +9,40 @@ RSpec.describe TagRule do
|
||||
end
|
||||
end
|
||||
|
||||
describe ".matching_variant_tag_rules_by_enterprises" do
|
||||
let(:enterprise) { create(:enterprise) }
|
||||
let!(:rule1) {
|
||||
create(:filter_variants_tag_rule, enterprise:, preferred_variant_tags: "filtered" )
|
||||
}
|
||||
let!(:rule2) {
|
||||
create(:filter_variants_tag_rule, enterprise:, preferred_variant_tags: "filtered" )
|
||||
}
|
||||
let!(:rule3) {
|
||||
create(:filter_variants_tag_rule, enterprise: create(:enterprise),
|
||||
preferred_variant_tags: "filtered" )
|
||||
}
|
||||
let!(:rule4) {
|
||||
create(:filter_variants_tag_rule, enterprise:, preferred_variant_tags: "other-tag" )
|
||||
}
|
||||
let!(:rule5) {
|
||||
create(:filter_order_cycles_tag_rule, enterprise:, preferred_exchange_tags: "filtered" )
|
||||
}
|
||||
|
||||
it "returns a list of rule partially matching the tag" do
|
||||
rules = described_class.matching_variant_tag_rules_by_enterprises(enterprise.id, "filte")
|
||||
|
||||
expect(rules).to include rule1, rule2
|
||||
expect(rules).not_to include rule3, rule4, rule5
|
||||
end
|
||||
|
||||
context "when no matching rules" do
|
||||
it "returns an empty array" do
|
||||
rules = described_class.matching_variant_tag_rules_by_enterprises(enterprise.id, "no-tag")
|
||||
expect(rules).to eq([])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#tags' do
|
||||
subject(:rule) { Class.new(TagRule).new }
|
||||
|
||||
|
||||
@@ -68,6 +68,20 @@ RSpec.describe OrderCycles::DistributedProductsService do
|
||||
|
||||
expect(products_relation).not_to include product
|
||||
end
|
||||
|
||||
context "with variant_tag enabled" do
|
||||
subject(:products_relation) {
|
||||
described_class.new(
|
||||
distributor, order_cycle, customer, variant_tag_enabled: true
|
||||
).products_relation
|
||||
}
|
||||
|
||||
it "calls VariantTagRulesFilterer" do
|
||||
expect(VariantTagRulesFilterer).to receive(:new).and_call_original
|
||||
|
||||
products_relation
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with variant overrides" do
|
||||
@@ -81,6 +95,12 @@ RSpec.describe OrderCycles::DistributedProductsService do
|
||||
create(:variant_override, hub: distributor, variant:, count_on_hand: 0)
|
||||
}
|
||||
|
||||
it "calls ProductTagRulesFilterer" do
|
||||
expect(ProductTagRulesFilterer).to receive(:new).and_call_original
|
||||
|
||||
products_relation
|
||||
end
|
||||
|
||||
it "does not return product when an override is out of stock" do
|
||||
expect(products_relation).not_to include product
|
||||
end
|
||||
|
||||
119
spec/services/variant_tag_rules_filterer_spec.rb
Normal file
119
spec/services/variant_tag_rules_filterer_spec.rb
Normal file
@@ -0,0 +1,119 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe VariantTagRulesFilterer do
|
||||
subject(:filterer) { described_class.new(distributor:, customer:, variants_relation:) }
|
||||
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
let(:product) { create(:product) }
|
||||
let!(:variant_hidden_by_default) { create(:variant, product:, supplier: distributor) }
|
||||
let!(:variant_hidden_by_rule) { create(:variant, product:, supplier: distributor) }
|
||||
let!(:variant_shown_by_rule) { create(:variant, product:, supplier: distributor) }
|
||||
let!(:variant_hidden_for_another_customer) { create(:variant, product:, supplier: distributor) }
|
||||
let(:customer) { create(:customer, enterprise: distributor) }
|
||||
let(:variants_relation) { Spree::Variant.where(supplier: distributor) }
|
||||
|
||||
describe "#call" do
|
||||
let!(:hide_rule) {
|
||||
create(:filter_variants_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_variant_tags: "hide_these_variants",
|
||||
preferred_customer_tags: "hide_from_these_customers",
|
||||
preferred_matched_variants_visibility: "hidden" )
|
||||
}
|
||||
let!(:show_rule) {
|
||||
create(:filter_variants_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_variant_tags: "show_these_variants",
|
||||
preferred_customer_tags: "show_for_these_customers",
|
||||
preferred_matched_variants_visibility: "visible" )
|
||||
}
|
||||
let!(:non_applicable_rule) {
|
||||
create(:filter_variants_tag_rule,
|
||||
enterprise: distributor,
|
||||
preferred_variant_tags: "hide_these_other_variants",
|
||||
preferred_customer_tags: "hide_from_other_customers",
|
||||
preferred_matched_variants_visibility: "hidden" )
|
||||
}
|
||||
|
||||
context "when the distributor has no rules" do
|
||||
it "returns the relation unchanged" do
|
||||
expect(filterer.call).to eq variants_relation
|
||||
end
|
||||
end
|
||||
|
||||
context "with hide rule" do
|
||||
it "hides the variant matching the rule" do
|
||||
customer.update_attribute(:tag_list, hide_rule.preferred_customer_tags)
|
||||
variant_hidden_by_rule.update_attribute(:tag_list, hide_rule.preferred_variant_tags)
|
||||
|
||||
expect(filterer.call).not_to include(variant_hidden_by_rule)
|
||||
end
|
||||
|
||||
context "with mutiple conflicting rules" do
|
||||
it "applies the show rule" do
|
||||
# Customer has show rule tag and hide rule tag
|
||||
customer.update_attribute(
|
||||
:tag_list, [hide_rule.preferred_customer_tags, show_rule.preferred_customer_tags]
|
||||
)
|
||||
# Variant has show rule tag and hide rule tag
|
||||
variant_hidden_by_rule.update_attribute(
|
||||
:tag_list, [hide_rule.preferred_variant_tags, show_rule.preferred_variant_tags,]
|
||||
)
|
||||
expect(filterer.call).to include(variant_hidden_by_rule)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with variant hidden by default" do
|
||||
let(:default_hide_rule) {
|
||||
create(:filter_variants_tag_rule,
|
||||
enterprise: distributor,
|
||||
is_default: true,
|
||||
preferred_variant_tags: "hide_these_variants_from_everyone",
|
||||
preferred_matched_variants_visibility: "hidden")
|
||||
}
|
||||
|
||||
before do
|
||||
variant_hidden_by_default.update_attribute(
|
||||
:tag_list, default_hide_rule.preferred_variant_tags
|
||||
)
|
||||
end
|
||||
|
||||
it "excludes variant hidden by default" do
|
||||
expect(filterer.call).not_to include(variant_hidden_by_default)
|
||||
end
|
||||
|
||||
context "with variant rule overriding default rule" do
|
||||
it "includes variant hidden by default" do
|
||||
customer.update_attribute(:tag_list, show_rule.preferred_customer_tags)
|
||||
# Variant has default rule tag and show rule tag
|
||||
variant_hidden_by_default.update_attribute(
|
||||
:tag_list, [default_hide_rule.preferred_variant_tags, show_rule.preferred_variant_tags]
|
||||
)
|
||||
|
||||
expect(filterer.call).to include(variant_hidden_by_default)
|
||||
end
|
||||
|
||||
context "with mutiple conflicting rules applying to same variant" do
|
||||
it "applies the show rule" do
|
||||
# customer has show rule and hide rule tag
|
||||
customer.update_attribute(
|
||||
:tag_list, [show_rule.preferred_customer_tags, hide_rule.preferred_customer_tags]
|
||||
)
|
||||
|
||||
# Variant has default rule tag and show rule tag and hide rule tag
|
||||
variant_hidden_by_default.update_attribute(
|
||||
:tag_list,
|
||||
[default_hide_rule.preferred_variant_tags, show_rule.preferred_variant_tags,
|
||||
hide_rule.preferred_variant_tags]
|
||||
)
|
||||
|
||||
expect(filterer.call).to include(variant_hidden_by_default)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -733,19 +733,16 @@ RSpec.describe '
|
||||
it_behaves_like "edit link with", "openfoodnetwork.org", "http://openfoodnetwork.org"
|
||||
end
|
||||
|
||||
shared_examples "edit link with invalid" do |url|
|
||||
it "url: #{url}" do
|
||||
fill_in "enterprise_white_label_logo_link", with: url
|
||||
context "with an invalid link" do
|
||||
it "can not edit white label logo link" do
|
||||
fill_in "enterprise_white_label_logo_link", with: "invalid url"
|
||||
click_button 'Update'
|
||||
expect(page)
|
||||
.to have_content "Link for the logo used in shopfront '#{url}' is an invalid URL"
|
||||
expect(page).to have_content(
|
||||
"Link for the logo used in shopfront 'invalid url' is an invalid URL"
|
||||
)
|
||||
expect(distributor1.reload.white_label_logo_link).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "can not edit white label logo link" do
|
||||
it_behaves_like "edit link with invalid", "invalid url"
|
||||
end
|
||||
end
|
||||
|
||||
it "can check/uncheck the hide_groups_tab attribute" do
|
||||
|
||||
@@ -8,6 +8,71 @@ RSpec.describe 'Tag Rules' do
|
||||
|
||||
let!(:enterprise) { create(:distributor_enterprise) }
|
||||
|
||||
describe "loading rules" do
|
||||
let!(:default_order_cycle_tag_rule) {
|
||||
create(:filter_order_cycles_tag_rule, enterprise:, is_default: true)
|
||||
}
|
||||
let!(:inventory_order_cycle_rule) { create(:filter_order_cycles_tag_rule, enterprise:) }
|
||||
let!(:default_inventory_tag_rule) {
|
||||
create(:filter_products_tag_rule, enterprise:, is_default: true)
|
||||
}
|
||||
let!(:inventory_tag_rule) { create(:filter_products_tag_rule, enterprise:) }
|
||||
let!(:default_variant_tag_rule) {
|
||||
create(:filter_variants_tag_rule, enterprise:, is_default: true)
|
||||
}
|
||||
let!(:variant_tag_rule) { create(:filter_variants_tag_rule, enterprise:) }
|
||||
|
||||
before do
|
||||
visit_tag_rules
|
||||
end
|
||||
|
||||
it "displays all existing rules" do
|
||||
within "#default-tag-rule" do
|
||||
expect(page).to have_content "Order Cycles tagged"
|
||||
expect(page).not_to have_content "Inventory variants tagged"
|
||||
expect(page).not_to have_content "Variants tagged"
|
||||
end
|
||||
|
||||
within "#customer-tag-rule" do
|
||||
expect(page).to have_content "Order Cycles tagged"
|
||||
expect(page).not_to have_content "Inventory variants tagged"
|
||||
expect(page).not_to have_content "Variants tagged"
|
||||
end
|
||||
end
|
||||
|
||||
context "with inventory enabled", feature: :inventory do
|
||||
it "does not display filter by variants rules" do
|
||||
within "#default-tag-rule" do
|
||||
expect(page).to have_content "Order Cycles tagged"
|
||||
expect(page).to have_content "Inventory variants tagged"
|
||||
expect(page).not_to have_content "Variants tagged"
|
||||
end
|
||||
|
||||
within "#customer-tag-rule" do
|
||||
expect(page).to have_content "Order Cycles tagged"
|
||||
expect(page).to have_content "Inventory variants tagged"
|
||||
expect(page).not_to have_content "Variants tagged"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with variant tag enabled", feature: :variant_tag do
|
||||
it "does not display filter by inventory variants rules" do
|
||||
within "#default-tag-rule" do
|
||||
expect(page).to have_content "Order Cycles tagged"
|
||||
expect(page).not_to have_content "Inventory variants tagged"
|
||||
expect(page).to have_content "Variants tagged"
|
||||
end
|
||||
|
||||
within "#customer-tag-rule" do
|
||||
expect(page).to have_content "Order Cycles tagged"
|
||||
expect(page).not_to have_content "Inventory variants tagged"
|
||||
expect(page).to have_content "Variants tagged"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "creating" do
|
||||
before do
|
||||
visit_tag_rules
|
||||
@@ -90,6 +155,33 @@ RSpec.describe 'Tag Rules' do
|
||||
expect(tag_rule.preferred_matched_order_cycles_visibility).to eq "hidden"
|
||||
end
|
||||
|
||||
context "when variant_tag enabled", feature: :variant_tag do
|
||||
it "allows creation of filter variant type" do
|
||||
# Creating a new tag
|
||||
expect(page).to have_content 'No tags apply to this enterprise yet'
|
||||
click_button '+ Add A New Tag'
|
||||
fill_in_tag "New-Product"
|
||||
|
||||
# New FilterProducts Rule
|
||||
click_button '+ Add A New Rule'
|
||||
tomselect_select 'Show or Hide variants in my shop', from: 'rule_type_selector'
|
||||
click_button "Add Rule"
|
||||
within("#customer-tag-rule #tr_1001") do
|
||||
fill_in_tag "new-product"
|
||||
tomselect_select "VISIBLE",
|
||||
from: "enterprise_tag_rules_attributes_1001_preferred_matched_" \
|
||||
"variants_visibility"
|
||||
end
|
||||
|
||||
click_button 'Update'
|
||||
|
||||
tag_rule = TagRule::FilterVariants.last
|
||||
expect(tag_rule.preferred_customer_tags).to eq "New-Product"
|
||||
expect(tag_rule.preferred_variant_tags).to eq "new-product"
|
||||
expect(tag_rule.preferred_matched_variants_visibility).to eq "visible"
|
||||
end
|
||||
end
|
||||
|
||||
context "when inventory enabled", feature: :inventory do
|
||||
it "allows creation of filter variant type" do
|
||||
# Creating a new tag
|
||||
@@ -118,7 +210,7 @@ RSpec.describe 'Tag Rules' do
|
||||
end
|
||||
end
|
||||
|
||||
context "updating" do
|
||||
context "updating", feature: :inventory do
|
||||
let!(:default_fsm_tag_rule) {
|
||||
create(:filter_shipping_methods_tag_rule, enterprise:,
|
||||
preferred_matched_shipping_methods_visibility:
|
||||
@@ -242,10 +334,10 @@ RSpec.describe 'Tag Rules' do
|
||||
|
||||
context "deleting" do
|
||||
let!(:tag_rule) {
|
||||
create(:filter_products_tag_rule, enterprise:, preferred_customer_tags: "member" )
|
||||
create(:filter_order_cycles_tag_rule, enterprise:, preferred_customer_tags: "member" )
|
||||
}
|
||||
let!(:default_rule) {
|
||||
create(:filter_products_tag_rule, is_default: true, enterprise: )
|
||||
create(:filter_order_cycles_tag_rule, is_default: true, enterprise: )
|
||||
}
|
||||
|
||||
before do
|
||||
|
||||
341
yarn.lock
341
yarn.lock
@@ -17,6 +17,15 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.18.6"
|
||||
|
||||
"@babel/code-frame@^7.10.4":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
|
||||
integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.27.1"
|
||||
js-tokens "^4.0.0"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
"@babel/code-frame@^7.22.13":
|
||||
version "7.22.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
|
||||
@@ -339,6 +348,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
|
||||
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
|
||||
integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
|
||||
|
||||
"@babel/helper-validator-option@^7.21.0":
|
||||
version "7.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180"
|
||||
@@ -1036,6 +1050,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||
|
||||
"@babel/runtime@^7.12.5":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326"
|
||||
integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==
|
||||
|
||||
"@babel/runtime@^7.15.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2"
|
||||
@@ -1502,11 +1521,30 @@
|
||||
resolved "https://registry.yarnpkg.com/@stimulus-components/rails-nested-form/-/rails-nested-form-5.0.0.tgz#b443ad8ba5220328cfd704ca956ebf95ab8c4848"
|
||||
integrity sha512-qrmmurT+KBPrz9iBlyrgJa6Di8i0j328kSk2SUR53nK5W0kDhw1YxVC91aUR+7EsFKiwJT1iB7oDSwpDhDQPeA==
|
||||
|
||||
"@testing-library/dom@<10.0.0":
|
||||
version "9.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.4.tgz#50696ec28376926fec0a1bf87d9dbac5e27f60ce"
|
||||
integrity sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.10.4"
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@types/aria-query" "^5.0.1"
|
||||
aria-query "5.1.3"
|
||||
chalk "^4.1.0"
|
||||
dom-accessibility-api "^0.5.9"
|
||||
lz-string "^1.5.0"
|
||||
pretty-format "^27.0.2"
|
||||
|
||||
"@tootallnate/once@1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
||||
|
||||
"@types/aria-query@^5.0.1":
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708"
|
||||
integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
|
||||
|
||||
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14":
|
||||
version "7.1.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024"
|
||||
@@ -1959,6 +1997,13 @@ argparse@^1.0.7:
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
aria-query@5.1.3:
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
|
||||
integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==
|
||||
dependencies:
|
||||
deep-equal "^2.0.5"
|
||||
|
||||
arr-diff@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
|
||||
@@ -1974,6 +2019,14 @@ arr-union@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
|
||||
integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
|
||||
|
||||
array-buffer-byte-length@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b"
|
||||
integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==
|
||||
dependencies:
|
||||
call-bound "^1.0.3"
|
||||
is-array-buffer "^3.0.5"
|
||||
|
||||
array-flatten@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
@@ -2567,6 +2620,16 @@ call-bind@^1.0.0, call-bind@^1.0.2:
|
||||
function-bind "^1.1.1"
|
||||
get-intrinsic "^1.0.2"
|
||||
|
||||
call-bind@^1.0.5, call-bind@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c"
|
||||
integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.0"
|
||||
es-define-property "^1.0.0"
|
||||
get-intrinsic "^1.2.4"
|
||||
set-function-length "^1.2.2"
|
||||
|
||||
call-bind@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
|
||||
@@ -2578,17 +2641,7 @@ call-bind@^1.0.7:
|
||||
get-intrinsic "^1.2.4"
|
||||
set-function-length "^1.2.1"
|
||||
|
||||
call-bind@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c"
|
||||
integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.0"
|
||||
es-define-property "^1.0.0"
|
||||
get-intrinsic "^1.2.4"
|
||||
set-function-length "^1.2.2"
|
||||
|
||||
call-bound@^1.0.3, call-bound@^1.0.4:
|
||||
call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a"
|
||||
integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
|
||||
@@ -2667,6 +2720,14 @@ chalk@^4.0.0:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^4.1.0:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
char-regex@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
|
||||
@@ -3405,6 +3466,30 @@ deep-equal@^1.0.1:
|
||||
object-keys "^1.1.1"
|
||||
regexp.prototype.flags "^1.2.0"
|
||||
|
||||
deep-equal@^2.0.5:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1"
|
||||
integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==
|
||||
dependencies:
|
||||
array-buffer-byte-length "^1.0.0"
|
||||
call-bind "^1.0.5"
|
||||
es-get-iterator "^1.1.3"
|
||||
get-intrinsic "^1.2.2"
|
||||
is-arguments "^1.1.1"
|
||||
is-array-buffer "^3.0.2"
|
||||
is-date-object "^1.0.5"
|
||||
is-regex "^1.1.4"
|
||||
is-shared-array-buffer "^1.0.2"
|
||||
isarray "^2.0.5"
|
||||
object-is "^1.1.5"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.4"
|
||||
regexp.prototype.flags "^1.5.1"
|
||||
side-channel "^1.0.4"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
which-collection "^1.0.1"
|
||||
which-typed-array "^1.1.13"
|
||||
|
||||
deep-is@~0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||
@@ -3423,7 +3508,7 @@ default-gateway@^4.2.0:
|
||||
execa "^1.0.0"
|
||||
ip-regex "^2.1.0"
|
||||
|
||||
define-data-property@^1.1.4:
|
||||
define-data-property@^1.0.1, define-data-property@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
|
||||
integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
|
||||
@@ -3447,6 +3532,15 @@ define-properties@^1.2.0:
|
||||
has-property-descriptors "^1.0.0"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
define-properties@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
|
||||
integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
|
||||
dependencies:
|
||||
define-data-property "^1.0.1"
|
||||
has-property-descriptors "^1.0.0"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
define-property@^0.2.5:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
|
||||
@@ -3564,6 +3658,11 @@ dns-txt@^2.0.2:
|
||||
dependencies:
|
||||
buffer-indexof "^1.0.0"
|
||||
|
||||
dom-accessibility-api@^0.5.9:
|
||||
version "0.5.16"
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
|
||||
integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
|
||||
|
||||
dom-serialize@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
|
||||
@@ -3803,6 +3902,21 @@ es-errors@^1.3.0:
|
||||
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||
|
||||
es-get-iterator@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6"
|
||||
integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
get-intrinsic "^1.1.3"
|
||||
has-symbols "^1.0.3"
|
||||
is-arguments "^1.1.1"
|
||||
is-map "^2.0.2"
|
||||
is-set "^2.0.2"
|
||||
is-string "^1.0.7"
|
||||
isarray "^2.0.5"
|
||||
stop-iteration-iterator "^1.0.0"
|
||||
|
||||
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
|
||||
@@ -4370,7 +4484,7 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
|
||||
has-symbols "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
|
||||
get-intrinsic@^1.2.6, get-intrinsic@^1.3.0:
|
||||
get-intrinsic@^1.2.2, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
|
||||
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
|
||||
@@ -4920,6 +5034,15 @@ internal-ip@^4.3.0:
|
||||
default-gateway "^4.2.0"
|
||||
ipaddr.js "^1.9.0"
|
||||
|
||||
internal-slot@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961"
|
||||
integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
hasown "^2.0.2"
|
||||
side-channel "^1.1.0"
|
||||
|
||||
interpret@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
|
||||
@@ -4972,6 +5095,23 @@ is-arguments@^1.0.4:
|
||||
call-bind "^1.0.2"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-arguments@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b"
|
||||
integrity sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==
|
||||
dependencies:
|
||||
call-bound "^1.0.2"
|
||||
has-tostringtag "^1.0.2"
|
||||
|
||||
is-array-buffer@^3.0.2, is-array-buffer@^3.0.5:
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280"
|
||||
integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==
|
||||
dependencies:
|
||||
call-bind "^1.0.8"
|
||||
call-bound "^1.0.3"
|
||||
get-intrinsic "^1.2.6"
|
||||
|
||||
is-arrayish@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||
@@ -5061,6 +5201,14 @@ is-date-object@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
||||
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
|
||||
|
||||
is-date-object@^1.0.5:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7"
|
||||
integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==
|
||||
dependencies:
|
||||
call-bound "^1.0.2"
|
||||
has-tostringtag "^1.0.2"
|
||||
|
||||
is-descriptor@^0.1.0:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
|
||||
@@ -5130,6 +5278,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-map@^2.0.2, is-map@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
|
||||
integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
|
||||
|
||||
is-negative-zero@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
|
||||
@@ -5209,11 +5362,33 @@ is-regex@^1.1.2:
|
||||
call-bind "^1.0.2"
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
is-regex@^1.1.4:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22"
|
||||
integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==
|
||||
dependencies:
|
||||
call-bound "^1.0.2"
|
||||
gopd "^1.2.0"
|
||||
has-tostringtag "^1.0.2"
|
||||
hasown "^2.0.2"
|
||||
|
||||
is-resolvable@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
||||
integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
|
||||
|
||||
is-set@^2.0.2, is-set@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d"
|
||||
integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==
|
||||
|
||||
is-shared-array-buffer@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f"
|
||||
integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==
|
||||
dependencies:
|
||||
call-bound "^1.0.3"
|
||||
|
||||
is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
@@ -5229,6 +5404,14 @@ is-string@^1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
|
||||
integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
|
||||
|
||||
is-string@^1.0.7:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9"
|
||||
integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==
|
||||
dependencies:
|
||||
call-bound "^1.0.3"
|
||||
has-tostringtag "^1.0.2"
|
||||
|
||||
is-symbol@^1.0.2, is-symbol@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
|
||||
@@ -5248,6 +5431,19 @@ is-typedarray@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||
|
||||
is-weakmap@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd"
|
||||
integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==
|
||||
|
||||
is-weakset@^2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca"
|
||||
integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==
|
||||
dependencies:
|
||||
call-bound "^1.0.3"
|
||||
get-intrinsic "^1.2.6"
|
||||
|
||||
is-windows@^1.0.1, is-windows@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||
@@ -6096,6 +6292,11 @@ lru-cache@^6.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
lz-string@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
|
||||
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
|
||||
|
||||
make-dir@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||
@@ -6601,6 +6802,11 @@ object-inspect@^1.13.1:
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
|
||||
integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
|
||||
|
||||
object-inspect@^1.13.3:
|
||||
version "1.13.4"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213"
|
||||
integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
|
||||
|
||||
object-inspect@^1.9.0:
|
||||
version "1.12.2"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
|
||||
@@ -6614,6 +6820,14 @@ object-is@^1.0.1:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.3"
|
||||
|
||||
object-is@^1.1.5:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07"
|
||||
integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==
|
||||
dependencies:
|
||||
call-bind "^1.0.7"
|
||||
define-properties "^1.2.1"
|
||||
|
||||
object-keys@^1.0.12, object-keys@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
@@ -6636,6 +6850,18 @@ object.assign@^4.1.0, object.assign@^4.1.2:
|
||||
has-symbols "^1.0.1"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
object.assign@^4.1.4:
|
||||
version "4.1.7"
|
||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d"
|
||||
integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==
|
||||
dependencies:
|
||||
call-bind "^1.0.8"
|
||||
call-bound "^1.0.3"
|
||||
define-properties "^1.2.1"
|
||||
es-object-atoms "^1.0.0"
|
||||
has-symbols "^1.1.0"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
object.getownpropertydescriptors@^2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7"
|
||||
@@ -7675,7 +7901,7 @@ prettier@3.2.4:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.4.tgz#4723cadeac2ce7c9227de758e5ff9b14e075f283"
|
||||
integrity sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==
|
||||
|
||||
pretty-format@^27.5.1:
|
||||
pretty-format@^27.0.2, pretty-format@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
|
||||
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
|
||||
@@ -7965,6 +8191,18 @@ regexp.prototype.flags@^1.2.0:
|
||||
define-properties "^1.2.0"
|
||||
functions-have-names "^1.2.3"
|
||||
|
||||
regexp.prototype.flags@^1.5.1:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19"
|
||||
integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==
|
||||
dependencies:
|
||||
call-bind "^1.0.8"
|
||||
define-properties "^1.2.1"
|
||||
es-errors "^1.3.0"
|
||||
get-proto "^1.0.1"
|
||||
gopd "^1.2.0"
|
||||
set-function-name "^2.0.2"
|
||||
|
||||
regexpu-core@^5.3.1:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b"
|
||||
@@ -8326,6 +8564,16 @@ set-function-length@^1.2.1, set-function-length@^1.2.2:
|
||||
gopd "^1.0.1"
|
||||
has-property-descriptors "^1.0.2"
|
||||
|
||||
set-function-name@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985"
|
||||
integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==
|
||||
dependencies:
|
||||
define-data-property "^1.1.4"
|
||||
es-errors "^1.3.0"
|
||||
functions-have-names "^1.2.3"
|
||||
has-property-descriptors "^1.0.2"
|
||||
|
||||
set-value@^2.0.0, set-value@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
|
||||
@@ -8389,6 +8637,46 @@ shortcut-buttons-flatpickr@^0.4.0:
|
||||
resolved "https://registry.yarnpkg.com/shortcut-buttons-flatpickr/-/shortcut-buttons-flatpickr-0.4.0.tgz#a36e0a88a670ed2637b7b1adb5bee0914c29a7e7"
|
||||
integrity sha512-JKmT4my3Hm1e18OvG4Q6RcFhN4WRqqpTMkHrvZ7fup/dp6aTIWGVCHdRYtASkp/FCzDlJh6iCLQ/VcwwNpAMoQ==
|
||||
|
||||
side-channel-list@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad"
|
||||
integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
object-inspect "^1.13.3"
|
||||
|
||||
side-channel-map@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42"
|
||||
integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
|
||||
dependencies:
|
||||
call-bound "^1.0.2"
|
||||
es-errors "^1.3.0"
|
||||
get-intrinsic "^1.2.5"
|
||||
object-inspect "^1.13.3"
|
||||
|
||||
side-channel-weakmap@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea"
|
||||
integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
|
||||
dependencies:
|
||||
call-bound "^1.0.2"
|
||||
es-errors "^1.3.0"
|
||||
get-intrinsic "^1.2.5"
|
||||
object-inspect "^1.13.3"
|
||||
side-channel-map "^1.0.1"
|
||||
|
||||
side-channel@^1.0.4, side-channel@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9"
|
||||
integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
object-inspect "^1.13.3"
|
||||
side-channel-list "^1.0.0"
|
||||
side-channel-map "^1.0.1"
|
||||
side-channel-weakmap "^1.0.2"
|
||||
|
||||
side-channel@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
|
||||
@@ -8635,6 +8923,11 @@ statuses@2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
||||
|
||||
stimulus-autocomplete@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/stimulus-autocomplete/-/stimulus-autocomplete-3.1.0.tgz#7c9292706556ed0a87abf60ea2688bf0ea1176a8"
|
||||
integrity sha512-SmVViCdA8yCl99oV2kzllNOqYjx7wruY+1OjAVsDTkZMNFZG5j+SqDKHMYbu+dRFy/SWq/PParzwZHvLAgH+YA==
|
||||
|
||||
stimulus-flatpickr@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/stimulus-flatpickr/-/stimulus-flatpickr-1.4.0.tgz#a41071a3e69cfc50b7eaaacf356fc0ab1ab0543c"
|
||||
@@ -8657,6 +8950,14 @@ stimulus_reflex@3.5.5:
|
||||
"@rails/actioncable" "^6 || ^7 || ^8"
|
||||
cable_ready "^5.0.6"
|
||||
|
||||
stop-iteration-iterator@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad"
|
||||
integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
internal-slot "^1.1.0"
|
||||
|
||||
stream-browserify@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
|
||||
@@ -9619,12 +9920,22 @@ which-boxed-primitive@^1.0.2:
|
||||
is-string "^1.0.5"
|
||||
is-symbol "^1.0.3"
|
||||
|
||||
which-collection@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0"
|
||||
integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==
|
||||
dependencies:
|
||||
is-map "^2.0.3"
|
||||
is-set "^2.0.3"
|
||||
is-weakmap "^2.0.2"
|
||||
is-weakset "^2.0.3"
|
||||
|
||||
which-module@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
||||
|
||||
which-typed-array@^1.1.16:
|
||||
which-typed-array@^1.1.13, which-typed-array@^1.1.16:
|
||||
version "1.1.19"
|
||||
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956"
|
||||
integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==
|
||||
|
||||
Reference in New Issue
Block a user