diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprise_index_row_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprise_index_row_controller.js.coffee index 9ef5c27bdf..13c9b7dc08 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/enterprise_index_row_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprise_index_row_controller.js.coffee @@ -16,9 +16,25 @@ angular.module("admin.enterprises").controller "EnterpriseIndexRowCtrl", ($scope "Shop" when "any" "Hub" + else + "Choose" else switch $scope.enterprise.sells when "none" "Profile" - else + when "any" "Hub" + else + "Choose" + + $scope.updateRowText = -> + $scope.producer = $scope.producerText() + $scope.package = $scope.packageText() + $scope.producerError = ($scope.producer == "Choose") + $scope.packageError = ($scope.package == "Choose") + + + $scope.updateRowText() + + $scope.$on "enterprise:updated", -> + $scope.updateRowText() diff --git a/app/assets/javascripts/admin/enterprises/controllers/index_panel_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/index_panel_controller.js.coffee index dbf6ee47b2..6f568ca5ea 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/index_panel_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/index_panel_controller.js.coffee @@ -9,6 +9,7 @@ angular.module("admin.enterprises").controller 'indexPanelCtrl', ($scope, Enterp unless $scope.saved() $scope.saving = true Enterprises.save($scope.enterprise).then (data) -> + $scope.$emit("enterprise:updated") $scope.saving = false , (response) -> $scope.saving = false diff --git a/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml b/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml index 593d69bb91..02080288ee 100644 --- a/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml +++ b/app/assets/javascripts/templates/admin/panels/enterprise_package.html.haml @@ -1,106 +1,126 @@ -.enterprise_package_panel - %form{ name: "package", id: "package", novalidate: true, ng: { controller: 'indexPackagePanelCtrl' } } - -# Have to use hidden:'true' on this input rather than type:'hidden' as the latter seems to break ngPattern and therefore validation - %input{ hidden: "true", name: "sells", ng: { required: true, pattern: "/^(none|own|any)$/", value: "enterprise.sells"} } - %input{ hidden: "true", name: "producer_profile_only", ng: { required: "enterprise.is_primary_producer=='none'", disabled: "enterprise.is_primary_producer!='none'", value: "enterprise.producer_profile_only"} } +.row.enterprise_package_panel{ ng: { controller: 'indexPackagePanelCtrl' } } + .alpha.eight.columns + %div{ ng: { if: "!enterprise.is_primary_producer", switch: "enterprise.sells" } } + .info{ ng: { switch: { when: "none" } } } + %h3 Hub Profile - .row - .alpha.eight.columns - -# Non-Producer Info - .info{ ng: { show: "!enterprise.is_primary_producer && enterprise.sells=='none'" } } - %h3 Hub Profile + %p + %strong COST: ALWAYS FREE - %p - %strong COST: ALWAYS FREE + %p A Hub Profile gives you the ability to list your enterprise on the Open Food Network. Your enterprise will be visible on the map, and will be searchable in listings. - %p A Hub Profile gives you the ability to list your enterprise on the Open Food Network. Your enterprise will be visible on the map, and will be searchable in listings. + .info{ ng: { switch: { when: "any" } } } + %h3 Hub Shop - .info{ ng: { show: "!enterprise.is_primary_producer && enterprise.sells=='any'" } } - %h3 Hub Shop + %p + %strong COST: %2 OF SALES, CAPPED AT $50 PER MONTH - %p - %strong COST: %2 OF SALES, CAPPED AT $50 PER MONTH + %p A Hub Shop enables an enterprise to aggregate produce from other enterprises and to sell it through a shop on the Open Food Network. - %p A Hub Shop enables an enterprise to aggregate produce from other enterprises and to sell it through a shop on the Open Food Network. + %p Hubs can take many forms, whether they be a food co-op, a buying group, a veggie-box program, or a local grocery store. - %p Hubs can take many forms, whether they be a food co-op, a buying group, a veggie-box program, or a local grocery store. + %p If you also want to sell your own products, you will need to switch this enterprise to be a producer. - %p If you also want to sell your own products, you will need to switch this enterprise to be a producer. + .info{ ng: { switch: { default: true } } } + %h3 + Please Choose a Package + %i.icon-arrow-right + + %p + %strong Your enterprise will not be fully activated until a package is selected from the options on the left. + + %p + Click on an option to see more detailed information about each package, and hit the red SAVE button when you are done! - -# Producer Info - .info{ ng: { show: "enterprise.is_primary_producer && enterprise.sells=='none' && enterprise.producer_profile_only==false" } } + + %div{ ng: { if: "enterprise.is_primary_producer", switch: "enterprise.sells" } } + .info{ ng: { switch: { when: "none" } } } + %h3 Profile Only + + %p + %strong COST: ALWAYS FREE + + %p If you prefer to focus on producing food, and want to leave the work of selling it to someone else, you won't require a shop on the Open Food Network. + + %p Producers with a profile can market their produce through the Open Food Network by connecting and trading with existing shops. + + .info{ ng: { switch: { when: "own" } } } + %h3 Producer Shop + + %p + %strong COST: %2 OF SALES, CAPPED AT $50 PER MONTH + + %p A Producer Shop allows producers to offer their products for sale to customers through their very own Open Food Network shop. + + %p Producer Shops may sell produce that has been grown by the producer in question, but do not allow for aggregation of produce from elsewhere. + + .info{ ng: { switch: { when: "any" } } } + %h3 Producer Hub + + %p + %strong COST: %2 OF SALES, CAPPED AT $50 PER MONTH + + %p Producer Hubs can take many forms, whether they be a CSA, a veggie-box program, or a food co-op with a rooftop garden. + + %p The Open Food Network aims to support as many hub models as possible, so no matter your situation, we want to provide the tools you need to run your organisation or local food business. + + .info{ ng: { switch: { default: true } } } + %h3 + Please Choose a Package + %i.icon-arrow-right + + %p + %strong Your producer enterprise will not be fully activated until a package is selected from the options on the left. + + %p + Click on an option to see more detailed information about each package, and hit the red SAVE button when you are done! + + .omega.eight.columns{ ng: { switch: "enterprise.is_primary_producer" } } + %div{ ng: { switch: { when: "false" } } } + %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } } + .top %h3 Profile Only + %p Connect through OFN + .bottom ALWAYS FREE + %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } } + .top + %h3 Hub Shop + %p Sell produce from others + .bottom + \%2 OF SALES + %br + CAPPED AT $50 PER MONTH - %p - %strong COST: ALWAYS FREE - - %p If you prefer to focus on producing food, and want to leave the work of selling it to someone else, you won't require a shop on the Open Food Network. - - %p Producers with a profile can market their produce through the Open Food Network by connecting and trading with existing shops. - - .info{ ng: { show: "enterprise.is_primary_producer && enterprise.sells=='own' && enterprise.producer_profile_only==false" } } + %div{ ng: { switch: { when: "true" } } } + %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } } + .top + %h3 Profile Only + .bottom ALWAYS FREE + %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='own')", class: "{selected: enterprise.sells=='own', disabled: !enterprise.owned}" } } + .top %h3 Producer Shop - - %p - %strong COST: %2 OF SALES, CAPPED AT $50 PER MONTH - - %p A Producer Shop allows producers to offer their products for sale to customers through their very own Open Food Network shop. - - %p Producer Shops may sell produce that has been grown by the producer in question, but do not allow for aggregation of produce from elsewhere. - - .info{ ng: { show: "enterprise.is_primary_producer && enterprise.sells=='any' && enterprise.producer_profile_only==false" } } + %p Sell your own produce + .bottom + \%2 OF SALES + %br + CAPPED AT $50 PER MONTH + %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } } + .top %h3 Producer Hub + %p Sell produce from self and others + .bottom + \%2 OF SALES + %br + CAPPED AT $50 PER MONTH - %p - %strong COST: %2 OF SALES, CAPPED AT $50 PER MONTH - - %p Producer Hubs can take many forms, whether they be a CSA, a veggie-box program, or a food co-op with a rooftop garden. - - %p The Open Food Network aims to support as many hub models as possible, so no matter your situation, we want to provide the tools you need to run your organisation or local food business. - - .omega.eight.columns - %div{ ng: { if: "!enterprise.is_primary_producer"} } - %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } } - .top - %h3 Profile Only - %p Connect through OFN - .bottom ALWAYS FREE - %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } } - .top - %h3 Hub Shop - %p Sell produce from others - .bottom ALWAYS FREE - - %div{ ng: { if: "enterprise.is_primary_producer"} } - %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='none')", class: "{selected: enterprise.sells=='none', disabled: !enterprise.owned}" } } - .top - %h3 Profile Only - .bottom ALWAYS FREE - %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='own')", class: "{selected: enterprise.sells=='own', disabled: !enterprise.owned}" } } - .top - %h3 Producer Shop - %p Sell your own produce - .bottom - \%2 OF SALES - %br - CAPPED AT $50 PER MONTH - %a.button.selector{ ng: { click: "enterprise.owned && (enterprise.sells='any')", class: "{selected: enterprise.sells=='any', disabled: !enterprise.owned}" } } - .top - %h3 Producer Hub - %p Sell produce from self and others - .bottom - \%2 OF SALES - %br - CAPPED AT $50 PER MONTH - - %a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } } - %span{ ng: {hide: "saved() || saving" } } - SAVE - %i.icon-save - %span{ ng: {show: "saved() && !saving" } } - SAVED - %i.icon-ok-sign - %span{ ng: {show: "saving" } } - SAVING - %i.icon-refresh + %a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } } + %span{ ng: {hide: "saved() || saving" } } + SAVE + %i.icon-save + %span{ ng: {show: "saved() && !saving" } } + SAVED + %i.icon-ok-sign + %span{ ng: {show: "saving" } } + SAVING + %i.icon-refresh diff --git a/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml b/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml index c12e6a3af4..03e2fd9b35 100644 --- a/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml +++ b/app/assets/javascripts/templates/admin/panels/enterprise_producer.html.haml @@ -1,44 +1,39 @@ -.enterprise_producer_panel - %form{ name: "type", id: "type", novalidate: true, ng: { controller: 'indexProducerPanelCtrl' } } - -# Have to use hidden:'true' on this input rather than type:'hidden' as the latter seems to break ngPattern and therefore validation - %input{ hidden: "true", name: "is_primary_producer", ng: { required: true, value: "enterprise.is_primary_producer"} } +.row.enterprise_producer_panel{ ng: { controller: 'indexProducerPanelCtrl' } } + + .alpha.eight.columns + .info{ ng: { show: "enterprise.is_primary_producer==true" } } + %h3 Producer + %p Producers make yummy things to eat &/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it. + + %p Producers can also perform other functions, such as aggregating food from other enterprises and selling it through a shop on the Open Food Network. - .row - .alpha.eight.columns - .info{ ng: { show: "enterprise.is_primary_producer==true" } } - %h3 Producer - %p Producers make yummy things to eat &/or drink. You're a producer if you grow it, raise it, brew it, bake it, ferment it, milk it or mould it. + .info{ ng: { show: "enterprise.is_primary_producer==false" } } + %h3 Non-Producer + %p Non-producers do not produce any food themselves, meaning that they cannot create their own products for sale through the Open Food Network. - %p Producers can also perform other functions, such as aggregating food from other enterprises and selling it through a shop on the Open Food Network. + %p Instead, non-producers specialise in linking producers to the end eater, whether it be by aggregating, grading, packing, selling or delivering food. + .omega.eight.columns + %a.button.selector{ ng: { click: 'enterprise.owned && changeToProducer()', class: "{selected: enterprise.is_primary_producer==true, disabled: !enterprise.owned}" } } + .top + %h3 PRODUCER + %p Primary producers of food + .bottom eg. GROWERS, BAKERS, BREWERS, MAKERS - .info{ ng: { show: "enterprise.is_primary_producer==false" } } - %h3 Non-Producer - %p Non-producers do not produce any food themselves, meaning that they cannot create their own products for sale through the Open Food Network. + %a.button.selector{ ng: { click: 'enterprise.owned && changeToNonProducer()', class: "{selected: enterprise.is_primary_producer==false, disabled: !enterprise.owned}" } } + .top + %h3 Non-Producer + %p All other food enterprises + .bottom eg. Grocery stores, Food co-ops, Buying groups - %p Instead, non-producers specialise in linking producers to the end eater, whether it be by aggregating, grading, packing, selling or delivering food. - - .omega.eight.columns - %a.button.selector{ ng: { click: 'enterprise.owned && changeToProducer()', class: "{selected: enterprise.is_primary_producer==true, disabled: !enterprise.owned}" } } - .top - %h3 PRODUCER - %p Primary producers of food - .bottom eg. GROWERS, BAKERS, BREWERS, MAKERS - - %a.button.selector{ ng: { click: 'enterprise.owned && changeToNonProducer()', class: "{selected: enterprise.is_primary_producer==false, disabled: !enterprise.owned}" } } - .top - %h3 Non-Producer - %p All other food enterprises - .bottom eg. Grocery stores, Food co-ops, Buying groups - - %a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } } - %span{ ng: {hide: "saved() || saving" } } - SAVE - %i.icon-save - %span{ ng: {show: "saved() && !saving" } } - SAVED - %i.icon-ok-sign - %span{ ng: {show: "saving" } } - SAVING - %i.icon-refresh + %a.button.update.fullwidth{ ng: { show: "enterprise.owned", class: "{disabled: saved() && !saving, saving: saving}", click: "save()" } } + %span{ ng: {hide: "saved() || saving" } } + SAVE + %i.icon-save + %span{ ng: {show: "saved() && !saving" } } + SAVED + %i.icon-ok-sign + %span{ ng: {show: "saving" } } + SAVING + %i.icon-refresh diff --git a/app/assets/stylesheets/admin/index_panels.css.scss b/app/assets/stylesheets/admin/index_panels.css.scss index 1565a7e5ff..2453a34de5 100644 --- a/app/assets/stylesheets/admin/index_panels.css.scss +++ b/app/assets/stylesheets/admin/index_panels.css.scss @@ -7,10 +7,29 @@ tr.panel-toggle-row { -ms-user-select: none; user-select: none; + position: relative; + i { font-size: 1.2rem; } + &.error { + &::before { + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + display: inline-block; + text-decoration: inherit; + color: #DA5354; + position: absolute; + top: 5px; + right: 5px; + font-size: 2rem; + content: "\f071"; + -webkit-font-smoothing: antialiased + } + } + &.selected { background-color: #ffffff; border-left: 2px solid #444444; diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml index 662e1f6cb0..c48df2505d 100644 --- a/app/views/admin/enterprises/index.html.haml +++ b/app/views/admin/enterprises/index.html.haml @@ -58,10 +58,10 @@ %tr.enterprise.panel-toggle-row{ object: "enterprise", ng: { class: { even: "'even'", odd: "'odd'"} } } %td.name{ ng: { show: 'columns.name.visible' } } %span{ bo: { bind: "enterprise.name" } } - %td.producer.panel-toggle.text-center{ ng: { show: 'columns.producer.visible' }, name: "producer" } - %h5{ ng: { bind: "producerText()" } } - %td.package.panel-toggle.text-center{ ng: { show: 'columns.package.visible' }, name: "package" } - %h5{ ng: { bind: "packageText()" } } + %td.producer.panel-toggle.text-center{ ng: { show: 'columns.producer.visible', class: "{error: producerError}" }, name: "producer" } + %h5{ ng: { bind: "producer" } } + %td.package.panel-toggle.text-center{ ng: { show: 'columns.package.visible', class: "{error: packageError}" }, name: "package" } + %h5{ ng: { bind: "package" } } %td.status.panel-toggle.text-center{ ng: { show: 'columns.status.visible' }, name: "status" } %h5 Status %td.manage{ ng: { show: 'columns.manage.visible' } } diff --git a/spec/javascripts/unit/admin/enterprises/controllers/index_panel_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/index_panel_controller_spec.js.coffee index 2792f33465..b74595b775 100644 --- a/spec/javascripts/unit/admin/enterprises/controllers/index_panel_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/controllers/index_panel_controller_spec.js.coffee @@ -21,6 +21,7 @@ describe "indexPanelCtrl", -> beforeEach inject ($q) -> spyOn(scope, "saved").andReturn false + spyOn(scope, "$emit") deferred = $q.defer() spyOn(Enterprises, "save").andReturn(deferred.promise) scope.save() @@ -33,10 +34,12 @@ describe "indexPanelCtrl", -> deferred.resolve() $rootScope.$digest() - it "sets scope.saving to false", -> expect(scope.saving).toBe false + it "emits an 'enterprise:updated' event", -> + expect(scope.$emit).toHaveBeenCalledWith("enterprise:updated") + describe "when the save is unsuccessful", -> beforeEach inject ($rootScope) -> deferred.reject({ status: 404 }) @@ -44,3 +47,6 @@ describe "indexPanelCtrl", -> it "sets scope.saving to false", -> expect(scope.saving).toBe false + + it "does not emit an 'enterprise:updated' event", -> + expect(scope.$emit).not.toHaveBeenCalled()