From d278b72289a97326456015e758b78b9ddfd3434b Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Fri, 18 Mar 2016 12:22:54 +1100 Subject: [PATCH] Updating Tag Rules UI to allow management of new FilterShippingMethods rule type --- .../tag_rules_controller.js.coffee | 21 +++-- .../directives/new_rule_dialog.js.coffee | 31 +++++++ .../filter_shipping_methods.js.coffee | 4 + .../admin/new_tag_rule_dialog.html.haml | 10 +++ .../admin/tag_rules/discount_order.html.haml | 16 ++-- .../filter_shipping_methods.html.haml | 27 ++++++ .../admin/components/jquery_dialog.scss | 88 +++++++++++++++++++ app/assets/stylesheets/admin/select2.css.scss | 35 ++++++++ .../stylesheets/admin/tag_rules.css.scss | 9 +- .../api/admin/tag_rule_serializer.rb | 8 ++ .../enterprises/form/_tag_rules.html.haml | 3 +- spec/factories.rb | 2 +- spec/features/admin/tag_rules_spec.rb | 39 ++++++-- .../tag_rules_controller_spec.js.coffee | 5 +- .../tag_rule/filter_shipping_methods_spec.rb | 2 +- 15 files changed, 273 insertions(+), 27 deletions(-) create mode 100644 app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee create mode 100644 app/assets/javascripts/admin/tag_rules/directives/tag_rules/filter_shipping_methods.js.coffee create mode 100644 app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml create mode 100644 app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods.html.haml create mode 100644 app/assets/stylesheets/admin/components/jquery_dialog.scss diff --git a/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee b/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee index 7d8305118f..7209147b66 100644 --- a/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee +++ b/app/assets/javascripts/admin/tag_rules/controllers/tag_rules_controller.js.coffee @@ -1,6 +1,8 @@ angular.module("admin.tagRules").controller "TagRulesCtrl", ($scope, $http, enterprise) -> $scope.tagGroups = enterprise.tag_groups + $scope.visibilityOptions = [ { id: "visible", name: "VISIBLE" }, { id: "hidden", name: "NOT VISIBLE" } ] + updateRuleCounts = -> index = 0 for tagGroup in $scope.tagGroups @@ -13,13 +15,18 @@ angular.module("admin.tagRules").controller "TagRulesCtrl", ($scope, $http, ente for tagRule in tagGroup.rules tagRule.preferred_customer_tags = (tag.text for tag in tagGroup.tags).join(",") - $scope.addNewRuleTo = (tagGroup) -> - tagGroup.rules.push - id: null - preferred_customer_tags: (tag.text for tag in tagGroup.tags).join(",") - type: "TagRule::DiscountOrder" - calculator: - preferred_flat_percent: 0 + $scope.addNewRuleTo = (tagGroup, ruleType) -> + newRule = + id: null + preferred_customer_tags: (tag.text for tag in tagGroup.tags).join(",") + type: "TagRule::#{ruleType}" + switch ruleType + when "DiscountOrder" + newRule.calculator = { preferred_flat_percent: 0 } + when "FilterShippingMethods" + newRule.peferred_shipping_method_tags = [] + newRule.preferred_matched_shipping_methods_visibility = "visible" + tagGroup.rules.push(newRule) updateRuleCounts() $scope.addNewTag = -> diff --git a/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee b/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee new file mode 100644 index 0000000000..981699a96d --- /dev/null +++ b/app/assets/javascripts/admin/tag_rules/directives/new_rule_dialog.js.coffee @@ -0,0 +1,31 @@ +angular.module("admin.tagRules").directive 'newTagRuleDialog', ($compile, $templateCache) -> + restrict: 'A' + scope: true + link: (scope, element, attr) -> + # Compile modal template + template = $compile($templateCache.get('admin/new_tag_rule_dialog.html'))(scope) + + scope.ruleTypes = [ + { id: "DiscountOrder", name: 'Apply a discount to orders' } + { id: "FilterShippingMethods", name: 'Show/Hide shipping methods' } + ] + + scope.ruleType = "DiscountOrder" + + # Set Dialog options + template.dialog + autoOpen: false + resizable: false + width: 'auto' + scaleW: 0.4 + modal: true + clickOut: true + + # Link opening of dialog to click event on element + element.bind 'click', (e) -> + template.dialog('open') + + scope.addRule = (tagGroup, ruleType) -> + scope.addNewRuleTo(tagGroup, ruleType) + template.dialog('close') + return diff --git a/app/assets/javascripts/admin/tag_rules/directives/tag_rules/filter_shipping_methods.js.coffee b/app/assets/javascripts/admin/tag_rules/directives/tag_rules/filter_shipping_methods.js.coffee new file mode 100644 index 0000000000..1a75cf8ff2 --- /dev/null +++ b/app/assets/javascripts/admin/tag_rules/directives/tag_rules/filter_shipping_methods.js.coffee @@ -0,0 +1,4 @@ +angular.module("admin.tagRules").directive "filterShippingMethods", -> + restrict: "E" + replace: true + templateUrl: "admin/tag_rules/filter_shipping_methods.html" diff --git a/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml b/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml new file mode 100644 index 0000000000..653e0d175d --- /dev/null +++ b/app/assets/javascripts/templates/admin/new_tag_rule_dialog.html.haml @@ -0,0 +1,10 @@ +#new-tag-rule-dialog + .text-normal.margin-bottom-30.text-center + Select a rule type: + + .text-center.margin-bottom-30 + -# %select.fullwidth{ 'select2-min-search' => 5, 'ng-model' => 'newRuleType', 'ng-options' => 'ruleType.id as ruleType.name for ruleType in availableRuleTypes' } + %input.ofn-select2.fullwidth{ :id => 'rule_type_selector', ng: { model: "ruleType" }, data: "ruleTypes", 'min-search' => "5" } + + .text-center + %input.button.red.icon-plus{ type: 'button', value: "Add Rule", ng: { click: 'addRule(tagGroup, ruleType)' } } diff --git a/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml index 90886b2dab..358d9ce1a6 100644 --- a/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml +++ b/app/assets/javascripts/templates/admin/tag_rules/discount_order.html.haml @@ -28,12 +28,10 @@ name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][calculator_attributes][preferred_flat_percent]", ng: { value: "rule.calculator.preferred_flat_percent" } } - %span.text-normal {{ $index + 1 }}. Apply a discount of - %span.input-symbol.after - %span.text-normal % - %input.text-big{ type: "number", - id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", - min: -100, - max: 100, - ng: { model: "rule.calculator.preferred_flat_percent" }, 'invert-number' => true } - %span.text-normal to order subtotals + %span.text-normal {{ $index + 1 }}. Orders are discounted by + %input{ type: "number", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_calculator_attributes_preferred_flat_percent", + min: -100, + max: 100, + ng: { model: "rule.calculator.preferred_flat_percent" }, 'invert-number' => true } + %span.text-normal % diff --git a/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods.html.haml b/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods.html.haml new file mode 100644 index 0000000000..6552d834b5 --- /dev/null +++ b/app/assets/javascripts/templates/admin/tag_rules/filter_shipping_methods.html.haml @@ -0,0 +1,27 @@ +%div + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_id", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][id]", + ng: { value: "rule.id" } } + + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_type", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][type]", + value: "TagRule::FilterShippingMethods" } + + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_customer_tags", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_customer_tags]", + ng: { value: "rule.preferred_customer_tags" } } + + %input{ type: "hidden", + id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_shipping_method_tags", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_shipping_method_tags]", + ng: { value: "rule.preferred_customer_tags" } } + + %span.text-normal {{ $index + 1 }}. Shipping methods with matching tags are + %input.light.ofn-select2{ id: "enterprise_tag_rules_attributes_{{tagGroup.startIndex + $index}}_preferred_matched_shipping_methods_visibility", + name: "enterprise[tag_rules_attributes][{{tagGroup.startIndex + $index}}][preferred_matched_shipping_methods_visibility]", + ng: { model: "rule.preferred_matched_shipping_methods_visibility"}, + data: 'visibilityOptions', "min-search" => 5 } + -# %tags-with-translation{ object: "rule", "tags-attr" => "shipping_method_tags", "tag-list-attr" => "preferred_shipping_method_tags" } diff --git a/app/assets/stylesheets/admin/components/jquery_dialog.scss b/app/assets/stylesheets/admin/components/jquery_dialog.scss new file mode 100644 index 0000000000..2e36db6e33 --- /dev/null +++ b/app/assets/stylesheets/admin/components/jquery_dialog.scss @@ -0,0 +1,88 @@ +/** +Main colors: +dark: #545454 +light: #ccc +*/ +.ui-dialog { + border: 2px solid #4a4a4a; + border-radius:3px; + padding:0px; + -moz-box-shadow: 3px 3px 4px #797979; + -webkit-box-shadow: 3px 3px 4px #797979; + box-shadow: 3px 3px 4px #797979; + + /* For IE 8 */ + -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#545454')"; + + /* For IE 5.5 - 7 */ + filter: progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#545454'); +} + +.ui-dialog .ui-dialog-titlebar{ + border-radius: 3px; +} + +.ui-dialog .ui-state-hover { + &.ui-dialog-titlebar-close{ + + } +} + +/*.ui-dialog .ui-icon-closethick{background:url(/static/assets/dialogCloseButton.png);}*/ + +.ui-dialog .ui-widget-header{ + background-image: none; + background-color: #ffffff; + border:0px; + border-radius: 3px; + padding: 0px 5px 0px 5px; +} +.ui-dialog .ui-widget-content{ + border: none; + border-radius: 3px; + padding: 0px 50px 30px 50px; +} + +.ui-dialog .ui-corner-all{ + border-radius:0px; +} +.ui-dialog { + .ui-state-hover, .ui-state-focus{ + border: none; + background: none; + color: #545454; + } +} + +.ui-state-hover, .ui-widget-header .ui-state-hover, .ui-widget-content .ui-state-hover { + background-color: #ffffff; + background: none; +} + +.ui-dialog-titlebar-close { + float: right; + &:before { + color: #000000; + font-size: 2em; + font-weight: 400; + content: '\00d7'; + display: inline; + } + + &:hover { + &:before { + color: #da5354; + } + } + + .ui-icon { + &.ui-icon-closethick { + display: none; + } + } +} + +.ui-widget-overlay { + background: #e9e9e9; + opacity: 0.6; +} diff --git a/app/assets/stylesheets/admin/select2.css.scss b/app/assets/stylesheets/admin/select2.css.scss index daba11d099..f94515a54c 100644 --- a/app/assets/stylesheets/admin/select2.css.scss +++ b/app/assets/stylesheets/admin/select2.css.scss @@ -1,5 +1,8 @@ .select2-container { .select2-choice { + .select2-search-choice-close { + display: none; + } .select2-arrow { width: 22px; border: none; @@ -7,4 +10,36 @@ background-color: transparent; } } + + &.light { + .select2-choice{ + background-color: #ffffff; + font-weight: normal; + border: 1px solid #5498da !important; + color: #5498da !important; + + .select2-arrow { + &:before { + color: #5498da; + font-size: 1rem; + font-weight: 400; + content: '\25be'; + display: inline; + } + } + } + + &:hover, &.select2-container-active { + .select2-choice{ + color: #ffffff !important; + background-color: #5498da !important; + + .select2-arrow { + &:before { + color: #ffffff; + } + } + } + } + } } diff --git a/app/assets/stylesheets/admin/tag_rules.css.scss b/app/assets/stylesheets/admin/tag_rules.css.scss index 985ed7d727..028ad6ce1c 100644 --- a/app/assets/stylesheets/admin/tag_rules.css.scss +++ b/app/assets/stylesheets/admin/tag_rules.css.scss @@ -44,7 +44,7 @@ td { border: none; - padding: 4px 10px; + padding: 4px 10px 10px 10px; margin: 0px; input { @@ -59,3 +59,10 @@ margin-bottom: 10px; } } + +#new-tag-rule-dialog{ + .select2-chosen, .select2-result-label{ + font-size: 1rem; + font-weight: lighter; + } +} diff --git a/app/serializers/api/admin/tag_rule_serializer.rb b/app/serializers/api/admin/tag_rule_serializer.rb index a571b9fe5e..ac333c23c6 100644 --- a/app/serializers/api/admin/tag_rule_serializer.rb +++ b/app/serializers/api/admin/tag_rule_serializer.rb @@ -16,4 +16,12 @@ module Api::Admin::TagRule class DiscountOrderSerializer < BaseSerializer has_one :calculator, serializer: Api::Admin::Calculator::FlatPercentItemTotalSerializer end + + class FilterShippingMethodsSerializer < BaseSerializer + attributes :preferred_matched_shipping_methods_visibility, :shipping_method_tags + + def shipping_method_tags + object.preferred_shipping_method_tags.split(",") + end + end end diff --git a/app/views/admin/enterprises/form/_tag_rules.html.haml b/app/views/admin/enterprises/form/_tag_rules.html.haml index 9a3c0f7a40..1a50e4f353 100644 --- a/app/views/admin/enterprises/form/_tag_rules.html.haml +++ b/app/views/admin/enterprises/form/_tag_rules.html.haml @@ -24,9 +24,10 @@ %tr.tag_rule{ id: "tr_{{rule.id}}", ng: { repeat: "rule in tagGroup.rules" } } %td %discount-order{ bo: { if: "rule.type == 'TagRule::DiscountOrder'" } } + %filter-shipping-methods{ bo: { if: "rule.type == 'TagRule::FilterShippingMethods'" } } %td.actions %a{ ng: { click: "deleteTagRule(tagGroup, rule)" }, :class => "delete-tag-rule icon-trash no-text" } .add_rule.text-center - %input.button.icon-plus{ type: 'button', value: "+ Add A New Rule", ng: { click: 'addNewRuleTo(tagGroup)' } } + %input.button.icon-plus{ type: 'button', value: "+ Add A New Rule", "new-tag-rule-dialog" => true } .add_tage %input.button.red.icon-plus{ type: 'button', value: "+ Add A New Tag", ng: { click: 'addNewTag()' } } diff --git a/spec/factories.rb b/spec/factories.rb index 11b4fed010..b4a3a1b573 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -286,7 +286,7 @@ FactoryGirl.define do month { 1 + rand(12) } end - factory :filter_shipping_method_tag_rule, class: TagRule::FilterShippingMethods do + factory :filter_shipping_methods_tag_rule, class: TagRule::FilterShippingMethods do enterprise { FactoryGirl.create :distributor_enterprise } end diff --git a/spec/features/admin/tag_rules_spec.rb b/spec/features/admin/tag_rules_spec.rb index d905b2fef1..a4e6e62d24 100644 --- a/spec/features/admin/tag_rules_spec.rb +++ b/spec/features/admin/tag_rules_spec.rb @@ -12,47 +12,76 @@ feature 'Tag Rules', js: true do visit main_app.edit_admin_enterprise_path(enterprise) end - it "creates a new rule" do + it "allows creation of rules of each type" do click_link "Tag Rules" + # Creating a new tag expect(page).to_not have_selector '.customer_tag' expect(page).to have_content 'No tags apply to this enterprise yet' click_button '+ Add A New Tag' find(:css, "tags-input .tags input").set "volunteer\n" + # New DiscountOrder Rule expect(page).to have_content 'No rules apply to this tag yet' click_button '+ Add A New Rule' + select2_select 'Apply a discount to orders', from: 'rule_type_selector' + click_button "Add Rule" fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 22 + # New FilterShippingMethods Rule + click_button '+ Add A New Rule' + select2_select 'Show/Hide shipping methods', from: 'rule_type_selector' + click_button "Add Rule" + select2_select "NOT VISIBLE", from: "enterprise_tag_rules_attributes_1_preferred_matched_shipping_methods_visibility" + click_button 'Update' tag_rule = TagRule::DiscountOrder.last expect(tag_rule.preferred_customer_tags).to eq "volunteer" expect(tag_rule.calculator.preferred_flat_percent).to eq -22 + + tag_rule = TagRule::FilterShippingMethods.last + expect(tag_rule.preferred_customer_tags).to eq "volunteer" + expect(tag_rule.preferred_shipping_method_tags).to eq "volunteer" + expect(tag_rule.preferred_matched_shipping_methods_visibility).to eq "hidden" end end context "updating" do - let!(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } + let!(:do_tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "member" ) } + let!(:fsm_tag_rule) { create(:filter_shipping_methods_tag_rule, enterprise: enterprise, preferred_matched_shipping_methods_visibility: "hidden", preferred_customer_tags: "member" ) } before do login_to_admin_section visit main_app.edit_admin_enterprise_path(enterprise) end - it "saves changes to the rule" do + it "saves changes to rules of each type" do click_link "Tag Rules" + # Tag group exists expect(first('.customer_tag .header')).to have_content "For customers tagged:" expect(first('tags-input .tag-list ti-tag-item')).to have_content "member" find(:css, "tags-input .tags input").set "volunteer\n" + + # DiscountOrder rule expect(page).to have_field "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: '0' fill_in "enterprise_tag_rules_attributes_0_calculator_attributes_preferred_flat_percent", with: 45 + # FilterShippingMethods rule + expect(page).to have_select2 "enterprise_tag_rules_attributes_1_preferred_matched_shipping_methods_visibility", selected: 'NOT VISIBLE' + select2_select 'VISIBLE', from: "enterprise_tag_rules_attributes_1_preferred_matched_shipping_methods_visibility" + click_button 'Update' - expect(tag_rule.preferred_customer_tags).to eq "member,volunteer" - expect(tag_rule.calculator.preferred_flat_percent).to eq -45 + # DiscountOrder rule + expect(do_tag_rule.preferred_customer_tags).to eq "member,volunteer" + expect(do_tag_rule.calculator.preferred_flat_percent).to eq -45 + + # FilterShippingMethods rule + expect(fsm_tag_rule.preferred_customer_tags).to eq "member,volunteer" + expect(fsm_tag_rule.preferred_shipping_method_tags).to eq "member,volunteer" + expect(fsm_tag_rule.preferred_matched_shipping_methods_visibility).to eq "visible" end end diff --git a/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee b/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee index 0b92822a0a..1e132ec07d 100644 --- a/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/tag_rules/controllers/tag_rules_controller_spec.js.coffee @@ -23,10 +23,11 @@ describe "TagRulesCtrl", -> describe "adding a new tag group", -> beforeEach -> - scope.addNewRuleTo(scope.tagGroups[0]) + scope.addNewRuleTo(scope.tagGroups[0], "DiscountOrder") - it "adds a new rule to the rules array for the tagGroup", -> + it "adds a new rule of the specified type to the rules array for the tagGroup", -> expect(scope.tagGroups[0].rules.length).toEqual 3 + expect(scope.tagGroups[0].rules[2].type).toEqual "TagRule::DiscountOrder" it "updates tagGroup start indices", -> expect(scope.tagGroups[0].startIndex).toEqual 0 diff --git a/spec/models/tag_rule/filter_shipping_methods_spec.rb b/spec/models/tag_rule/filter_shipping_methods_spec.rb index 2c9da2a17b..539aa3c6ca 100644 --- a/spec/models/tag_rule/filter_shipping_methods_spec.rb +++ b/spec/models/tag_rule/filter_shipping_methods_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe TagRule::DiscountOrder, type: :model do - let!(:tag_rule) { create(:filter_shipping_method_tag_rule) } + let!(:tag_rule) { create(:filter_shipping_methods_tag_rule) } describe "determining whether tags match for a given shipping method" do context "when the shipping method is nil" do