diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js index f8d3ceb721..7dc9d1116f 100644 --- a/app/assets/javascripts/admin/all.js +++ b/app/assets/javascripts/admin/all.js @@ -22,6 +22,7 @@ //= require ./payment_methods/payment_methods //= require ./products/products //= require ./shipping_methods/shipping_methods +//= require ./utils/utils //= require ./users/users //= require textAngular.min.js //= require textAngular-sanitize.min.js diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee index 46eb4a84c1..cfcf6319af 100644 --- a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee +++ b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee @@ -1,10 +1,18 @@ angular.module("admin.enterprises") - .controller "enterpriseCtrl", ($scope, Enterprise, longDescription, PaymentMethods, ShippingMethods) -> + .controller "enterpriseCtrl", ($scope, longDescription, NavigationCheck, Enterprise, PaymentMethods, ShippingMethods) -> $scope.Enterprise = Enterprise.enterprise $scope.PaymentMethods = PaymentMethods.paymentMethods $scope.ShippingMethods = ShippingMethods.shippingMethods + $scope.navClear = NavigationCheck.clear + # htmlVariable is used by textAngular wysiwyg for the long descrtiption. $scope.htmlVariable = longDescription + # Provide a callback for generating warning messages displayed before leaving the page. This is passed in + # from a directive "nav-check" in the page - if we pass it here it will be called in the test suite, + # and on all new uses of this contoller, and we might not want that . + $scope.enterpriseNavCallback = -> + "You are editing an enterprise!" + for payment_method in $scope.PaymentMethods payment_method.selected = payment_method.id in $scope.Enterprise.payment_method_ids diff --git a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee index 9b67bc14f4..e1e43854d1 100644 --- a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee +++ b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee @@ -1 +1 @@ -angular.module("admin.enterprises", ["admin.payment_methods", "admin.shipping_methods", "admin.users", "textAngular"]) \ No newline at end of file +angular.module("admin.enterprises", ["admin.payment_methods", "admin.utils", "admin.shipping_methods", "admin.users", "textAngular"]) \ No newline at end of file diff --git a/app/assets/javascripts/admin/enterprises/services/enterprise.js.coffee b/app/assets/javascripts/admin/enterprises/services/enterprise.js.coffee index b6e6a6147e..26061ef720 100644 --- a/app/assets/javascripts/admin/enterprises/services/enterprise.js.coffee +++ b/app/assets/javascripts/admin/enterprises/services/enterprise.js.coffee @@ -1,4 +1,5 @@ angular.module("admin.enterprises") + # Populate Enterprise.enterprise with enterprise json array from the page. .factory 'Enterprise', (enterprise) -> new class Enterprise - enterprise: enterprise \ No newline at end of file + enterprise: enterprise diff --git a/app/assets/javascripts/admin/utils/directives/navigation_check.js.coffee b/app/assets/javascripts/admin/utils/directives/navigation_check.js.coffee new file mode 100644 index 0000000000..95f52505eb --- /dev/null +++ b/app/assets/javascripts/admin/utils/directives/navigation_check.js.coffee @@ -0,0 +1,9 @@ +angular.module("admin.utils").directive "navCheck", (NavigationCheck)-> + restrict: 'A' + scope: + navCallback: '&' + link: (scope,element,attributes) -> + # Define navigationCallback on a controller in scope, otherwise this default will be used: + scope.navCallback ||= -> + "You will lose any unsaved work!" + NavigationCheck.register(scope.navCallback) diff --git a/app/assets/javascripts/admin/utils/services/navigation_check.js.coffee b/app/assets/javascripts/admin/utils/services/navigation_check.js.coffee new file mode 100644 index 0000000000..ff1041474c --- /dev/null +++ b/app/assets/javascripts/admin/utils/services/navigation_check.js.coffee @@ -0,0 +1,46 @@ +angular.module("admin.utils") + .factory "NavigationCheck", ($window, $rootScope) -> + new class NavigationCheck + callbacks = [] + constructor: -> + if $window.addEventListener + $window.addEventListener "beforeunload", @onBeforeUnloadHandler + else + $window.onbeforeunload = @onBeforeUnloadHandler + + $rootScope.$on "$locationChangeStart", @locationChangeStartHandler + + + # Action for regular browser navigation. + onBeforeUnloadHandler: ($event) => + message = @getMessage() + if message + ($event or $window.event).preventDefault() + message + + # Action for angular navigation. + locationChangeStartHandler: ($event) => + message = @getMessage() + if message and not $window.confirm(message) + $event.stopPropagation() if $event.stopPropagation + $event.preventDefault() if $event.preventDefault + $event.cancelBubble = true + $event.returnValue = false + + # Runs callback functions to retreive most recently added non-empty message. + getMessage: -> + message = null + message = callback() ? message for callback in callbacks + message + + register: (callback) => + callbacks.push callback + + clear: => + if $window.addEventListener + $window.removeEventListener "beforeunload", @onBeforeUnloadHandler + else + $window.onbeforeunload = null + + $rootScope.$on "$locationChangeStart", null + diff --git a/app/assets/javascripts/admin/utils/utils.js.coffee b/app/assets/javascripts/admin/utils/utils.js.coffee new file mode 100644 index 0000000000..4d58ae930a --- /dev/null +++ b/app/assets/javascripts/admin/utils/utils.js.coffee @@ -0,0 +1 @@ +angular.module("admin.utils", []) \ No newline at end of file diff --git a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee similarity index 63% rename from app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee rename to app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee index b44d63eadf..1e43c17465 100644 --- a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee @@ -1,13 +1,14 @@ -Darkswarm.controller "HubsCtrl", ($scope, Hubs, Search, $document, $rootScope, HashNavigation, FilterSelectorsService, MapModal) -> - $scope.Hubs = Hubs - $scope.hubs = Hubs.visible +Darkswarm.controller "EnterprisesCtrl", ($scope, Enterprises, Search, $document, $rootScope, HashNavigation, FilterSelectorsService, EnterpriseModal) -> + $scope.Enterprises = Enterprises $scope.totalActive = FilterSelectorsService.totalActive $scope.clearAll = FilterSelectorsService.clearAll $scope.filterText = FilterSelectorsService.filterText $scope.FilterSelectorsService = FilterSelectorsService $scope.query = Search.search() + $scope.openModal = EnterpriseModal.open + $scope.activeTaxons = [] $scope.show_profiles = false - $scope.openModal = MapModal.open + $scope.filtersActive = false $scope.$watch "query", (query)-> Search.search query diff --git a/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee deleted file mode 100644 index b16dcdfa77..0000000000 --- a/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee +++ /dev/null @@ -1,14 +0,0 @@ -Darkswarm.controller "ProducersCtrl", ($scope, Producers, $filter, FilterSelectorsService, Search, MapModal) -> - $scope.Producers = Producers - $scope.totalActive = FilterSelectorsService.totalActive - $scope.clearAll = FilterSelectorsService.clearAll - $scope.filterText = FilterSelectorsService.filterText - $scope.FilterSelectorsService = FilterSelectorsService - $scope.filtersActive = false - $scope.activeTaxons = [] - $scope.query = Search.search() - $scope.show_profiles = false - $scope.openModal = MapModal.open - - $scope.$watch "query", (query)-> - Search.search query diff --git a/app/assets/javascripts/darkswarm/controllers/registration_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee similarity index 84% rename from app/assets/javascripts/darkswarm/controllers/registration_controller.js.coffee rename to app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee index 2fcc0f32fd..3be0378619 100644 --- a/app/assets/javascripts/darkswarm/controllers/registration_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee @@ -3,7 +3,7 @@ Darkswarm.controller "RegistrationCtrl", ($scope, RegistrationService, Enterpris $scope.enterprise = EnterpriseRegistrationService.enterprise $scope.select = RegistrationService.select - $scope.steps = ['details','address','contact','about','images','social'] + $scope.steps = ['details','contact','type','about','images','social'] $scope.countries = availableCountries diff --git a/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/registration/registration_form_controller.js.coffee similarity index 100% rename from app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee rename to app/assets/javascripts/darkswarm/controllers/registration/registration_form_controller.js.coffee diff --git a/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee index 6db73f7af3..7a5ebb78f3 100644 --- a/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee @@ -1,3 +1,4 @@ -Darkswarm.controller "ProducersTabCtrl", ($scope, CurrentHub, Enterprises) -> - # Injecting Enterprises so CurrentHub.producers is dereferenced +Darkswarm.controller "ProducersTabCtrl", ($scope, CurrentHub, Enterprises, EnterpriseModal) -> + # Injecting Enterprises so CurrentHub.producers is dereferenced. + # We should probably dereference here instead and separate out CurrentHub dereferencing from the Enterprise factory. $scope.CurrentHub = CurrentHub diff --git a/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee index eb501bcb4a..0bbe0bbd23 100644 --- a/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee @@ -1,18 +1,11 @@ Darkswarm.controller "TabsCtrl", ($scope, $rootScope, $location, OrderCycle) -> + # Return active if supplied path matches url hash path. $scope.active = (path)-> - if !OrderCycle.selected() and $location.hash() == "" and path == "about" - true - else - $location.hash() == path + $location.hash() == path - - $scope.tabs = ["contact", "about", "groups", "producers"] - for tab in $scope.tabs - $scope[tab] = - path: tab - - $scope.select = (tab)-> - if $scope.active(tab.path) + # Toggle tab selected status by setting the url hash path. + $scope.select = (path)-> + if $scope.active(path) $location.hash "" else - $location.hash tab.path + $location.hash path diff --git a/app/assets/javascripts/darkswarm/directives/producer_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee similarity index 71% rename from app/assets/javascripts/darkswarm/directives/producer_modal.js.coffee rename to app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee index af2b13f157..fa6378afe2 100644 --- a/app/assets/javascripts/darkswarm/directives/producer_modal.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.directive "producerModal", ($modal)-> +Darkswarm.directive "enterpriseModal", ($modal)-> restrict: 'E' replace: true template: "" @@ -6,5 +6,4 @@ Darkswarm.directive "producerModal", ($modal)-> link: (scope, elem, attrs, ctrl)-> elem.on "click", (ev)=> ev.stopPropagation() - scope.modalInstance = $modal.open(controller: ctrl, templateUrl: 'producer_modal.html', scope: scope) - + scope.modalInstance = $modal.open(controller: ctrl, templateUrl: 'enterprise_modal.html', scope: scope) diff --git a/app/assets/javascripts/darkswarm/directives/hub_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/hub_modal.js.coffee deleted file mode 100644 index 6eb0299ab4..0000000000 --- a/app/assets/javascripts/darkswarm/directives/hub_modal.js.coffee +++ /dev/null @@ -1,8 +0,0 @@ -Darkswarm.directive "hubModal", ($modal)-> - restrict: 'E' - replace: true - template: "{{enterprise.name}}" - link: (scope, elem, attrs, ctrl)-> - elem.on "click", (ev)=> - ev.stopPropagation() - scope.modalInstance = $modal.open(controller: ctrl, templateUrl: 'hub_modal.html', scope: scope) diff --git a/app/assets/javascripts/darkswarm/directives/inline_alert.js.coffee b/app/assets/javascripts/darkswarm/directives/inline_alert.js.coffee new file mode 100644 index 0000000000..9b37934375 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/inline_alert.js.coffee @@ -0,0 +1,7 @@ +Darkswarm.directive "ofnInlineAlert", -> + restrict: 'A' + scope: true + link: (scope, elem, attrs) -> + scope.visible = true + scope.close = -> + scope.visible = false diff --git a/app/assets/javascripts/darkswarm/directives/inline_flash.js.coffee b/app/assets/javascripts/darkswarm/directives/inline_flash.js.coffee deleted file mode 100644 index 46550b854f..0000000000 --- a/app/assets/javascripts/darkswarm/directives/inline_flash.js.coffee +++ /dev/null @@ -1,6 +0,0 @@ -Darkswarm.directive "ofnInlineFlash", -> - restrict: 'E' - controller: ($scope) -> - $scope.visible = true - $scope.closeFlash = -> - $scope.visible = false diff --git a/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee index 1fecfbf804..2b38b3d31f 100644 --- a/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee @@ -3,7 +3,7 @@ Darkswarm.directive "ofnRegistrationLimitModal", (Navigation, $modal, Loading) - link: (scope, elem, attr)-> scope.modalInstance = $modal.open templateUrl: 'registration/limit_reached.html' - windowClass: "login-modal large" + windowClass: "login-modal register-modal xlarge" backdrop: 'static' scope.modalInstance.result.then scope.close, scope.close diff --git a/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee b/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee index acbd3fd637..5c2272135b 100644 --- a/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee @@ -1,4 +1,5 @@ Darkswarm.filter "capitalize", -> + # Convert to basic sentence case. (input, scope) -> - input = input.toLowerCase() if input? + input = input.toLowerCase() if input? input.substring(0, 1).toUpperCase() + input.substring(1) diff --git a/app/assets/javascripts/darkswarm/filters/filter_hubs.js.coffee b/app/assets/javascripts/darkswarm/filters/filter_hubs.js.coffee deleted file mode 100644 index b6adb84508..0000000000 --- a/app/assets/javascripts/darkswarm/filters/filter_hubs.js.coffee +++ /dev/null @@ -1,9 +0,0 @@ -Darkswarm.filter 'hubs', (Matcher)-> - (hubs, text) -> - hubs ||= [] - text ?= "" - - hubs.filter (hub)=> - Matcher.match [ - hub.name, hub.address.zipcode, hub.address.city, hub.address.state - ], text diff --git a/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee b/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee deleted file mode 100644 index ed5b8c1bea..0000000000 --- a/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee +++ /dev/null @@ -1,6 +0,0 @@ -Darkswarm.filter 'filterProducers', (hubsFilter)-> - (producers, text) -> - producers ||= [] - text ?= "" - hubsFilter(producers, text) - diff --git a/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee b/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee index ffa98c5a33..7087e09fc3 100644 --- a/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee @@ -1,15 +1,14 @@ -# Convert number to string currency using injected currency configuration. -# -# @requires currencyConfig json - /app/serializers/api/currency_config_serializer.rb -# @return: string Darkswarm.filter "localizeCurrency", (currencyConfig)-> + # Convert number to string currency using injected currency configuration. (amount) -> + # Set country code (eg. "US"). currency_code = if currencyConfig.display_currency then " " + currencyConfig.currency else "" + # Set decimal points, 2 or 0 if hide_cents. decimals = if currencyConfig.hide_cents == "true" then 0 else 2 - # We need to use parseFloat before toFixed as the amount should be a passed in as a string. + # We need to use parseFloat before toFixed as the amount should come in as a string. amount_fixed = parseFloat(amount).toFixed(decimals) - # Build the final price string. + # Build the final price string. TODO use spree decimal point and spacer character settings. if currencyConfig.symbol_position == 'before' currencyConfig.symbol + amount_fixed + currency_code else diff --git a/app/assets/javascripts/darkswarm/filters/search_enterprises.js.coffee b/app/assets/javascripts/darkswarm/filters/search_enterprises.js.coffee new file mode 100644 index 0000000000..2c42fe93d3 --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/search_enterprises.js.coffee @@ -0,0 +1,10 @@ +Darkswarm.filter 'searchEnterprises', (Matcher)-> + # Search multiple fields of enterprises for matching text fragment. + (enterprises, text) -> + enterprises ||= [] + text ?= "" + + enterprises.filter (enterprise)=> + Matcher.match [ + enterprise.name, enterprise.address.zipcode, enterprise.address.city, enterprise.address.state + ], text diff --git a/app/assets/javascripts/darkswarm/filters/show_hub_profiles.js.coffee b/app/assets/javascripts/darkswarm/filters/show_hub_profiles.js.coffee new file mode 100644 index 0000000000..b97a0c35e4 --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/show_hub_profiles.js.coffee @@ -0,0 +1,8 @@ +Darkswarm.filter 'showHubProfiles', ()-> + # Filter hub_profile enterprises in or out. + (enterprises, show_profiles) -> + enterprises ||= [] + show_profiles ?= false + + enterprises.filter (enterprise)=> + show_profiles or enterprise.is_distributor diff --git a/app/assets/javascripts/darkswarm/filters/show_profiles.js.coffee b/app/assets/javascripts/darkswarm/filters/show_profiles.js.coffee deleted file mode 100644 index e3fce7d2f6..0000000000 --- a/app/assets/javascripts/darkswarm/filters/show_profiles.js.coffee +++ /dev/null @@ -1,7 +0,0 @@ -Darkswarm.filter 'showProfiles', ()-> - (enterprises, show_profiles) -> - enterprises ||= [] - show_profiles ?= true - - enterprises.filter (enterprise)=> - show_profiles or enterprise.has_shopfront diff --git a/app/assets/javascripts/darkswarm/filters/taxons.js.coffee b/app/assets/javascripts/darkswarm/filters/taxons.js.coffee index 3eb32ce1a4..a7ccc9b0cc 100644 --- a/app/assets/javascripts/darkswarm/filters/taxons.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/taxons.js.coffee @@ -1,13 +1,16 @@ -Darkswarm.filter 'taxons', (Matcher)-> - # Filter anything that responds to object.taxons, and/or object.primary_taxon +Darkswarm.filter 'taxons', ()-> + # Filter anything that responds to object.taxons, object.supplied_taxon or object.primary_taxon. (objects, ids) -> objects ||= [] ids ?= [] if ids.length == 0 + # No taxons selected, pass all objects through. objects else objects.filter (obj)-> taxons = obj.taxons - taxons.concat obj.supplied_taxons if obj.supplied_taxons + # Combine object taxons with supplied taxons, if they exist. + taxons = taxons.concat obj.supplied_taxons if obj.supplied_taxons + # Match primary taxon if it exists, then taxon array. obj.primary_taxon?.id in ids || taxons.some (taxon)-> taxon.id in ids diff --git a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee index 5f5ae520e3..f2d7049e67 100644 --- a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee +++ b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redirections)-> +Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redirections, Loading)-> new class AuthenticationService selectedPath: "/login" @@ -25,4 +25,9 @@ Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redir active: Navigation.active close: -> - Navigation.navigate "/" + if location.pathname == "/" + Navigation.navigate "/" + else + Loading.message = "Taking you back to the home page" + location.hash = "" + location.pathname = "/" diff --git a/app/assets/javascripts/darkswarm/services/current_hub.js.coffee b/app/assets/javascripts/darkswarm/services/current_hub.js.coffee index 5e6a385b8a..ade8d75d18 100644 --- a/app/assets/javascripts/darkswarm/services/current_hub.js.coffee +++ b/app/assets/javascripts/darkswarm/services/current_hub.js.coffee @@ -1,3 +1,4 @@ -Darkswarm.factory 'CurrentHub', ($location, $filter, currentHub) -> +Darkswarm.factory 'CurrentHub', (currentHub) -> + # Populate CurrentHub.hub from json in page. This is probably redundant now. new class CurrentHub hub: currentHub diff --git a/app/assets/javascripts/darkswarm/services/current_order.js.coffee b/app/assets/javascripts/darkswarm/services/current_order.js.coffee index cd2402f0c0..6570ddcd06 100644 --- a/app/assets/javascripts/darkswarm/services/current_order.js.coffee +++ b/app/assets/javascripts/darkswarm/services/current_order.js.coffee @@ -1,3 +1,4 @@ Darkswarm.factory 'CurrentOrder', (currentOrder) -> + # Populate Currentorder.order from json in page. This is probably redundant now. new class CurrentOrder order: currentOrder diff --git a/app/assets/javascripts/darkswarm/services/enterprise_modal.js.coffee b/app/assets/javascripts/darkswarm/services/enterprise_modal.js.coffee new file mode 100644 index 0000000000..a0fb3ce5c5 --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/enterprise_modal.js.coffee @@ -0,0 +1,8 @@ +Darkswarm.factory "EnterpriseModal", ($modal, $rootScope)-> + # Build a modal popup for an enterprise. + new class EnterpriseModal + open: (enterprise)-> + scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise + + scope.enterprise = enterprise + $modal.open(templateUrl: "enterprise_modal.html", scope: scope) diff --git a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee index 50143e006b..6040f1d150 100644 --- a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee +++ b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee @@ -1,13 +1,21 @@ -Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer)-> +Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter)-> new class Enterprises - enterprises_by_id: {} # id/object pairs for lookup + enterprises_by_id: {} constructor: -> + # Populate Enterprises.enterprises from json in page. @enterprises = enterprises + # Map enterprises to id/object pairs for lookup. for enterprise in enterprises @enterprises_by_id[enterprise.id] = enterprise + # Replace enterprise and taxons ids with actual objects. @dereferenceEnterprises() @dereferenceTaxons() - + @visible_enterprises = visibleFilter @enterprises + @producers = @visible_enterprises.filter (enterprise)-> + enterprise.category in ["producer_hub", "producer_shop", "producer"] + @hubs = @visible_enterprises.filter (enterprise)-> + enterprise.category in ["hub", "hub_profile", "producer_hub", "producer_shop"] + dereferenceEnterprises: -> if CurrentHub.hub?.id CurrentHub.hub = @enterprises_by_id[CurrentHub.hub.id] @@ -16,6 +24,7 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer) Dereferencer.dereference enterprise.producers, @enterprises_by_id dereferenceTaxons: -> - for enterprise in @enterprises + for enterprise in @enterprises Dereferencer.dereference enterprise.taxons, Taxons.taxons_by_id Dereferencer.dereference enterprise.supplied_taxons, Taxons.taxons_by_id + diff --git a/app/assets/javascripts/darkswarm/services/hubs.js.coffee b/app/assets/javascripts/darkswarm/services/hubs.js.coffee deleted file mode 100644 index ba3d9f3c24..0000000000 --- a/app/assets/javascripts/darkswarm/services/hubs.js.coffee +++ /dev/null @@ -1,9 +0,0 @@ -Darkswarm.factory 'Hubs', ($filter, Enterprises, visibleFilter) -> - new class Hubs - constructor: -> - @hubs = @order Enterprises.enterprises.filter (hub)-> - hub.has_hub_listing - @visible = visibleFilter @hubs - - order: (hubs)-> - $filter('orderBy')(hubs, ['-active', '+orders_close_at']) diff --git a/app/assets/javascripts/darkswarm/services/map.js.coffee b/app/assets/javascripts/darkswarm/services/map.js.coffee index 43750acdb2..703c3c54bf 100644 --- a/app/assets/javascripts/darkswarm/services/map.js.coffee +++ b/app/assets/javascripts/darkswarm/services/map.js.coffee @@ -1,7 +1,7 @@ -Darkswarm.factory "OfnMap", (Enterprises, MapModal, visibleFilter)-> +Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter)-> new class OfnMap constructor: -> - @enterprises = (@extend(enterprise) for enterprise in visibleFilter(Enterprises.enterprises)) + @enterprises = (@extend(enterprise) for enterprise in visibleFilter(Enterprises.enterprises)) # Adding methods to each enterprise @@ -14,4 +14,4 @@ Darkswarm.factory "OfnMap", (Enterprises, MapModal, visibleFilter)-> icon: enterprise.icon id: enterprise.id reveal: => - MapModal.open enterprise + EnterpriseModal.open enterprise diff --git a/app/assets/javascripts/darkswarm/services/map_modal.js.coffee b/app/assets/javascripts/darkswarm/services/map_modal.js.coffee deleted file mode 100644 index c9ed30f558..0000000000 --- a/app/assets/javascripts/darkswarm/services/map_modal.js.coffee +++ /dev/null @@ -1,12 +0,0 @@ -Darkswarm.factory "MapModal", ($modal, $rootScope)-> - new class MapModal - open: (enterprise)-> - scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise - - scope.enterprise = enterprise - if enterprise.is_distributor - scope.hub = enterprise - $modal.open(templateUrl: "hub_modal.html", scope: scope) - else - scope.producer = enterprise - $modal.open(templateUrl: "map_modal_producer.html", scope: scope) diff --git a/app/assets/javascripts/darkswarm/services/matcher.js.coffee b/app/assets/javascripts/darkswarm/services/matcher.js.coffee index aadd6a4128..9360afdd1f 100644 --- a/app/assets/javascripts/darkswarm/services/matcher.js.coffee +++ b/app/assets/javascripts/darkswarm/services/matcher.js.coffee @@ -1,6 +1,7 @@ Darkswarm.factory "Matcher", -> - new class Matcher - match: (properties, text)-> - properties.some (prop)-> - prop ||= "" - prop.toLowerCase().indexOf(text.toLowerCase()) != -1 + # Match text fragment in an array of strings. + new class Matcher + match: (properties, text)-> + properties.some (prop)-> + prop ||= "" + prop.toLowerCase().indexOf(text.toLowerCase()) != -1 diff --git a/app/assets/javascripts/darkswarm/services/producers.js.coffee b/app/assets/javascripts/darkswarm/services/producers.js.coffee deleted file mode 100644 index 65d8e42c5d..0000000000 --- a/app/assets/javascripts/darkswarm/services/producers.js.coffee +++ /dev/null @@ -1,7 +0,0 @@ -Darkswarm.factory 'Producers', (Enterprises, visibleFilter) -> - new class Producers - constructor: -> - @producers = Enterprises.enterprises.filter (enterprise)-> - enterprise.is_primary_producer - @visible = visibleFilter @producers - diff --git a/app/assets/javascripts/darkswarm/services/taxons.js.coffee b/app/assets/javascripts/darkswarm/services/taxons.js.coffee index cbe6c118e0..984eb0df22 100644 --- a/app/assets/javascripts/darkswarm/services/taxons.js.coffee +++ b/app/assets/javascripts/darkswarm/services/taxons.js.coffee @@ -1,8 +1,10 @@ Darkswarm.factory "Taxons", (taxons)-> new class Taxons - taxons: taxons + # Populate Taxons.taxons from json in page. + taxons: taxons taxons_by_id: {} constructor: -> + # Map taxons to id/object pairs for lookup. for taxon in @taxons @taxons_by_id[taxon.id] = taxon diff --git a/app/assets/javascripts/darkswarm/services/variants.js.coffee b/app/assets/javascripts/darkswarm/services/variants.js.coffee index bc2050e4d4..6562bd9e0b 100644 --- a/app/assets/javascripts/darkswarm/services/variants.js.coffee +++ b/app/assets/javascripts/darkswarm/services/variants.js.coffee @@ -5,6 +5,7 @@ Darkswarm.factory 'Variants', -> @variants[variant.id] ||= @extend variant extend: (variant)-> + # Add totalPrice method to calculate line item total. This should be on a line item! variant.totalPrice = -> variant.price_with_fees * variant.line_item.quantity variant.basePricePercentage = Math.round(variant.price / variant.price_with_fees * 100) diff --git a/app/assets/javascripts/search/all.js b/app/assets/javascripts/search/all.js deleted file mode 100644 index c684eed4b1..0000000000 --- a/app/assets/javascripts/search/all.js +++ /dev/null @@ -1,18 +0,0 @@ -// This is a manifest file that'll be compiled into including all the files listed below. -// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically -// be included in the compiled file accessible from http://example.com/assets/application.js -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// the compiled file. -// - -//= require jquery -//= require jquery_ujs -//= require jquery-ui -//= require spin -//= require foundation -//= require_tree . -// - -// Hacky fix for issue - http://foundation.zurb.com/forum/posts/2112-foundation-5100-syntax-error-in-js -Foundation.set_namespace = function() {}; -$(function(){ $(document).foundation(); }); diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee deleted file mode 100644 index 684215a438..0000000000 --- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee +++ /dev/null @@ -1,444 +0,0 @@ -Gmaps = {} - -Gmaps.triggerOldOnload = -> - Gmaps.oldOnload() if typeof(Gmaps.oldOnload) == 'function' - -Gmaps.loadMaps = -> - #loop through all variable names. - #there should only be maps inside so it trigger their load function - for key, value of Gmaps - searchLoadIncluded = key.search(/load/) - if searchLoadIncluded == -1 - load_function_name = "load_" + key - Gmaps[load_function_name]() - -window.Gmaps = Gmaps - -class @Gmaps4Rails - - constructor: -> - #map config - @map = null #DEPRECATED: will still contain a copy of serviceObject below as transition - @serviceObject = null #contains the map we're working on - @visibleInfoWindow = null #contains the current opened infowindow - @userLocation = null #contains user's location if geolocalization was performed and successful - - #empty slots - @geolocationSuccess = -> false #triggered when geolocation succeeds. Can be customized. - @geolocationFailure = -> false #triggered when geolocation fails. If customized, must be like= function(navigator_handles_geolocation){} where 'navigator_handles_geolocation' is a boolean - @callback = -> false #to let user set a custom callback function - @customClusterer = -> false #to let user set custom clusterer pictures - @infobox = -> false #to let user use custom infoboxes - @jsTemplate = false #to let user create infowindows client side - - @default_map_options = - id: 'map' - draggable: true - detect_location: false # should the browser attempt to use geolocation detection features of HTML5? - center_on_user: false # centers map on the location detected through the browser - center_latitude: 0 - center_longitude: 0 - zoom: 7 - maxZoom: null - minZoom: null - auto_adjust : true # adjust the map to the markers if set to true - auto_zoom: true # zoom given by auto-adjust - bounds: [] # adjust map to these limits. Should be [{"lat": , "lng": }] - raw: {} # raw json to pass additional options - - @default_markers_conf = - #Marker config - title: "" - #MarkerImage config - picture : "" - width: 22 - length: 32 - draggable: false # how to modify: <%= gmaps( "markers" => { "data" => @object.to_gmaps4rails, "options" => { "draggable" => true }}) %> - #clustering config - do_clustering: false # do clustering if set to true - randomize: false # Google maps can't display two markers which have the same coordinates. This randomizer enables to prevent this situation from happening. - max_random_distance: 100 # in meters. Each marker coordinate could be altered by this distance in a random direction - list_container: null # id of the ul that will host links to all markers - offset: 0 # used when adding_markers to an existing map. Because new markers are concated with previous one, offset is here to prevent the existing from being re-created. - raw: {} # raw json to pass additional options - - #Stored variables - @markers = [] # contains all markers. A marker contains the following: {"description": , "longitude": , "title":, "latitude":, "picture": "", "width": "", "length": "", "sidebar": "", "serviceObject": google_marker} - @boundsObject = null # contains current bounds from markers, polylines etc... - @polygons = [] # contains raw data, array of arrays (first element could be a hash containing options) - @polylines = [] # contains raw data, array of arrays (first element could be a hash containing options) - @circles = [] # contains raw data, array of hash - @markerClusterer = null # contains all marker clusterers - @markerImages = [] - - #Polyline Styling - @polylines_conf = #default style for polylines - strokeColor: "#FF0000" - strokeOpacity: 1 - strokeWeight: 2 - clickable: false - zIndex: null - - #tnitializes the map - initialize : -> - @serviceObject = @createMap() - @map = @serviceObject #beware, soon deprecated - if (@map_options.detect_location == true or @map_options.center_on_user == true) - @findUserLocation(this) - #resets sidebar if needed - @resetSidebarContent() - - findUserLocation : (map_object) -> - if (navigator.geolocation) - #try to retrieve user's position - positionSuccessful = (position) -> - map_object.userLocation = map_object.createLatLng(position.coords.latitude, position.coords.longitude) - #change map's center to focus on user's geoloc if asked - if(map_object.map_options.center_on_user == true) - map_object.centerMapOnUser() - map_object.geolocationSuccess() - positionFailure = -> - map_object.geolocationFailure(true) - - navigator.geolocation.getCurrentPosition( positionSuccessful, positionFailure) - else - #failure but the navigator doesn't handle geolocation - map_object.geolocationFailure(false) - - - #//////////////////////////////////////////////////// - #//////////////////// DIRECTIONS //////////////////// - #//////////////////////////////////////////////////// - - create_direction : -> - directionsDisplay = new google.maps.DirectionsRenderer() - directionsService = new google.maps.DirectionsService() - - directionsDisplay.setMap(@serviceObject) - #display panel only if required - if @direction_conf.display_panel - directionsDisplay.setPanel(document.getElementById(@direction_conf.panel_id)) - - directionsDisplay.setOptions - suppressMarkers: false - suppressInfoWindows: false - suppressPolylines: false - - request = - origin: @direction_conf.origin - destination: @direction_conf.destination - waypoints: @direction_conf.waypoints - optimizeWaypoints: @direction_conf.optimizeWaypoints - unitSystem: google.maps.DirectionsUnitSystem[@direction_conf.unitSystem] - avoidHighways: @direction_conf.avoidHighways - avoidTolls: @direction_conf.avoidTolls - region: @direction_conf.region - travelMode: google.maps.DirectionsTravelMode[@direction_conf.travelMode] - language: "en" - - directionsService.route request, (response, status) -> - if (status == google.maps.DirectionsStatus.OK) - directionsDisplay.setDirections(response) - - #//////////////////////////////////////////////////// - #///////////////////// CIRCLES ////////////////////// - #//////////////////////////////////////////////////// - - #Loops through all circles - #Loops through all circles and draws them - create_circles : -> - for circle in @circles - @create_circle circle - - create_circle : (circle) -> - #by convention, default style configuration could be integrated in the first element - if circle == @circles[0] - @circles_conf.strokeColor = circle.strokeColor if circle.strokeColor? - @circles_conf.strokeOpacity = circle.strokeOpacity if circle.strokeOpacity? - @circles_conf.strokeWeight = circle.strokeWeight if circle.strokeWeight? - @circles_conf.fillColor = circle.fillColor if circle.fillColor? - @circles_conf.fillOpacity = circle.fillOpacity if circle.fillOpacity? - - if circle.lat? and circle.lng? - # always check if a config is given, if not, use defaults - # NOTE: is there a cleaner way to do this? Maybe a hash merge of some sort? - newCircle = new google.maps.Circle - center: @createLatLng(circle.lat, circle.lng) - strokeColor: circle.strokeColor || @circles_conf.strokeColor - strokeOpacity: circle.strokeOpacity || @circles_conf.strokeOpacity - strokeWeight: circle.strokeWeight || @circles_conf.strokeWeight - fillOpacity: circle.fillOpacity || @circles_conf.fillOpacity - fillColor: circle.fillColor || @circles_conf.fillColor - clickable: circle.clickable || @circles_conf.clickable - zIndex: circle.zIndex || @circles_conf.zIndex - radius: circle.radius - - circle.serviceObject = newCircle - newCircle.setMap(@serviceObject) - - # clear circles - clear_circles : -> - for circle in @circles - @clear_circle circle - - clear_circle : (circle) -> - circle.serviceObject.setMap(null) - - hide_circles : -> - for circle in @circles - @hide_circle circle - - hide_circle : (circle) -> - circle.serviceObject.setMap(null) - - show_circles : -> - for circle in @circles - @show_circle @circle - - show_circle : (circle) -> - circle.serviceObject.setMap(@serviceObject) - - #//////////////////////////////////////////////////// - #///////////////////// POLYGONS ///////////////////// - #//////////////////////////////////////////////////// - - #polygons is an array of arrays. It loops. - create_polygons : -> - for polygon in @polygons - @create_polygon(polygon) - - #creates a single polygon, triggered by create_polygons - create_polygon : (polygon) -> - polygon_coordinates = [] - - #Polygon points are in an Array, that's why looping is necessary - for point in polygon - latlng = @createLatLng(point.lat, point.lng) - polygon_coordinates.push(latlng) - #first element of an Array could contain specific configuration for this particular polygon. If no config given, use default - if point == polygon[0] - strokeColor = point.strokeColor || @polygons_conf.strokeColor - strokeOpacity = point.strokeOpacity || @polygons_conf.strokeOpacity - strokeWeight = point.strokeWeight || @polygons_conf.strokeWeight - fillColor = point.fillColor || @polygons_conf.fillColor - fillOpacity = point.fillOpacity || @polygons_conf.fillOpacity - clickable = point.clickable || @polygons_conf.clickable - - #Construct the polygon - new_poly = new google.maps.Polygon - paths: polygon_coordinates - strokeColor: strokeColor - strokeOpacity: strokeOpacity - strokeWeight: strokeWeight - fillColor: fillColor - fillOpacity: fillOpacity - clickable: clickable - map: @serviceObject - - #save polygon in list - polygon.serviceObject = new_poly - - - - #//////////////////////////////////////////////////// - #///////////////////// MARKERS ////////////////////// - #//////////////////////////////////////////////////// - - #creates, clusterizes and adjusts map - create_markers : -> - @createServiceMarkersFromMarkers() - @clusterize() - - #create google.maps Markers from data provided by user - createServiceMarkersFromMarkers : -> - for marker, index in @markers - if not @markers[index].serviceObject? - #extract options, test if value passed or use default - Lat = @markers[index].lat - Lng = @markers[index].lng - - #alter coordinates if randomize is true - if @markers_conf.randomize - LatLng = @randomize(Lat, Lng) - #retrieve coordinates from the array - Lat = LatLng[0] - Lng = LatLng[1] - - #save object - @markers[index].serviceObject = @createMarker - "marker_picture": if @markers[index].picture then @markers[index].picture else @markers_conf.picture - "marker_width": if @markers[index].width then @markers[index].width else @markers_conf.width - "marker_height": if @markers[index].height then @markers[index].height else @markers_conf.length - "marker_title": if @markers[index].title then @markers[index].title else null - "marker_anchor": if @markers[index].marker_anchor then @markers[index].marker_anchor else null - "shadow_anchor": if @markers[index].shadow_anchor then @markers[index].shadow_anchor else null - "shadow_picture": if @markers[index].shadow_picture then @markers[index].shadow_picture else null - "shadow_width": if @markers[index].shadow_width then @markers[index].shadow_width else null - "shadow_height": if @markers[index].shadow_height then @markers[index].shadow_height else null - "marker_draggable": if @markers[index].draggable then @markers[index].draggable else @markers_conf.draggable - "rich_marker": if @markers[index].rich_marker then @markers[index].rich_marker else null - "zindex": if @markers[index].zindex then @markers[index].zindex else null - "Lat": Lat - "Lng": Lng - "index": index - - #add infowindowstuff if enabled - @createInfoWindow(@markers[index]) - #create sidebar if enabled - @createSidebar(@markers[index]) - - @markers_conf.offset = @markers.length - - #creates Image Anchor Position or return null if nothing passed - createImageAnchorPosition : (anchorLocation) -> - if (anchorLocation == null) - return null - else - return @createPoint(anchorLocation[0], anchorLocation[1]) - - - #replace old markers with new markers on an existing map - replaceMarkers : (new_markers, adjustBounds = true) -> - @clearMarkers() - #reset previous markers - @markers = new Array - #reset current bounds - @boundsObject = @createLatLngBounds() if adjustBounds - #reset sidebar content if exists - @resetSidebarContent() - #add new markers - @markers_conf.offset = 0 - @addMarkers(new_markers, adjustBounds) - - #add new markers to on an existing map - addMarkers : (new_markers, adjustBounds = true) -> - #update the list of markers to take into account - @markers = @markers.concat(new_markers) - #put markers on the map - @create_markers() - @adjustMapToBounds() if adjustBounds - - #//////////////////////////////////////////////////// - #///////////////////// SIDEBAR ////////////////////// - #//////////////////////////////////////////////////// - - #//creates sidebar - createSidebar : (marker_container) -> - if (@markers_conf.list_container) - ul = document.getElementById(@markers_conf.list_container) - li = document.createElement('li') - aSel = document.createElement('a') - aSel.href = 'javascript:void(0);' - html = if marker_container.sidebar? then marker_container.sidebar else "Marker" - aSel.innerHTML = html - currentMap = this - aSel.onclick = @sidebar_element_handler(currentMap, marker_container.serviceObject, 'click') - li.appendChild(aSel) - ul.appendChild(li) - - #moves map to marker clicked + open infowindow - sidebar_element_handler : (currentMap, marker, eventType) -> - return () -> - currentMap.map.panTo(marker.position) - google.maps.event.trigger(marker, eventType) - - - resetSidebarContent : -> - if @markers_conf.list_container isnt null - ul = document.getElementById(@markers_conf.list_container) - ul.innerHTML = "" - - #//////////////////////////////////////////////////// - #////////////////// MISCELLANEOUS /////////////////// - #//////////////////////////////////////////////////// - - #to make the map fit the different LatLng points - adjustMapToBounds : -> - #FIRST_STEP: retrieve all bounds - #create the bounds object only if necessary - if @map_options.auto_adjust or @map_options.bounds isnt null - @boundsObject = @createLatLngBounds() - - #if autodjust is true, must get bounds from markers polylines etc... - if @map_options.auto_adjust - #from markers - @extendBoundsWithMarkers() - - #from polylines: - @updateBoundsWithPolylines() - - #from polygons: - @updateBoundsWithPolygons() - - #from circles - @updateBoundsWithCircles() - - #in every case, I've to take into account the bounds set up by the user - @extendMapBounds() - - #SECOND_STEP: ajust the map to the bounds - @adaptMapToBounds() - - #//////////////////////////////////////////////////// - #/////////////////// POLYLINES ////////////////////// - #//////////////////////////////////////////////////// - - #replace old markers with new markers on an existing map - replacePolylines : (new_polylines) -> - #reset previous polylines and kill them from map - @destroy_polylines() - #set new polylines - @polylines = new_polylines - #create - @create_polylines() - #.... and adjust map boundaries - @adjustMapToBounds() - - destroy_polylines : -> - for polyline in @polylines - #delete polylines from map - polyline.serviceObject.setMap(null) - #empty array - @polylines = [] - - #polylines is an array of arrays. It loops. - create_polylines : -> - for polyline in @polylines - @create_polyline polyline - - #//////////////////////////////////////////////////// - #///////////////// Basic functions ////////////////// - #///////////////////tests coded////////////////////// - - #//basic function to check existence of a variable - exists : (var_name) -> - return (var_name != "" and typeof var_name != "undefined") - - - #randomize - randomize : (Lat0, Lng0) -> - #distance in meters between 0 and max_random_distance (positive or negative) - dx = @markers_conf.max_random_distance * @random() - dy = @markers_conf.max_random_distance * @random() - Lat = parseFloat(Lat0) + (180/Math.PI)*(dy/6378137) - Lng = parseFloat(Lng0) + ( 90/Math.PI)*(dx/6378137)/Math.cos(Lat0) - return [Lat, Lng] - - mergeObjectWithDefault : (object1, object2) -> - copy_object1 = {} - for key, value of object1 - copy_object1[key] = value - - for key, value of object2 - unless copy_object1[key]? - copy_object1[key] = value - return copy_object1 - - mergeWithDefault : (objectName) -> - default_object = @["default_" + objectName] - object = @[objectName] - @[objectName] = @mergeObjectWithDefault(object, default_object) - return true - - #gives a value between -1 and 1 - random : -> return(Math.random() * 2 -1) diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee deleted file mode 100644 index 9eb53a6b76..0000000000 --- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee +++ /dev/null @@ -1,174 +0,0 @@ -###################################################################################################### -############################################## Bing Maps ########################################## -###################################################################################################### - -#// http://wiki.openstreetmap.org/wiki/OpenLayers -#// http://openlayers.org/dev/examples -#//http://docs.openlayers.org/contents.html - -class @Gmaps4RailsBing extends Gmaps4Rails - - constructor: -> - super - @map_options = - type: "road" # aerial, auto, birdseye, collinsBart, mercator, ordnanceSurvey, road - @markers_conf = - infobox: "description" #description or htmlContent - - @mergeWithDefault("map_options") - @mergeWithDefault("markers_conf") - - #//////////////////////////////////////////////////// - #/////////////// Basic Objects ////////////// - #//////////////////////////////////////////////////// - - getMapType: -> - switch @map_options.type - when "road" then return Microsoft.Maps.MapTypeId.road - when "aerial" then return Microsoft.Maps.MapTypeId.aerial - when "auto" then return Microsoft.Maps.MapTypeId.auto - when "birdseye" then return Microsoft.Maps.MapTypeId.birdseye - when "collinsBart" then return Microsoft.Maps.MapTypeId.collinsBart - when "mercator" then return Microsoft.Maps.MapTypeId.mercator - when "ordnanceSurvey" then return Microsoft.Maps.MapTypeId.ordnanceSurvey - else return Microsoft.Maps.MapTypeId.auto - - createPoint: (lat, lng) -> - return new Microsoft.Maps.Point(lat, lng) - - createLatLng:(lat, lng) -> - return new Microsoft.Maps.Location(lat, lng) - - createLatLngBounds: -> - - createMap: -> - return new Microsoft.Maps.Map(document.getElementById(@map_options.id), { - credentials: @map_options.provider_key, - mapTypeId: @getMapType(), - center: @createLatLng(@map_options.center_latitude, @map_options.center_longitude), - zoom: @map_options.zoom - }) - - createSize: (width, height) -> - return new google.maps.Size(width, height) - - #//////////////////////////////////////////////////// - #////////////////////// Markers ///////////////////// - #//////////////////////////////////////////////////// - - createMarker: (args) -> - markerLatLng = @createLatLng(args.Lat, args.Lng) - marker - #// Marker sizes are expressed as a Size of X,Y - if args.marker_picture == "" - marker = new Microsoft.Maps.Pushpin(@createLatLng(args.Lat, args.Lng), { - draggable: args.marker_draggable, - anchor: @createImageAnchorPosition(args.Lat, args.Lng), - text: args.marker_title - } - ); - else - marker = new Microsoft.Maps.Pushpin(@createLatLng(args.Lat, args.Lng), { - draggable: args.marker_draggable, - anchor: @createImageAnchorPosition(args.Lat, args.Lng), - icon: args.marker_picture, - height: args.marker_height, - text: args.marker_title, - width: args.marker_width - } - ); - @addToMap(marker) - return marker - - #// clear markers - clearMarkers: -> - for marker in @markers - @clearMarker marker - - clearMarker: (marker) -> - @removeFromMap(marker.serviceObject) - - #//show and hide markers - showMarkers: -> - for marker in @markers - @showMarker marker - - showMarker: (marker) -> - marker.serviceObject.setOptions({ visible: true }) - - hideMarkers: -> - for marker in @markers - @hideMarker marker - - hideMarker: (marker) -> - marker.serviceObject.setOptions({ visible: false }) - - extendBoundsWithMarkers: -> - locationsArray = [] - for marker in @markers - locationsArray.push(marker.serviceObject.getLocation()) - @boundsObject = Microsoft.Maps.LocationRect.fromLocations(locationsArray) - - #//////////////////////////////////////////////////// - #/////////////////// Clusterer ////////////////////// - #//////////////////////////////////////////////////// - - createClusterer: (markers_array) -> - - clearClusterer: -> - - #//creates clusters - clusterize: -> - - #//////////////////////////////////////////////////// - #/////////////////// INFO WINDOW //////////////////// - #//////////////////////////////////////////////////// - - #// creates infowindows - createInfoWindow: (marker_container) -> - if marker_container.description? - #//create the infowindow - if @markers_conf.infobox == "description" - marker_container.info_window = new Microsoft.Maps.Infobox(marker_container.serviceObject.getLocation(), { description: marker_container.description, visible: false, showCloseButton: true}) - else - marker_container.info_window = new Microsoft.Maps.Infobox(marker_container.serviceObject.getLocation(), { htmlContent: marker_container.description, visible: false}) - - #//add the listener associated - currentMap = this - Microsoft.Maps.Events.addHandler(marker_container.serviceObject, 'click', @openInfoWindow(currentMap, marker_container.info_window)) - @addToMap(marker_container.info_window) - - openInfoWindow: (currentMap, infoWindow) -> - return -> - # Close the latest selected marker before opening the current one. - if currentMap.visibleInfoWindow - currentMap.visibleInfoWindow.setOptions({ visible: false }) - infoWindow.setOptions({ visible:true }) - currentMap.visibleInfoWindow = infoWindow - - #//////////////////////////////////////////////////// - #/////////////////// Other methods ////////////////// - #//////////////////////////////////////////////////// - - fitBounds: -> - @serviceObject.setView({bounds: @boundsObject}) - - addToMap: (object)-> - @serviceObject.entities.push(object) - - removeFromMap: (object)-> - @serviceObject.entities.remove(object) - - centerMapOnUser: -> - @serviceObject.setView({ center: @userLocation}) - - updateBoundsWithPolylines: ()-> - - updateBoundsWithPolygons: ()-> - - updateBoundsWithCircles: ()-> - - extendMapBounds :-> - - adaptMapToBounds: -> - @fitBounds() \ No newline at end of file diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee deleted file mode 100644 index ed52ddc15a..0000000000 --- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee +++ /dev/null @@ -1,339 +0,0 @@ -####################################################################################################### -############################################## Google maps ########################################## -####################################################################################################### - -class @Gmaps4RailsGoogle extends Gmaps4Rails - - constructor: -> - super - #Map settings - @map_options = - disableDefaultUI: false - disableDoubleClickZoom: false - type: "ROADMAP" # HYBRID, ROADMAP, SATELLITE, TERRAIN - - #markers + info styling - @markers_conf = - clusterer_gridSize: 50 - clusterer_maxZoom: 5 - custom_cluster_pictures: null - custom_infowindow_class: null - - @mergeWithDefault("map_options") - @mergeWithDefault("markers_conf") - - @kml_options = - clickable: true - preserveViewport: false - suppressInfoWindows: false - - #Polygon Styling - @polygons_conf = # default style for polygons - strokeColor: "#FFAA00" - strokeOpacity: 0.8 - strokeWeight: 2 - fillColor: "#000000" - fillOpacity: 0.35 - clickable: false - - #Circle Styling - @circles_conf = #default style for circles - fillColor: "#00AAFF" - fillOpacity: 0.35 - strokeColor: "#FFAA00" - strokeOpacity: 0.8 - strokeWeight: 2 - clickable: false - zIndex: null - - #Direction Settings - @direction_conf = - panel_id: null - display_panel: false - origin: null - destination: null - waypoints: [] #[{location: "toulouse,fr", stopover: true}, {location: "Clermont-Ferrand, fr", stopover: true}] - optimizeWaypoints: false - unitSystem: "METRIC" #IMPERIAL - avoidHighways: false - avoidTolls: false - region: null - travelMode: "DRIVING" #WALKING, BICYCLING - - #//////////////////////////////////////////////////// - #/////////////// Basic Objects ////////////// - #//////////////////////////////////////////////////// - - createPoint : (lat, lng) -> - return new google.maps.Point(lat, lng) - - createLatLng : (lat, lng) -> - return new google.maps.LatLng(lat, lng) - - createLatLngBounds : -> - return new google.maps.LatLngBounds() - - createMap : -> - defaultOptions = - maxZoom: @map_options.maxZoom - minZoom: @map_options.minZoom - zoom: @map_options.zoom - center: @createLatLng(@map_options.center_latitude, @map_options.center_longitude) - mapTypeId: google.maps.MapTypeId[@map_options.type] - mapTypeControl: @map_options.mapTypeControl - disableDefaultUI: @map_options.disableDefaultUI - disableDoubleClickZoom: @map_options.disableDoubleClickZoom - draggable: @map_options.draggable - - mergedOptions = @mergeObjectWithDefault @map_options.raw, defaultOptions - - return new google.maps.Map document.getElementById(@map_options.id), mergedOptions - - - createMarkerImage : (markerPicture, markerSize, origin, anchor, scaledSize) -> - return new google.maps.MarkerImage(markerPicture, markerSize, origin, anchor, scaledSize) - - createSize : (width, height) -> - return new google.maps.Size(width, height) - - #//////////////////////////////////////////////////// - #////////////////////// Markers ///////////////////// - #//////////////////////////////////////////////////// - - createMarker : (args) -> - markerLatLng = @createLatLng(args.Lat, args.Lng) - #Marker sizes are expressed as a Size of X,Y - if args.marker_picture == "" and args.rich_marker == null - defaultOptions = {position: markerLatLng, map: @serviceObject, title: args.marker_title, draggable: args.marker_draggable, zIndex: args.zindex} - mergedOptions = @mergeObjectWithDefault @markers_conf.raw, defaultOptions - return new google.maps.Marker mergedOptions - - if (args.rich_marker != null) - return new RichMarker({ - position: markerLatLng - map: @serviceObject - draggable: args.marker_draggable - content: args.rich_marker - flat: if args.marker_anchor == null then false else args.marker_anchor[1] - anchor: if args.marker_anchor == null then 0 else args.marker_anchor[0] - zIndex: args.zindex - }) - - #default behavior - #calculate MarkerImage anchor location - imageAnchorPosition = @createImageAnchorPosition args.marker_anchor - shadowAnchorPosition = @createImageAnchorPosition args.shadow_anchor - #create or retrieve existing MarkerImages - markerImage = @createOrRetrieveImage(args.marker_picture, args.marker_width, args.marker_height, imageAnchorPosition) - shadowImage = @createOrRetrieveImage(args.shadow_picture, args.shadow_width, args.shadow_height, shadowAnchorPosition) - defaultOptions = {position: markerLatLng, map: @serviceObject, icon: markerImage, title: args.marker_title, draggable: args.marker_draggable, shadow: shadowImage, zIndex: args.zindex} - mergedOptions = @mergeObjectWithDefault @markers_conf.raw, defaultOptions - return new google.maps.Marker mergedOptions - - #checks if obj is included in arr Array and returns the position or false - includeMarkerImage : (arr, obj) -> - for object, index in arr - return index if object.url == obj - return false - - #checks if MarkerImage exists before creating a new one - #returns a MarkerImage or false if ever something wrong is passed as argument - createOrRetrieveImage : (currentMarkerPicture, markerWidth, markerHeight, imageAnchorPosition) -> - return null if (currentMarkerPicture == "" or currentMarkerPicture == null ) - - test_image_index = @includeMarkerImage(@markerImages, currentMarkerPicture) - switch test_image_index - when false - markerImage = @createMarkerImage(currentMarkerPicture, @createSize(markerWidth, markerHeight), null, imageAnchorPosition, null ) - @markerImages.push(markerImage) - return markerImage - break - else - return @markerImages[test_image_index] if typeof test_image_index == 'number' - return false - - #clear markers - clearMarkers : -> - for marker in @markers - @clearMarker marker - - #show and hide markers - showMarkers : -> - for marker in @markers - @showMarker marker - - hideMarkers : -> - for marker in @markers - @hideMarker marker - - clearMarker : (marker) -> - marker.serviceObject.setMap(null) - - showMarker : (marker) -> - marker.serviceObject.setVisible(true) - - hideMarker : (marker) -> - marker.serviceObject.setVisible(false) - - extendBoundsWithMarkers : -> - for marker in @markers - @boundsObject.extend(marker.serviceObject.position) - - #//////////////////////////////////////////////////// - #/////////////////// Clusterer ////////////////////// - #//////////////////////////////////////////////////// - - createClusterer : (markers_array) -> - return new MarkerClusterer( @serviceObject, markers_array, { maxZoom: @markers_conf.clusterer_maxZoom, gridSize: @markers_conf.clusterer_gridSize, styles: @customClusterer() }) - - clearClusterer : -> - @markerClusterer.clearMarkers() - - #creates clusters - clusterize : -> - if @markers_conf.do_clustering == true - #first clear the existing clusterer if any - @clearClusterer() if @markerClusterer != null - - markers_array = new Array - for marker in @markers - markers_array.push(marker.serviceObject) - - @markerClusterer = @createClusterer(markers_array) - - #//////////////////////////////////////////////////// - #/////////////////// INFO WINDOW //////////////////// - #//////////////////////////////////////////////////// - - #// creates infowindows - createInfoWindow : (marker_container) -> - if typeof(@jsTemplate) == "function" or marker_container.description? - marker_container.description = @jsTemplate(marker_container) if typeof(@jsTemplate) == "function" - if @markers_conf.custom_infowindow_class != null - #creating custom infowindow - boxText = document.createElement("div") - boxText.setAttribute("class", @markers_conf.custom_infowindow_class) #to customize - boxText.innerHTML = marker_container.description - marker_container.infowindow = new InfoBox(@infobox(boxText)) - currentMap = this - google.maps.event.addListener(marker_container.serviceObject, 'click', @openInfoWindow(currentMap, marker_container.infowindow, marker_container.serviceObject)) - else - #create default infowindow - marker_container.infowindow = new google.maps.InfoWindow({content: marker_container.description }) - #add the listener associated - currentMap = this - google.maps.event.addListener(marker_container.serviceObject, 'click', @openInfoWindow(currentMap, marker_container.infowindow, marker_container.serviceObject)) - - openInfoWindow : (currentMap, infoWindow, marker) -> - return -> - # Close the latest selected marker before opening the current one. - currentMap.visibleInfoWindow.close() if currentMap.visibleInfoWindow != null - infoWindow.open(currentMap.serviceObject, marker) - currentMap.visibleInfoWindow = infoWindow - - #//////////////////////////////////////////////////// - #///////////////// KML ////////////////// - #//////////////////////////////////////////////////// - - createKmlLayer : (kml) -> - kml_options = kml.options || {} - kml_options = @mergeObjectWithDefault(kml_options, @kml_options) - kml = new google.maps.KmlLayer( kml.url, kml_options) - kml.setMap(@serviceObject) - return kml - - #//////////////////////////////////////////////////// - #/////////////////// POLYLINES ////////////////////// - #//////////////////////////////////////////////////// - - #creates a single polyline, triggered by create_polylines - create_polyline : (polyline) -> - polyline_coordinates = [] - - #2 cases here, either we have a coded array of LatLng or we have an Array of LatLng - for element in polyline - #if we have a coded array - if element.coded_array? - decoded_array = new google.maps.geometry.encoding.decodePath(element.coded_array) - #loop through every point in the array - for point in decoded_array - polyline_coordinates.push(point) - - #or we have an array of latlng - else - #by convention, a single polyline could be customized in the first array or it uses default values - if element == polyline[0] - strokeColor = element.strokeColor || @polylines_conf.strokeColor - strokeOpacity = element.strokeOpacity || @polylines_conf.strokeOpacity - strokeWeight = element.strokeWeight || @polylines_conf.strokeWeight - clickable = element.clickable || @polylines_conf.clickable - zIndex = element.zIndex || @polylines_conf.zIndex - - #add latlng if positions provided - if element.lat? && element.lng? - latlng = @createLatLng(element.lat, element.lng) - polyline_coordinates.push(latlng) - - # Construct the polyline - new_poly = new google.maps.Polyline - path: polyline_coordinates - strokeColor: strokeColor - strokeOpacity: strokeOpacity - strokeWeight: strokeWeight - clickable: clickable - zIndex: zIndex - - #save polyline - polyline.serviceObject = new_poly - new_poly.setMap(@serviceObject) - - - updateBoundsWithPolylines: ()-> - for polyline in @polylines - polyline_points = polyline.serviceObject.latLngs.getArray()[0].getArray() - for point in polyline_points - @boundsObject.extend point - - #//////////////////////////////////////////////////// - #///////////////// KML ////////////////// - #//////////////////////////////////////////////////// - - create_kml : -> - for kml in @kml - kml.serviceObject = @createKmlLayer kml - - #//////////////////////////////////////////////////// - #/////////////////// Other methods ////////////////// - #//////////////////////////////////////////////////// - - fitBounds : -> - @serviceObject.fitBounds(@boundsObject) unless @boundsObject.isEmpty() - - centerMapOnUser : -> - @serviceObject.setCenter(@userLocation) - - updateBoundsWithPolygons: ()-> - for polygon in @polygons - polygon_points = polygon.serviceObject.latLngs.getArray()[0].getArray() - for point in polygon_points - @boundsObject.extend point - - updateBoundsWithCircles: ()-> - for circle in @circles - @boundsObject.extend(circle.serviceObject.getBounds().getNorthEast()) - @boundsObject.extend(circle.serviceObject.getBounds().getSouthWest()) - - extendMapBounds: ()-> - for bound in @map_options.bounds - #create points from bounds provided - @boundsObject.extend @createLatLng(bound.lat, bound.lng) - - adaptMapToBounds:()-> - #if autozoom is false, take user info into account - if !@map_options.auto_zoom - map_center = @boundsObject.getCenter() - @map_options.center_latitude = map_center.lat() - @map_options.center_longitude = map_center.lng() - @serviceObject.setCenter(map_center) - else - @fitBounds() diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee deleted file mode 100644 index 08fca694d0..0000000000 --- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee +++ /dev/null @@ -1,145 +0,0 @@ -####################################################################################################### -############################################## Map Quest ############################################# -####################################################################################################### -# http://developer.mapquest.com/web/documentation/sdk/javascript/v7.0/api/MQA.Poi.html - -class @Gmaps4RailsMapquest extends Gmaps4Rails - - constructor: -> - super - #Map settings - @map_options = {type: "map"} #map type (map, sat, hyb) - @markers_conf = {} - @mergeWithDefault "markers_conf" - @mergeWithDefault "map_options" - - #//////////////////////////////////////////////////// - #/////////////// Basic Objects ////////////// - #//////////////////////////////////////////////////// - - createPoint: (lat, lng) -> - return new MQA.Poi({lat: lat, lng: lng}) - - createLatLng: (lat, lng) -> - return {lat: lat, lng: lng} - - createLatLngBounds: -> - - createMap: -> - map = new MQA.TileMap( #// Constructs an instance of MQA.TileMap - document.getElementById(@map_options.id), #//the id of the element on the page you want the map to be added into - @map_options.zoom, #//intial zoom level of the map - {lat: @map_options.center_latitude, lng: @map_options.center_longitude}, - @map_options.type) #//map type (map, sat, hyb) - - MQA.withModule('zoomcontrol3', (-> - map.addControl( - new MQA.LargeZoomControl3(), - new MQA.MapCornerPlacement(MQA.MapCorner.TOP_LEFT) - ) - )) - return map - - createMarkerImage: (markerPicture, markerSize, origin, anchor, scaledSize) -> - - #//////////////////////////////////////////////////// - #////////////////////// Markers ///////////////////// - #//////////////////////////////////////////////////// - - createMarker: (args)-> - marker = new MQA.Poi( {lat: args.Lat, lng: args.Lng} ) - - if args.marker_picture != "" - icon = new MQA.Icon(args.marker_picture, args.marker_height, args.marker_width) - marker.setIcon(icon) - if args.marker_anchor != null - marker.setBias({x: args.marker_anchor[0], y: args.marker_anchor[1]}) - - if args.shadow_picture != "" - icon = new MQA.Icon(args.shadow_picture, args.shadow_height, args.shadow_width) - marker.setShadow(icon) - - if args.shadow_anchor != null - marker.setShadowOffset({x: args.shadow_anchor[0], y: args.shadow_anchor[1]}) - - @addToMap marker - return marker - - - #// clear markers - clearMarkers: -> - for marker in markers - @clearMarker marker - - #//show and hide markers - showMarkers: -> - for marker in markers - @showMarker marker - - hideMarkers: -> - for marker in markers - @hideMarker marker - - clearMarker: (marker) -> - @removeFromMap(marker.serviceObject) - - showMarker: (marker) -> - #// marker.serviceObject - - hideMarker: (marker) -> - #// marker.serviceObject - - extendBoundsWithMarkers: -> - if @markers.length >=2 - @boundsObject = new MQA.RectLL(@markers[0].serviceObject.latLng, @markers[1].serviceObject.latLng) - for marker in @markers - @boundsObject.extend marker.serviceObject.latLng - - #//////////////////////////////////////////////////// - #/////////////////// Clusterer ////////////////////// - #//////////////////////////////////////////////////// - - createClusterer: (markers_array) -> - - clearClusterer: -> - - #//creates clusters - clusterize: -> - - #//////////////////////////////////////////////////// - #/////////////////// INFO WINDOW //////////////////// - #//////////////////////////////////////////////////// - - #// creates infowindows - createInfoWindow: (marker_container) -> - marker_container.serviceObject.setInfoTitleHTML(marker_container.description) - #//TODO: how to disable the mouseover display when using setInfoContentHTML? - #//marker_container.serviceObject.setInfoContentHTML(marker_container.description); - - #//////////////////////////////////////////////////// - #/////////////////// Other methods ////////////////// - #//////////////////////////////////////////////////// - - fitBounds: -> - @serviceObject.zoomToRect @boundsObject if @markers.length >=2 - @serviceObject.setCenter @markers[0].serviceObject.latLng if @markers.length == 1 - - centerMapOnUser: -> - @serviceObject.setCenter @userLocation - - addToMap: (object) -> - @serviceObject.addShape object - - removeFromMap: (object)-> - @serviceObject.removeShape object - - updateBoundsWithPolylines: ()-> - - updateBoundsWithPolygons: ()-> - - updateBoundsWithCircles: ()-> - - extendMapBounds :-> - - adaptMapToBounds: -> - @fitBounds() \ No newline at end of file diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee deleted file mode 100644 index 1cddc04e39..0000000000 --- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee +++ /dev/null @@ -1,261 +0,0 @@ -####################################################################################################### -############################################## Open Layers ########################################## -####################################################################################################### - -#// http://wiki.openstreetmap.org/wiki/OpenLayers -#// http://openlayers.org/dev/examples -#//http://docs.openlayers.org/contents.html - -class @Gmaps4RailsOpenlayers extends Gmaps4Rails - - constructor: -> - super - @map_options = {} - @mergeWithDefault "map_options" - @markers_conf = {} - @mergeWithDefault "markers_conf" - - @openMarkers = null - @markersLayer = null - @markersControl = null - @polylinesLayer = null - - #//////////////////////////////////////////////////// - #/////////////// Basic Objects //////////////////// - #//////////////////////////////////////////////////// - - createPoint: (lat, lng)-> - - createLatLng: (lat, lng)-> - return new OpenLayers.LonLat(lng, lat).transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913")) # transform from WGS 1984 to Spherical Mercator Projection - - createAnchor: (offset)-> - return null if offset == null - return new OpenLayers.Pixel(offset[0], offset[1]) - - createSize: (width, height)-> - return new OpenLayers.Size(width, height) - - createLatLngBounds: -> - return new OpenLayers.Bounds() - - createMap: -> - #//todo add customization: kind of map and other map options - map = new OpenLayers.Map(@map_options.id) - map.addLayer(new OpenLayers.Layer.OSM()) - map.setCenter(@createLatLng(@map_options.center_latitude, @map_options.center_longitude), #// Center of the map - @map_options.zoom) #// Zoom level - return map - - #//////////////////////////////////////////////////// - #////////////////////// Markers ///////////////////// - #//////////////////////////////////////////////////// - #//http://openlayers.org/dev/examples/marker-shadow.html - createMarker: (args) -> - style_mark = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']) - style_mark.fillOpacity = 1 - - #//creating markers' dedicated layer - if (@markersLayer == null) - @markersLayer = new OpenLayers.Layer.Vector("Markers", null) - @serviceObject.addLayer(@markersLayer) - #//TODO move? - @markersLayer.events.register("featureselected", @markersLayer, @onFeatureSelect) - @markersLayer.events.register("featureunselected", @markersLayer, @onFeatureUnselect) - @markersControl = new OpenLayers.Control.SelectFeature(@markersLayer) - @serviceObject.addControl(@markersControl) - @markersControl.activate() - #//showing default pic if none available - if args.marker_picture == "" - #style_mark.graphicWidth = 24 - style_mark.graphicHeight = 30 - style_mark.externalGraphic = "http://openlayers.org/dev/img/marker-blue.png" - #//creating custom pic - else - style_mark.graphicWidth = args.marker_width - style_mark.graphicHeight = args.marker_height - style_mark.externalGraphic = args.marker_picture - #//adding anchor if any - if args.marker_anchor != null - style_mark.graphicXOffset = args.marker_anchor[0] - style_mark.graphicYOffset = args.marker_anchor[1] - #//adding shadow if any - if args.shadow_picture != "" - style_mark.backgroundGraphic = args.shadow_picture - style_mark.backgroundWidth = args.shadow_width - style_mark.backgroundHeight = args.shadow_height - #//adding shadow's anchor if any - if args.shadow_anchor != null - style_mark.backgroundXOffset = args.shadow_anchor[0] - style_mark.backgroundYOffset = args.shadow_anchor[1] - - style_mark.graphicTitle = args.marker_title - marker = new OpenLayers.Feature.Vector( - new OpenLayers.Geometry.Point(args.Lng, args.Lat), - null, - style_mark) - #//changing coordinates so that it actually appears on the map! - marker.geometry.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913")) - #//adding layer to the map - @markersLayer.addFeatures([marker]) - - return marker - - #//clear markers - clearMarkers: -> - @clearMarkersLayerIfExists() - @markersLayer = null - @boundsObject = new OpenLayers.Bounds() - - clearMarkersLayerIfExists: -> - @serviceObject.removeLayer(@markersLayer) if @markersLayer != null and @serviceObject.getLayer(@markersLayer.id) != null - - extendBoundsWithMarkers: -> - console.log "here" - for marker in @markers - @boundsObject.extend(@createLatLng(marker.lat,marker.lng)) - - #//////////////////////////////////////////////////// - #/////////////////// Clusterer ////////////////////// - #//////////////////////////////////////////////////// - #//too ugly to be considered valid :( - - createClusterer: (markers_array)-> - options = - pointRadius: "${radius}" - fillColor: "#ffcc66" - fillOpacity: 0.8 - strokeColor: "#cc6633" - strokeWidth: "${width}" - strokeOpacity: 0.8 - funcs = - context: - width: (feature) -> - return (feature.cluster) ? 2 : 1 - radius: (feature) -> - pix = 2 - pix = Math.min(feature.attributes.count, 7) + 2 if feature.cluster - return pix - - style = new OpenLayers.Style options, funcs - - strategy = new OpenLayers.Strategy.Cluster() - - clusters = new OpenLayers.Layer.Vector "Clusters", - strategies: [strategy] - styleMap: new OpenLayers.StyleMap - "default": style - "select": - fillColor: "#8aeeef" - strokeColor: "#32a8a9" - - @clearMarkersLayerIfExists() - @serviceObject.addLayer(clusters) - clusters.addFeatures(markers_array) - return clusters - - clusterize: -> - - if @markers_conf.do_clustering == true - #//first clear the existing clusterer if any - if @markerClusterer != null - @clearClusterer() - markers_array = new Array - for marker in @markers - markers_array.push(marker.serviceObject) - @markerClusterer = @createClusterer markers_array - - clearClusterer: -> - @serviceObject.removeLayer @markerClusterer - - #//////////////////////////////////////////////////// - #/////////////////// INFO WINDOW //////////////////// - #//////////////////////////////////////////////////// - - #// creates infowindows - createInfoWindow: (marker_container) -> - marker_container.serviceObject.infoWindow = marker_container.description if marker_container.description? - - onPopupClose: (evt) -> - #// 'this' is the popup. - @markersControl.unselect @feature - - onFeatureSelect: (evt) -> - feature = evt.feature - popup = new OpenLayers.Popup.FramedCloud("featurePopup", - feature.geometry.getBounds().getCenterLonLat(), - new OpenLayers.Size(300,200), - feature.infoWindow, - null, true, @onPopupClose) - feature.popup = popup - popup.feature = feature - @map.addPopup popup - - onFeatureUnselect: (evt) -> - feature = evt.feature - if feature.popup - #//popup.feature = null; - @map.removePopup feature.popup - feature.popup.destroy() - feature.popup = null - - #//////////////////////////////////////////////////// - #/////////////////// POLYLINES ////////////////////// - #//////////////////////////////////////////////////// - - create_polyline : (polyline) -> - - if(@polylinesLayer == null) - @polylinesLayer = new OpenLayers.Layer.Vector("Polylines", null) - @serviceObject.addLayer(@polylinesLayer) - @polylinesLayer.events.register("featureselected", @polylinesLayer, @onFeatureSelect) - @polylinesLayer.events.register("featureunselected", @polylinesLayer, @onFeatureUnselect) - @polylinesControl = new OpenLayers.Control.DrawFeature(@polylinesLayer, OpenLayers.Handler.Path) - @serviceObject.addControl(@polylinesControl) - - polyline_coordinates = [] - - for element in polyline - #by convention, a single polyline could be customized in the first array or it uses default values - if element == polyline[0] - strokeColor = element.strokeColor || @polylines_conf.strokeColor - strokeOpacity = element.strokeOpacity || @polylines_conf.strokeOpacity - strokeWeight = element.strokeWeight || @polylines_conf.strokeWeight - clickable = element.clickable || @polylines_conf.clickable - zIndex = element.zIndex || @polylines_conf.zIndex - - #add latlng if positions provided - if element.lat? && element.lng? - latlng = new OpenLayers.Geometry.Point(element.lng, element.lat) - polyline_coordinates.push(latlng) - - line_points = new OpenLayers.Geometry.LineString(polyline_coordinates); - line_style = { strokeColor: strokeColor, strokeOpacity: strokeOpacity, strokeWidth: strokeWeight }; - - polyline = new OpenLayers.Feature.Vector(line_points, null, line_style); - polyline.geometry.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913")) - - @polylinesLayer.addFeatures([polyline]) - - return polyline - - updateBoundsWithPolylines: ()-> - - updateBoundsWithPolygons: ()-> - - updateBoundsWithCircles: ()-> - - # #//////////////////////////////////////////////////// - # #/////////////////// Other methods ////////////////// - # #//////////////////////////////////////////////////// - - fitBounds: -> - @serviceObject.zoomToExtent(@boundsObject, true) - - centerMapOnUser: -> - @serviceObject.setCenter @userLocation - - extendMapBounds :-> - - adaptMapToBounds: -> - @fitBounds() diff --git a/app/assets/javascripts/search/jquery.backstretch.js b/app/assets/javascripts/search/jquery.backstretch.js deleted file mode 100644 index 4cb7175e99..0000000000 --- a/app/assets/javascripts/search/jquery.backstretch.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! Backstretch - v2.0.4 - 2013-06-19 - * http://srobbin.com/jquery-plugins/backstretch/ - * Copyright (c) 2013 Scott Robbin; Licensed MIT */ -(function(a,d,p){a.fn.backstretch=function(c,b){(c===p||0===c.length)&&a.error("No images were supplied for Backstretch");0===a(d).scrollTop()&&d.scrollTo(0,0);return this.each(function(){var d=a(this),g=d.data("backstretch");if(g){if("string"==typeof c&&"function"==typeof g[c]){g[c](b);return}b=a.extend(g.options,b);g.destroy(!0)}g=new q(this,c,b);d.data("backstretch",g)})};a.backstretch=function(c,b){return a("body").backstretch(c,b).data("backstretch")};a.expr[":"].backstretch=function(c){return a(c).data("backstretch")!==p};a.fn.backstretch.defaults={centeredX:!0,centeredY:!0,duration:5E3,fade:0};var r={left:0,top:0,overflow:"hidden",margin:0,padding:0,height:"100%",width:"100%",zIndex:-999999},s={position:"absolute",display:"none",margin:0,padding:0,border:"none",width:"auto",height:"auto",maxHeight:"none",maxWidth:"none",zIndex:-999999},q=function(c,b,e){this.options=a.extend({},a.fn.backstretch.defaults,e||{});this.images=a.isArray(b)?b:[b];a.each(this.images,function(){a("")[0].src=this});this.isBody=c===document.body;this.$container=a(c);this.$root=this.isBody?l?a(d):a(document):this.$container;c=this.$container.children(".backstretch").first();this.$wrap=c.length?c:a('
').css(r).appendTo(this.$container);this.isBody||(c=this.$container.css("position"),b=this.$container.css("zIndex"),this.$container.css({position:"static"===c?"relative":c,zIndex:"auto"===b?0:b,background:"none"}),this.$wrap.css({zIndex:-999998}));this.$wrap.css({position:this.isBody&&l?"fixed":"absolute"});this.index=0;this.show(this.index);a(d).on("resize.backstretch",a.proxy(this.resize,this)).on("orientationchange.backstretch",a.proxy(function(){this.isBody&&0===d.pageYOffset&&(d.scrollTo(0,1),this.resize())},this))};q.prototype={resize:function(){try{var a={left:0,top:0},b=this.isBody?this.$root.width():this.$root.innerWidth(),e=b,g=this.isBody?d.innerHeight?d.innerHeight:this.$root.height():this.$root.innerHeight(),j=e/this.$img.data("ratio"),f;j>=g?(f=(j-g)/2,this.options.centeredY&&(a.top="-"+f+"px")):(j=g,e=j*this.$img.data("ratio"),f=(e-b)/2,this.options.centeredX&&(a.left="-"+f+"px"));this.$wrap.css({width:b,height:g}).find("img:not(.deleteable)").css({width:e,height:j}).css(a)}catch(h){}return this},show:function(c){if(!(Math.abs(c)>this.images.length-1)){var b=this,e=b.$wrap.find("img").addClass("deleteable"),d={relatedTarget:b.$container[0]};b.$container.trigger(a.Event("backstretch.before",d),[b,c]);this.index=c;clearInterval(b.interval);b.$img=a("").css(s).bind("load",function(f){var h=this.width||a(f.target).width();f=this.height||a(f.target).height();a(this).data("ratio",h/f);a(this).fadeIn(b.options.speed||b.options.fade,function(){e.remove();b.paused||b.cycle();a(["after","show"]).each(function(){b.$container.trigger(a.Event("backstretch."+this,d),[b,c])})});b.resize()}).appendTo(b.$wrap);b.$img.attr("src",b.images[c]);return b}},next:function(){return this.show(this.indexe||d.operamini&&"[object OperaMini]"==={}.toString.call(d.operamini)||n&&7458>t||-1e||h&&6>h||"palmGetResource"in d&&e&&534>e||-1=k)})(jQuery,window); \ No newline at end of file diff --git a/app/assets/javascripts/search/jquery.offcanvas.js b/app/assets/javascripts/search/jquery.offcanvas.js deleted file mode 100644 index 4f65c081b9..0000000000 --- a/app/assets/javascripts/search/jquery.offcanvas.js +++ /dev/null @@ -1,62 +0,0 @@ -//;(function (window, document, $) { -// alert($("#sidebarButton").html()); -//}(this, document, jQuery)); - - - - -//;(function (window, document, $) { -// // Set the negative margin on the top menu for slide-menu pages -// var $selector1 = $('#topMenu'), -// events = 'click.fndtn'; -// if ($selector1.length > 0) $selector1.css("margin-top", $selector1.height() * -1); -// -// // Watch for clicks to show the sidebar -// var $selector2 = $('#sidebarButton'); -// if ($selector2.length > 0) { -// $('#sidebarButton').on(events, function (e) { -// console.log("testing one two three"); -// e.preventDefault(); -// $('body').toggleClass('active'); -// }); -// } -// else { -// console.log("not supposed to be there"); -// } -// -// // Watch for clicks to show the menu for slide-menu pages -// var $selector3 = $('#menuButton'); -// if ($selector3.length > 0) { -// $('#menuButton').on(events, function (e) { -// e.preventDefault(); -// $('body').toggleClass('active-menu'); -// }); -// } -// -// // // Adjust sidebars and sizes when resized -// // $(window).resize(function() { -// // // if (!navigator.userAgent.match(/Android/i)) $('body').removeClass('active'); -// // var $selector4 = $('#topMenu'); -// // if ($selector4.length > 0) $selector4.css("margin-top", $selector4.height() * -1); -// // }); -// -// // Switch panels for the paneled nav on mobile -// var $selector5 = $('#switchPanels'); -// if ($selector5.length > 0) { -// $('#switchPanels dd').on(events, function (e) { -// e.preventDefault(); -// var switchToPanel = $(this).children('a').attr('href'), -// switchToIndex = $(switchToPanel).index(); -// $(this).toggleClass('active').siblings().removeClass('active'); -// $(switchToPanel).parent().css("left", (switchToIndex * (-100) + '%')); -// }); -// } -// -// $('#nav li a').on(events, function (e) { -// alert("test"); -// e.preventDefault(); -// var href = $(this).attr('href'), -// $target = $(href); -// $('html, body').animate({scrollTop : $target.offset().top}, 300); -// }); -//}(this, document, jQuery)); diff --git a/app/assets/javascripts/search/modernizr.foundation.js b/app/assets/javascripts/search/modernizr.foundation.js deleted file mode 100644 index 4eb3d0655f..0000000000 --- a/app/assets/javascripts/search/modernizr.foundation.js +++ /dev/null @@ -1,4 +0,0 @@ -/* Modernizr 2.6.2 (Custom Build) | MIT & BSD - * Build: http://modernizr.com/download/#-inlinesvg-svg-svgclippaths-touch-shiv-mq-cssclasses-teststyles-prefixes-ie8compat-load - */ -;window.Modernizr=function(a,b,c){function y(a){j.cssText=a}function z(a,b){return y(m.join(a+";")+(b||""))}function A(a,b){return typeof a===b}function B(a,b){return!!~(""+a).indexOf(b)}function C(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:A(f,"function")?f.bind(d||b):f}return!1}var d="2.6.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n={svg:"http://www.w3.org/2000/svg"},o={},p={},q={},r=[],s=r.slice,t,u=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},v=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return u("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},w={}.hasOwnProperty,x;!A(w,"undefined")&&!A(w.call,"undefined")?x=function(a,b){return w.call(a,b)}:x=function(a,b){return b in a&&A(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=s.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(s.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(s.call(arguments)))};return e}),o.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:u(["@media (",m.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},o.svg=function(){return!!b.createElementNS&&!!b.createElementNS(n.svg,"svg").createSVGRect},o.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==n.svg},o.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(l.call(b.createElementNS(n.svg,"clipPath")))};for(var D in o)x(o,D)&&(t=D.toLowerCase(),e[t]=o[D](),r.push((e[t]?"":"no-")+t));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)x(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},y(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=m,e.mq=v,e.testStyles=u,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+r.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f - $('#cart_adjustments').hide() - - $('th.cart-adjustment-header').html('Distribution...') - $('th.cart-adjustment-header a').click -> - $('#cart_adjustments').toggle() - $('th.cart-adjustment-header a').html('Distribution') - false diff --git a/app/assets/javascripts/store/controllers/cart.js.coffee b/app/assets/javascripts/store/controllers/cart.js.coffee deleted file mode 100644 index cc7538249e..0000000000 --- a/app/assets/javascripts/store/controllers/cart.js.coffee +++ /dev/null @@ -1,20 +0,0 @@ -'use strict' - -angular.module('store', ['ngResource']). - controller('CartCtrl', ['$scope', '$window', 'CartFactory', ($scope, $window, CartFactory) -> - - $scope.state = 'Empty' - - $scope.loadCart = (cart_id) -> - if cart_id? - CartFactory.load cart_id, (cart) -> - $scope.cart = cart - if $scope.cart?.orders?.length > 0 - $scope.state = "There's something there...." - - $scope.addVariant = (variant, quantity) -> - - ]) - .config(['$httpProvider', ($httpProvider) -> - $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content') - ]) diff --git a/app/assets/javascripts/store/factories/cart.js.coffee b/app/assets/javascripts/store/factories/cart.js.coffee deleted file mode 100644 index e327fc336d..0000000000 --- a/app/assets/javascripts/store/factories/cart.js.coffee +++ /dev/null @@ -1,11 +0,0 @@ -'use strict' - -angular.module('store'). - factory('CartFactory', ['$resource', '$window', '$http', ($resource, $window, $http) -> - Cart = $resource '/open_food_network/cart/:cart_id.json', {}, - { 'show': { method: 'GET'} } - - load: (id, callback) -> - Cart.show {cart_id: id}, (cart) -> - callback(cart) - ]) diff --git a/app/assets/javascripts/store/products.js b/app/assets/javascripts/store/products.js deleted file mode 100644 index 127e8ce28f..0000000000 --- a/app/assets/javascripts/store/products.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Update the price on the product details page in real time when the variant or the quantity are changed. - **/ - -$(document).ready(function() { - // Product page with variant choice - $("#product-variants input[type='radio']").change(products_update_price_with_variant); - $("#quantity").change(products_update_price_with_variant); - $("#quantity").change(); - - // Product page with master price only - $(".add-to-cart input.title:not(#quantity):not(.max_quantity)").change(products_update_price_without_variant).change(); - - // Product page other - $("#distributor_id").change(function() { - var distributor_html = distributors[$(this).val()]; - if(!distributor_html) { - distributor_html = 'When you select a distributor for your order, their address and pickup times will be displayed here.'; - } - $("#product-distributor-details .distributor-details").html(distributor_html); - }); -}); - - -function products_update_price_with_variant() { - var variant_price = $("#product-variants input[type='radio']:checked").parent().find("span.price").html().trim(); - variant_price = variant_price.substr(2, variant_price.length-3); - - var quantity = $("#quantity").val(); - - $("#product-price span.price").html("$"+(parseFloat(variant_price) * parseInt(quantity)).toFixed(2)); -} - - -function products_update_price_without_variant() { - var master_price = $("#product-price span.price").data('master-price'); - if(master_price == null) { - // Store off the master price - master_price = $("#product-price span.price").html(); - master_price = master_price.substring(1); - $("#product-price span.price").data('master-price', master_price); - } - - var quantity = $(this).val(); - - $("#product-price span.price").html("$"+(parseFloat(master_price)*parseInt(quantity)).toFixed(2)); -} diff --git a/app/assets/javascripts/store/shop_front.js.coffee b/app/assets/javascripts/store/shop_front.js.coffee deleted file mode 100644 index 1a7044732f..0000000000 --- a/app/assets/javascripts/store/shop_front.js.coffee +++ /dev/null @@ -1,4 +0,0 @@ -$(document).ready -> - $("#order_order_cycle_id").change -> $("#order_cycle_select").submit() - $("#reset_order_cycle").click -> return false unless confirm "Changing your collection date will clear your cart." - $(".shop-distributor.empties-cart").click -> return false unless confirm "Changing your location will clear your cart." diff --git a/app/assets/javascripts/templates/hub_modal.html.haml b/app/assets/javascripts/templates/enterprise_modal.html.haml similarity index 100% rename from app/assets/javascripts/templates/hub_modal.html.haml rename to app/assets/javascripts/templates/enterprise_modal.html.haml diff --git a/app/assets/javascripts/templates/map_modal_producer.html.haml b/app/assets/javascripts/templates/map_modal_producer.html.haml deleted file mode 100644 index dff26519d3..0000000000 --- a/app/assets/javascripts/templates/map_modal_producer.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -%ng-include{src: "'partials/enterprise_header.html'"} -%ng-include{src: "'partials/enterprise_details.html'"} -%ng-include{src: "'partials/hub_actions.html'"} -%ng-include{src: "'partials/close.html'"} diff --git a/app/assets/javascripts/templates/partials/enterprise_header.html.haml b/app/assets/javascripts/templates/partials/enterprise_header.html.haml index 8612ba968e..27a57ed90b 100644 --- a/app/assets/javascripts/templates/partials/enterprise_header.html.haml +++ b/app/assets/javascripts/templates/partials/enterprise_header.html.haml @@ -1,13 +1,13 @@ -.highlight{"ng-class" => "{'has_shopfront' : enterprise.has_shopfront}"} +.highlight{"ng-class" => "{'is_distributor' : enterprise.is_distributor}"} .highlight-top.row .small-12.medium-7.large-8.columns - %h3{"ng-if" => "enterprise.has_shopfront"} + %h3{"ng-if" => "enterprise.is_distributor"} %a{"bo-href" => "enterprise.path", "ofn-empties-cart" => "enterprise", bindonce: true} %i{"ng-class" => "enterprise.icon_font"} %span {{ enterprise.name }} - %h3{"ng-if" => "!enterprise.has_shopfront", "ng-class" => "{'is_producer' : enterprise.is_primary_producer}"} + %h3{"ng-if" => "!enterprise.is_distributor", "ng-class" => "{'is_producer' : enterprise.is_primary_producer}"} %i{"ng-class" => "enterprise.icon_font"} %span {{ enterprise.name }} .small-12.medium-5.large-4.columns.text-right.small-only-text-left %p {{ [enterprise.address.city, enterprise.address.state_name] | printArray}} - %img.hero-img{"ng-src" => "{{enterprise.promo_image}}"} \ No newline at end of file + %img.hero-img{"ng-src" => "{{enterprise.promo_image}}"} diff --git a/app/assets/javascripts/templates/partials/hub_actions.html.haml b/app/assets/javascripts/templates/partials/hub_actions.html.haml index fa9ff4e183..61e74afb42 100644 --- a/app/assets/javascripts/templates/partials/hub_actions.html.haml +++ b/app/assets/javascripts/templates/partials/hub_actions.html.haml @@ -1,4 +1,4 @@ -.row.pad-top{bindonce: true, "ng-if" => "enterprise.hubs.length > 0 && enterprise.has_shopfront"} +.row.pad-top{bindonce: true, "ng-if" => "enterprise.hubs.length > 0 && enterprise.is_distributor"} .cta-container.small-12.columns %label Shop for diff --git a/app/assets/javascripts/templates/partials/hub_details.html.haml b/app/assets/javascripts/templates/partials/hub_details.html.haml index 27933cefcb..815200821a 100644 --- a/app/assets/javascripts/templates/partials/hub_details.html.haml +++ b/app/assets/javascripts/templates/partials/hub_details.html.haml @@ -1,4 +1,4 @@ -.row.pad-top{bindonce: true, ng: { if: 'enterprise.has_shopfront' } } +.row.pad-top{bindonce: true, ng: { if: 'enterprise.is_distributor' } } .cta-container.small-12.columns .row .small-4.columns diff --git a/app/assets/javascripts/templates/producer_modal.html.haml b/app/assets/javascripts/templates/producer_modal.html.haml deleted file mode 100644 index db6f927e21..0000000000 --- a/app/assets/javascripts/templates/producer_modal.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -%ng-include{src: "'partials/enterprise_header.html'"} -%ng-include{src: "'partials/enterprise_details.html'"} -%ng-include{src: "'partials/close.html'"} diff --git a/app/assets/javascripts/templates/registration/about.html.haml b/app/assets/javascripts/templates/registration/about.html.haml index d014c96d90..5b7d2e6df6 100644 --- a/app/assets/javascripts/templates/registration/about.html.haml +++ b/app/assets/javascripts/templates/registration/about.html.haml @@ -1,44 +1,47 @@ .container#registration-about - .header - %h2 Nice one! - %h5 - Now let's flesh out the details about - %span.brick{"ng-show" => "enterprise.is_distributor"} - {{ enterprise.name }} - %span.turquoise{"ng-show" => "!enterprise.is_distributor" } - {{ enterprise.name }} - %ng-include{ src: "'registration/steps.html'" } + .row + .small-12.columns + %header + %h2 Nice one! + %h5 + Now let's flesh out the details about + %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } + {{ enterprise.name }} + %form{ name: 'about', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('images',about)" } } .row .small-12.columns - .alert-box.alert{"data-alert" => ""} - {{ enterprise.name }} won't be visible on the Open Food Network until you enter a long and short description. - %a.close{:href => "#"} × - - .alert-box.info{"data-alert" => ""} - {{ enterprise.name }} has been created on the Open Food Network. If you leave at any point from here onwards, your enterprise will be saved, and you can always login to the admin section to update or continue filling out your enterprise details. - %a.close{:href => "#"} × + .alert-box.info{ "ofn-inline-alert" => true, ng: { show: "visible" } } + %h6 Success! {{ enterprise.name }} added to the Open Food Network + %span If you exit the wizard at any stage, login and go to admin to edit or update your enterprise details. + %a.close{ ng: { click: "close()" } } × .small-12.large-8.columns .row .small-12.columns - %label{ for: 'enterprise_description' } Short Description: - %input.chunky.small-12.columns{ id: 'enterprise_description', placeholder: "A short sentence describing your enterprise", ng: { model: 'enterprise.description' } } + .field + %label{ for: 'enterprise_description' } Short Description: + %input.chunky{ id: 'enterprise_description', placeholder: "A short sentence describing your enterprise", ng: { model: 'enterprise.description' } } .row .small-12.columns - %label{ for: 'enterprise_long_desc' } Long Description: - %textarea.chunky.small-12.columns{ id: 'enterprise_long_desc', rows: 6, placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words.", ng: { model: 'enterprise.long_description' } } - %small {{ enterprise.long_description.length }} characters / up to 600 recommended + .field + %label{ for: 'enterprise_long_desc' } Long Description: + %textarea.chunky{ id: 'enterprise_long_desc', rows: 6, placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words.", ng: { model: 'enterprise.long_description' } } + %small {{ enterprise.long_description.length }} characters / up to 600 recommended .small-12.large-4.columns .row .small-12.columns - %label{ for: 'enterprise_abn' } ABN: - %input.chunky.small-12.columns{ id: 'enterprise_abn', placeholder: "eg. 99 123 456 789", ng: { model: 'enterprise.abn' } } + .field + %label{ for: 'enterprise_abn' } ABN: + %input.chunky{ id: 'enterprise_abn', placeholder: "eg. 99 123 456 789", ng: { model: 'enterprise.abn' } } .row .small-12.columns - %label{ for: 'enterprise_acn' } ACN: - %input.chunky.small-12.columns{ id: 'enterprise_acn', placeholder: "eg. 123 456 789", ng: { model: 'enterprise.acn' } } + .field + %label{ for: 'enterprise_acn' } ACN: + %input.chunky{ id: 'enterprise_acn', placeholder: "eg. 123 456 789", ng: { model: 'enterprise.acn' } } + .row.buttons.pad-top .small-12.columns - %input.button.primary{ type: "submit", value: "Continue" } + %input.button.primary.right{ type: "submit", value: "Continue" } + diff --git a/app/assets/javascripts/templates/registration/address.html.haml b/app/assets/javascripts/templates/registration/address.html.haml deleted file mode 100644 index 6fe39e9285..0000000000 --- a/app/assets/javascripts/templates/registration/address.html.haml +++ /dev/null @@ -1,60 +0,0 @@ -.container#registration-address - .header - %h2 - Greetings - %span{ ng: { class: "{brick: enterprise.is_distributor, turquoise: !enterprise.is_distributor}" } } - {{ enterprise.name }} - - %h5 Now we need to know where you are - %ng-include{ src: "'registration/steps.html'" } - %form{ name: 'address', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('contact',address)" } } - .row.content - .small-12.medium-12.large-7.columns - .row - .small-12.columns.field - %label{ for: 'enterprise_address' } Address: - %input.chunky.small-12.columns{ id: 'enterprise_address', name: 'address1', required: true, placeholder: "eg. 123 Cranberry Drive", required: true, ng: { model: 'enterprise.address.address1' } } - %span.error.small-12.columns{ ng: { show: "address.address1.$error.required && submitted" } } - You need to enter an address. - .row - .small-12.large-8.columns.field - %label{ for: 'enterprise_city' } Suburb: - %input.chunky.small-12.columns{ id: 'enterprise_city', name: 'city', required: true, placeholder: "eg. Northcote", ng: { model: 'enterprise.address.city' } } - %span.error.small-12.columns{ ng: { show: "address.city.$error.required && submitted" } } - You need to enter a suburb. - .small-12.large-4.columns.field - %label{ for: 'enterprise_zipcode' } Postcode: - %input.chunky.small-12.columns{ id: 'enterprise_zipcode', name: 'zipcode', required: true, placeholder: "eg. 3070", ng: { model: 'enterprise.address.zipcode' } } - %span.error.small-12.columns{ ng: { show: "address.zipcode.$error.required && submitted" } } - You need to enter a postcode. - .row - .small-12.large-8.columns.field - %label{ for: 'enterprise_country' } Country: - %select.chunky.small-12.columns{ id: 'enterprise_country', name: 'country', required: true, ng: { model: 'enterprise.country', options: 'c as c.name for c in countries' } } - %span.error.small-12.columns{ ng: { show: "address.country.$error.required && submitted" } } - You need to enter a country. - .small-12.large-4.columns.field - %label{ for: 'enterprise_state' } State: - %select.chunky.small-12.columns{ id: 'enterprise_state', name: 'state', ng: { model: 'enterprise.address.state_id', options: 's.id as s.abbr for s in enterprise.country.states', show: 'countryHasStates()', required: 'countryHasStates()' } } - %span.error.small-12.columns{ ng: { show: "address.state.$error.required && submitted" } } - You need to enter a state. - .small-12.medium-12.large-5.hide-for-small-only - // This is the location area - / %h6 - / Location display - / %i.ofn-i_013-help.has-tip{ 'data-tooltip' => true, title: "Choose how you want to display your enterprise's address on the Open Food Network. By default, full location is shown everywhere including street name and number."} - / .row - / .small-12.columns - / %label.indent-checkbox - / %input{ type: 'checkbox', id: 'enterpise_suburb_only', ng: { model: 'enterprise.suburb_only' } } - / Hide my street name and street number from the public (ie. only show the suburb) - / .small-12.columns - / %label.indent-checkbox - / %input{ type: 'checkbox', id: 'enterprise_on_map', ng: { model: 'enterprise.on_map' } } - / Blur my location on the map (show an approximate, not exact pin) - - .row.buttons - .small-12.columns - %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('details')" } } -   - %input.button.primary{ type: "submit", value: "Continue" } diff --git a/app/assets/javascripts/templates/registration/contact.html.haml b/app/assets/javascripts/templates/registration/contact.html.haml index c7262248d0..916a614c0b 100644 --- a/app/assets/javascripts/templates/registration/contact.html.haml +++ b/app/assets/javascripts/templates/registration/contact.html.haml @@ -1,12 +1,13 @@ .container#registration-contact - .header - %h2 Last step to create your enterprise! - %h5 - Who is responsible for managing - %span{ ng: { class: "{brick: enterprise.is_distributor, turquoise: !enterprise.is_distributor}" } } - {{ enterprise.name }}? %ng-include{ src: "'registration/steps.html'" } - %form{ name: 'contact', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "create(contact)" } } + .row + .small-12.columns + %header + %h2 Greetings! + %h5 + Who is responsible for managing {{ enterprise.name }}? + + %form{ name: 'contact', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('type',contact)" } } .row.content .small-12.medium-12.large-7.columns .row @@ -39,8 +40,8 @@ / .small-12.columns / %label.indent-checkbox / %input{ type: 'checkbox', id: 'contact_phone_profile', ng: { model: 'enterprise.phone_in_profile' } }   Display phone in profile + .row.buttons .small-12.columns - %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('address')" } } -   - %input.button.primary{ type: "submit", value: "Continue" } + %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('details')" } } + %input.button.primary.right{ type: "submit", value: "Continue" } diff --git a/app/assets/javascripts/templates/registration/details.html.haml b/app/assets/javascripts/templates/registration/details.html.haml index bb358a1864..f1c0503f78 100644 --- a/app/assets/javascripts/templates/registration/details.html.haml +++ b/app/assets/javascripts/templates/registration/details.html.haml @@ -1,42 +1,77 @@ .container#registration-details{bindonce: true} - .header - %h2 Let's Get Started - %h5{ bo: { if: "enterprise.type != 'single'" } } Woot! First we need to know what sort of enterprise you are: - %h5{ bo: { if: "enterprise.type == 'single'" } } Woot! First we need to know the name of your farm: %ng-include{ src: "'registration/steps.html'" } - %form{ name: 'details', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('address',details)" } } - .row - .small-12.columns.field - %label{ for: 'enterprise_name', bo: { if: "enterprise.type != 'single'" } } Enterprise Name: - %label{ for: 'enterprise_name', bo: { if: "enterprise.type == 'single'" } } Farm Name: - %input.chunky.small-12.columns{ id: 'enterprise_name', name: 'name', placeholder: "eg. Charlie's Awesome Farm", required: true, ng: { model: 'enterprise.name' } } - %span.error.small-12.columns{ ng: { show: "details.name.$error.required && submitted" } } - You need to enter a name for your enterprise! + .row + .small-12.columns + %header + %h2 Let's Get Started + %h5{ bo: { if: "enterprise.type != 'own'" } } Woot! First we need to know a little bit about your enterprise: + %h5{ bo: { if: "enterprise.type == 'own'" } } Woot! First we need to know a little bit about your farm: - .row#enterprise-types{ 'data-equalizer' => true, bo: { if: "enterprise.type != 'single'" } } - .small-12.columns.field + %form{ name: 'details', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "selectIfValid('contact',details)" } } + + .row + .small-12.medium-9.large-12.columns.end + .field + %label{ for: 'enterprise_name', bo: { if: "enterprise.type != 'own'" } } Enterprise Name: + %label{ for: 'enterprise_name', bo: { if: "enterprise.type == 'own'" } } Farm Name: + %input.chunky{ id: 'enterprise_name', name: 'name', placeholder: "e.g. Charlie's Awesome Farm", required: true, ng: { model: 'enterprise.name' } } + %span.error{ ng: { show: "details.name.$error.required && submitted" } } + Please choose a unique name for your enterprise + + .row + .small-12.medium-9.large-6.columns + .field + %label{ for: 'enterprise_address' } Address line 1: + %input.chunky{ id: 'enterprise_address', name: 'address1', required: true, placeholder: "e.g. 123 Cranberry Drive", required: true, ng: { model: 'enterprise.address.address1' } } + %span.error{ ng: { show: "details.address1.$error.required && submitted" } } + Please enter an address + .field + %label{ for: 'enterprise_address2' } Address line 2: + %input.chunky{ id: 'enterprise_address2', name: 'address2', required: false, placeholder: "", required: false, ng: { model: 'enterprise.address.address2' } } + + .small-12.medium-9.large-6.columns.end .row - .small-12.columns - %label Choose one: + .small-12.medium-8.large-8.columns + .field + %label{ for: 'enterprise_city' } Suburb: + %input.chunky{ id: 'enterprise_city', name: 'city', required: true, placeholder: "e.g. Northcote", ng: { model: 'enterprise.address.city' } } + %span.error{ ng: { show: "details.city.$error.required && submitted" } } + Please enter a suburb + .small-12.medium-4.large-4.columns + .field + %label{ for: 'enterprise_zipcode' } Postcode: + %input.chunky{ id: 'enterprise_zipcode', name: 'zipcode', required: true, placeholder: "e.g. 3070", ng: { model: 'enterprise.address.zipcode' } } + %span.error{ ng: { show: "details.zipcode.$error.required && submitted" } } + Postcode required .row - .small-12.medium-4.large-4.columns{ 'data-equalizer-watch' => true } - %a.panel#producer-panel{ href: "#", ng: { click: "enterprise.is_distributor = false; enterprise.is_primary_producer = true", class: "{selected: (!enterprise.is_distributor && enterprise.is_primary_producer)}" } } - .left - / %render-svg{ path: "/assets/map-icon-producer.svg" } - %h4 I'm A 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. - .small-12.medium-4.large-4.columns{ 'data-equalizer-watch' => true } - %a.panel#hub-panel{ href: "#", ng: { click: "enterprise.is_distributor = true; enterprise.is_primary_producer = false", class: "{selected: (enterprise.is_distributor && !enterprise.is_primary_producer)}" } } - .left - / %render-svg{ path: "/assets/map-icon-hub.svg" } - %h4 I'm A Hub - %p Hubs connect the producer to the eater. Hubs can be co-ops, independent retailers, buying groups, wholesalers, CSA box schemes, farm-gate stalls, etc. - .small-12.medium-4.large-4.columns{ 'data-equalizer-watch' => true } - %a.panel#both-panel{ href: "#", ng: { click: "enterprise.is_distributor = true; enterprise.is_primary_producer = true", class: "{selected: (enterprise.is_distributor && enterprise.is_primary_producer)}" } } - .left - / %render-svg{path: "/assets/map-icon-both.svg"} - %h4 I'm Both - %p Hey there, Jack-of-all-trades! Not only do you produce things to eat &/or drink, you also want to sell your yummies through an Open Food Network shopfront. + .small-12.medium-4.large-4.columns + .field + %label{ for: 'enterprise_state' } State: + %select.chunky{ id: 'enterprise_state', name: 'state', ng: { model: 'enterprise.address.state_id', options: 's.id as s.abbr for s in enterprise.country.states', show: 'countryHasStates()', required: 'countryHasStates()' } } + %span.error{ ng: { show: "details.state.$error.required && submitted" } } + State required + .small-12.medium-8.large-8.columns + .field + %label{ for: 'enterprise_country' } Country: + %select.chunky{ id: 'enterprise_country', name: 'country', required: true, ng: { model: 'enterprise.country', options: 'c as c.name for c in countries' } } + %span.error{ ng: { show: "details.country.$error.required && submitted" } } + Please select a country + / .small-12.medium-12.large-5.hide-for-small-only + / %h6 + / Location display + / %i.ofn-i_013-help.has-tip{ 'data-tooltip' => true, title: "Choose how you want to display your enterprise's address on the Open Food Network. By default, full location is shown everywhere including street name and number."} + / .row + / .small-12.columns + / %label.indent-checkbox + / %input{ type: 'checkbox', id: 'enterpise_suburb_only', ng: { model: 'enterprise.suburb_only' } } + / Hide my street name and street number from the public (ie. only show the suburb) + / .small-12.columns + / %label.indent-checkbox + / %input{ type: 'checkbox', id: 'enterprise_on_map', ng: { model: 'enterprise.on_map' } } + / Blur my location on the map (show an approximate, not exact pin) + + .row.buttons .small-12.columns + %hr %input.button.primary.right{ type: "submit", value: "Continue" } diff --git a/app/assets/javascripts/templates/registration/finished.html.haml b/app/assets/javascripts/templates/registration/finished.html.haml index 75489c607a..f647a2d8bb 100644 --- a/app/assets/javascripts/templates/registration/finished.html.haml +++ b/app/assets/javascripts/templates/registration/finished.html.haml @@ -1,18 +1,24 @@ .container#registration-finished - .header - %h2 Well done! - %h5 - You have successfully completed the profile for - %span.brick{"ng-show" => "enterprise.is_distributor"} - {{ enterprise.name }} - %span.turquoise{"ng-show" => "!enterprise.is_distributor" } - {{ enterprise.name }} - .content{ style: 'text-align: center'} - %h3 Why not check it out on the Open Food Network? - %a.button.primary{ type: "button", href: "/map" } Go to Map Page > + .row + .small-12.columns.pad-top + %header + %h2 Finished! + .panel.callout + %p + Thanks for filling out the details for + %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } + {{ enterprise.name }} + %p You can change or update your enterprise at any stage by logging into Open Food Network and going to Admin. + .row + .small-12.columns.text-center + %h4 + Activate + %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } + {{ enterprise.name }} - %br - %br + %p + We've sent a confirmation email to + %strong {{ enterprise.email }}. + %br Please follow the instructions there to make your enterprise visible on the Open Food Network. - %h3 Next step - add some products: - %a.button.primary{ type: "button", href: "/admin/products/new" } Add a Product > + %a.button.primary{ type: "button", href: "/" } Open Food Network home > diff --git a/app/assets/javascripts/templates/registration/images.html.haml b/app/assets/javascripts/templates/registration/images.html.haml index 889bc4596f..5b2ac39b5a 100644 --- a/app/assets/javascripts/templates/registration/images.html.haml +++ b/app/assets/javascripts/templates/registration/images.html.haml @@ -1,20 +1,22 @@ .container#registration-images{ 'nv-file-drop' => true, uploader: "imageUploader", options:"{ alias: imageStep }", ng: { controller: "EnterpriseImageCtrl" } } - .header - %h2 Thanks! - %h5 Let's upload some pretty pictures so your profile looks great! :) %ng-include{ src: "'registration/steps.html'" } + .row + .small-12.columns + %header + %h2 Thanks! + %h5 Let's upload some pretty pictures so your profile looks great! :) + %form{ name: 'images', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "select('social')" } } .row{ ng: { repeat: 'image_step in imageSteps', show: "imageStep == image_step" } } %ng-include{ src: "'registration/images/'+ image_step + '.html'" } .row.buttons.pad-top{ ng: { if: "imageStep == 'logo'" } } .small-12.columns - %input.button.primary{ type: "button", value: "Back", ng: { click: "select('about')" } } + %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('about')" } }   - %input.button.primary{ type: "button", value: "Continue", ng: { click: "imageSelect('promo')" } } + %input.button.primary.right{ type: "button", value: "Continue", ng: { click: "imageSelect('promo')" } } .row.buttons.pad-top{ ng: { if: "imageStep == 'promo'" } } .small-12.columns - %input.button.primary{ type: "button", value: "Back", ng: { click: "imageSelect('logo')" } } -   - %input.button.primary{ type: "submit", value: "Continue" } + %input.button.secondary{ type: "button", value: "Back", ng: { click: "imageSelect('logo')" } } + %input.button.primary.right{ type: "submit", value: "Continue" } diff --git a/app/assets/javascripts/templates/registration/images/logo.html.haml b/app/assets/javascripts/templates/registration/images/logo.html.haml index 803b103a58..4b19c14cff 100644 --- a/app/assets/javascripts/templates/registration/images/logo.html.haml +++ b/app/assets/javascripts/templates/registration/images/logo.html.haml @@ -36,6 +36,10 @@ .row.pad-top .small-12.columns.center #image-placeholder.logo - %img{ ng: { show: "imageSrc()", src: '{{ imageSrc() }}' } } - .message{ ng: { hide: "imageSrc()" } } + %img{ ng: { show: "imageSrc() && !imageUploader.isUploading", src: '{{ imageSrc() }}' } } + .message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } } Your logo will appear here for review once uploaded + .loading{ ng: { hide: "!imageUploader.isUploading" } } + %img.spinner{ src: "/assets/loading.gif" } + %br/ + Uploading... diff --git a/app/assets/javascripts/templates/registration/images/promo.html.haml b/app/assets/javascripts/templates/registration/images/promo.html.haml index f134834d5c..d98721dc6d 100644 --- a/app/assets/javascripts/templates/registration/images/promo.html.haml +++ b/app/assets/javascripts/templates/registration/images/promo.html.haml @@ -34,6 +34,10 @@ .row.pad-top .small-12.columns.center #image-placeholder.promo - %img{ ng: { show: "imageSrc()", src: '{{ imageSrc() }}' } } - .message{ ng: { hide: "imageSrc()" } } + %img{ ng: { show: "imageSrc() && !imageUploader.isUploading", src: '{{ imageSrc() }}' } } + .message{ ng: { hide: "imageSrc() || imageUploader.isUploading" } } Your logo will appear here for review once uploaded + .loading{ ng: { hide: "!imageUploader.isUploading" } } + %img.spinner{ src: "/assets/loading.gif" } + %br/ + Uploading... diff --git a/app/assets/javascripts/templates/registration/introduction.html.haml b/app/assets/javascripts/templates/registration/introduction.html.haml index ccefdefbe0..60a8547b4a 100644 --- a/app/assets/javascripts/templates/registration/introduction.html.haml +++ b/app/assets/javascripts/templates/registration/introduction.html.haml @@ -1,39 +1,45 @@ -%div - .header - %h2 Hi there! - %h4 This wizard will step you through creating a profile - .row - .small-12.medium-3.large-2.columns.text-right.hide-for-small-only - %img{:src => "/assets/potatoes.png"} - .small-12.medium-9.large-10.columns - %p - Your profile gives you an online presence on the - %strong Open Food Network, - allowing you to easily connect with potential customers or partners. You can always choose to update your info later, as well as choose to upgrade your Profile to and Online Store, where you can sell products, track orders and receive payments. Creating a profile takes about 5-10 minutes. - .row{ 'data-equalizer' => true } - .small-12.medium-6.large-6.columns.pad-top{ 'data-equalizer-watch' => true } - %h5 You'll need the following: - %ul.check-list - %li - Your enterprise address and contact details - %li - Your logo image - %li - A pretty picture for your profile header - %li - Some 'About Us' text - - .small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true} - .highlight-box - %h5 Your profile entitles you to: - %ul.small-block-grid-1 - %li - %i.ofn-i_020-search - A searchable listing - %li - %i.ofn-i_040-hub - A pin on the OFN map - .row - .small-12.columns +.row + .small-12.columns + %header + %h2 Hi there! + %h4 + %small + %i.ofn-i_040-hub + Create your enterprise profile + .hide-for-large-up %hr - %input.button.primary{ type: "button", value: "Let's get started!", ng: { click: "select('details')" } } + %input.button.small.primary{ type: "button", value: "Let's get started!", ng: { click: "select('details')" } } + %hr + +.row{ 'data-equalizer' => true } + .small-12.medium-12.large-6.columns.pad-top{ 'data-equalizer-watch' => true } + %h5 You'll need: + %ul.check-list + %li + 5-10 minutes + %li + Enterprise address + %li + Primary contact details + %li + Your logo image + %li + Landscape image for your profile + %li + 'About Us' text + + .small-9.medium-8.large-5.columns.pad-top.end{ 'data-equalizer-watch' => true} + %h5 + What do I get? + %p + Your profile helps people + %strong find + and + %strong contact + you on the Open Food Network. + %p Use this space to tell the story of your enterprise, to help drive connections to your social and online presence. + +.row.show-for-large-up + .small-12.columns + %hr + %input.button.primary.right{ type: "button", value: "Let's get started!", ng: { click: "select('details')" } } diff --git a/app/assets/javascripts/templates/registration/limit_reached.html.haml b/app/assets/javascripts/templates/registration/limit_reached.html.haml index e2131c1727..778d980289 100644 --- a/app/assets/javascripts/templates/registration/limit_reached.html.haml +++ b/app/assets/javascripts/templates/registration/limit_reached.html.haml @@ -1,15 +1,16 @@ -%div - .header.center - %h2 Oh no! - %h4 You have reached the limit! - .row - .small-12.medium-3.large-2.columns.text-right.hide-for-small-only - %img{:src => "/assets/potatoes.png"} - .small-12.medium-9.large-10.columns - %p - You have reached the limit for the number of enterprises you are allowed to own on the - %strong Open Food Network. - .row - .small-12.columns - %hr - %input.button.primary{ type: "button", value: "Return to the homepage", ng: { click: "close()" } } +.row + .small-12.columns + %header + %h2 Oh no! + %h4 You have reached the limit! +.row + .small-12.medium-3.large-2.columns.text-right.hide-for-small-only + %img{:src => "/assets/potatoes.png"} + .small-12.medium-9.large-10.columns + %p + You have reached the limit for the number of enterprises you are allowed to own on the + %strong Open Food Network. +.row + .small-12.columns + %hr + %input.button.primary{ type: "button", value: "Return to the homepage", ng: { click: "close()" } } diff --git a/app/assets/javascripts/templates/registration/social.html.haml b/app/assets/javascripts/templates/registration/social.html.haml index 1b3490ffa0..4f448734f1 100644 --- a/app/assets/javascripts/templates/registration/social.html.haml +++ b/app/assets/javascripts/templates/registration/social.html.haml @@ -1,35 +1,49 @@ .container#registration-social - .header - %h2 Last step! - %h5 How can people find {{ enterprise.name }} online? %ng-include{ src: "'registration/steps.html'" } + + .row + .small-12.columns + %header + %h2 Final step! + %h5 + How can people find + %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } + {{ enterprise.name }} + online? + %form{ name: 'social', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "update('finished',social)" } } .row.content .small-12.large-7.columns .row .small-12.columns - %label{ for: 'enterprise_website' } Website: - %input.chunky.small-12.columns{ id: 'enterprise_website', placeholder: "eg. openfoodnetwork.org.au", ng: { model: 'enterprise.website' } } + .field + %label{ for: 'enterprise_website' } Website: + %input.chunky{ id: 'enterprise_website', placeholder: "eg. openfoodnetwork.org.au", ng: { model: 'enterprise.website' } } .row .small-12.columns - %label{ for: 'enterprise_facebook' } Facebook: - %input.chunky.small-12.columns{ id: 'enterprise_facebook', placeholder: "eg. www.facebook.com/PageNameHere", ng: { model: 'enterprise.facebook' } } + .field + %label{ for: 'enterprise_facebook' } Facebook: + %input.chunky{ id: 'enterprise_facebook', placeholder: "eg. www.facebook.com/PageNameHere", ng: { model: 'enterprise.facebook' } } .row .small-12.columns - %label{ for: 'enterprise_linkedin' } LinkedIn: - %input.chunky.small-12.columns{ id: 'enterprise_linkedin', placeholder: "eg. www.linkedin.com/YourNameHere", ng: { model: 'enterprise.linkedin' } } + .field + %label{ for: 'enterprise_linkedin' } LinkedIn: + %input.chunky{ id: 'enterprise_linkedin', placeholder: "eg. www.linkedin.com/YourNameHere", ng: { model: 'enterprise.linkedin' } } .small-12.large-5.columns .row .small-12.columns - %label{ for: 'enterprise_twitter' } Twitter: - %input.chunky.small-12.columns{ id: 'enterprise_twitter', placeholder: "eg. @twitter_handle", ng: { model: 'enterprise.twitter' } } + .field + %label{ for: 'enterprise_twitter' } Twitter: + %input.chunky{ id: 'enterprise_twitter', placeholder: "eg. @twitter_handle", ng: { model: 'enterprise.twitter' } } .row .small-12.columns - %label{ for: 'enterprise_instagram' } Instagram: - %input.chunky.small-12.columns{ id: 'enterprise_instagram', placeholder: "eg. @instagram_handle", ng: { model: 'enterprise.instagram' } } + .field + %label{ for: 'enterprise_instagram' } Instagram: + %input.chunky{ id: 'enterprise_instagram', placeholder: "eg. @instagram_handle", ng: { model: 'enterprise.instagram' } } .row.buttons .small-12.columns %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('images')" } } -   - %input.button.primary{ type: "submit", value: "Continue" } + %input.button.primary.right{ type: "submit", value: "Continue" } + + diff --git a/app/assets/javascripts/templates/registration/steps.html.haml b/app/assets/javascripts/templates/registration/steps.html.haml index 65e3cb6b48..8b420de293 100644 --- a/app/assets/javascripts/templates/registration/steps.html.haml +++ b/app/assets/javascripts/templates/registration/steps.html.haml @@ -1,5 +1,3 @@ .row#progress-bar .small-12.medium-2.columns.item{ ng: { repeat: 'step in steps', class: "{active: (currentStep() == step),'show-for-medium-up': (currentStep() != step)}" } } {{ $index+1 + ". " + step }} - - \ No newline at end of file diff --git a/app/assets/javascripts/templates/registration/type.html.haml b/app/assets/javascripts/templates/registration/type.html.haml new file mode 100644 index 0000000000..48d45cb66a --- /dev/null +++ b/app/assets/javascripts/templates/registration/type.html.haml @@ -0,0 +1,46 @@ +.container#registration-type{bindonce: true} + + %ng-include{ src: "'registration/steps.html'" } + + .row + .small-12.columns + %header + %h2 + Last step to add + %span{ ng: { class: "{brick: !enterprise.is_primary_producer, turquoise: enterprise.is_primary_producer}" } } + {{ enterprise.name }}! + %h4 + Are you a producer? + + %form{ name: 'type', novalidate: true, ng: { controller: "RegistrationFormCtrl", submit: "create(type)" } } + .row#enterprise-types{ 'data-equalizer' => true, bo: { if: "enterprise.type != 'own'" } } + .small-12.columns.field + .row + .small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true } + %a.btnpanel#producer-panel{ href: "#", ng: { click: "enterprise.is_primary_producer = true", class: "{selected: enterprise.is_primary_producer}" } } + %i.ofn-i_059-producer + %h4 Yes, I'm a producer + + .small-12.medium-6.large-6.columns{ 'data-equalizer-watch' => true } + %a.btnpanel#hub-panel{ href: "#", ng: { click: "enterprise.is_primary_producer = false", class: "{selected: enterprise.is_primary_producer == false}" } } + %i.ofn-i_063-hub + %h4 No, I'm not a producer + + .row + .small-12.columns + %input.chunky{ id: 'enterprise_is_primary_producer', name: 'is_primary_producer', hidden: true, required: true, ng: { model: 'enterprise.is_primary_producer' } } + %span.error{ ng: { show: "type.is_primary_producer.$error.required && submitted" } } + Please choose one. Are you are producer? + .row + .small-12.columns + .panel.callout + .left + %i.ofn-i_013-help +   + %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 Hubs connect the producer to the eater. Hubs can be co-ops, independent retailers, buying groups, wholesalers, CSA box schemes, farm-gate stalls, etc. + + .row.buttons + .small-12.columns + %input.button.secondary{ type: "button", value: "Back", ng: { click: "select('contact')" } } + %input.button.primary.right{ type: "submit", value: "Continue" } diff --git a/app/assets/stylesheets/admin/alert.css.sass b/app/assets/stylesheets/admin/alert.css.sass new file mode 100644 index 0000000000..d86cb38f55 --- /dev/null +++ b/app/assets/stylesheets/admin/alert.css.sass @@ -0,0 +1,15 @@ +.alert + border: 3px solid #919191 + border-radius: 6px + margin-bottom: 20px + color: #919191 + padding: 5px 10px + h6 + color: #919191 + .message + font-weight: bold + &:hover + border-color: #DA5354 + color: #DA5354 + h6 + color: #DA5354 diff --git a/app/assets/stylesheets/darkswarm/producer_node.css.sass b/app/assets/stylesheets/darkswarm/producer_node.css.sass index 22541055f4..7bac2dde6d 100644 --- a/app/assets/stylesheets/darkswarm/producer_node.css.sass +++ b/app/assets/stylesheets/darkswarm/producer_node.css.sass @@ -32,7 +32,7 @@ span text-decoration: underline - &.has_shopfront, &.has_shopfront i.ofn-i_059-producer, &.has_shopfront i.ofn-i_060-producer-reversed + &.is_distributor, &.is_distributor i.ofn-i_059-producer, &.is_distributor i.ofn-i_060-producer-reversed color: $clr-brick &:hover, &:active, &:focus color: $clr-brick-bright diff --git a/app/assets/stylesheets/darkswarm/registration.css.sass b/app/assets/stylesheets/darkswarm/registration.css.sass index 3261fc0fd4..f63eb30421 100644 --- a/app/assets/stylesheets/darkswarm/registration.css.sass +++ b/app/assets/stylesheets/darkswarm/registration.css.sass @@ -2,44 +2,25 @@ @import mixins #registration-modal - .header + header text-align: center - background-color: #efefef - padding-bottom: 1rem + // background-color: #efefef + @media all and (max-width: 64em) + text-align: left .container background-color: #ffffff - .content - // margin-bottom: 15px i font-size: 150% - .buttons - - ofn-inline-flash - display: block - padding: 15px - position: relative - margin-bottom: 10px - &.brick - background-color: $clr-brick-light - border: 2px solid $clr-brick - color: $clr-brick - &.turquoise - background-color: $clr-turquoise-light - border: 2px solid $clr-turquoise - color: $clr-turquoise - .close-button - position: absolute - top: 0px - right: 0px - .field - margin-bottom: 15px + margin-bottom: 1em - input.chunky + .chunky padding: 8px - font-size: 105% + font-size: 1rem + margin: 0 + width: 100% label.indent-checkbox display: block @@ -51,9 +32,9 @@ label margin-bottom: 3px - ol, ul - // font-size: 80% + ol, ul, p font-size: 0.875rem + ol, ul padding: 0 margin: 0 ol @@ -62,22 +43,24 @@ .highlight-box background: white padding: 1rem 1.2rem - @media all and (max-width: 640px) + @media all and (max-width: 64em) margin-top: 1rem #progress-bar margin-bottom: 15px .item - padding: 12px 0px + font-size: 0.75rem + padding: 10px 0px text-transform: uppercase text-align: center - background-color: #333 - border: 2px solid #333 + background-color: $clr-blue + border: 2px solid $clr-blue color: #fff .item.active - background-color: #cccccc - border: 2px solid #333 - color: #333 + background-color: $disabled-light + border: 2px solid $clr-blue + color: $clr-blue + font-weight: 700 @include box-shadow(inset 0 0 1px 0 #fff) @@ -110,39 +93,57 @@ font-size: 18px font-weight: bold color: #373737 - background-color: #e1e1e1 + background-color: #f1f1f1 text-align: center border: 3px dashed #494949 margin-left: auto margin-right: auto + .spinner + width: 100px &.logo .message padding-top: 6em + .loading + padding-top: 4em width: 306px height: 306px &.promo .message padding-top: 4em + .loading + padding-top: 1em width: 726px height: 166px -#registration-details +#registration-type #enterprise-types - a.panel + a.btnpanel display: block + padding: 1rem + margin-bottom: 1rem background-color: #efefef color: black - @media all and (min-width: 768px) - min-height: 200px + text-align: center + border: 1px solid transparent + i + font-size: 3rem + h4 + margin-top: 1rem + &:hover background-color: #fff + &#producer-panel:hover + border: 1px solid $clr-turquoise &, & * color: $clr-turquoise + &#hub-panel:hover, &#both-panel:hover + border: 1px solid $clr-brick &, & * color: $clr-brick + &.selected &, & * color: #fff diff --git a/app/assets/stylesheets/darkswarm/ui.css.sass b/app/assets/stylesheets/darkswarm/ui.css.sass index 3bbce61552..d18adae1b4 100644 --- a/app/assets/stylesheets/darkswarm/ui.css.sass +++ b/app/assets/stylesheets/darkswarm/ui.css.sass @@ -74,8 +74,10 @@ button.success, .button.success .profile-checkbox display: inline-block - input[type="checkbox"] + label + label margin: 0 0.2rem + float: right + // Responsive @media screen and (min-width: 768px) diff --git a/app/assets/stylesheets/distributors.css.scss b/app/assets/stylesheets/distributors.css.scss deleted file mode 100644 index de8cd669a0..0000000000 --- a/app/assets/stylesheets/distributors.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the distributors controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/groups.css.scss b/app/assets/stylesheets/groups.css.scss deleted file mode 100644 index c2a5f9013b..0000000000 --- a/app/assets/stylesheets/groups.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the groups controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/shop/checkout.css.scss b/app/assets/stylesheets/shop/checkout.css.scss deleted file mode 100644 index 727d410b19..0000000000 --- a/app/assets/stylesheets/shop/checkout.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the Shop::Checkout controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/store/openfoodnetwork.css.scss b/app/assets/stylesheets/store/openfoodnetwork.css.scss deleted file mode 100644 index f98642cc22..0000000000 --- a/app/assets/stylesheets/store/openfoodnetwork.css.scss +++ /dev/null @@ -1,381 +0,0 @@ -@import "screen"; -@import "compass/css3/border-radius"; - -/* General purpose styles */ - -a:hover { - color: lighten($link_text_color, 20) !important; -} - -/* Cleared div for clearing previous floating elements */ -div.cleared { - clear: both; -} - -p.hint { - margin: 0 0 0.5em 0; - padding: 0; - color: #6a6a6a; -} - -table { - tbody, tfoot { - tr { - &.alt, &.odd { - background-color: lighten($link_text_color, 75) !important; - } - } - } -} - - - -/* Style current distributor in main nav bar */ -#header { - margin-bottom: 40px; - - #logo { - position: relative; - margin-top: 40px; - padding-top: 10px; - - h1 { - position: absolute; - top: 10px; - left: 0px; - } - - .change-location { - position: absolute; - top: 22px; - right: -104px; - } - } -} - -nav #main-nav-bar { - position: absolute; - top: 0px; - left: 10px; - width: 100%; - - #link-to-cart { - position: relative; - top: 40px; - right: 16px; - } - - li { - &#current-distribution { - float: right; - clear: right; - margin: 0.5em 5px 0 0; - - a { - font-size: 12px; - padding: 0; - } - } - } -} - -nav#top-nav-bar { - position: relative; - z-index: 999; - margin-top: 20px; -} - - -/* Based on Spree's nav#taxonomies style. Copied instead of - * extended with SASS because SASS does not allow extending - * nested selectors. - */ -nav#filters { - .filter_name { - text-transform: uppercase; - border-bottom: 1px solid lighten($body_text_color, 60); - margin-bottom: 5px; - font-size: $main_navigation_header_font_size; - } - - .filter_choices { - padding-left: 20px; - margin-bottom: 5px; - list-style: disc outside; - - li { - a { - font-size: $main_navigation_font_size; - } - span.inactive { - color: #999; - } - } - } - - input[type=submit] { - margin-bottom: 15px; - } -} - - -/* Distributor and order cycle selection and display */ -#distribution-choice { - margin-bottom: 2em; - padding: 5px; - border: 2px solid #ccc; - @include border-radius(10px); -} - -#distribution-selection { - overflow: auto; - margin-bottom: 2em; - padding: 20px; - background-color: #f3f3f3; - - .distributors { - float: left; - margin-right: 4em; - - option.local { - background-color: #cfc; - } - option.remote { - background-color: #fcc; - } - } - - .order-cycles { - - select { - padding: 10px; - margin-top: 15px; - border: 2px solid white; - width: 100%; - } - - float: left; - - tr.local { - background-color: #cfc; - } - tr.remote { - background-color: #fcc; - } - } -} - -.countdown-panel { - background: url("../countdown.png") no-repeat left; - min-height: 60px; - min-width: 70px; - padding-left: 77px; -} - - -/* Style the product source on the product details page in the - * same manner as the product properties table above it. - */ -#product-source { - @extend #product-properties; -} -#product-properties td, #product-source td { - width: 50%; -} - - -/* Apply Spree's ul#products style to ul.product-listing. When viewing products - * split by distributor, a separate product listing is displayed for local and - * remote products, so the #products id is removed to avoid its duplication. - */ -ul.product-listing { - &:after { - content: " "; - display: block; - clear: both; - visibility: hidden; - line-height: 0; - height: 0; - } - - li { - text-align: center; - font-weight: bold; - margin-bottom: 20px; - - a { - display: block; - - &.info { - height: 35px; - margin-top: 5px; - font-size: $product_list_name_font_size; - color: $product_link_text_color; - border-bottom: 1px solid lighten($body_text_color, 60); - overflow: hidden; - } - } - - .product-image { - border: 1px solid lighten($body_text_color, 60); - padding: 5px; - min-height: 110px; - background-color: $product_background_color; - - &:hover { - border-color: $link_text_color; - } - - } - - .price { - color: $link_text_color; - font-size: $product_list_price_font_size; - padding-top: 5px; - display: block; - } - } -} - - -/* Enterprise description */ -.enterprise-description { - margin-bottom: 2em; -} - - -/* Supplier page distributor listing */ -#supplier-distributors li a.inactive { - color: #999; -} - - -/* Highlight local products in distributor-split product listings */ -#products-local ul { - margin-bottom: 1em; - padding: 10px; - @include border-radius(10px); - background-color: #def; -} - - -/* Distributor details on product details page */ -fieldset#product-distributor-details { - float: right; - margin-top: 0; - width: 250px; - @extend #shipping; -} - -#product-variants { - float: none; - - ul { - list-style-type: none; - } -} - - -/* Add to cart form on product details page */ -#cart-form { - .error-distributor { - font-size: 120%; - font-weight: bold; - color: #f00; - - a { - color: #f00; - text-decoration: underline; - } - } - - .distributor-fixed { - } -} - - -/* View cart form */ -#subtotal { - width: 100%; -} -.links { - float: right; - text-align: right; -} -#empty-cart { - float: left; - margin-top: 15px !important; - - p { - padding: 0; - } -} - -/* Checkout address page */ -#checkout .alternative-available-distributors { - padding-top: 30px; -} - - -/* Delivery fees table on checkout page */ -#delivery-fees { - clear: both; - padding-top: 6em; - - table#delivery { - width: 100%; - - tbody { - tr { - td.item-shipping-cost, td.item-shipping-method { - text-align: center; - } - td.item-shipping-cost { - @extend span.price; - @extend span.price.selling; - } - } - } - } - - .subtotal { - width: 100%; - text-align: right; - text-transform: uppercase; - margin-top: 15px; - - span.order-total { - @extend span.price; - } - } -} - -/* Alert for EFT Payment during checkout process */ -div#eft-payment-alert { - border: 2px solid red; - padding-left: 5px; -} - -/* Large 'Save and Continue/Process My Order' button under Order Summary on the checkout pages */ -#add_new_save_checkout_button { - display: none; - margin-top: 30px; - padding-top: 10px; - border-top: 1px solid #d9d9db; -} - -.secondary { - color: #6A6A6A; -} - -.hide { - display: none; -} - - -/* Distributor details */ -.distributor-details .next-collection-at { - font-size: 20px; - font-weight: bold; - color: #de790c; -} diff --git a/app/assets/stylesheets/store/variables.css.scss b/app/assets/stylesheets/store/variables.css.scss deleted file mode 100644 index 4cb074ae48..0000000000 --- a/app/assets/stylesheets/store/variables.css.scss +++ /dev/null @@ -1,64 +0,0 @@ -/*--------------------------------------*/ -/* Colors -/*--------------------------------------*/ -$c_green: #8dba53 !default; /* Spree green */ -$c_red: #e45353 !default; /* Error red */ - -$layout_background_color: #FFFFFF !default; -$title_text_color: #404042 !default; -$body_text_color: #404042 !default; -$link_text_color: #00ADEE !default; - -$product_background_color: #FFFFFF !default; -$product_title_text_color: #404042 !default; -$product_body_text_color: #404042 !default; -$product_link_text_color: #BBBBBB !default; - -/*--------------------------------------*/ -/* Fonts import from remote -/*--------------------------------------*/ -@import url(//fonts.googleapis.com/css?family=Ubuntu:400,700,400italic,700italic|&subset=latin,cyrillic,greek,greek-ext,latin-ext,cyrillic-ext); - -/*--------------------------------------*/ -/* Font families -/*--------------------------------------*/ -$ff_base: 'Ubuntu', sans-serif !default; - -/*-------------------------------------- - | Font sizes - |-------------------------------------- - |- Navigation - | */ - $header_navigation_font_size: 14px !default; - $horizontal_navigation_font_size: 16px !default; - $main_navigation_header_font_size: 14px !default; - $main_navigation_font_size: 12px !default; -/*|------------------------------------ - |- Product Listing - | */ - $product_list_name_font_size: 12px !default; - $product_list_price_font_size: 16px !default; - $product_list_header_font_size: 20px !default; - $product_list_search_font_size: 14px !default; -/*|------------------------------------ - |- Product Details - | */ - $product_detail_name_font_size: 24px !default; - $product_detail_description_font_size: 12px !default; - $product_detail_price_font_size: 20px !default; - $product_detail_title_font_size: 14px !default; -/*|------------------------------------ - |- Basic - | */ - $heading_font_size: 24px !default; - $sub_heading_font_size: 14px !default; - $button_font_size: 12px !default; - $input_box_font_size: 13px !default; - $base_font_size: 12px !default; - $border_color: lighten($body_text_color, 60) !default; - $default_border: 1px solid $border_color !default; - $button_border_color: rgba(0, 138, 189, .75) !default; - $table_head_color: lighten($body_text_color, 60) !default; - - -@import "./store/variables_changes.css.scss"; diff --git a/app/assets/stylesheets/store/variables_changes.css.scss b/app/assets/stylesheets/store/variables_changes.css.scss deleted file mode 100644 index c4007abd2f..0000000000 --- a/app/assets/stylesheets/store/variables_changes.css.scss +++ /dev/null @@ -1 +0,0 @@ -$link_text_color: #006066; diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index bde7bff431..7d38e408d6 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -3,11 +3,11 @@ module Admin before_filter :load_enterprise_set, :only => :index before_filter :load_countries, :except => :index before_filter :load_methods_and_fees, :only => [:new, :edit, :update, :create] - before_filter :check_type, only: :update - before_filter :check_bulk_type, only: :bulk_update + before_filter :check_can_change_sells, only: :update + before_filter :check_can_change_bulk_sells, only: :bulk_update before_filter :override_owner, only: :create - before_filter :check_owner, only: :update - before_filter :check_bulk_owner, only: :bulk_update + before_filter :check_can_change_owner, only: :update + before_filter :check_can_change_bulk_owner, only: :bulk_update helper 'spree/products' include OrderCyclesHelper @@ -50,7 +50,8 @@ module Admin end def collection - Enterprise.managed_by(spree_current_user).order('is_distributor DESC, is_primary_producer ASC, name') + # TODO was ordered with is_distributor DESC as well, not sure why or how we want ot sort this now + Enterprise.managed_by(spree_current_user).order('is_primary_producer ASC, name') end def collection_actions @@ -63,29 +64,29 @@ module Admin @enterprise_fees = EnterpriseFee.managed_by(spree_current_user).for_enterprise(@enterprise).order(:fee_type, :name).all end - def check_bulk_type + def check_can_change_bulk_sells unless spree_current_user.admin? params[:enterprise_set][:collection_attributes].each do |i, enterprise_params| - enterprise_params.delete :type + enterprise_params.delete :sells end end end - def check_type - params[:enterprise].delete :type unless spree_current_user.admin? + def check_can_change_sells + params[:enterprise].delete :sells unless spree_current_user.admin? end def override_owner params[:enterprise][:owner_id] = spree_current_user.id unless spree_current_user.admin? end - def check_owner + def check_can_change_owner unless ( spree_current_user == @enterprise.owner ) || spree_current_user.admin? params[:enterprise].delete :owner_id end end - def check_bulk_owner + def check_can_change_bulk_owner unless spree_current_user.admin? params[:enterprise_set][:collection_attributes].each do |i, enterprise_params| enterprise_params.delete :owner_id diff --git a/app/controllers/devise/confirmations_controller_decorator.rb b/app/controllers/devise/confirmations_controller_decorator.rb new file mode 100644 index 0000000000..ef34f28445 --- /dev/null +++ b/app/controllers/devise/confirmations_controller_decorator.rb @@ -0,0 +1,7 @@ +Devise::ConfirmationsController.class_eval do + protected + # Override of devise method in Devise::ConfirmationsController + def after_confirmation_path_for(resource_name, resource) + spree.admin_path + end +end \ No newline at end of file diff --git a/app/controllers/registration_controller.rb b/app/controllers/registration_controller.rb index 4fae68bb79..5a21d28a44 100644 --- a/app/controllers/registration_controller.rb +++ b/app/controllers/registration_controller.rb @@ -2,17 +2,12 @@ require 'open_food_network/spree_api_key_loader' class RegistrationController < BaseController include OpenFoodNetwork::SpreeApiKeyLoader - before_filter :load_spree_api_key, only: [:index, :store] + before_filter :load_spree_api_key, only: [:index] before_filter :check_user, except: :authenticate layout 'registration' def index - @enterprise_attributes = { type: 'profile' } - end - - def store - @enterprise_attributes = { is_distributor: true, is_primary_producer: true, type: 'single' } - render :index + @enterprise_attributes = { sells: 'none' } end private diff --git a/app/controllers/spree/admin/image_settings_controller_decorator.rb b/app/controllers/spree/admin/image_settings_controller_decorator.rb new file mode 100644 index 0000000000..5fe7dae551 --- /dev/null +++ b/app/controllers/spree/admin/image_settings_controller_decorator.rb @@ -0,0 +1,11 @@ +Spree::Admin::ImageSettingsController.class_eval do + # Spree stores attachent definitions in JSON. This converts the style name and format to + # strings. However, when paperclip encounters these, it doesn't recognise the format. + # Here we solve that problem by converting format and style name to symbols. + def update_paperclip_settings_with_format_styles + update_paperclip_settings_without_format_styles + Spree::Image.reformat_styles + end + + alias_method_chain :update_paperclip_settings, :format_styles +end diff --git a/app/controllers/spree/admin/overview_controller_decorator.rb b/app/controllers/spree/admin/overview_controller_decorator.rb index a2288dab88..4ae1c1b095 100644 --- a/app/controllers/spree/admin/overview_controller_decorator.rb +++ b/app/controllers/spree/admin/overview_controller_decorator.rb @@ -1,6 +1,7 @@ Spree::Admin::OverviewController.class_eval do def index - @enterprises = Enterprise.managed_by(spree_current_user).order('is_distributor DESC, is_primary_producer ASC, name') + # TODO was sorted with is_distributor DESC as well, not sure why or how we want ot sort this now + @enterprises = Enterprise.managed_by(spree_current_user).order('is_primary_producer ASC, name') @product_count = Spree::Product.active.managed_by(spree_current_user).count @order_cycle_count = OrderCycle.active.managed_by(spree_current_user).count end diff --git a/app/controllers/spree/admin/reports_controller_decorator.rb b/app/controllers/spree/admin/reports_controller_decorator.rb index 0abfd783b5..22dd41549f 100644 --- a/app/controllers/spree/admin/reports_controller_decorator.rb +++ b/app/controllers/spree/admin/reports_controller_decorator.rb @@ -6,24 +6,6 @@ require 'open_food_network/order_grouper' require 'open_food_network/customers_report' Spree::Admin::ReportsController.class_eval do - # Fetches user's distributors, suppliers and order_cycles - before_filter :load_data, only: [:customers, :products_and_inventory] - - # Render a partial for orders and fulfillment description - respond_override :index => { :html => { :success => lambda { - @reports[:orders_and_fulfillment][:description] = - render_to_string(partial: 'orders_and_fulfillment_description', layout: false, locals: {report_types: REPORT_TYPES[:orders_and_fulfillment]}).html_safe - @reports[:products_and_inventory][:description] = - render_to_string(partial: 'products_and_inventory_description', layout: false, locals: {report_types: REPORT_TYPES[:products_and_inventory]}).html_safe - @reports[:customers][:description] = - render_to_string(partial: 'customers_description', layout: false, locals: {report_types: REPORT_TYPES[:customers]}).html_safe - } } } - - # OVERRIDING THIS so we use a method not a constant for available reports - def index - @reports = available_reports - respond_with(@reports) - end REPORT_TYPES = { orders_and_fulfillment: [ @@ -42,6 +24,26 @@ Spree::Admin::ReportsController.class_eval do ] } + # Fetches user's distributors, suppliers and order_cycles + before_filter :load_data, only: [:customers, :products_and_inventory] + + # Render a partial for orders and fulfillment description + respond_override :index => { :html => { :success => lambda { + @reports[:orders_and_fulfillment][:description] = + render_to_string(partial: 'orders_and_fulfillment_description', layout: false, locals: {report_types: REPORT_TYPES[:orders_and_fulfillment]}).html_safe + @reports[:products_and_inventory][:description] = + render_to_string(partial: 'products_and_inventory_description', layout: false, locals: {report_types: REPORT_TYPES[:products_and_inventory]}).html_safe + @reports[:customers][:description] = + render_to_string(partial: 'customers_description', layout: false, locals: {report_types: REPORT_TYPES[:customers]}).html_safe + } } } + + + # Overide spree reports list. + def index + @reports = authorized_reports + respond_with(@reports) + end + # This action is short because we refactored it like bosses def customers @report_types = REPORT_TYPES[:customers] @@ -592,28 +594,29 @@ Spree::Admin::ReportsController.class_eval do private def load_data + # Load distributors either owned by the user or selling their enterprises products. my_distributors = Enterprise.is_distributor.managed_by(spree_current_user) my_suppliers = Enterprise.is_primary_producer.managed_by(spree_current_user) distributors_of_my_products = Enterprise.with_distributed_products_outer.merge(Spree::Product.in_any_supplier(my_suppliers)) @distributors = my_distributors | distributors_of_my_products + # Load suppliers either owned by the user or supplying products their enterprises distribute. suppliers_of_products_I_distribute = my_distributors.map { |d| Spree::Product.in_distributor(d) }.flatten.map(&:supplier).uniq @suppliers = my_suppliers | suppliers_of_products_I_distribute @order_cycles = OrderCycle.active_or_complete.accessible_by(spree_current_user).order('orders_close_at DESC') end - def available_reports + def authorized_reports reports = { :orders_and_distributors => {:name => "Orders And Distributors", :description => "Orders with distributor details"}, :bulk_coop => {:name => "Bulk Co-Op", :description => "Reports for Bulk Co-Op orders"}, :payments => {:name => "Payment Reports", :description => "Reports for Payments"}, :orders_and_fulfillment => {:name => "Orders & Fulfillment Reports", :description => ''}, :customers => {:name => "Customers", :description => 'Customer details'}, - :products_and_inventory => {:name => "Products & Inventory", :description => ''} + :products_and_inventory => {:name => "Products & Inventory", :description => ''}, + :sales_total => { :name => "Sales Total", :description => "Sales Total For All Orders" } } - if spree_current_user.has_spree_role? 'admin' - reports[:sales_total] = { :name => "Sales Total", :description => "Sales Total For All Orders" } - end - reports + # Return only reports the user is authorized to view. + reports.select { |action| can? action, :report } end def total_units(line_items) diff --git a/app/helpers/admin/image_settings_helper.rb b/app/helpers/admin/image_settings_helper.rb new file mode 100644 index 0000000000..10c34fe372 --- /dev/null +++ b/app/helpers/admin/image_settings_helper.rb @@ -0,0 +1,21 @@ +module Admin + module ImageSettingsHelper + def admin_image_settings_format_options + [['Unchanged', ''], ['PNG', 'png'], ['JPEG', 'jpg']] + end + + def admin_image_settings_geometry_from_style(style) + geometry, format = admin_image_settings_split_style style + geometry + end + + def admin_image_settings_format_from_style(style) + geometry, format = admin_image_settings_split_style style + format + end + + def admin_image_settings_split_style(style) + [style, nil].flatten[0..1] + end + end +end diff --git a/app/mailers/enterprise_mailer.rb b/app/mailers/enterprise_mailer.rb index 73c09e1e56..4e37d112e0 100644 --- a/app/mailers/enterprise_mailer.rb +++ b/app/mailers/enterprise_mailer.rb @@ -1,8 +1,16 @@ +require 'devise/mailers/helpers' class EnterpriseMailer < Spree::BaseMailer - def creation_confirmation(enterprise) - find_enterprise(enterprise) - subject = "#{@enterprise.name} is now on #{Spree::Config[:site_name]}" - mail(:to => @enterprise.owner.email, :from => from_address, :subject => subject) + include Devise::Mailers::Helpers + + def confirmation_instructions(record, token, opts={}) + @token = token + find_enterprise(record) + opts = { + subject: "Please confirm your email for #{@enterprise.name}", + to: [ @enterprise.owner.email, @enterprise.email ].uniq, + from: from_address, + } + devise_mail(record, :confirmation_instructions, opts) end private diff --git a/app/mailers/spree/user_mailer_decorator.rb b/app/mailers/spree/user_mailer_decorator.rb index ff8bdc4691..dd21a3d0d7 100644 --- a/app/mailers/spree/user_mailer_decorator.rb +++ b/app/mailers/spree/user_mailer_decorator.rb @@ -1,5 +1,5 @@ Spree::UserMailer.class_eval do - + def signup_confirmation(user) @user = user mail(:to => user.email, :from => from_address, diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index dfb7945878..0cc6fd19b9 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -1,12 +1,14 @@ class Enterprise < ActiveRecord::Base - TYPES = %w(full single profile) + SELLS = %w(none own any) ENTERPRISE_SEARCH_RADIUS = 100 + devise :confirmable, reconfirmable: true + self.inheritance_column = nil acts_as_gmappable :process_geocoding => false - after_create :send_creation_email + before_create :check_email has_and_belongs_to_many :groups, class_name: 'EnterpriseGroup' has_many :producer_properties, foreign_key: 'producer_id' @@ -34,7 +36,7 @@ class Enterprise < ActiveRecord::Base path: 'public/images/enterprises/logos/:id/:style/:basename.:extension' has_attached_file :promo_image, - styles: { large: "1200x260#", medium: "720x156#", thumb: "100x100>" }, + styles: { large: ["1200x260#", :jpg], medium: ["720x156#", :jpg], thumb: ["100x100>", :jpg] }, url: '/images/enterprises/promo_images/:id/:style/:basename.:extension', path: 'public/images/enterprises/promo_images/:id/:style/:basename.:extension' @@ -47,11 +49,12 @@ class Enterprise < ActiveRecord::Base validates :name, presence: true - validates :type, presence: true, inclusion: {in: TYPES} + validates :sells, presence: true, inclusion: {in: SELLS} validates :address, presence: true, associated: true validates :email, presence: true validates_presence_of :owner validate :enforce_ownership_limit, if: lambda { owner_id_changed? && !owner_id.nil? } + validates_length_of :description, :maximum => 255 before_validation :ensure_owner_is_manager, if: lambda { owner_id_changed? && !owner_id.nil? } before_validation :set_unused_address_fields @@ -59,8 +62,10 @@ class Enterprise < ActiveRecord::Base scope :by_name, order('name') scope :visible, where(:visible => true) + scope :confirmed, where('confirmed_at IS NOT NULL') + scope :unconfirmed, where('confirmed_at IS NULL') scope :is_primary_producer, where(:is_primary_producer => true) - scope :is_distributor, where(:is_distributor => true) + scope :is_distributor, where('sells != ?', 'none') scope :supplying_variant_in, lambda { |variants| joins(:supplied_products => :variants_including_master).where('spree_variants.id IN (?)', variants).select('DISTINCT enterprises.*') } scope :with_supplied_active_products_on_hand, lambda { joins(:supplied_products) @@ -211,36 +216,30 @@ class Enterprise < ActiveRecord::Base Spree::Variant.joins(:product => :product_distributions).where('product_distributions.distributor_id=?', self.id) end - # Replaces currententerprse type field. - def sells - # Type: full - single - profile becomes Sells: all - own - none - # Remove this return later. - return "none" if !is_distributor || type == "profile" - return "own" if type == "single" || suppliers == [self] - "all" + def is_distributor + self.sells != "none" end # Simplify enterprise categories for frontend logic and icons, and maybe other things. - def enterprise_category + def category # Make this crazy logic human readable so we can argue about it sanely. - # This can be simplified later, it's like this for readablitlty during changes. - category = is_primary_producer ? "producer_" : "non_producer_" - category << "sell_" + sells + cat = self.is_primary_producer ? "producer_" : "non_producer_" + cat << "sells_" + self.sells # Map backend cases to front end cases. - case category - when "producer_sell_all" - "producer_hub" # Producer hub who sells own and others produce and supplies other hubs. - when "producer_sell_own" - "producer_shop" # Producer with shopfront and supplies other hubs. - when "producer_sell_none" - "producer" # Producer only supplies through others. - when "non_producer_sell_all" - "hub" # Hub selling others products in order cycles. - when "non_producer_sell_own" - "hub" # Wholesaler selling through own shopfront? - when "non_producer_sell_none" - "hub_profile" # Hub selling outside the system. + case cat + when "producer_sells_any" + :producer_hub # Producer hub who sells own and others produce and supplies other hubs. + when "producer_sells_own" + :producer_shop # Producer with shopfront and supplies other hubs. + when "producer_sells_none" + :producer # Producer only supplies through others. + when "non_producer_sells_any" + :hub # Hub selling others products in order cycles. + when "non_producer_sells_own" + :hub # Wholesaler selling through own shopfront? Does this need a separate name? Should it exist? + when "non_producer_sells_none" + :hub_profile # Hub selling outside the system. end end @@ -260,14 +259,20 @@ class Enterprise < ActiveRecord::Base select('DISTINCT spree_taxons.*') end + protected + + def devise_mailer + EnterpriseMailer + end + private - def send_creation_email - EnterpriseMailer.creation_confirmation(self).deliver + def check_email + skip_confirmation! if owner.enterprises.confirmed.map(&:email).include?(email) end def strip_url(url) - url.andand.sub /(https?:\/\/)?/, '' + url.andand.sub(/(https?:\/\/)?/, '') end def set_unused_address_fields diff --git a/app/models/enterprise_group.rb b/app/models/enterprise_group.rb index cdc9d9932a..6e61b1a564 100644 --- a/app/models/enterprise_group.rb +++ b/app/models/enterprise_group.rb @@ -15,7 +15,7 @@ class EnterpriseGroup < ActiveRecord::Base path: 'public/images/enterprise_groups/logos/:id/:style/:basename.:extension' has_attached_file :promo_image, - styles: {large: "1200x260#"}, + styles: {large: ["1200x260#", :jpg]}, url: '/images/enterprise_groups/promo_images/:id/:style/:basename.:extension', path: 'public/images/enterprise_groups/promo_images/:id/:style/:basename.:extension' diff --git a/app/models/spree/ability_decorator.rb b/app/models/spree/ability_decorator.rb index 5fdb4c2342..1fb824d09c 100644 --- a/app/models/spree/ability_decorator.rb +++ b/app/models/spree/ability_decorator.rb @@ -1,6 +1,8 @@ class AbilityDecorator include CanCan::Ability + # All abilites are allocated from this initialiser, currently in 5 chunks. + # Spree also defines other abilities. def initialize(user) add_base_abilities user if is_new_user? user add_enterprise_management_abilities user if can_manage_enterprises? user @@ -9,27 +11,31 @@ class AbilityDecorator add_relationship_management_abilities user if can_manage_relationships? user end - + # New users have no enterprises. def is_new_user?(user) user.enterprises.blank? end + # Users can manage an enterprise if they have one. def can_manage_enterprises?(user) user.enterprises.present? end + # Users can manage products if they have an enterprise. def can_manage_products?(user) - can_manage_enterprises? user + can_manage_enterprises?(user) && user.enterprises.is_primary_producer.present? end + # Users can manage orders if they have a sells own/any enterprise. def can_manage_orders?(user) - ( user.enterprises.map(&:type) & %w(single full) ).any? + ( user.enterprises.map(&:sells) & %w(own any) ).any? end def can_manage_relationships?(user) can_manage_enterprises? user end + # New users can create an enterprise, and gain other permissions from doing this. def add_base_abilities(user) can [:create], Enterprise end @@ -47,6 +53,12 @@ class AbilityDecorator can [:read, :edit, :update, :bulk_update], Enterprise do |enterprise| user.enterprises.include? enterprise end + + # All enterprises can have fees, though possibly suppliers don't need them? + can [:index, :create], EnterpriseFee + can [:admin, :read, :edit, :bulk_update, :destroy], EnterpriseFee do |enterprise_fee| + user.enterprises.include? enterprise_fee.enterprise + end end def add_product_management_abilities(user) @@ -66,6 +78,9 @@ class AbilityDecorator can [:admin, :index, :read, :search], Spree::Taxon can [:admin, :index, :read, :create, :edit], Spree::Classification + + # Reports page + can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory], :report end def add_order_management_abilities(user) @@ -76,7 +91,7 @@ class AbilityDecorator # during the order creation process from the admin backend order.distributor.nil? || user.enterprises.include?(order.distributor) end - can [:admin, :bulk_management], Spree::Order if user.admin? || user.enterprises.any?(&:is_distributor?) + can [:admin, :bulk_management], Spree::Order if user.admin? || user.enterprises.any?(&:is_distributor) can [:admin, :create], Spree::LineItem can [:admin, :index, :read, :create, :edit, :update, :fire], Spree::Payment @@ -90,11 +105,6 @@ class AbilityDecorator end can [:for_order_cycle], Enterprise - can [:index, :create], EnterpriseFee - can [:admin, :read, :edit, :bulk_update, :destroy], EnterpriseFee do |enterprise_fee| - user.enterprises.include? enterprise_fee.enterprise - end - can [:admin, :index, :read, :create, :edit, :update], ExchangeVariant can [:admin, :index, :read, :create, :edit, :update], Exchange can [:admin, :index, :read, :create, :edit, :update], ExchangeFee @@ -111,7 +121,7 @@ class AbilityDecorator end # Reports page - can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :bulk_coop, :payments, :orders_and_fulfillment, :products_and_inventory], :report + can [:admin, :index, :customers, :group_buys, :bulk_coop, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory], :report end diff --git a/app/models/spree/image_decorator.rb b/app/models/spree/image_decorator.rb new file mode 100644 index 0000000000..7d86aae3a6 --- /dev/null +++ b/app/models/spree/image_decorator.rb @@ -0,0 +1,23 @@ +Spree::Image.class_eval do + # Spree stores attachent definitions in JSON. This converts the style name and format to + # strings. However, when paperclip encounters these, it doesn't recognise the format. + # Here we solve that problem by converting format and style name to symbols. + # See also: ImageSettingsController decorator. + # + # eg. {'mini' => ['48x48>', 'png']} is converted to {mini: ['48x48>', :png]} + def self.format_styles(styles) + styles_a = styles.map do |name, style| + style[1] = style[1].to_sym if style.is_a? Array + [name.to_sym, style] + end + + Hash[styles_a] + end + + def self.reformat_styles + Spree::Image.attachment_definitions[:attachment][:styles] = + format_styles(Spree::Image.attachment_definitions[:attachment][:styles]) + end + + reformat_styles +end diff --git a/app/overrides/spree/admin/image_settings/edit/add_image_format.html.haml.deface b/app/overrides/spree/admin/image_settings/edit/add_image_format.html.haml.deface new file mode 100644 index 0000000000..deb79b5bbf --- /dev/null +++ b/app/overrides/spree/admin/image_settings/edit/add_image_format.html.haml.deface @@ -0,0 +1,11 @@ +/ replace_contents '#styles_list' + +- @styles.each_with_index do |(style_name, style_value), index| + .field.three.columns + = label_tag "attachment_styles[#{style_name}]", style_name + %a.destroy_style.with-tip{:alt => t(:destroy), :href => "#", :title => t(:destroy)} + %i.icon-trash + = text_field_tag "attachment_styles[#{style_name}][]", admin_image_settings_geometry_from_style(style_value), :class => 'fullwidth' + %br/ + - current_format = admin_image_settings_format_from_style(style_value) || '' + = select_tag "attachment_styles[#{style_name}][]", options_for_select(admin_image_settings_format_options, current_format), :class => 'fullwidth', :id => "attachment_styles_format_#{style_name}" diff --git a/app/serializers/api/admin/enterprise_serializer.rb b/app/serializers/api/admin/enterprise_serializer.rb index 7b82ad0cae..e6d00b7aae 100644 --- a/app/serializers/api/admin/enterprise_serializer.rb +++ b/app/serializers/api/admin/enterprise_serializer.rb @@ -1,3 +1,3 @@ class Api::Admin::EnterpriseSerializer < ActiveModel::Serializer - attributes :name, :id, :is_primary_producer, :is_distributor, :payment_method_ids, :shipping_method_ids + attributes :name, :id, :is_primary_producer, :is_distributor, :sells, :category, :payment_method_ids, :shipping_method_ids end diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index 6841337024..a2aa506d2f 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -17,9 +17,6 @@ end class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer attributes :orders_close_at, :active - #TODO: Remove these later - attributes :icon, :icon_font, :producer_icon_font, :has_shopfront, :has_hub_listing, :enterprise_category - def orders_close_at OrderCycle.first_closing_for(object).andand.orders_close_at end @@ -28,60 +25,6 @@ class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer @options[:active_distributors].andand.include? object end - def enterprise_category - object.enterprise_category - end - - def has_shopfront - object.is_distributor && object.type != 'profile' - end - - # Used to select enterprises for hub listing - def has_hub_listing - has_shopfront || object.enterprise_category == "hub_profile" - end - - # Map svg icons. - def icon - icons = { - "hub" => "/assets/map_005-hub.svg", - "hub_profile" => "/assets/map_006-hub-profile.svg", - "producer_hub" => "/assets/map_005-hub.svg", - "producer_shop" => "/assets/map_003-producer-shop.svg", - "producer" => "/assets/map_001-producer-only.svg", - "producer_profile" => "/assets/map_002-producer-only-profile.svg", - } - icons[object.enterprise_category] - end - - # Choose regular icon font for enterprises. - def icon_font - icon_fonts = { - "hub" => "ofn-i_063-hub", - "hub_profile" => "ofn-i_064-hub-reversed", - "producer_hub" => "ofn-i_063-hub", - "producer_shop" => "ofn-i_059-producer", - "producer" => "ofn-i_059-producer", - "producer_profile" => "ofn-i_060-producer-reversed", - } - icon_fonts[object.enterprise_category] - end - - # Choose producer page icon font - yes, sadly its got to be different. - # This duplicates some code but covers the producer page edge case where - # producer-hub has a producer icon without needing to duplicate the category logic in angular. - def producer_icon_font - icon_fonts = { - "hub" => "", - "hub_profile" => "", - "producer_hub" => "ofn-i_059-producer", - "producer_shop" => "ofn-i_059-producer", - "producer" => "ofn-i_059-producer", - "producer_profile" => "ofn-i_060-producer-reversed", - "empty" => "", - } - icon_fonts[object.enterprise_category] - end end @@ -92,8 +35,8 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer attributes :name, :id, :description, :latitude, :longitude, :long_description, :website, :instagram, :linkedin, :twitter, :facebook, :is_primary_producer, :is_distributor, :phone, :visible, - :email, :hash, :logo, :promo_image, :path, - :pickup, :delivery + :email, :hash, :logo, :promo_image, :path, :pickup, :delivery, + :icon, :icon_font, :producer_icon_font, :category has_many :distributed_taxons, key: :taxons, serializer: Api::IdSerializer has_many :supplied_taxons, serializer: Api::IdSerializer @@ -102,6 +45,10 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer has_one :address, serializer: Api::AddressSerializer + def visible + object.visible && object.confirmed? + end + def pickup object.shipping_methods.where(:require_ship_address => false).present? end @@ -131,4 +78,42 @@ class Api::CachedEnterpriseSerializer < ActiveModel::Serializer def path "/enterprises/#{object.to_param}/shop" end + + # Map svg icons. + def icon + icons = { + :hub => "/assets/map_005-hub.svg", + :hub_profile => "/assets/map_006-hub-profile.svg", + :producer_hub => "/assets/map_005-hub.svg", + :producer_shop => "/assets/map_003-producer-shop.svg", + :producer => "/assets/map_001-producer-only.svg", + } + icons[object.category] + end + + # Choose regular icon font for enterprises. + def icon_font + icon_fonts = { + :hub => "ofn-i_063-hub", + :hub_profile => "ofn-i_064-hub-reversed", + :producer_hub => "ofn-i_063-hub", + :producer_shop => "ofn-i_059-producer", + :producer => "ofn-i_059-producer", + } + icon_fonts[object.category] + end + + # Choose producer page icon font - yes, sadly its got to be different. + # This duplicates some code but covers the producer page edge case where + # producer-hub has a producer icon without needing to duplicate the category logic in angular. + def producer_icon_font + icon_fonts = { + :hub => "", + :hub_profile => "", + :producer_hub => "ofn-i_059-producer", + :producer_shop => "ofn-i_059-producer", + :producer => "ofn-i_059-producer", + } + icon_fonts[object.category] + end end diff --git a/app/serializers/api/state_serializer.rb b/app/serializers/api/state_serializer.rb index bcf9221ec5..7a76e340b4 100644 --- a/app/serializers/api/state_serializer.rb +++ b/app/serializers/api/state_serializer.rb @@ -1,3 +1,7 @@ class Api::StateSerializer < ActiveModel::Serializer attributes :id, :name, :abbr + + def abbr + object.abbr.upcase + end end \ No newline at end of file diff --git a/app/views/admin/enterprises/_form.html.haml b/app/views/admin/enterprises/_form.html.haml index 921807b425..20bd174de9 100644 --- a/app/views/admin/enterprises/_form.html.haml +++ b/app/views/admin/enterprises/_form.html.haml @@ -32,13 +32,9 @@ .row .three.columns.alpha - %label Enterprise Type(s) - .with-tip{'data-powertip' => "Select 'Producer' if you are a primary producer of food. Select 'Hub' if you want a shop-front. You can choose either or both."} + %label Primary Producer + .with-tip{'data-powertip' => "Select 'Producer' if you are a primary producer of food."} %a What's this? - .two.columns - = f.check_box :is_distributor, 'ng-model' => 'Enterprise.is_distributor' -   - = f.label :is_distributor, 'Hub' .five.columns.omega = f.check_box :is_primary_producer, 'ng-model' => 'Enterprise.is_primary_producer'   @@ -47,21 +43,21 @@ .row .alpha.eleven.columns .three.columns.alpha - = f.label :type, 'Profile type' - .with-tip{'data-powertip' => "Full - enterprise may have products and relationships.
Single - enterprise may have products but no relationships.
Profile - enterprise has a profile but no products or relationships.
"} + = f.label :sells, 'Sells' + .with-tip{'data-powertip' => "None - enterprise does not sell to customers directly.
Own - Enterprise sells own products to customers.
Any - Enterprise can sell own or other enterprises products.
"} %a What's this? .two.columns - = f.radio_button :type, "full" + = f.radio_button :sells, "none", 'ng-model' => 'Enterprise.sells'   - = f.label :type, "Full", value: "full" + = f.label :sells, "None", value: "none" .two.columns - = f.radio_button :type, "single" + = f.radio_button :sells, "own", 'ng-model' => 'Enterprise.sells'   - = f.label :type, "Single", value: "single" + = f.label :sells, "Own", value: "own" .four.columns.omega - = f.radio_button :type, "profile" + = f.radio_button :sells, "any", 'ng-model' => 'Enterprise.sells'   - = f.label :type, "Profile", value: "profile" + = f.label :sells, "Any", value: "any" .row .three.columns.alpha %label Visible in search? @@ -177,7 +173,7 @@ .alpha.three.columns = f.label :description, 'Short Description' .omega.eight.columns - = f.text_field :description, placeholder: 'Tell us about your enterprise in one or two sentences' + = f.text_field :description, maxlength: 255, placeholder: 'Tell us about your enterprise in one or two sentences' .row .alpha.three.columns = f.label :long_description, 'About Us' diff --git a/app/views/admin/enterprises/_ng_form.html.haml b/app/views/admin/enterprises/_ng_form.html.haml index 18e9d6ed36..e3ebe95877 100644 --- a/app/views/admin/enterprises/_ng_form.html.haml +++ b/app/views/admin/enterprises/_ng_form.html.haml @@ -3,7 +3,7 @@ = admin_inject_payment_methods = admin_inject_shipping_methods -.sixteen.columns.alpha{ ng: { app: 'admin.enterprises', controller: 'enterpriseCtrl' } } +.sixteen.columns.alpha{ ng: { app: 'admin.enterprises', controller: 'enterpriseCtrl' }, nav: { check: '', callback: 'enterpriseNavCallback()' }} .eleven.columns.alpha = render 'form', f: f .one.column   diff --git a/app/views/admin/enterprises/_sidebar.html.haml b/app/views/admin/enterprises/_sidebar.html.haml index ac4966fa80..a4c149c9e9 100644 --- a/app/views/admin/enterprises/_sidebar.html.haml +++ b/app/views/admin/enterprises/_sidebar.html.haml @@ -1,6 +1,6 @@ +- if can? :admin, EnterpriseFee + = render 'sidebar_enterprise_fees', f: f - if can? :admin, Spree::PaymentMethod = render 'sidebar_payment_methods', f: f - if can? :admin, Spree::ShippingMethod = render 'sidebar_shipping_methods', f: f -- if can? :admin, EnterpriseFee - = render 'sidebar_enterprise_fees', f: f diff --git a/app/views/admin/enterprises/_sidebar_enterprise_fees.html.haml b/app/views/admin/enterprises/_sidebar_enterprise_fees.html.haml index 50de7e07d9..566d87b832 100644 --- a/app/views/admin/enterprises/_sidebar_enterprise_fees.html.haml +++ b/app/views/admin/enterprises/_sidebar_enterprise_fees.html.haml @@ -1,5 +1,5 @@ - enterprise_fees_color = @enterprise_fees.count > 0 ? "blue" : "red" -.sidebar_item.four.columns.alpha#enterprise_fees{ ng: { show: 'Enterprise.is_distributor' } } +.sidebar_item.four.columns.alpha#enterprise_fees{ ng: { show: 'Enterprise.category != "producer_hub"' } } .four.columns.alpha.header{ class: "#{enterprise_fees_color}" } %span.four.columns.alpha.centered Enterprise Fees .four.columns.alpha.list{ class: "#{enterprise_fees_color}" } diff --git a/app/views/admin/enterprises/_sidebar_payment_methods.html.haml b/app/views/admin/enterprises/_sidebar_payment_methods.html.haml index cb31cc7fff..2d8a174e5c 100644 --- a/app/views/admin/enterprises/_sidebar_payment_methods.html.haml +++ b/app/views/admin/enterprises/_sidebar_payment_methods.html.haml @@ -1,4 +1,4 @@ -.sidebar_item.four.columns.alpha#payment_methods{ ng: { show: 'Enterprise.is_distributor' } } +.sidebar_item.four.columns.alpha#payment_methods{ ng: { show: 'Enterprise.sells != "none"' } } .four.columns.alpha.header{ ng: { class: "paymentMethodsColor()" } } %span.four.columns.alpha.centered Payment Methods .four.columns.alpha.list{ ng: { class: "paymentMethodsColor()" } } diff --git a/app/views/admin/enterprises/_sidebar_shipping_methods.html.haml b/app/views/admin/enterprises/_sidebar_shipping_methods.html.haml index 14f65fb755..3e9a0434c0 100644 --- a/app/views/admin/enterprises/_sidebar_shipping_methods.html.haml +++ b/app/views/admin/enterprises/_sidebar_shipping_methods.html.haml @@ -1,4 +1,4 @@ -.sidebar_item.four.columns.alpha#shipping_methods{ ng: { show: 'Enterprise.is_distributor' } } +.sidebar_item.four.columns.alpha#shipping_methods{ ng: { show: 'Enterprise.sells != "none"' } } .four.columns.alpha.header{ ng: { class: "shippingMethodsColor()" } } %span.four.columns.alpha.centered Shipping Methods .four.columns.alpha.list{ ng: { class: "shippingMethodsColor()" } } diff --git a/app/views/admin/enterprises/edit.html.haml b/app/views/admin/enterprises/edit.html.haml index 6a8d48b24b..3c132bf5e2 100644 --- a/app/views/admin/enterprises/edit.html.haml +++ b/app/views/admin/enterprises/edit.html.haml @@ -4,7 +4,7 @@ Editing: = @enterprise.name -= form_for [main_app, :admin, @enterprise] do |f| += form_for [main_app, :admin, @enterprise], html: { "ng-app" => 'admin.enterprises', "ng-submit" => "navClear()", "ng-controller" => 'enterpriseCtrl' , "nav-check" => '', "nav-callback" => 'enterpriseNavCallback()' } do |f| = render 'ng_form', f: f .twelve.columns.alpha = render partial: 'spree/admin/shared/edit_resource_links' diff --git a/app/views/admin/enterprises/index.html.haml b/app/views/admin/enterprises/index.html.haml index 03e5335155..ee1bdde996 100644 --- a/app/views/admin/enterprises/index.html.haml +++ b/app/views/admin/enterprises/index.html.haml @@ -8,7 +8,7 @@ = render 'admin/shared/enterprises_sub_menu' -= form_for @enterprise_set, :url => main_app.bulk_update_admin_enterprises_path do |f| += form_for @enterprise_set, url: main_app.bulk_update_admin_enterprises_path do |f| %table#listing_enterprises.index %colgroup %col{style: "width: 25%;"}/ @@ -23,9 +23,9 @@ %tr{"data-hook" => "enterprises_header"} %th Name %th Role - %th Visible? - if spree_current_user.admin? - %th Type + %th Sells + %th Visible? - if spree_current_user.admin? %th Owner %th @@ -37,12 +37,9 @@ %td = enterprise_form.check_box :is_primary_producer Producer - %br/ - = enterprise_form.check_box :is_distributor - Hub - %td= enterprise_form.check_box :visible - if spree_current_user.admin? - %td= enterprise_form.select :type, Enterprise::TYPES, {}, class: 'select2 fullwidth' + %td= enterprise_form.select :sells, Enterprise::SELLS, {}, class: 'select2 fullwidth' + %td= enterprise_form.check_box :visible - if spree_current_user.admin? %td= enterprise_form.select :owner_id, enterprise.users.map{ |e| [ e.email, e.id ] }, {}, class: "select2 fullwidth" %td{"data-hook" => "admin_users_index_row_actions"} diff --git a/app/views/admin/enterprises/new.html.haml b/app/views/admin/enterprises/new.html.haml index 32f6ea0a8d..3df3551f47 100644 --- a/app/views/admin/enterprises/new.html.haml +++ b/app/views/admin/enterprises/new.html.haml @@ -3,7 +3,7 @@ - content_for :page_title do New Enterprise -= form_for [main_app, :admin, @enterprise] do |f| - = render partial: 'ng_form', :locals => { f: f } += form_for [main_app, :admin, @enterprise], html: { "ng-app" => 'admin.enterprises', "ng-submit" => "navClear()", "ng-controller" => 'enterpriseCtrl' , "nav-check" => '', "nav-callback" => 'enterpriseNavCallback()' } do |f| + = render 'ng_form', f: f .twelve.columns.alpha = render partial: 'spree/admin/shared/new_resource_links' diff --git a/app/views/admin/order_cycles/_row.html.haml b/app/views/admin/order_cycles/_row.html.haml index 75e84de025..3b8c6f34bc 100644 --- a/app/views/admin/order_cycles/_row.html.haml +++ b/app/views/admin/order_cycles/_row.html.haml @@ -5,12 +5,12 @@ %td= order_cycle_form.text_field :orders_open_at, :class => 'datetimepicker', :value => order_cycle.orders_open_at %td= order_cycle_form.text_field :orders_close_at, :class => 'datetimepicker', :value => order_cycle.orders_close_at %td.suppliers - - order_cycle.suppliers.managed_by(spree_current_user).each do |s| + - order_cycle.suppliers.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |s| = s.name %br/ %td= order_cycle.coordinator.name %td.distributors - - order_cycle.distributors.managed_by(spree_current_user).each do |d| + - order_cycle.distributors.merge(OpenFoodNetwork::Permissions.new(spree_current_user).order_cycle_enterprises).each do |d| = d.name %br/ diff --git a/app/views/admin/order_cycles/index.html.haml b/app/views/admin/order_cycles/index.html.haml index 1fac06f507..f9f832d83d 100644 --- a/app/views/admin/order_cycles/index.html.haml +++ b/app/views/admin/order_cycles/index.html.haml @@ -5,8 +5,6 @@ %li#new_order_cycle_link = button_link_to "New Order Cycle", main_app.new_admin_order_cycle_path, :icon => 'icon-plus', :id => 'admin_new_order_cycle_link' - - = form_for @order_cycle_set, :url => main_app.bulk_update_admin_order_cycles_path do |f| %table.index#listing_order_cycles %colgroup diff --git a/app/views/checkout/_shipping.html.haml b/app/views/checkout/_shipping.html.haml index 27089527a2..5ba5ed15e4 100644 --- a/app/views/checkout/_shipping.html.haml +++ b/app/views/checkout/_shipping.html.haml @@ -83,4 +83,4 @@ .row .small-12.columns.text-right - %button.primary{"ng-disabled" => "shipping.$invalid", "ng-click" => "next($event)", "ofn-focus" => "accordion['shipping']"} Next + %button.primary{"ng-disabled" => "shipping.$invalid", "ng-click" => "next($event)"} Next diff --git a/app/views/enterprise_mailer/confirmation_instructions.html.haml b/app/views/enterprise_mailer/confirmation_instructions.html.haml new file mode 100644 index 0000000000..19bab3f5ee --- /dev/null +++ b/app/views/enterprise_mailer/confirmation_instructions.html.haml @@ -0,0 +1,100 @@ +/ ORIGINAL & UGLY: +/ %p= "Welcome #{@resource.contact}!" +/ %p= "Please confirm your email address for #{@resource.name}." +/ %p= "Click the link below to activate your enterprise:" +/ %p= link_to 'Confirm this email address', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) + + +%html{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;", :xmlns => "http://www.w3.org/1999/xhtml"} + %head{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + / If you delete this meta tag, Half Life 3 will never be released. + %meta{:content => "width=device-width", :name => "viewport", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"}/ + %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"}/ + %title{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} Open Food Network + %link{:href => "http://rohanmitchell.com/random/template/basic-email-template/stylesheets/email.css", :rel => "stylesheet", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;", :type => "text/css"}/ + %body{:bgcolor => "#FFFFFF", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-webkit-text-size-adjust: none;height: 100%;width: 100%!important;"} + / HEADER + %table.head-wrap{:bgcolor => "#333333", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;width: 100%;"} + %tr{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td.header.container{:style => "margin: 0 auto!important;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;display: block!important;max-width: 600px!important;clear: both!important;"} + .content{:style => "margin: 0 auto;padding: 15px;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;max-width: 600px;display: block;"} + %table{:bgcolor => "#333333", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;width: 100%;"} + %tr{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %img{:src => "https://openfoodnetwork.org.au/assets/ofn_logo_beta-8e4dfc79deb25def2d107dea52dce492.png", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;max-width: 100%;", :width => "200"}/ + %td{:align => "right", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %h6.collapse{:style => "margin: 0!important;padding: 0;font-family: \"HelveticaNeue-Light\", \"Helvetica Neue Light\", \"Helvetica Neue\", Helvetica, Arial, \"Lucida Grande\", sans-serif;line-height: 1.1;margin-bottom: 15px;color: #999;font-weight: 900;font-size: 14px;text-transform: uppercase;"} Open Food Network + %td{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + / /HEADER + / BODY + %table.body-wrap{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;width: 100%;"} + %tr{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td.container{:bgcolor => "#FFFFFF", :style => "margin: 0 auto!important;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;display: block!important;max-width: 600px!important;clear: both!important;"} + .content{:style => "margin: 0 auto;padding: 15px;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;max-width: 600px;display: block;"} + %table{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;width: 100%;"} + %tr{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %h3{:style => "margin: 0;padding: 0;font-family: \"HelveticaNeue-Light\", \"Helvetica Neue Light\", \"Helvetica Neue\", Helvetica, Arial, \"Lucida Grande\", sans-serif;line-height: 1.1;margin-bottom: 15px;color: #000;font-weight: 500;font-size: 27px;"}= "Welcome, #{@resource.contact}!" + %p.lead{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;margin-bottom: 10px;font-weight: normal;font-size: 17px;line-height: 1.6;"} + = "Please confirm email address for your enterprise " + %strong + = "#{@resource.name}." + %p   + / Callout Panel + %p.callout{:style => "margin: 0; padding: 15px;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;margin-bottom: 15px;font-weight: normal;font-size: 14px;line-height: 1.6;background-color: #e1f0f5;"} + = "Click the link below to confirm email and to activate your enterprise. This link can be used only once:" + %br + %strong + = link_to 'Confirm this email address »', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) + / /Callout Panel + %p   + %p{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;margin-bottom: 10px;font-weight: normal;font-size: 14px;line-height: 1.6;"}= "We're so excited that you're joining the Open Food Network! Don't hestitate to get in touch if you have any questions." + %p   + / social & contact + %table.social{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;background-color: #ebebeb;width: 100%;", :width => "100%"} + %tr{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + / column 1 + %table.column{:align => "left", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;width: 280px;float: left;min-width: 279px;"} + %tr{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td{:style => "margin: 0;padding: 15px;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %h5{:style => "margin: 0;padding: 0;font-family: \"HelveticaNeue-Light\", \"Helvetica Neue Light\", \"Helvetica Neue\", Helvetica, Arial, \"Lucida Grande\", sans-serif;line-height: 1.1;margin-bottom: 15px;color: #000;font-weight: 900;font-size: 17px;"} Connect with Us: + %p{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;margin-bottom: 10px;font-weight: normal;font-size: 14px;line-height: 1.6;"} + %a.soc-btn.fb{:href => "https://www.facebook.com/OpenFoodNet", :style => "margin: 0;padding: 3px 7px;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #FFF;font-size: 12px;margin-bottom: 10px;text-decoration: none;font-weight: bold;display: block;text-align: center;background-color: #3B5998!important;", :target => "_blank"} Facebook + %a.soc-btn.tw{:href => "https://twitter.com/OpenFoodNet", :style => "margin: 0;padding: 3px 7px;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #FFF;font-size: 12px;margin-bottom: 10px;text-decoration: none;font-weight: bold;display: block;text-align: center;background-color: #1daced!important;", :target => "_blank"} Twitter + %a.soc-btn.li{:href => "http://www.linkedin.com/groups/Open-Food-Foundation-4743336", :style => "margin: 0;padding: 3px 7px;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #FFF;font-size: 12px;margin-bottom: 10px;text-decoration: none;font-weight: bold;display: block;text-align: center;background-color: #0073b2!important;", :target => "_blank"} LinkedIn + / /column 1 + / column 2 + %table.column{:align => "left", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;width: 280px;float: left;min-width: 279px;"} + %tr{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td{:style => "margin: 0;padding: 15px;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %h5{:style => "margin: 0;padding: 0;font-family: \"HelveticaNeue-Light\", \"Helvetica Neue Light\", \"Helvetica Neue\", Helvetica, Arial, \"Lucida Grande\", sans-serif;line-height: 1.1;margin-bottom: 15px;color: #000;font-weight: 900;font-size: 17px;"} Email us: + %p{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;margin-bottom: 10px;font-weight: normal;font-size: 14px;line-height: 1.6;"} + %strong{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %a{:href => "hello@openfoodnetwork.org", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #0096ad;"} hello@openfoodnetwork.org + / /column 2 + %span.clear{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;display: block;clear: both;"} + / /social & contact + / /content + %td{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + / /BODY + / FOOTER + %table.footer-wrap{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;width: 100%;clear: both!important;"} + %tr{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td.container{:style => "margin: 0 auto!important;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;display: block!important;max-width: 600px!important;clear: both!important;"} + / content + .content{:style => "margin: 0 auto;padding: 15px;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;max-width: 600px;display: block;"} + %table{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;width: 100%;"} + %tr{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %td{:align => "center", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + %p{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;margin-bottom: 10px;font-weight: normal;font-size: 14px;line-height: 1.6;"} + %a{:href => "https://openfoodnetwork.org.au/Terms-of-service.pdf", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #0096ad;", :target => "_blank"} Terms of service + | + %a{:href => "http://www.openfoodnetwork.org.au", :style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;color: #0096ad;", :target => "_blank"} Open Food Network + / | Unsubscribe + / /content + %td{:style => "margin: 0;padding: 0;font-family: \"Helvetica Neue\", \"Helvetica\", Helvetica, Arial, sans-serif;"} + / /FOOTER diff --git a/app/views/enterprise_mailer/creation_confirmation.html.haml b/app/views/enterprise_mailer/creation_confirmation.html.haml deleted file mode 100644 index 0df3bf06a0..0000000000 --- a/app/views/enterprise_mailer/creation_confirmation.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -%h1 - = @enterprise.name + " has been created" - -%h3 - Why not check it out on - %a{ href: "#{map_url}" } - = Spree::Config[:site_name] + "?" - -If you have any questions, please get in touch with us at: hello@openfoodnetwork.org diff --git a/app/views/groups/index.html.haml b/app/views/groups/index.html.haml index b033c93d12..1aa5b02629 100644 --- a/app/views/groups/index.html.haml +++ b/app/views/groups/index.html.haml @@ -40,8 +40,9 @@ %h5 Our hubs & producers %ul.small-block-grid-2 %li{"ng-repeat" => "enterprise in group.enterprises", "scroll-after-load" => true} - %hub-modal{"ng-if" => "enterprise.is_distributor"} - %producer-modal{"ng-if" => "!enterprise.is_distributor", "show-hub-actions" => 'true'} + %enterprise-modal{"ng-if" => "enterprise.is_distributor"} + {{ enterprise.name }} + %enterprise-modal{"ng-if" => "!enterprise.is_distributor", "show-hub-actions" => 'true'} {{ enterprise.name }} diff --git a/app/views/home/_fat.html.haml b/app/views/home/_fat.html.haml index 406431b9b7..b3741e5aec 100644 --- a/app/views/home/_fat.html.haml +++ b/app/views/home/_fat.html.haml @@ -23,7 +23,7 @@ %label Our producers %ul.small-block-grid-2.medium-block-grid-1.large-block-grid-2 %li{"ng-repeat" => "enterprise in hub.producers"} - %producer-modal + %enterprise-modal %i.ofn-i_036-producers %span {{ enterprise.name }} diff --git a/app/views/home/_filters.html.haml b/app/views/home/_filters.html.haml index ff13a0c33c..52fceb6688 100644 --- a/app/views/home/_filters.html.haml +++ b/app/views/home/_filters.html.haml @@ -10,7 +10,7 @@ .light Filter by Type %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-5 - %taxon-selector{objects: "hubs | hubs:query", + %taxon-selector{objects: "Enterprises.hubs | searchEnterprises:query", results: "activeTaxons"} .small-12.large-3.columns %h5.tdhead diff --git a/app/views/home/_hubs.html.haml b/app/views/home/_hubs.html.haml index 67cb6abb40..1e5151469e 100644 --- a/app/views/home/_hubs.html.haml +++ b/app/views/home/_hubs.html.haml @@ -1,24 +1,17 @@ -= inject_enterprises -#hubs.hubs{"ng-controller" => "HubsCtrl"} += inject_enterprises +#hubs.hubs{"ng-controller" => "EnterprisesCtrl"} .row .small-12.columns %h1 Shop in your local area - #active-table-search.row.pad-top - .small-12.columns - %input{type: :text, - "ng-model" => "query", - placeholder: "Search by name or suburb...", - "ng-debounce" => "150", - "ofn-disable-enter" => true} - + = render partial: "shared/components/enterprise_search" = render partial: "home/filters" .row{bindonce: true} .small-12.columns .active_table - %hub.active_table_node.row.animate-repeat{"ng-repeat" => "hub in filteredHubs = (hubs | hubs:query | taxons:activeTaxons | shipping:shippingTypes | showProfiles:show_profiles )", - "ng-class" => "{'is_profile' : !hub.has_shopfront, 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", + %hub.active_table_node.row.animate-repeat{"ng-repeat" => "hub in filteredEnterprises = (Enterprises.hubs | visible | searchEnterprises:query | taxons:activeTaxons | shipping:shippingTypes | showHubProfiles:show_profiles | orderBy:['-active', '+orders_close_at'])", + "ng-class" => "{'is_profile' : hub.category == 'hub_profile', 'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", "scroll-after-load" => true, "ng-controller" => "HubNodeCtrl", id: "{{hub.hash}}"} @@ -26,9 +19,4 @@ = render partial: 'home/skinny' = render partial: 'home/fat' - .row{"ng-show" => "filteredHubs.length == 0"} - .columns.small-12 - %p.no-results - Sorry, no results found for - %strong {{query}}. - Try another search? + = render partial: 'shared/components/enterprise_no_results' diff --git a/app/views/home/_skinny.html.haml b/app/views/home/_skinny.html.haml index 2010bffe1e..7100a06974 100644 --- a/app/views/home/_skinny.html.haml +++ b/app/views/home/_skinny.html.haml @@ -1,4 +1,4 @@ -.row.active_table_row{"ng-if" => "hub.has_shopfront", "ng-click" => "toggle()", "ng-class" => "{'closed' : !open(), 'has_shopfront' : producer.has_shopfront}", bindonce: true} +.row.active_table_row{"ng-if" => "hub.is_distributor", "ng-click" => "toggle()", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}", bindonce: true} .columns.small-12.medium-6.large-5.skinny-head %a.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} @@ -24,7 +24,7 @@ %em Shopping here %span.margin-top{ bo: { if: "!current()" } } Orders closed -.row.active_table_row{"ng-if" => "!hub.has_shopfront", "ng-class" => "closed"} +.row.active_table_row{"ng-if" => "!hub.is_distributor", "ng-class" => "closed"} .columns.small-12.medium-6.large-5.skinny-head %a.hub{"ng-click" => "openModal(hub)", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} %i{ng: {class: "hub.icon_font"}} diff --git a/app/views/json/partials/_enterprise.rabl b/app/views/json/partials/_enterprise.rabl index 5b52898482..b8800e22ae 100644 --- a/app/views/json/partials/_enterprise.rabl +++ b/app/views/json/partials/_enterprise.rabl @@ -21,9 +21,9 @@ node :promo_image do |enterprise| end node :icon do |e| - if e.is_primary_producer? and e.is_distributor? + if e.is_primary_producer and e.is_distributor image_path "map_003-producer-shop.svg" - elsif e.is_primary_producer? + elsif e.is_primary_producer image_path "map_001-producer-only.svg" else image_path "map_005-hub.svg" diff --git a/app/views/modals/_producer.html.haml b/app/views/modals/_producer.html.haml deleted file mode 100644 index 51a45cfe5f..0000000000 --- a/app/views/modals/_producer.html.haml +++ /dev/null @@ -1,56 +0,0 @@ -%ofn-modal{title: "{{enterprise.name}}"} - .highlight - .highlight-top - %p.right - {{ [enterprise.address.city, enterprise.address.state_name] | printArray}} - %h3 - %i.ofn-i_036-producers - {{ enterprise.name }} - %img.hero-img{"ng-src" => "{{enterprise.promo_image}}"} - - .row{bindonce: true} - .small-12.large-8.columns - %div{"ng-show" => "enterprise.long_description.length > 0 || enterprise.logo"} - %p.modal-header About - .about-container - %img.enterprise-logo{"bo-src" => "enterprise.logo", "bo-if" => "enterprise.logo"} - %p.text-small{"ng-bind-html" => "enterprise.long_description"} - - .small-12.large-4.columns - %div.modal-centered{"bo-if" => "enterprise.email || enterprise.website || enterprise.phone"} - %p.modal-header Contact - %p{"bo-if" => "enterprise.phone"} - {{ enterprise.phone }} - - %p{"bo-if" => "enterprise.email"} - %a{"ng-href" => "{{enterprise.email | stripUrl}}", target: "_blank", mailto: true } - %span.email - {{ enterprise.email | stripUrl }} - - %p{"bo-show" => "enterprise.website"} - %a{"ng-href" => "http://{{enterprise.website}}", target: "_blank" } - {{ enterprise.website | stripUrl }} - - %div.modal-centered{"bo-if" => "enterprise.twitter || enterprise.facebook || enterprise.linkedin || enterprise.instagram"} - %p.modal-header Follow - .follow-icons{bindonce: true} - %span{"bo-show" => "enterprise.twitter"} - %a{"ng-href" => "http://twitter.com/{{enterprise.twitter}}", target: "_blank"} - %i.ofn-i_041-twitter - - %span{"bo-show" => "enterprise.facebook"} - %a{"ng-href" => "http://{{enterprise.facebook | stripUrl}}", target: "_blank"} - %i.ofn-i_044-facebook - - %span{"bo-show" => "enterprise.linkedin"} - %a{"ng-href" => "http://{{enterprise.linkedin | stripUrl}}", target: "_blank"} - %i.ofn-i_042-linkedin - - %span{"bo-show" => "enterprise.instagram"} - %a{"ng-href" => "http://instagram.com/{{enterprise.instagram}}", target: "_blank"} - %i.ofn-i_043-instagram - - %a.close-reveal-modal{"ng-click" => "$close()"} - %i.ofn-i_009-close - - diff --git a/app/views/producers/_filters.html.haml b/app/views/producers/_filters.html.haml index 92f0870e7e..00472dc91c 100644 --- a/app/views/producers/_filters.html.haml +++ b/app/views/producers/_filters.html.haml @@ -11,5 +11,5 @@ .light Filter by Type %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-6 - %taxon-selector{objects: "Producers.visible | filterProducers:query ", + %taxon-selector{objects: "Enterprises.producers | searchEnterprises:query ", results: "activeTaxons"} diff --git a/app/views/producers/_skinny.html.haml b/app/views/producers/_skinny.html.haml index 37767b3789..aaa59b20e8 100644 --- a/app/views/producers/_skinny.html.haml +++ b/app/views/producers/_skinny.html.haml @@ -1,11 +1,11 @@ -.row.active_table_row{"ng-click" => "toggle()", "ng-class" => "{'closed' : !open(), 'has_shopfront' : producer.has_shopfront}"} +.row.active_table_row{"ng-click" => "toggle()", "ng-class" => "{'closed' : !open(), 'is_distributor' : producer.is_distributor}"} .columns.small-12.medium-4.large-4.skinny-head - %span{"bo-if" => "producer.has_shopfront" } - %a.has_shopfront{"bo-href" => "producer.path" } + %span{"bo-if" => "producer.is_distributor" } + %a.is_distributor{"bo-href" => "producer.path" } %i{ng: {class: "producer.producer_icon_font"}} %span.margin-top %strong {{ producer.name }} - %span.producer-name{"bo-if" => "!producer.has_shopfront" } + %span.producer-name{"bo-if" => "!producer.is_distributor" } %i{ng: {class: "producer.producer_icon_font"}} %span.margin-top %strong {{ producer.name }} diff --git a/app/views/producers/index.html.haml b/app/views/producers/index.html.haml index fe487585e6..0e46795701 100644 --- a/app/views/producers/index.html.haml +++ b/app/views/producers/index.html.haml @@ -1,22 +1,10 @@ -= inject_enterprises -.producers.pad-top{"ng-controller" => "ProducersCtrl"} += inject_enterprises +.producers.pad-top{"ng-controller" => "EnterprisesCtrl"} .row .small-12.columns.pad-top %h1 Find local producers - / %div - / Find a - / %ofn-modal{title: "producer"} - / = render partial: "modals/producers" - / from the list below: - - #active-table-search.row - .small-12.columns - %input.animate-show{type: :text, - "ng-model" => "query", - placeholder: "Search by producer or suburb...", - "ng-debounce" => "150", - "ofn-disable-enter" => true} + = render partial: "shared/components/enterprise_search" = render partial: "producers/filters" .row{bindonce: true} @@ -24,7 +12,7 @@ .active_table %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", "scroll-after-load" => true, - "ng-repeat" => "producer in producers = (Producers.visible | filterProducers:query | taxons:activeTaxons)", + "ng-repeat" => "producer in filteredEnterprises = (Enterprises.producers | visible | searchEnterprises:query | taxons:activeTaxons)", "ng-controller" => "ProducerNodeCtrl", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", id: "{{producer.hash}}"} @@ -33,10 +21,6 @@ = render partial: 'producers/skinny' = render partial: 'producers/fat' - %producer.row{"ng-show" => "producers.length == 0"} - %p.no-results - Sorry, no results found for - %strong {{query}}. - Try another search? + = render partial: 'shared/components/enterprise_no_results' = render partial: "shared/footer" diff --git a/app/views/shared/components/_enterprise_no_results.html.haml b/app/views/shared/components/_enterprise_no_results.html.haml new file mode 100644 index 0000000000..2e6edd0875 --- /dev/null +++ b/app/views/shared/components/_enterprise_no_results.html.haml @@ -0,0 +1,5 @@ +%producer.row{"ng-show" => "filteredEnterprises.length == 0"} + %p.no-results + Sorry, no results found for + %strong {{query}}. + Try another search? diff --git a/app/views/shared/components/_enterprise_search.html.haml b/app/views/shared/components/_enterprise_search.html.haml new file mode 100644 index 0000000000..0ccb23220c --- /dev/null +++ b/app/views/shared/components/_enterprise_search.html.haml @@ -0,0 +1,7 @@ +#active-table-search.row + .small-12.columns + %input{type: :text, + "ng-model" => "query", + placeholder: "Search by name or suburb...", + "ng-debounce" => "150", + "ofn-disable-enter" => true} diff --git a/app/views/shared/components/_show_profiles.html.haml b/app/views/shared/components/_show_profiles.html.haml index c2232c3555..638cc47c27 100644 --- a/app/views/shared/components/_show_profiles.html.haml +++ b/app/views/shared/components/_show_profiles.html.haml @@ -1,6 +1,7 @@ .small-12.medium-6.columns.text-right .profile-checkbox - %input{"ng-model" => "show_profiles", type: "checkbox", name: "profile"}>< - %label Show profiles %button.button.secondary.tiny.help-btn.ng-scope{:popover => "Profiles do not have a shopfront on the Open Food Network, but may have their own physical or online shop elsewhere", "popover-placement" => "left"}>< %i.ofn-i_013-help + %label + %input{"ng-model" => "show_profiles", type: "checkbox", name: "profile"} + Show profiles diff --git a/app/views/shop/products/_summary.html.haml b/app/views/shop/products/_summary.html.haml index 25c71f276a..82d5f7c297 100644 --- a/app/views/shop/products/_summary.html.haml +++ b/app/views/shop/products/_summary.html.haml @@ -10,7 +10,7 @@ %em from %span - %producer-modal + %enterprise-modal %i.ofn-i_036-producers {{ enterprise.name }} diff --git a/app/views/shopping_shared/_producers.html.haml b/app/views/shopping_shared/_producers.html.haml index d6ff09be4c..25c0c0599d 100644 --- a/app/views/shopping_shared/_producers.html.haml +++ b/app/views/shopping_shared/_producers.html.haml @@ -4,5 +4,6 @@ %h5 {{CurrentHub.hub.name}}'s producers: %ul.small-block-grid-2.large-block-grid-4 %li{"ng-repeat" => "enterprise in CurrentHub.hub.producers"} - %i.ofn-i_036-producers - = render partial: "modals/producer" + %enterprise-modal + %i.ofn-i_036-producers + {{ enterprise.name }} diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 7a78a191b6..c19ec56755 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -1,14 +1,14 @@ #tabs{"ng-controller" => "TabsCtrl"} .row %tabset.small-12.columns - // WILL can we add some logic here to make the distributor name not appear at small sizes? e.g. add a class?) + -# Build all tabs. - for name, heading in { about: "About #{current_distributor.name}", producers: "Producers", groups: "Groups", contact: "Contact"} - + -# tabs take tab path in 'active' and 'select' functions defined in TabsCtrl. %tab{heading: heading, id: "tab_#{name}", - active: "active(#{name}.path)", - select: "select(#{name})"} + active: "active(\'#{name}\')", + select: "select(\'#{name}\')"} = render "shopping_shared/#{name}" diff --git a/app/views/spree/admin/overview/_unconfirmed.html.haml b/app/views/spree/admin/overview/_unconfirmed.html.haml new file mode 100644 index 0000000000..05c4df2a0d --- /dev/null +++ b/app/views/spree/admin/overview/_unconfirmed.html.haml @@ -0,0 +1,4 @@ +- @enterprises.unconfirmed.each do |enterprise| + .alert + %h6= "Action Required: Please confirm the email address for #{enterprise.name}." + %span.message= "We've sent a confirmation email to #{enterprise.email}, so please check there for further instructions. Thanks!" \ No newline at end of file diff --git a/app/views/spree/admin/overview/index.html.haml b/app/views/spree/admin/overview/index.html.haml index a46a31b404..d83a71bc40 100644 --- a/app/views/spree/admin/overview/index.html.haml +++ b/app/views/spree/admin/overview/index.html.haml @@ -1,10 +1,17 @@ %h1{ :style => 'margin-bottom: 30px'} Dashboard +- if @enterprises.unconfirmed.any? + + = render partial: "spree/admin/overview/unconfirmed" + + %hr + - if @enterprises.empty? = render partial: "spree/admin/overview/enterprises" - else + - if can? :admin, Spree::Product = render partial: "spree/admin/overview/products" diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb new file mode 100644 index 0000000000..9fef52fc8c --- /dev/null +++ b/config/initializers/devise.rb @@ -0,0 +1,5 @@ +Devise.setup do |config| + # Add a default scope to devise, to prevent it from checking + # whether other devise enabled models are signed into a session or not + config.default_scope = :spree_user +end \ No newline at end of file diff --git a/config/ng-test.conf.js b/config/ng-test.conf.js index eadaf984ae..f87aa3d48a 100644 --- a/config/ng-test.conf.js +++ b/config/ng-test.conf.js @@ -27,9 +27,16 @@ module.exports = function(config) { 'app/assets/javascripts/admin/util.js.erb' ], + preprocessors: { + '**/*.coffee': ['coffee'] + }, + coffeePreprocessor: { options: { sourceMap: true + }, + transformPath: function(path) { + return path.replace(/\.coffee$/, '.js'); } }, diff --git a/config/routes.rb b/config/routes.rb index 3e063641dd..ca4a6b59ff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,7 +6,6 @@ Openfoodnetwork::Application.routes.draw do get "/map", to: "map#index", as: :map get "/register", to: "registration#index", as: :registration - get "/register/store", to: "registration#store", as: :store_registration get "/register/auth", to: "registration#authenticate", as: :registration_auth resource :shop, controller: "shop" do @@ -35,6 +34,8 @@ Openfoodnetwork::Application.routes.draw do end end + devise_for :enterprise + namespace :admin do resources :order_cycles do post :bulk_update, on: :collection, as: :bulk_update diff --git a/db/migrate/20140927005000_add_dummy_for_missing_emails.rb b/db/migrate/20140927005000_add_dummy_for_missing_emails.rb new file mode 100644 index 0000000000..08f638e2a3 --- /dev/null +++ b/db/migrate/20140927005000_add_dummy_for_missing_emails.rb @@ -0,0 +1,7 @@ +class AddDummyForMissingEmails < ActiveRecord::Migration + def up + Enterprise.all.each do |enterprise| + enterprise.update_column(:email, "missing@example.com") if enterprise.read_attribute(:email).blank? + end + end +end diff --git a/db/migrate/20140927005043_enterprise_config_refactor.rb b/db/migrate/20140927005043_enterprise_config_refactor.rb new file mode 100644 index 0000000000..6b51ac6e50 --- /dev/null +++ b/db/migrate/20140927005043_enterprise_config_refactor.rb @@ -0,0 +1,57 @@ +class EnterpriseConfigRefactor < ActiveRecord::Migration + class Enterprise < ActiveRecord::Base + self.inheritance_column = nil + end + + def up + add_column :enterprises, :sells, :string, null: false, default: 'none' + add_index :enterprises, :sells + add_index :enterprises, [:is_primary_producer, :sells] + + Enterprise.reset_column_information + + Enterprise.all.each do |enterprise| + enterprise.update_attributes!({:sells => sells_what?(enterprise)}) + end + + remove_column :enterprises, :type + remove_column :enterprises, :is_distributor + end + + def down + # This process is lossy. Producer profiles wont exist. + add_column :enterprises, :type, :string, null: false, default: 'profile' + add_column :enterprises, :is_distributor, :boolean + + Enterprise.reset_column_information + + Enterprise.all.each do |enterprise| + enterprise.update_attributes!({ + :type => type?(enterprise), + :is_distributor => distributes?(enterprise) + }) + end + + remove_column :enterprises, :sells + end + + def sells_what?(enterprise) + is_distributor = enterprise.read_attribute(:is_distributor) + is_primary_producer = enterprise.read_attribute(:is_primary_producer) + type = enterprise.read_attribute(:type) + return "own" if type == "single" && (is_distributor || is_primary_producer) + return "none" if !is_distributor || type == "profile" + return "any" + end + + def distributes?(enterprise) + enterprise.read_attribute(:sells) != "none" + end + + def type?(enterprise) + sells = enterprise.read_attribute(:sells) + return "profile" if sells == "none" + return "single" if sells == "own" + return "full" + end +end diff --git a/db/migrate/20141010043405_add_confirmable_to_enterprise.rb b/db/migrate/20141010043405_add_confirmable_to_enterprise.rb new file mode 100644 index 0000000000..22203fe419 --- /dev/null +++ b/db/migrate/20141010043405_add_confirmable_to_enterprise.rb @@ -0,0 +1,16 @@ +class AddConfirmableToEnterprise < ActiveRecord::Migration + def up + add_column :enterprises, :confirmation_token, :string + add_column :enterprises, :confirmed_at, :datetime + add_column :enterprises, :confirmation_sent_at, :datetime + add_column :enterprises, :unconfirmed_email, :string + add_index :enterprises, :confirmation_token, :unique => true + + # Existing enterprises are assumed to be confirmed + Enterprise.update_all(:confirmed_at => Time.now) + end + + def down + remove_columns :enterprises, :confirmation_token, :confirmed_at, :confirmation_sent_at, :unconfirmed_email + end +end diff --git a/db/schema.rb b/db/schema.rb index 73f10210a7..974dc8ac9b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20140904003026) do +ActiveRecord::Schema.define(:version => 20141010043405) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -238,7 +238,6 @@ ActiveRecord::Schema.define(:version => 20140904003026) do t.string "description" t.text "long_description" t.boolean "is_primary_producer" - t.boolean "is_distributor" t.string "contact" t.string "phone" t.string "email" @@ -249,8 +248,8 @@ ActiveRecord::Schema.define(:version => 20140904003026) do t.integer "address_id" t.string "pickup_times" t.string "next_collection_at" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.text "distributor_info" t.string "logo_file_name" t.string "logo_content_type" @@ -264,12 +263,19 @@ ActiveRecord::Schema.define(:version => 20140904003026) do t.string "facebook" t.string "instagram" t.string "linkedin" - t.string "type", :default => "profile", :null => false - t.integer "owner_id", :null => false + t.integer "owner_id", :null => false + t.string "confirmation_token" + t.datetime "confirmed_at" + t.datetime "confirmation_sent_at" + t.string "unconfirmed_email" + t.string "sells", :default => "none", :null => false end add_index "enterprises", ["address_id"], :name => "index_enterprises_on_address_id" + add_index "enterprises", ["confirmation_token"], :name => "index_enterprises_on_confirmation_token", :unique => true + add_index "enterprises", ["is_primary_producer", "sells"], :name => "index_enterprises_on_is_primary_producer_and_sells" add_index "enterprises", ["owner_id"], :name => "index_enterprises_on_owner_id" + add_index "enterprises", ["sells"], :name => "index_enterprises_on_sells" create_table "exchange_fees", :force => true do |t| t.integer "exchange_id" diff --git a/script/backup.sh b/script/backup.sh index 462aef6abe..114a8352c6 100755 --- a/script/backup.sh +++ b/script/backup.sh @@ -4,4 +4,5 @@ set -e +mkdir -p db/backup ssh $1 "pg_dump -h localhost -U openfoodweb openfoodweb_production |gzip" > db/backup/$1-`date +%Y%m%d`.sql.gz diff --git a/spec/controllers/admin/enterprises_controller_spec.rb b/spec/controllers/admin/enterprises_controller_spec.rb index eafdd33c92..4120f163e2 100644 --- a/spec/controllers/admin/enterprises_controller_spec.rb +++ b/spec/controllers/admin/enterprises_controller_spec.rb @@ -84,28 +84,28 @@ module Admin end describe "updating an enterprise" do - let(:profile_enterprise) { create(:enterprise, type: 'profile') } + let(:profile_enterprise) { create(:enterprise, sells: 'none') } context "as manager" do - it "does not allow 'type' to be changed" do + it "does not allow 'sells' to be changed" do profile_enterprise.enterprise_roles.build(user: user).save controller.stub spree_current_user: user - enterprise_params = { id: profile_enterprise.id, enterprise: { type: 'full' } } + enterprise_params = { id: profile_enterprise.id, enterprise: { sells: 'any' } } spree_put :update, enterprise_params profile_enterprise.reload - expect(profile_enterprise.type).to eq 'profile' + expect(profile_enterprise.sells).to eq 'none' end end context "as super admin" do - it "allows 'type' to be changed" do + it "allows 'sells' to be changed" do controller.stub spree_current_user: admin_user - enterprise_params = { id: profile_enterprise.id, enterprise: { type: 'full' } } + enterprise_params = { id: profile_enterprise.id, enterprise: { sells: 'any' } } spree_put :update, enterprise_params profile_enterprise.reload - expect(profile_enterprise.type).to eq 'full' + expect(profile_enterprise.sells).to eq 'any' end end end @@ -123,38 +123,38 @@ module Admin user.save! user end - let!(:profile_enterprise1) { create(:enterprise, type: 'profile', owner: original_owner ) } - let!(:profile_enterprise2) { create(:enterprise, type: 'profile', owner: original_owner ) } + let!(:profile_enterprise1) { create(:enterprise, sells: 'none', owner: original_owner ) } + let!(:profile_enterprise2) { create(:enterprise, sells: 'none', owner: original_owner ) } context "as manager" do - it "does not allow 'type' or 'owner' to be changed" do + it "does not allow 'sells' or 'owner' to be changed" do profile_enterprise1.enterprise_roles.build(user: new_owner).save profile_enterprise2.enterprise_roles.build(user: new_owner).save controller.stub spree_current_user: new_owner - bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, type: 'full', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, type: 'full', owner_id: new_owner.id } } } } + bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, sells: 'any', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, sells: 'any', owner_id: new_owner.id } } } } spree_put :bulk_update, bulk_enterprise_params profile_enterprise1.reload profile_enterprise2.reload - expect(profile_enterprise1.type).to eq 'profile' - expect(profile_enterprise2.type).to eq 'profile' + expect(profile_enterprise1.sells).to eq 'none' + expect(profile_enterprise2.sells).to eq 'none' expect(profile_enterprise1.owner).to eq original_owner expect(profile_enterprise2.owner).to eq original_owner end end context "as super admin" do - it "allows 'type' and 'owner' to be changed" do + it "allows 'sells' and 'owner' to be changed" do profile_enterprise1.enterprise_roles.build(user: new_owner).save profile_enterprise2.enterprise_roles.build(user: new_owner).save controller.stub spree_current_user: admin_user - bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, type: 'full', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, type: 'full', owner_id: new_owner.id } } } } + bulk_enterprise_params = { enterprise_set: { collection_attributes: { '0' => { id: profile_enterprise1.id, sells: 'any', owner_id: new_owner.id }, '1' => { id: profile_enterprise2.id, sells: 'any', owner_id: new_owner.id } } } } spree_put :bulk_update, bulk_enterprise_params profile_enterprise1.reload profile_enterprise2.reload - expect(profile_enterprise1.type).to eq 'full' - expect(profile_enterprise2.type).to eq 'full' + expect(profile_enterprise1.sells).to eq 'any' + expect(profile_enterprise2.sells).to eq 'any' expect(profile_enterprise1.owner).to eq new_owner expect(profile_enterprise2.owner).to eq new_owner end diff --git a/spec/controllers/devise/confirmation_controller_spec.rb b/spec/controllers/devise/confirmation_controller_spec.rb new file mode 100644 index 0000000000..b8719bbd8f --- /dev/null +++ b/spec/controllers/devise/confirmation_controller_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe Devise::ConfirmationsController do + context "after confirmation" do + before do + e = create(:enterprise, confirmed_at: nil) + @request.env["devise.mapping"] = Devise.mappings[:enterprise] + spree_get :show, confirmation_token: e.confirmation_token + end + + it "should redirect to admin root" do + expect(response).to redirect_to spree.admin_path + end + end +end \ No newline at end of file diff --git a/spec/controllers/registration_controller_spec.rb b/spec/controllers/registration_controller_spec.rb index e51a28f73a..8ef292dc68 100644 --- a/spec/controllers/registration_controller_spec.rb +++ b/spec/controllers/registration_controller_spec.rb @@ -7,11 +7,6 @@ describe RegistrationController do get :index response.should redirect_to registration_auth_path(anchor: "signup?after_login=/register") end - - it "store" do - get :store - response.should redirect_to registration_auth_path(anchor: "signup?after_login=/register/store") - end end describe "redirecting when user has reached enterprise ownership limit" do @@ -41,12 +36,5 @@ describe RegistrationController do expect(assigns(:spree_api_key)).to eq user.spree_api_key end end - - describe "store" do - it "loads the spree api key" do - get :store - expect(assigns(:spree_api_key)).to eq user.spree_api_key - end - end end end diff --git a/spec/controllers/spree/admin/payment_methods_controller_spec.rb b/spec/controllers/spree/admin/payment_methods_controller_spec.rb index f3266f69c2..c27fe16f19 100644 --- a/spec/controllers/spree/admin/payment_methods_controller_spec.rb +++ b/spec/controllers/spree/admin/payment_methods_controller_spec.rb @@ -73,4 +73,4 @@ describe Spree::Admin::PaymentMethodsController do end end end -end \ No newline at end of file +end diff --git a/spec/factories.rb b/spec/factories.rb index db035ba16a..60b94b08c0 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -92,21 +92,22 @@ FactoryGirl.define do factory :enterprise, :class => Enterprise do owner { FactoryGirl.create :user } sequence(:name) { |n| "Enterprise #{n}" } - type 'full' + sells 'any' description 'enterprise' long_description '

Hello, world!

This is a paragraph.

' email 'enterprise@example.com' address { FactoryGirl.create(:address) } + confirmed_at { Time.now } end factory :supplier_enterprise, :parent => :enterprise do is_primary_producer true - is_distributor false + sells "none" end factory :distributor_enterprise, :parent => :enterprise do is_primary_producer false - is_distributor true + sells "any" end factory :enterprise_relationship do diff --git a/spec/features/admin/enterprise_user_spec.rb b/spec/features/admin/enterprise_user_spec.rb index 70e6a9b265..232ffb68ae 100644 --- a/spec/features/admin/enterprise_user_spec.rb +++ b/spec/features/admin/enterprise_user_spec.rb @@ -11,10 +11,10 @@ feature %q{ let!(:user) { create_enterprise_user } let!(:supplier1) { create(:supplier_enterprise, name: 'Supplier 1') } let!(:supplier2) { create(:supplier_enterprise, name: 'Supplier 2') } - let(:supplier_profile) { create(:supplier_enterprise, name: 'Supplier profile', type: 'profile') } + let(:supplier_profile) { create(:supplier_enterprise, name: 'Supplier profile', sells: 'none') } let!(:distributor1) { create(:distributor_enterprise, name: 'Distributor 3') } let!(:distributor2) { create(:distributor_enterprise, name: 'Distributor 4') } - let(:distributor_profile) { create(:distributor_enterprise, name: 'Distributor profile', type: 'profile') } + let(:distributor_profile) { create(:distributor_enterprise, name: 'Distributor profile', sells: 'none') } describe "creating an enterprise user" do context "with a limitted number of owned enterprises" do @@ -53,7 +53,10 @@ feature %q{ end end - describe "with only a profile-level enterprise" do + # This case no longer exists as anyone with an enterprise can supply into the system. + # Or can they?? There is no producer profile anyway. + # TODO discuss what parts of this are still necessary in which cases. + pending "with only a profile-level enterprise" do before do user.enterprise_roles.create! enterprise: supplier_profile user.enterprise_roles.create! enterprise: distributor_profile diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index d3d23aabcb..06b1fd31a6 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -16,7 +16,7 @@ feature %q{ within("tr.enterprise-#{s.id}") do expect(page).to have_content s.name - expect(page).to have_select "enterprise_set_collection_attributes_1_type" + expect(page).to have_select "enterprise_set_collection_attributes_1_sells" expect(page).to have_content "Edit Profile" expect(page).to have_content "Delete" expect(page).to_not have_content "Payment Methods" @@ -26,7 +26,7 @@ feature %q{ within("tr.enterprise-#{d.id}") do expect(page).to have_content d.name - expect(page).to have_select "enterprise_set_collection_attributes_0_type" + expect(page).to have_select "enterprise_set_collection_attributes_0_sells" expect(page).to have_content "Edit Profile" expect(page).to have_content "Delete" expect(page).to have_content "Payment Methods" @@ -37,7 +37,7 @@ feature %q{ scenario "editing enterprises in bulk" do s = create(:supplier_enterprise) - d = create(:distributor_enterprise, type: 'profile') + d = create(:distributor_enterprise, sells: 'none') d_manager = create_enterprise_user d_manager.enterprise_roles.build(enterprise: d).save expect(d.owner).to_not eq d_manager @@ -48,14 +48,14 @@ feature %q{ within("tr.enterprise-#{d.id}") do expect(page).to have_checked_field "enterprise_set_collection_attributes_0_visible" uncheck "enterprise_set_collection_attributes_0_visible" - select 'full', from: "enterprise_set_collection_attributes_0_type" + select 'any', from: "enterprise_set_collection_attributes_0_sells" select d_manager.email, from: 'enterprise_set_collection_attributes_0_owner_id' end click_button "Update" flash_message.should == 'Enterprises updated successfully' distributor = Enterprise.find(d.id) expect(distributor.visible).to eq false - expect(distributor.type).to eq 'full' + expect(distributor.sells).to eq 'any' expect(distributor.owner).to eq d_manager end @@ -82,15 +82,16 @@ feature %q{ click_link 'New Enterprise' # Checking shipping and payment method sidebars work + choose "Any" uncheck 'enterprise_is_primary_producer' - check 'enterprise_is_distributor' + page.should_not have_checked_field "enterprise_payment_method_ids_#{payment_method.id}" page.should_not have_checked_field "enterprise_shipping_method_ids_#{shipping_method.id}" # Filling in details fill_in 'enterprise_name', :with => 'Eaterprises' select2_search admin.email, from: 'Owner' - choose 'Full' + choose 'Any' check "enterprise_payment_method_ids_#{payment_method.id}" check "enterprise_shipping_method_ids_#{shipping_method.id}" select2_search eg1.name, from: 'Groups' @@ -134,7 +135,7 @@ feature %q{ end fill_in 'enterprise_name', :with => 'Eaterprises' - choose 'Single' + choose 'Own' select2_search user.email, from: 'Owner' fill_in 'enterprise_description', :with => 'Connecting farmers and eaters' long_description = find :css, "text-angular div.ta-scroll-window div.ta-bind" @@ -142,14 +143,23 @@ feature %q{ # Check Angularjs switching of sidebar elements uncheck 'enterprise_is_primary_producer' - uncheck 'enterprise_is_distributor' + choose 'None' + page.should have_selector "#enterprise_fees", visible: false page.should have_selector "#payment_methods", visible: false page.should have_selector "#shipping_methods", visible: false - page.should have_selector "#enterprise_fees", visible: false - check 'enterprise_is_distributor' + check 'enterprise_is_primary_producer' + page.should have_selector "#enterprise_fees" + page.should have_selector "#payment_methods", visible: false + page.should have_selector "#shipping_methods", visible: false + uncheck 'enterprise_is_primary_producer' + choose 'Own' + page.should have_selector "#enterprise_fees" page.should have_selector "#payment_methods" page.should have_selector "#shipping_methods" + choose 'Any' page.should have_selector "#enterprise_fees" + page.should have_selector "#payment_methods" + page.should have_selector "#shipping_methods" select2_search eg1.name, from: 'Groups' @@ -277,16 +287,14 @@ feature %q{ within("tr.enterprise-#{distributor1.id}") do expect(page).to have_content distributor1.name - expect(page).to have_checked_field "enterprise_set_collection_attributes_0_is_distributor" expect(page).to have_unchecked_field "enterprise_set_collection_attributes_0_is_primary_producer" - expect(page).to_not have_select "enterprise_set_collection_attributes_0_type" + expect(page).to_not have_select "enterprise_set_collection_attributes_0_sells" end within("tr.enterprise-#{supplier1.id}") do expect(page).to have_content supplier1.name - expect(page).to have_unchecked_field "enterprise_set_collection_attributes_1_is_distributor" expect(page).to have_checked_field "enterprise_set_collection_attributes_1_is_primary_producer" - expect(page).to_not have_select "enterprise_set_collection_attributes_1_type" + expect(page).to_not have_select "enterprise_set_collection_attributes_1_sells" end expect(page).to_not have_content "supplier2.name" diff --git a/spec/features/admin/image_settings_spec.rb b/spec/features/admin/image_settings_spec.rb new file mode 100644 index 0000000000..a83a072235 --- /dev/null +++ b/spec/features/admin/image_settings_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +feature %q{ + As an admin + I want to manage image formats +} do + include AuthenticationWorkflow + include WebHelper + + before(:all) do + styles = {"mini" => "48x48>", + "small" => "100x100>", + "product" => "240x240>", + "large" => "600x600>"} + + Spree::Config[:attachment_styles] = ActiveSupport::JSON.encode(styles) + Spree::Image.attachment_definitions[:attachment][:styles] = ActiveSupport::JSON.decode(Spree::Config[:attachment_styles]) + Spree::Image.reformat_styles + end + + scenario "setting the image format for a paperclip style" do + # When I go to the image settings page + login_to_admin_section + visit spree.edit_admin_image_settings_path + + # All the styles should default to "Unchanged" + page.should have_select 'attachment_styles_format_mini', selected: 'Unchanged' + page.should have_select 'attachment_styles_format_small', selected: 'Unchanged' + page.should have_select 'attachment_styles_format_product', selected: 'Unchanged' + page.should have_select 'attachment_styles_format_large', selected: 'Unchanged' + + # When I change a style to "PNG" and save + select 'PNG', from: 'attachment_styles_format_mini' + click_button 'Update' + + # Then the change should be saved to the image formats + page.should have_content "Image Settings successfully updated." + page.should have_select 'attachment_styles_format_mini', selected: 'PNG' + + styles = Spree::Image.attachment_definitions[:attachment][:styles] + styles[:mini].should == ['48x48>', :png] + end +end diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index febd966109..be91d442e1 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -471,6 +471,10 @@ feature %q{ # I should see only the order cycle I am coordinating page.should have_content oc_user_coordinating.name page.should_not have_content oc_for_other_user.name + + # The order cycle should show enterprises that I manage + page.should have_selector 'td.suppliers', text: supplier_managed.name + page.should have_selector 'td.distributors', text: distributor_managed.name # The order cycle should not show enterprises that I don't manage page.should_not have_selector 'td.suppliers', text: supplier_unmanaged.name diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb index 1904f3d5af..19767d8ea2 100644 --- a/spec/features/admin/variants_spec.rb +++ b/spec/features/admin/variants_spec.rb @@ -1,4 +1,4 @@ -require "spec_helper" +require 'spec_helper' feature %q{ As an admin diff --git a/spec/features/consumer/producers_spec.rb b/spec/features/consumer/producers_spec.rb index 98bef7f084..6e09365195 100644 --- a/spec/features/consumer/producers_spec.rb +++ b/spec/features/consumer/producers_spec.rb @@ -3,14 +3,14 @@ require 'spec_helper' feature %q{ As a consumer I want to see a list of producers - So that I can shop at hubs distributing their products + So that I can shop at hubs distributing their products }, js: true do include UIComponentHelper let!(:producer) { create(:supplier_enterprise) } let!(:invisible_producer) { create(:supplier_enterprise, visible: false) } let(:taxon) { create(:taxon) } let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) } - + before do visit producers_path end @@ -20,7 +20,7 @@ feature %q{ expand_active_table_node producer.name page.should have_content producer.supplied_taxons.first.name.split.map(&:capitalize).join(' ') end - + it "doesn't show invisible producers" do page.should_not have_content invisible_producer.name end diff --git a/spec/features/consumer/registration_spec.rb b/spec/features/consumer/registration_spec.rb index 7ac46a8970..ea09450467 100644 --- a/spec/features/consumer/registration_spec.rb +++ b/spec/features/consumer/registration_spec.rb @@ -17,25 +17,22 @@ feature "Registration", js: true do # Enter Login details fill_in "Email", with: user.email fill_in "Password", with: user.password - click_login_and_ensure_content "This wizard will step you through creating a profile" + click_login_and_ensure_content "Hi there!" expect(URI.parse(current_url).path).to eq registration_path # Done reading introduction - click_button_and_ensure_content "Let's get started!", "Woot! First we need to know what sort of enterprise you are:" + click_button_and_ensure_content "Let's get started!", "Woot! First we need to know a little bit about your enterprise:" # Filling in details fill_in 'enterprise_name', with: "My Awesome Enterprise" - click_link 'both-panel' - - click_button_and_ensure_content "Continue", "Greetings My Awesome Enterprise" # Filling in address fill_in 'enterprise_address', with: '123 Abc Street' fill_in 'enterprise_city', with: 'Northcote' fill_in 'enterprise_zipcode', with: '3070' select 'Australia', from: 'enterprise_country' - select 'Vic', from: 'enterprise_state' + select 'VIC', from: 'enterprise_state' click_button 'Continue' # Filling in Contact Details @@ -45,11 +42,16 @@ feature "Registration", js: true do fill_in 'enterprise_phone', with: '12 3456 7890' click_button 'Continue' + # Choosing a type + expect(page).to have_content 'Last step to add My Awesome Enterprise!' + click_link 'producer-panel' + click_button 'Continue' + # Enterprise should be created expect(page).to have_content 'Nice one!' e = Enterprise.find_by_name('My Awesome Enterprise') expect(e.address.address1).to eq "123 Abc Street" - expect(e.is_distributor).to eq true + expect(e.sells).to eq "none" expect(e.is_primary_producer).to eq true expect(e.contact).to eq "Saskia Munroe" @@ -75,7 +77,7 @@ feature "Registration", js: true do click_button 'Continue' # Filling in social - expect(page).to have_content 'Last step!' + expect(page).to have_content 'How can people find My Awesome Enterprise online?' fill_in 'enterprise_website', with: 'www.shop.com' fill_in 'enterprise_facebook', with: 'FaCeBoOk' fill_in 'enterprise_linkedin', with: 'LiNkEdIn' @@ -84,7 +86,7 @@ feature "Registration", js: true do click_button 'Continue' # Done - expect(page).to have_content "You have successfully completed the profile for My Awesome Enterprise" + expect(page).to have_content "Finished!" e.reload expect(e.website).to eq "www.shop.com" expect(e.facebook).to eq "FaCeBoOk" @@ -92,31 +94,6 @@ feature "Registration", js: true do expect(e.twitter).to eq "@TwItTeR" expect(e.instagram).to eq "@InStAgRaM" end - - it "Allows a logged in user to register a store" do - visit store_registration_path - - expect(URI.parse(current_url).path).to eq registration_auth_path - - page.has_selector? "dd", text: "Log in" - switch_to_login_tab - - # Enter Login details - fill_in "Email", with: user.email - fill_in "Password", with: user.password - click_login_and_ensure_content "This wizard will step you through creating a profile" - - expect(URI.parse(current_url).path).to eq store_registration_path - - # Done reading introduction - click_button_and_ensure_content "Let's get started!", "Woot! First we need to know the name of your farm:" - - # Details Page - expect(page).to have_content "Woot! First we need to know the name of your farm:" - expect(page).to_not have_selector '#enterprise-types' - - # Everything from here should be covered in 'profile' spec - end end def switch_to_login_tab diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 56c578f91b..d4db6bff29 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -167,7 +167,7 @@ feature "As a consumer I want to shop with a distributor", js: true do visit shop_path end - it "should save group buy data to ze cart" do + it "should save group buy data to the cart" do fill_in "variants[#{variant.id}]", with: 6 fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 7 page.should have_in_cart product.name diff --git a/spec/javascripts/unit/darkswarm/filters/filter_hubs_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/filter_hubs_spec.js.coffee deleted file mode 100644 index fc2702a72b..0000000000 --- a/spec/javascripts/unit/darkswarm/filters/filter_hubs_spec.js.coffee +++ /dev/null @@ -1,45 +0,0 @@ -describe 'filtering Hubs', -> - filter = null - filterHubs = null - hubs = [{ - name: "frogs" - other: "roger" - address: - zipcode: "cats" - city: "cambridge" - state: "kansas" - }, { - name: "donkeys" - other: "roger" - address: - zipcode: "" - city: "Wellington" - state: "uzbekistan" - }] - - beforeEach -> - module 'Darkswarm' - inject ($filter) -> - filter = $filter - filterHubs = $filter('hubs') - - it 'has a hub filter', -> - expect(filter('hubs')).not.toBeNull() - - it "filters by name", -> - expect(filterHubs(hubs, 'donkeys').length).toEqual 1 - - it "is case insensitive", -> - expect(filterHubs(hubs, 'DONKEYS').length).toEqual 1 - - it "filters by state", -> - expect(filterHubs(hubs, 'kansas').length).toEqual 1 - - it "filters by zipcode", -> - expect(filterHubs(hubs, 'cats').length).toEqual 1 - - it "gives all hubs when no argument is specified", -> - expect(filterHubs(hubs, '').length).toEqual 2 - - it "does not filter by anything else", -> - expect(filterHubs(hubs, 'roger').length).toEqual 0 diff --git a/spec/javascripts/unit/darkswarm/filters/filter_producers_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/filter_producers_spec.js.coffee deleted file mode 100644 index 29d8986b56..0000000000 --- a/spec/javascripts/unit/darkswarm/filters/filter_producers_spec.js.coffee +++ /dev/null @@ -1,28 +0,0 @@ -describe 'filtering producers', -> - filter = null - filterProducers = null - producers = [{ - name: "frogs" - other: "roger" - address: - zipcode: "cats" - city: "cambridge" - state: "kansas" - }, { - name: "donkeys" - other: "roger" - address: - zipcode: "" - city: "Wellington" - state: "uzbekistan" - }] - - beforeEach -> - module 'Darkswarm' - inject ($filter) -> - filter = $filter - filterProducers = $filter('filterProducers') - - - it 'has a producer filter', -> - expect(filter('filterProducers')).not.toBeNull() diff --git a/spec/javascripts/unit/darkswarm/filters/search_enterprises_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/search_enterprises_spec.js.coffee new file mode 100644 index 0000000000..2717e50cfb --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/search_enterprises_spec.js.coffee @@ -0,0 +1,40 @@ +describe 'filtering Enterprises', -> + filter = null + enterprises = [{ + name: "frogs" + other: "roger" + address: + zipcode: "cats" + city: "cambridge" + state: "kansas" + }, { + name: "donkeys" + other: "roger" + address: + zipcode: "" + city: "Wellington" + state: "uzbekistan" + }] + + beforeEach -> + module 'Darkswarm' + inject ($filter) -> + filter = $filter('searchEnterprises') + + it "filters by name", -> + expect(filter(enterprises, 'donkeys').length).toEqual 1 + + it "is case insensitive", -> + expect(filter(enterprises, 'DONKEYS').length).toEqual 1 + + it "filters by state", -> + expect(filter(enterprises, 'kansas').length).toEqual 1 + + it "filters by zipcode", -> + expect(filter(enterprises, 'cats').length).toEqual 1 + + it "gives all enterprises when no argument is specified", -> + expect(filter(enterprises, '').length).toEqual 2 + + it "does not filter by anything else", -> + expect(filter(enterprises, 'roger').length).toEqual 0 diff --git a/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee index 59d4ae9826..94dd7d39d2 100644 --- a/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee @@ -1,24 +1,30 @@ describe "Enterprises service", -> Enterprises = null - CurrentHubMock = {} + CurrentHubMock = {} taxons = [ {id: 1, name: "test"} ] enterprises = [ - {id: 1, type: "hub", producers: [{id: 2}], taxons: [{id: 1}]}, - {id: 2, type: "producer", hubs: [{id: 1}]}, - {id: 3, type: "producer", hubs: [{id: 1}]} + {id: 1, visible: true, category: "hub", producers: [{id: 5}], taxons: [{id: 1}]}, + {id: 2, visible: true, category: "hub", producers: [{id: 6}]} + {id: 3, visible: true, category: "hub_profile"} + {id: 4, visible: false, category: "hub", producers: [{id: 7}]} + {id: 5, visible: true, category: "producer_hub", hubs: [{id: 1}]}, + {id: 6, visible: true, category: "producer_shop", hubs: [{id: 2}]}, + {id: 7, visible: true, category: "producer", hubs: [{id: 2}]} + {id: 8, visible: false, category: "producer", hubs: [{id: 2}]} ] + H1: 0 beforeEach -> module 'Darkswarm' module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock + $provide.value "CurrentHub", CurrentHubMock null - angular.module('Darkswarm').value('enterprises', enterprises) - angular.module('Darkswarm').value('taxons', taxons) + angular.module('Darkswarm').value('enterprises', enterprises) + angular.module('Darkswarm').value('taxons', taxons) inject ($injector)-> - Enterprises = $injector.get("Enterprises") + Enterprises = $injector.get("Enterprises") it "stores enterprises as id/object pairs", -> expect(Enterprises.enterprises_by_id["1"]).toBe enterprises[0] @@ -31,8 +37,39 @@ describe "Enterprises service", -> expect(Enterprises.enterprises[0]).toBe Enterprises.enterprises_by_id["1"] it "dereferences references to other enterprises", -> - expect(Enterprises.enterprises_by_id["1"].producers[0]).toBe enterprises[1] - expect(Enterprises.enterprises_by_id["3"].hubs[0]).toBe enterprises[0] + expect(Enterprises.enterprises_by_id["1"].producers[0]).toBe enterprises[4] + expect(Enterprises.enterprises_by_id["5"].hubs[0]).toBe enterprises[0] it "dereferences taxons", -> expect(Enterprises.enterprises[0].taxons[0]).toBe taxons[0] + + it "filters Enterprise.hubs into a new array", -> + expect(Enterprises.hubs[0]).toBe Enterprises.enterprises[0] + # Because the $filter is a new sorted array + # We check to see the objects in both arrays are still the same + Enterprises.enterprises[0].active = false + expect(Enterprises.hubs[0].active).toBe false + + it "filters Enterprises.producers into a new array", -> + expect(Enterprises.producers[0]).toBe Enterprises.enterprises[4] + Enterprises.enterprises[4].active = false + expect(Enterprises.producers[0].active).toBe false + + it "only includes visible enterprises in hubs array", -> + expect(Enterprises.hubs).toContain Enterprises.enterprises[0] + expect(Enterprises.hubs).not.toContain Enterprises.enterprises[3] + + it "only includes visible enterprises in producers array", -> + expect(Enterprises.producers).toContain Enterprises.enterprises[4] + expect(Enterprises.producers).not.toContain Enterprises.enterprises[7] + + it "includes hub, hub_profile, producer_hub and, producer_shop enterprises in hubs array", -> + expect(Enterprises.hubs).toContain Enterprises.enterprises[0] + expect(Enterprises.hubs).toContain Enterprises.enterprises[2] + expect(Enterprises.hubs).toContain Enterprises.enterprises[4] + expect(Enterprises.hubs).toContain Enterprises.enterprises[5] + + it "includes producer_hub, producer_shop and producer enterprises in producers array", -> + expect(Enterprises.producers).toContain Enterprises.enterprises[4] + expect(Enterprises.producers).toContain Enterprises.enterprises[5] + expect(Enterprises.producers).toContain Enterprises.enterprises[6] diff --git a/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee deleted file mode 100644 index f2620da65b..0000000000 --- a/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee +++ /dev/null @@ -1,45 +0,0 @@ -describe "Hubs service", -> - Hubs = null - Enterprises = null - CurrentHubMock = {} - hubs = [ - { - id: 2 - active: false - orders_close_at: new Date() - is_distributor: true - has_hub_listing: true - } - { - id: 3 - active: false - orders_close_at: new Date() - is_distributor: true - has_hub_listing: true - } - { - id: 1 - active: true - orders_close_at: new Date() - is_distributor: true - has_hub_listing: true - } - ] - - - beforeEach -> - module 'Darkswarm' - angular.module('Darkswarm').value('enterprises', hubs) - module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock - null - inject ($injector)-> - Enterprises = $injector.get("Enterprises") - Hubs = $injector.get("Hubs") - - it "filters Enterprise.hubs into a new array", -> - expect(Hubs.hubs[0]).toBe Enterprises.enterprises[2] - # Because the $filter is a new sorted array - # We check to see the objects in both arrays are still the same - Enterprises.enterprises[2].active = false - expect(Hubs.hubs[0].active).toBe false diff --git a/spec/javascripts/unit/darkswarm/services/producers_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/producers_spec.js.coffee deleted file mode 100644 index dc9ac0fc8f..0000000000 --- a/spec/javascripts/unit/darkswarm/services/producers_spec.js.coffee +++ /dev/null @@ -1,21 +0,0 @@ -describe "Producers service", -> - Producers = null - Enterprises = null - CurrentHubMock = - hub: - id: 1 - enterprises = [ - {is_primary_producer: true} - ] - - beforeEach -> - module 'Darkswarm' - module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock - null - angular.module('Darkswarm').value('enterprises', enterprises) - inject ($injector)-> - Producers = $injector.get("Producers") - - it "delegates producers array to Enterprises", -> - expect(Producers.producers[0]).toBe enterprises[0] diff --git a/spec/mailers/enterprise_mailer_spec.rb b/spec/mailers/enterprise_mailer_spec.rb index 412870bad7..dd1e150eb7 100644 --- a/spec/mailers/enterprise_mailer_spec.rb +++ b/spec/mailers/enterprise_mailer_spec.rb @@ -1,13 +1,16 @@ require 'spec_helper' describe EnterpriseMailer do + let!(:enterprise) { create(:enterprise) } + before do - @enterprise = create(:enterprise) ActionMailer::Base.deliveries = [] end - it "should send an email when given an enterprise" do - EnterpriseMailer.creation_confirmation(@enterprise).deliver + it "should send an email confirmation when given an enterprise" do + EnterpriseMailer.confirmation_instructions(enterprise, 'token').deliver ActionMailer::Base.deliveries.count.should == 1 + mail = ActionMailer::Base.deliveries.first + expect(mail.subject).to eq "Please confirm your email for #{enterprise.name}" end end \ No newline at end of file diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 241d1fafc7..206be1e131 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -3,6 +3,25 @@ require 'spec_helper' describe Enterprise do include AuthenticationWorkflow + describe "sending emails" do + describe "on creation" do + let!(:user) { create_enterprise_user( enterprise_limit: 2 ) } + let!(:enterprise) { create(:enterprise, owner: user) } + + it "when the email address has not already been confirmed" do + mail_message = double "Mail::Message" + EnterpriseMailer.should_receive(:confirmation_instructions).and_return mail_message + mail_message.should_receive :deliver + create(:enterprise, owner: user, email: "unknown@email.com", confirmed_at: nil ) + end + + it "when the email address has already been confirmed" do + EnterpriseMailer.should_not_receive(:confirmation_instructions) + e = create(:enterprise, owner: user, email: enterprise.email, confirmed_at: nil) + end + end + end + describe "associations" do it { should belong_to(:owner) } it { should have_many(:supplied_products) } @@ -86,6 +105,7 @@ describe Enterprise do subject { FactoryGirl.create(:distributor_enterprise, :address => FactoryGirl.create(:address)) } it { should validate_presence_of(:name) } it { should validate_presence_of(:email) } + it { should ensure_length_of(:description).is_at_most(255) } it "requires an owner" do expect{ @@ -102,6 +122,7 @@ describe Enterprise do it { should delegate(:city).to(:address) } it { should delegate(:state_name).to(:address) } end + describe "scopes" do describe 'active' do it 'find active enterprises' do @@ -111,6 +132,28 @@ describe Enterprise do end end + describe "confirmed" do + it "find enterprises with a confirmed date" do + s1 = create(:supplier_enterprise) + d1 = create(:distributor_enterprise) + s2 = create(:supplier_enterprise, confirmed_at: nil) + d2 = create(:distributor_enterprise, confirmed_at: nil) + expect(Enterprise.confirmed).to include s1, d1 + expect(Enterprise.confirmed).to_not include s2, d2 + end + end + + describe "unconfirmed" do + it "find enterprises without a confirmed date" do + s1 = create(:supplier_enterprise) + d1 = create(:distributor_enterprise) + s2 = create(:supplier_enterprise, confirmed_at: nil) + d2 = create(:distributor_enterprise, confirmed_at: nil) + expect(Enterprise.unconfirmed).to_not include s1, d1 + expect(Enterprise.unconfirmed).to include s2, d2 + end + end + describe "distributors_with_active_order_cycles" do it "finds active distributors by order cycles" do s = create(:supplier_enterprise) @@ -503,64 +546,24 @@ describe Enterprise do end end - pending "provide enterprise category" do - - # Swap type values full > sell_all, single > sell_own profile > sell_none - # swap is_distributor for new can_supply flag. - let(:producer_sell_all_can_supply) { - create(:enterprise, is_primary_producer: true, type: "full", is_distributor: true) - } - let(:producer_sell_all_cant_supply) { - create(:enterprise, is_primary_producer: true, type: "full", is_distributor: false) - } - let(:producer_sell_own_can_supply) { - create(:enterprise, is_primary_producer: true, type: "single", is_distributor: true) - } - let(:producer_sell_own_cant_supply) { - create(:enterprise, is_primary_producer: true, type: "single", is_distributor: false) - } - let(:producer_sell_none_can_supply) { - create(:enterprise, is_primary_producer: true, type: "profile", is_distributor: true) - } - let(:producer_sell_none_cant_supply) { - create(:enterprise, is_primary_producer: true, type: "profile", is_distributor: false) - } - let(:non_producer_sell_all_can_supply) { - create(:enterprise, is_primary_producer: true, type: "full", is_distributor: true) - } - let(:non_producer_sell_all_cant_supply) { - create(:enterprise, is_primary_producer: true, type: "full", is_distributor: false) - } - let(:non_producer_sell_own_can_supply) { - create(:enterprise, is_primary_producer: true, type: "single", is_distributor: true) - } - let(:non_producer_sell_own_cant_supply) { - create(:enterprise, is_primary_producer: true, type: "single", is_distributor: false) - } - let(:non_producer_sell_none_can_supply) { - create(:enterprise, is_primary_producer: false, type: "profile", is_distributor: true) - } - let(:non_producer_sell_none_cant_supply) { - create(:enterprise, is_primary_producer: false, type: "profile", is_distributor: false) - } + describe "provide enterprise category" do + let(:producer_sell_all) { build(:enterprise, is_primary_producer: true, sells: "any") } + let(:producer_sell_own) { build(:enterprise, is_primary_producer: true, sells: "own") } + let(:producer_sell_none) { build(:enterprise, is_primary_producer: true, sells: "none") } + let(:non_producer_sell_all) { build(:enterprise, is_primary_producer: false, sells: "any") } + let(:non_producer_sell_own) { build(:enterprise, is_primary_producer: false, sells: "own") } + let(:non_producer_sell_none) { build(:enterprise, is_primary_producer: false, sells: "none") } it "should output enterprise categories" do - producer_sell_all_can_supply.is_primary_producer.should == true - producer_sell_all_can_supply.supplies.should == true - producer_sell_all_can_supply.type.should == "full" + producer_sell_all.is_primary_producer.should == true + producer_sell_all.sells.should == "any" - producer_sell_all_can_supply.enterprise_category.should == "producer_hub" - producer_sell_all_cant_supply.enterprise_category.should == "producer_hub" - producer_sell_own_can_supply.enterprise_category.should == "producer_shop" - producer_sell_own_cant_supply.enterprise_category.should == "producer_shop" - producer_sell_none_can_supply.enterprise_category.should == "producer" - producer_sell_none_cant_supply.enterprise_category.should == "producer_profile" - non_producer_sell_all_can_supply.enterprise_category.should == "hub" - non_producer_sell_all_cant_supply.enterprise_category.should == "hub" - non_producer_sell_own_can_supply.enterprise_category.should == "hub" - non_producer_sell_own_cant_supply.enterprise_category.should == "hub" - non_producer_sell_none_can_supply.enterprise_category.should == "hub_profile" - non_producer_sell_none_cant_supply.enterprise_category.should == "hub_profile" + producer_sell_all.category.should == :producer_hub + producer_sell_own.category.should == :producer_shop + producer_sell_none.category.should == :producer + non_producer_sell_all.category.should == :hub + non_producer_sell_own.category.should == :hub + non_producer_sell_none.category.should == :hub_profile end end end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 9785d5ed2d..56aa51a0d0 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -44,8 +44,8 @@ describe OrderCycle do end it "finds order cycles accessible by a user" do - e1 = create(:enterprise, is_primary_producer: true, is_distributor: true) - e2 = create(:enterprise, is_primary_producer: true, is_distributor: true) + e1 = create(:enterprise, is_primary_producer: true, sells: "any") + e2 = create(:enterprise, is_primary_producer: true, sells: "any") user = create(:user, enterprises: [e2], spree_roles: []) user.spree_roles = [] diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 2d2d1cfb01..e523cce755 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -9,13 +9,46 @@ module Spree describe "broad permissions" do subject { AbilityDecorator.new(user) } let(:user) { create(:user) } - let(:enterprise_full) { create(:enterprise, type: 'full') } - let(:enterprise_single) { create(:enterprise, type: 'single') } - let(:enterprise_profile) { create(:enterprise, type: 'profile') } + let(:enterprise_any) { create(:enterprise, sells: 'any') } + let(:enterprise_own) { create(:enterprise, sells: 'own') } + let(:enterprise_none) { create(:enterprise, sells: 'none') } + let(:enterprise_any_producer) { create(:enterprise, sells: 'any', is_primary_producer: true) } + let(:enterprise_own_producer) { create(:enterprise, sells: 'own', is_primary_producer: true) } + let(:enterprise_none_producer) { create(:enterprise, sells: 'none', is_primary_producer: true) } - context "as manager of a 'full' type enterprise" do + context "as manager of an enterprise who sells 'any'" do before do - user.enterprise_roles.create! enterprise: enterprise_full + user.enterprise_roles.create! enterprise: enterprise_any + end + + it { subject.can_manage_products?(user).should be_false } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_true } + end + + context "as manager of an enterprise who sell 'own'" do + before do + user.enterprise_roles.create! enterprise: enterprise_own + end + + it { subject.can_manage_products?(user).should be_false } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_true } + end + + context "as manager of an enterprise who sells 'none'" do + before do + user.enterprise_roles.create! enterprise: enterprise_none + end + + it { subject.can_manage_products?(user).should be_false } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_false } + end + + context "as manager of a producer enterprise who sells 'any'" do + before do + user.enterprise_roles.create! enterprise: enterprise_any_producer end it { subject.can_manage_products?(user).should be_true } @@ -23,9 +56,9 @@ module Spree it { subject.can_manage_orders?(user).should be_true } end - context "as manager of a 'single' type enterprise" do + context "as manager of a producer enterprise who sell 'own'" do before do - user.enterprise_roles.create! enterprise: enterprise_single + user.enterprise_roles.create! enterprise: enterprise_own_producer end it { subject.can_manage_products?(user).should be_true } @@ -33,9 +66,9 @@ module Spree it { subject.can_manage_orders?(user).should be_true } end - context "as manager of a 'profile' type enterprise" do + context "as manager of a producer enterprise who sells 'none'" do before do - user.enterprise_roles.create! enterprise: enterprise_profile + user.enterprise_roles.create! enterprise: enterprise_none_producer end it { subject.can_manage_products?(user).should be_true } @@ -151,6 +184,14 @@ module Spree should_not have_ability(:destroy, for: er2) end + it "should be able to read some reports" do + should have_ability([:admin, :index, :customers, :bulk_coop, :orders_and_fulfillment, :products_and_inventory], for: :report) + end + + it "should not be able to read other reports" do + should_not have_ability([:sales_total, :group_buys, :payments, :orders_and_distributors], for: :report) + end + end context "when is a distributor enterprise user" do @@ -237,17 +278,26 @@ module Spree it "should not be able to destroy enterprise relationships for other enterprises" do should_not have_ability(:destroy, for: er1) end + + it "should be able to read some reports" do + should have_ability([:admin, :index, :customers, :group_buys, :bulk_coop, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory], for: :report) + end + + it "should not be able to read other reports" do + should_not have_ability([:sales_total], for: :report) + end + end - context 'Order Cycle co-ordinator' do - + context 'Order Cycle co-ordinator, distributor enterprise manager' do let (:user) do user = create(:user) user.spree_roles = [] - s1.enterprise_roles.build(user: user).save + d1.enterprise_roles.build(user: user).save user end - let(:oc1) { create(:simple_order_cycle, {coordinator: s1}) } + + let(:oc1) { create(:simple_order_cycle, {coordinator: d1}) } let(:oc2) { create(:simple_order_cycle) } it "should be able to read/write OrderCycles they are the co-ordinator of" do @@ -265,6 +315,10 @@ module Spree it "should be able to read/write EnterpriseFees" do should have_ability([:admin, :index, :read, :create, :edit, :bulk_update, :destroy], for: EnterpriseFee) end + + it "should be able to add enterprises to order cycles" do + should have_ability([:admin, :index, :for_order_cycle, :create], for: Enterprise) + end end context 'enterprise manager' do @@ -284,7 +338,7 @@ module Spree end it 'should have the ability administrate and create enterpises' do - should have_ability([:admin, :index, :for_order_cycle, :create], for: Enterprise) + should have_ability([:admin, :index, :create], for: Enterprise) end end end diff --git a/spec/models/spree/image_spec.rb b/spec/models/spree/image_spec.rb new file mode 100644 index 0000000000..56665fa641 --- /dev/null +++ b/spec/models/spree/image_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +module Spree + describe Image do + describe "attachment definitions" do + let(:name_str) { {"mini" => "48x48>"} } + let(:formatted) { {mini: ["48x48>", "png"]} } + + it "converts style names to symbols" do + Image.format_styles(name_str).should == {:mini => "48x48>"} + end + + it "converts formats to symbols" do + Image.format_styles(formatted).should == {:mini => ["48x48>", :png]} + end + end + end +end diff --git a/spec/serializers/enterprise_serializer_spec.rb b/spec/serializers/enterprise_serializer_spec.rb index 5fe57a99e2..f3409f2d57 100644 --- a/spec/serializers/enterprise_serializer_spec.rb +++ b/spec/serializers/enterprise_serializer_spec.rb @@ -13,9 +13,27 @@ describe Api::EnterpriseSerializer do serializer = Api::EnterpriseSerializer.new enterprise serializer.to_json.should match taxon.id.to_s end - + it "will render urls" do serializer = Api::EnterpriseSerializer.new enterprise serializer.to_json.should match "map_005-hub.svg" end + + describe "visibility" do + before do + enterprise.stub(:visible).and_return true + end + + it "is visible when confirmed" do + enterprise.stub(:confirmed?).and_return true + serializer = Api::EnterpriseSerializer.new enterprise + expect(serializer.to_json).to match "\"visible\":true" + end + + it "is not visible when unconfirmed" do + enterprise.stub(:confirmed?).and_return false + serializer = Api::EnterpriseSerializer.new enterprise + expect(serializer.to_json).to match "\"visible\":false" + end + end end