diff --git a/.ruby-version b/.ruby-version index 2aaf2528c3..ae6d5b9cbe 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-1.9.3-p392 +1.9.3-p392 diff --git a/.travis.yml b/.travis.yml index 4d9301a327..90328e3a8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,3 +10,5 @@ before_script: script: - RAILS_ENV=test bundle exec rake db:migrate --trace - bundle exec rake spec +notifications: + email: false diff --git a/Gemfile b/Gemfile index cb6f7a8f96..af6b81b3a8 100644 --- a/Gemfile +++ b/Gemfile @@ -32,6 +32,7 @@ gem "active_model_serializers" gem 'oj' gem 'deface', :github => 'spree/deface', :ref => '1110a13' gem 'paperclip' +gem 'dalli' gem 'geocoder' gem 'gmaps4rails' gem 'spinjs-rails' @@ -85,6 +86,7 @@ end group :test do gem 'webmock' + gem 'perftools.rb' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 9afad20d05..626b1a6a2c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,45 +14,45 @@ GIT GIT remote: git://github.com/openfoodfoundation/spree.git - revision: da651b40f5c6cdd32e00b060729eb9aefd4f615f + revision: bbe5e779bcb883a1726ad4006d7c06b06c3f5372 branch: 1-3-stable specs: - spree (1.3.3) - spree_api (= 1.3.3) - spree_cmd (= 1.3.3) - spree_core (= 1.3.3) - spree_promo (= 1.3.3) - spree_sample (= 1.3.3) - spree_api (1.3.3) - spree_core (= 1.3.3) + spree (1.3.6.beta) + spree_api (= 1.3.6.beta) + spree_cmd (= 1.3.6.beta) + spree_core (= 1.3.6.beta) + spree_promo (= 1.3.6.beta) + spree_sample (= 1.3.6.beta) + spree_api (1.3.6.beta) + spree_core (= 1.3.6.beta) versioncake (= 0.4.0) - spree_cmd (1.3.3) + spree_cmd (1.3.6.beta) thor (>= 0.14.6) - spree_core (1.3.3) + spree_core (1.3.6.beta) activemerchant (~> 1.34) acts_as_list (= 0.1.4) awesome_nested_set (= 2.1.5) - aws-sdk (~> 1.3.4) + aws-sdk (~> 1.11.1) cancan (= 1.6.8) deface (>= 0.9.0) ffaker (~> 1.15.0) highline (= 1.6.18) jquery-rails (~> 2.2.0) json (>= 1.5.5) - kaminari (= 0.13.0) - money (= 5.0.0) + kaminari (= 0.14.1) + money (= 5.1.1) paperclip (~> 2.8) rabl (= 0.7.2) - rails (~> 3.2.14) + rails (~> 3.2.16) ransack (= 0.7.2) select2-rails (= 3.2.1) state_machine (= 1.1.2) stringex (~> 1.3.2) truncate_html (~> 0.5.5) - spree_promo (1.3.3) - spree_core (= 1.3.3) - spree_sample (1.3.3) - spree_core (= 1.3.3) + spree_promo (1.3.6.beta) + spree_core (= 1.3.6.beta) + spree_sample (1.3.6.beta) + spree_core (= 1.3.6.beta) GIT remote: git://github.com/openfoodfoundation/spree_paypal_express.git @@ -74,10 +74,10 @@ GIT GIT remote: git://github.com/spree/spree_auth_devise.git - revision: 6a50345b73bcec614a8fbd358a2367c00c8ab56f + revision: ba95589a85368297c844f096c2a0c121e5b08138 branch: 1-3-stable specs: - spree_auth_devise (1.0.0) + spree_auth_devise (1.3.0) cancan (~> 1.6.7) devise (~> 2.2.3) devise-encryptable (= 0.1.2) @@ -120,10 +120,10 @@ GEM active_link_to (1.0.0) active_model_serializers (0.8.1) activemodel (>= 3.0) - active_utils (2.0.2) + active_utils (2.2.1) activesupport (>= 2.3.11) i18n - activemerchant (1.43.0) + activemerchant (1.43.1) active_utils (~> 2.0, >= 2.0.1) activesupport (>= 2.3.14, < 5.0.0) builder (>= 2.1.2, < 4.0.0) @@ -158,12 +158,13 @@ GEM awesome_nested_set (2.1.5) activerecord (>= 3.0.0) awesome_print (1.0.2) - aws-sdk (1.3.9) - httparty (~> 0.7) + aws-sdk (1.11.1) json (~> 1.4) nokogiri (>= 1.4.4) uuidtools (~> 2.1) - bcrypt-ruby (3.1.2) + bcrypt (3.1.7) + bcrypt-ruby (3.1.5) + bcrypt (>= 3.1.3) bugsnag (1.5.2) httparty (>= 0.6, < 1.0) multi_json (~> 1.0) @@ -192,7 +193,7 @@ GEM coffee-script-source execjs coffee-script-source (1.3.3) - colorize (0.7.2) + colorize (0.7.3) columnize (0.3.6) comfortable_mexican_sofa (1.6.24) active_link_to (~> 1.0.0) @@ -206,6 +207,7 @@ GEM compass (>= 0.12.2, < 0.14) crack (0.4.1) safe_yaml (~> 0.9.0) + dalli (2.7.2) database_cleaner (0.7.1) db2fog (0.8.0) activerecord (~> 3.0) @@ -217,7 +219,7 @@ GEM debugger-ruby_core_source (~> 1.2.3) debugger-linecache (1.2.0) debugger-ruby_core_source (1.2.3) - devise (2.2.7) + devise (2.2.8) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) railties (~> 3.1) @@ -300,10 +302,9 @@ GEM json_spec (1.1.1) multi_json (~> 1.0) rspec (~> 2.0) - kaminari (0.13.0) + kaminari (0.14.1) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - railties (>= 3.0.0) kgio (2.7.4) launchy (2.1.2) addressable (~> 2.3) @@ -320,27 +321,27 @@ GEM treetop (~> 1.4.8) method_source (0.8.1) mime-types (1.25.1) - mini_portile (0.5.3) + mini_portile (0.6.0) momentjs-rails (2.5.1) railties (>= 3.1) - money (5.0.0) - i18n (~> 0.4) - json + money (5.1.1) + i18n (~> 0.6.0) multi_json (1.10.1) multi_xml (0.5.5) net-scp (1.1.2) net-ssh (>= 2.6.5) net-ssh (2.6.8) newrelic_rpm (3.6.7.152) - nokogiri (1.6.1) - mini_portile (~> 0.5.0) + nokogiri (1.6.2.1) + mini_portile (= 0.6.0) oj (2.1.2) - orm_adapter (0.4.0) + orm_adapter (0.5.0) paperclip (2.8.0) activerecord (>= 2.3.0) activesupport (>= 2.3.2) cocaine (>= 0.0.2) mime-types + perftools.rb (2.0.1) pg (0.13.2) poltergeist (1.5.0) capybara (~> 2.1) @@ -349,7 +350,7 @@ GEM websocket-driver (>= 0.2.0) polyamorous (0.5.0) activerecord (~> 3.0) - polyglot (0.3.4) + polyglot (0.3.5) pry (0.9.12.2) coderay (~> 1.0.5) method_source (~> 0.8) @@ -385,7 +386,7 @@ GEM rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) raindrops (0.9.0) - rake (10.3.1) + rake (10.3.2) ransack (0.7.2) actionpack (~> 3.0) activerecord (~> 3.0) @@ -506,6 +507,7 @@ DEPENDENCIES comfortable_mexican_sofa compass-rails custom_error_message! + dalli database_cleaner (= 0.7.1) db2fog debugger-linecache @@ -531,6 +533,7 @@ DEPENDENCIES newrelic_rpm oj paperclip + perftools.rb pg poltergeist pry-debugger diff --git a/INSTALL.sh b/INSTALL.sh deleted file mode 100755 index 8ff0b660a1..0000000000 --- a/INSTALL.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/sh -# -########################### -# Linux install script # -# ----------------------- # -# Tested on Debian wheezy # -########################### - -echo 'Checking dependencies...' - -# Rails is installed by bundler later -#echo -n 'Rails 3.2.. ' -#if rails -v | grep -q 'Rails 3.2'; then -# echo 'ok' -#else -# echo 'not found' -# exit 1 -#fi - -echo -n 'Ruby 1.9.3.. ' -revision=$(ruby -v | grep -E -o '^ruby 1\.9\.([0-9]+)' | cut -d . -f 3) -if [ "$revision" -gt 2 ]; then - echo 'ok' -else - echo 'not found' - exit 1 -fi - -echo -n 'PostgreSQL.. ' -if psql -? > /dev/null 2>&1; then - echo 'ok' -else - echo 'not found' - exit 1 -fi - - -psqlCreateCommands=" - createuser -s ofn - psql postgres -c \"ALTER USER ofn WITH ENCRYPTED PASSWORD 'f00d'\" - createdb -O ofn open_food_network_dev - createdb -O ofn open_food_network_test - createdb -O ofn open_food_network_prod -" -PGPASSFILE=$(mktemp) -echo '' -echo -n 'Checking PostgreSQL database.. ' -echo 'localhost:5432:open_food_network_dev:ofn:f00d1' > $PGPASSFILE -export PGPASSFILE -if psql -w -U ofn open_food_network_dev -c 'select 1' > /dev/null 2>&1; then - echo 'ok' -else - echo 'no access' - echo '' - echo 'Database needs setup. Try automatic setup with sudo? [yes]' - read autosetup - if [ -z "$autosetup" ] || [ "$autosetup" = "yes" ]; then - if sudo su postgres -c "$psqlCreateCommands"; then - echo 'User and databases created.' - else - echo 'Failed to create user and databases.' - autosetup='no' - fi - else - autosetup='no' - fi - if [ "$autosetup" = 'no' ]; then - echo '' - echo 'Execute the following commands as database admin user (e.g. postgres):' - echo "$psqlCreateCommands" - rm "$PGPASSFILE" - exit 1 - fi -fi -rm "$PGPASSFILE" - -echo '' -echo 'Installing all gems. That can take a while.' -bundle install - -echo '' -echo 'Seeding database..' -bundle exec rake db:schema:load db:seed -echo 'Skipping sample data (out of date)' -#bundle exec rake openfoodnetwork:dev:load_sample_data -echo 'You can run `rails server` now to start.' - -echo '' -echo 'Executing tests..' -bundle exec rake db:test:load -bundle exec rspec spec - -if [ "$?" -eq 0 ]; then - echo '' - echo 'All done.' - echo 'You can run `rails server` now.' -fi diff --git a/app/assets/images/subtle_white_feathers.png b/app/assets/images/subtle_white_feathers.png new file mode 100644 index 0000000000..dd699f66ba Binary files /dev/null and b/app/assets/images/subtle_white_feathers.png differ diff --git a/app/assets/javascripts/admin/admin.js.coffee b/app/assets/javascripts/admin/admin.js.coffee index edebc019e5..a0235a568f 100644 --- a/app/assets/javascripts/admin/admin.js.coffee +++ b/app/assets/javascripts/admin/admin.js.coffee @@ -1,3 +1,3 @@ -angular.module("ofn.admin", ["ngResource", "ngAnimate", "ofn.dropdown"]).config ($httpProvider) -> +angular.module("ofn.admin", ["ngResource", "ngAnimate", "ofn.dropdown", "admin.products"]).config ($httpProvider) -> $httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content") $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*" \ No newline at end of file diff --git a/app/assets/javascripts/admin/directives/display_as.js.coffee b/app/assets/javascripts/admin/directives/display_as.js.coffee index a13d65bc5e..21f83ebf9f 100644 --- a/app/assets/javascripts/admin/directives/display_as.js.coffee +++ b/app/assets/javascripts/admin/directives/display_as.js.coffee @@ -17,7 +17,7 @@ angular.module("ofn.admin").directive "ofnDisplayAs", (optionValueNamer) -> variant_unit_scale: variant_unit_scale variant_unit: variant_unit variant_unit_name: scope.product.variant_unit_name - + scope.placeholder_text = new optionValueNamer(variant_object).name() productUnitProperties = -> diff --git a/app/assets/javascripts/admin/products/units_controller.js.coffee b/app/assets/javascripts/admin/products/units_controller.js.coffee index c0fd9273ac..28f82b8da7 100644 --- a/app/assets/javascripts/admin/products/units_controller.js.coffee +++ b/app/assets/javascripts/admin/products/units_controller.js.coffee @@ -1,10 +1,10 @@ angular.module("admin.products") - .controller "unitsCtrl", ($scope) -> + .controller "unitsCtrl", ($scope, optionValueNamer) -> $scope.product = { master: {} } + $scope.product.master.product = $scope.product + $scope.placeholder_text = "" - $scope.$watch -> - $scope.product.variant_unit_with_scale - , -> + $scope.$watchCollection '[product.variant_unit_with_scale, product.master.unit_value_with_description]', -> if $scope.product.variant_unit_with_scale match = $scope.product.variant_unit_with_scale.match(/^([^_]+)_([\d\.]+)$/) if match @@ -16,9 +16,6 @@ angular.module("admin.products") else $scope.product.variant_unit = $scope.product.variant_unit_scale = null - $scope.$watch -> - $scope.product.master.unit_value_with_description - , -> if $scope.product.master.hasOwnProperty("unit_value_with_description") match = $scope.product.master.unit_value_with_description.match(/^([\d\.]+(?= |$)|)( |)(.*)$/) if match @@ -27,6 +24,8 @@ angular.module("admin.products") $scope.product.master.unit_value *= $scope.product.variant_unit_scale if $scope.product.master.unit_value && $scope.product.variant_unit_scale $scope.product.master.unit_description = match[3] + $scope.placeholder_text = new optionValueNamer($scope.product.master).name() + $scope.variant_unit_options = [ ["Weight (g)", "weight_1"], ["Weight (kg)", "weight_1000"], @@ -42,5 +41,3 @@ angular.module("admin.products") $scope.hasUnit = (product) -> product.variant_unit_with_scale? - - diff --git a/app/assets/javascripts/admin/services/option_value_namer.js.coffee b/app/assets/javascripts/admin/services/option_value_namer.js.coffee index ef8ed6e1aa..1c9d061415 100644 --- a/app/assets/javascripts/admin/services/option_value_namer.js.coffee +++ b/app/assets/javascripts/admin/services/option_value_namer.js.coffee @@ -1,4 +1,4 @@ -angular.module("ofn.admin").factory "optionValueNamer", ($resource) -> +angular.module("admin.products").factory "optionValueNamer", -> class OptionValueNamer constructor: (@variant) -> diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index f529ac3255..45f91366ba 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -11,6 +11,7 @@ #= require lodash.underscore.js #= require angular-scroll.min.js #= require angular-google-maps.min.js +#= require angular-timer.min.js #= require ../shared/mm-foundation-tpls-0.2.2.min.js #= require ../shared/bindonce.min.js #= require ../shared/ng-infinite-scroll.min.js diff --git a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee index acc71b1172..98053daebf 100644 --- a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee @@ -1,6 +1,14 @@ -Darkswarm.controller "HubsCtrl", ($scope, Hubs, $document, $rootScope, HashNavigation) -> +Darkswarm.controller "HubsCtrl", ($scope, Hubs, Search, $document, $rootScope, HashNavigation, FilterSelectorsService) -> $scope.Hubs = Hubs $scope.hubs = Hubs.visible + $scope.totalActive = FilterSelectorsService.totalActive + $scope.clearAll = FilterSelectorsService.clearAll + $scope.filterText = FilterSelectorsService.filterText + $scope.FilterSelectorsService = FilterSelectorsService + $scope.query = Search.search() + + $scope.$watch "query", (query)-> + Search.search query $rootScope.$on "$locationChangeSuccess", (newRoute, oldRoute) -> if HashNavigation.active "hubs" diff --git a/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee index 1a28b5b107..d88af1e53d 100644 --- a/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee @@ -1,2 +1,12 @@ -Darkswarm.controller "ProducersCtrl", ($scope, Producers) -> +Darkswarm.controller "ProducersCtrl", ($scope, Producers, $filter, FilterSelectorsService, Search) -> $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.$watch "query", (query)-> + Search.search query diff --git a/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee index 73406e072b..07ddb4bc61 100644 --- a/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/products/product_node_controller.js.coffee @@ -1,11 +1,6 @@ -Darkswarm.controller "ProductNodeCtrl", ($scope) -> - - $scope.price = -> - if $scope.product.variants.length > 0 - prices = (v.price for v in $scope.product.variants) - Math.min.apply(null, prices) - else - $scope.product.price - +Darkswarm.controller "ProductNodeCtrl", ($scope, $modal) -> $scope.enterprise = $scope.product.supplier # For the modal, so it's consistent - $scope.hasVariants = $scope.product.variants.length > 0 + + $scope.triggerProductModal = -> + $modal.open(templateUrl: "product_modal.html", scope: $scope) + diff --git a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee index 3860a2d56e..361c1ebca4 100644 --- a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee @@ -1,5 +1,9 @@ -Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Product, OrderCycle) -> +Darkswarm.controller "ProductsCtrl", ($scope, $rootScope, Product, OrderCycle, FilterSelectorsService) -> $scope.Product = Product + $scope.totalActive = FilterSelectorsService.totalActive + $scope.clearAll = FilterSelectorsService.clearAll + $scope.filterText = FilterSelectorsService.filterText + $scope.FilterSelectorsService = FilterSelectorsService $scope.limit = 3 $scope.ordering = {order: "name"} $scope.order_cycle = OrderCycle.order_cycle diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee index fc6f9dfed4..7a6cba7070 100644 --- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee +++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee @@ -5,7 +5,9 @@ window.Darkswarm = angular.module("Darkswarm", ["ngResource", 'infinite-scroll', 'angular-flash.service', 'templates', + 'timer', 'ngSanitize', + 'ngAnimate', 'google-maps', 'duScroll', ]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) -> diff --git a/app/assets/javascripts/darkswarm/directives/active_selector.js.coffee b/app/assets/javascripts/darkswarm/directives/active_selector.js.coffee new file mode 100644 index 0000000000..74523c2c3d --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/active_selector.js.coffee @@ -0,0 +1,12 @@ +Darkswarm.directive "activeSelector", -> + restrict: 'E' + transclude: true + replace: true + templateUrl: 'active_selector.html' + link: (scope, elem, attr)-> + scope.selector.emit = scope.emit + elem.bind "click", -> + scope.$apply -> + scope.selector.active = !scope.selector.active + scope.emit() + diff --git a/app/assets/javascripts/darkswarm/directives/product_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/product_modal.js.coffee deleted file mode 100644 index 428fde3633..0000000000 --- a/app/assets/javascripts/darkswarm/directives/product_modal.js.coffee +++ /dev/null @@ -1,9 +0,0 @@ -Darkswarm.directive "productModal", ($modal)-> - restrict: 'E' - replace: true - template: "" - transclude: true - link: (scope, elem, attrs, ctrl)-> - elem.on "click", => - scope.modalInstance = $modal.open(controller: ctrl, templateUrl: 'product_modal.html', scope: scope) - diff --git a/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee b/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee new file mode 100644 index 0000000000..3f752f8a3d --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/shipping_type_selector.js.coffee @@ -0,0 +1,19 @@ +Darkswarm.directive "shippingTypeSelector", (FilterSelectorsService)-> + restrict: 'E' + replace: true + templateUrl: 'shipping_type_selector.html' + link: (scope, elem, attr)-> + scope.shippingTypes = + pickup: false + delivery: false + + scope.selectors = + delivery: FilterSelectorsService.new + icon: "ofn-i_039-delivery" + pickup: FilterSelectorsService.new + icon: "ofn-i_038-takeaway" + + scope.emit = -> + scope.shippingTypes = + pickup: scope.selectors.pickup.active + delivery: scope.selectors.delivery.active diff --git a/app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee b/app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee new file mode 100644 index 0000000000..a682176d77 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/taxon_selector.js.coffee @@ -0,0 +1,36 @@ +Darkswarm.directive "taxonSelector", (FilterSelectorsService)-> + restrict: 'E' + replace: true + scope: + objects: "&" + results: "=" + templateUrl: "taxon_selector.html" + + link: (scope, elem, attr)-> + selectors_by_id = {} + selectors = ["foo"] + + scope.emit = -> + scope.results = selectors.filter (selector)-> + selector.active + .map (selector)-> + selector.taxon.id + + scope.selectors = -> + taxons = {} + selectors = [] + for object in scope.objects() + for taxon in object.taxons + taxons[taxon.id] = taxon + if object.supplied_taxons + for taxon in object.supplied_taxons + taxons[taxon.id] = taxon + + for id, taxon of taxons + if selector = selectors_by_id[id] + selectors.push selector + else + selector = selectors_by_id[id] = FilterSelectorsService.new + taxon: taxon + selectors.push selector + selectors diff --git a/app/assets/javascripts/darkswarm/filters/active.js.coffee b/app/assets/javascripts/darkswarm/filters/active.js.coffee new file mode 100644 index 0000000000..93bf1c658c --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/active.js.coffee @@ -0,0 +1,14 @@ +Darkswarm.filter 'active', ()-> + (objects, options)-> + objects ||= [] + options ?= null + + if options.open and !options.closed + objects.filter (obj)-> + obj.active + else if options.closed and !options.open + objects.filter (obj)-> + !obj.active + else + objects + diff --git a/app/assets/javascripts/darkswarm/filters/by_producer.js.coffee b/app/assets/javascripts/darkswarm/filters/by_producer.js.coffee new file mode 100644 index 0000000000..3b9ec12d2b --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/by_producer.js.coffee @@ -0,0 +1,6 @@ +Darkswarm.filter "byProducer", -> + (objects, id) -> + objects ||= [] + id ?= 0 + objects.filter (obj)-> + obj.producer.id == id diff --git a/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee b/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee new file mode 100644 index 0000000000..acbd3fd637 --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee @@ -0,0 +1,4 @@ +Darkswarm.filter "capitalize", -> + (input, scope) -> + input = input.toLowerCase() if input? + input.substring(0, 1).toUpperCase() + input.substring(1) diff --git a/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee b/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee index bbbb82fa30..ed5b8c1bea 100644 --- a/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee +++ b/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee @@ -2,5 +2,5 @@ Darkswarm.filter 'filterProducers', (hubsFilter)-> (producers, text) -> producers ||= [] text ?= "" - hubsFilter(producers, text) + diff --git a/app/assets/javascripts/darkswarm/filters/shipping.js.coffee b/app/assets/javascripts/darkswarm/filters/shipping.js.coffee new file mode 100644 index 0000000000..761e34337d --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/shipping.js.coffee @@ -0,0 +1,13 @@ +Darkswarm.filter 'shipping', ()-> + (objects, options)-> + objects ||= [] + options ?= null + + if options.pickup and !options.delivery + objects.filter (obj)-> + obj.pickup + else if options.delivery and !options.pickup + objects.filter (obj)-> + obj.delivery + else + objects diff --git a/app/assets/javascripts/darkswarm/filters/taxons.js.coffee b/app/assets/javascripts/darkswarm/filters/taxons.js.coffee new file mode 100644 index 0000000000..3eb32ce1a4 --- /dev/null +++ b/app/assets/javascripts/darkswarm/filters/taxons.js.coffee @@ -0,0 +1,13 @@ +Darkswarm.filter 'taxons', (Matcher)-> + # Filter anything that responds to object.taxons, and/or object.primary_taxon + (objects, ids) -> + objects ||= [] + ids ?= [] + if ids.length == 0 + objects + else + objects.filter (obj)-> + taxons = obj.taxons + taxons.concat obj.supplied_taxons if obj.supplied_taxons + obj.primary_taxon?.id in ids || taxons.some (taxon)-> + taxon.id in ids diff --git a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee index b442773d31..50143e006b 100644 --- a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee +++ b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee @@ -1,15 +1,21 @@ -Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Dereferencer)-> +Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer)-> new class Enterprises enterprises_by_id: {} # id/object pairs for lookup constructor: -> @enterprises = enterprises for enterprise in enterprises @enterprises_by_id[enterprise.id] = enterprise - @dereference() + @dereferenceEnterprises() + @dereferenceTaxons() - dereference: -> + dereferenceEnterprises: -> if CurrentHub.hub?.id CurrentHub.hub = @enterprises_by_id[CurrentHub.hub.id] for enterprise in @enterprises Dereferencer.dereference enterprise.hubs, @enterprises_by_id Dereferencer.dereference enterprise.producers, @enterprises_by_id + + dereferenceTaxons: -> + 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/filter_selectors.js.coffee b/app/assets/javascripts/darkswarm/services/filter_selectors.js.coffee new file mode 100644 index 0000000000..47ed12806d --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/filter_selectors.js.coffee @@ -0,0 +1,28 @@ +Darkswarm.factory "FilterSelectorsService", -> + # This stores all filters so we can access in-use counts etc + # Accessed via activeSelector Directive + new class FilterSelectorsService + selectors: [] + new: (obj = {})-> + obj.active = false + @selectors.push obj + obj + + totalActive: => + @selectors.filter (selector)-> + selector.active + .length + + filterText: (active)=> + total = @totalActive() + if total == 0 + if active then "Hide filters" else "Filter by" + else if total == 1 + "1 filter applied" + else + "#{@totalActive()} filters applied" + + clearAll: => + for selector in @selectors + selector.active = false + selector.emit() diff --git a/app/assets/javascripts/darkswarm/services/product.js.coffee b/app/assets/javascripts/darkswarm/services/product.js.coffee index 2c57512bff..48cdd6ae33 100644 --- a/app/assets/javascripts/darkswarm/services/product.js.coffee +++ b/app/assets/javascripts/darkswarm/services/product.js.coffee @@ -1,5 +1,5 @@ -Darkswarm.factory 'Product', ($resource, Enterprises) -> - new class Product +Darkswarm.factory 'Product', ($resource, Enterprises, Dereferencer, Taxons) -> + new class Products constructor: -> @update() @@ -10,7 +10,8 @@ Darkswarm.factory 'Product', ($resource, Enterprises) -> update: => @loading = true - @products = $resource("/shop/products").query => + @products = $resource("/shop/products").query (products)=> + @extend() @dereference() @loading = false @ @@ -18,3 +19,12 @@ Darkswarm.factory 'Product', ($resource, Enterprises) -> dereference: -> for product in @products product.supplier = Enterprises.enterprises_by_id[product.supplier.id] + Dereferencer.dereference product.taxons, Taxons.taxons_by_id + + extend: -> + for product in @products + if product.variants.length > 0 + prices = (v.price for v in product.variants) + product.price = Math.min.apply(null, prices) + + product.hasVariants = product.variants.length > 0 diff --git a/app/assets/javascripts/darkswarm/services/search.js.coffee b/app/assets/javascripts/darkswarm/services/search.js.coffee new file mode 100644 index 0000000000..10e601c4e0 --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/search.js.coffee @@ -0,0 +1,9 @@ +Darkswarm.factory "Search", ($location)-> + new class Search + search: (query = false)-> + if query + $location.search('query', query) + else if query == "" + $location.search('query', null) + else + $location.search()['query'] diff --git a/app/assets/javascripts/darkswarm/services/taxons.js.coffee b/app/assets/javascripts/darkswarm/services/taxons.js.coffee new file mode 100644 index 0000000000..cbe6c118e0 --- /dev/null +++ b/app/assets/javascripts/darkswarm/services/taxons.js.coffee @@ -0,0 +1,8 @@ +Darkswarm.factory "Taxons", (taxons)-> + new class Taxons + taxons: taxons + taxons_by_id: {} + constructor: -> + for taxon in @taxons + @taxons_by_id[taxon.id] = taxon + diff --git a/app/assets/javascripts/templates/active_selector.html.haml b/app/assets/javascripts/templates/active_selector.html.haml new file mode 100644 index 0000000000..14b12bbd9f --- /dev/null +++ b/app/assets/javascripts/templates/active_selector.html.haml @@ -0,0 +1,2 @@ +%li{"ng-class" => "{active: selector.active}"} + %a{"ng-transclude" => true} diff --git a/app/assets/javascripts/templates/shipping_type_selector.html.haml b/app/assets/javascripts/templates/shipping_type_selector.html.haml new file mode 100644 index 0000000000..8feb23e59f --- /dev/null +++ b/app/assets/javascripts/templates/shipping_type_selector.html.haml @@ -0,0 +1,3 @@ +%active-selector{"ng-repeat" => "(name, selector) in selectors"} + %i{"ng-class" => "selector.icon"} + {{ name | capitalize }} diff --git a/app/assets/javascripts/templates/taxon_selector.html.haml b/app/assets/javascripts/templates/taxon_selector.html.haml new file mode 100644 index 0000000000..02450c8d12 --- /dev/null +++ b/app/assets/javascripts/templates/taxon_selector.html.haml @@ -0,0 +1,3 @@ +%active-selector{"ng-repeat" => "selector in selectors()"} + %render-svg{path: "{{selector.taxon.icon}}"} + %span {{ selector.taxon.name }} diff --git a/app/assets/javascripts/templates/test.nghaml b/app/assets/javascripts/templates/test.nghaml deleted file mode 100644 index 99fb61374e..0000000000 --- a/app/assets/javascripts/templates/test.nghaml +++ /dev/null @@ -1 +0,0 @@ -Frogs diff --git a/app/assets/stylesheets/darkswarm/active_table.css.sass b/app/assets/stylesheets/darkswarm/active_table.css.sass index 33067b022a..a08f2c794b 100644 --- a/app/assets/stylesheets/darkswarm/active_table.css.sass +++ b/app/assets/stylesheets/darkswarm/active_table.css.sass @@ -2,17 +2,20 @@ @import mixins @import "compass/css3/user-interface" + +.no-results + font-size: 1.875rem + .active_table margin: 2em 0em @include user-select(none) .active_table_row - padding: 0.8em 0.5em display: block &:first-child cursor: pointer + padding: 1rem 0 .active_table .active_table_node - @include csstrans display: block border: 1px solid transparent @@ -20,13 +23,12 @@ border: 1px solid transparent &, & > a.row display: block - + .active_table_row.link border: 0 &.open .active_table_row:first-child - @include csstrans border-top: 1px solid $dark-grey border-left: 1px solid $dark-grey border-right: 1px solid $dark-grey @@ -45,7 +47,6 @@ background-color: rgba(255,255,255,0.2) .active_table_row.link - @include csstrans padding: 0 -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,0.35) box-shadow: 0 1px 1px 0 rgba(0,0,0,0.35) diff --git a/app/assets/stylesheets/darkswarm/active_table_search.css.sass b/app/assets/stylesheets/darkswarm/active_table_search.css.sass index e862f4322b..9920dc1d87 100644 --- a/app/assets/stylesheets/darkswarm/active_table_search.css.sass +++ b/app/assets/stylesheets/darkswarm/active_table_search.css.sass @@ -1,19 +1,112 @@ @import mixins +@import branding +@import big-input +@import animations + +// OVERRIDES +.row .row.filter-box + margin-left: 0 + margin-right: 0 + +.row.filter-box:first-child + border-top: 1px solid $clr-brick + +products .filter-box + background: #f7f7f7 + +.filter-box + background: rgba(245,245,245,0.6) + + .tdhead + padding: 0.25rem 0.5rem + margin-top: 0.9rem + + // OVERRIDES + [class*="block-grid-"] + margin: 0 0 0.5rem 0 + [class*="block-grid-"] > li + padding-bottom: 0.5rem !important + + + li + @include border-radius(12px) + padding-top: 0.5rem + margin-bottom: 0.25rem + &:hover, &:focus + background: rgba(255,255,255,0.25) + li.active + background: white + @include box-shadow(inset 0 1px 3px 0 rgba(143,48,29,0.5)) + + li.active a + color: $clr-brick + render-svg + svg + path + fill: $clr-brick + &:hover, &:focus + border-color: $clr-brick-bright + + li a + @include csstrans + display: table + table-layout: fixed + text-transform: capitalize + overflow: visible + // width: 100% + // height: 2rem + line-height: 1 + color: #444 + font-size: 0.875rem + span + display: table-cell + vertical-align: middle + text-align: left + i + display: block + font-size: 1.5rem + margin: 0 0.2rem 0 0 + + &:hover, &:focus + color: $clr-brick-bright + + render-svg + svg + path + fill: $clr-brick-bright + + &:active, &.active + color: $clr-brick + render-svg + svg + path + fill: $clr-brick + + + render-svg + display: block + width: 1.5rem + height: 1.5rem + margin: 0 0.2rem 0 0 + padding: 0 + svg + width: 1.5rem + height: 1.5rem + path + fill: #666 + +.button.filterbtn + margin-bottom: 0 !important + min-width: 160px #active-table-search - position: relative - - i.ofn-i_020-search - position: absolute - left: 26px - top: 12px - font-size: 1.6em - z-index: 2 - color: #b2b2b2 - + position: relative + @include placeholder(rgba(0,0,0,0.4), #777) input[type="text"] - font-size: 2em - @include csstrans - @include big-input - padding-left: 44px - \ No newline at end of file + @include big-input(rgba(0,0,0,0.3), #777, $clr-brick) + + + + + + diff --git a/app/assets/stylesheets/darkswarm/animations.sass b/app/assets/stylesheets/darkswarm/animations.sass index fc1db55bb5..6532cce60d 100644 --- a/app/assets/stylesheets/darkswarm/animations.sass +++ b/app/assets/stylesheets/darkswarm/animations.sass @@ -1,3 +1,58 @@ +@import mixins + +// ANIMATION FUNCTIONS + +@-webkit-keyframes slideInDown + 0% + opacity: 0 + -webkit-transform: translateY(-20px) + transform: translateY(-20px) + 100% + -webkit-transform: translateY(0) + transform: translateY(0) + +@keyframes slideInDown + 0% + opacity: 0 + -webkit-transform: translateY(-20px) + -ms-transform: translateY(-20px) + transform: translateY(-20px) + 100% + -webkit-transform: translateY(0) + -ms-transform: translateY(0) + transform: translateY(0) + +@-webkit-keyframes slideOutUp + 0% + -webkit-transform: translateY(0) + transform: translateY(0) + 100% + opacity: 0 + -webkit-transform: translateY(-20px) + transform: translateY(-20px) + + +@keyframes slideOutUp + 0% + -webkit-transform: translateY(0) + -ms-transform: translateY(0) + transform: translateY(0) + +@-webkit-keyframes fadeIn + 0% + opacity: 0 + 100% + opacity: 1 + + +@keyframes fadeIn + 0% + opacity: 0 + 100% + opacity: 1 + +// ANIMATION CLASSES + .fade opacity: 0 -webkit-transition: opacity .15s linear @@ -7,10 +62,10 @@ opacity: 1 .reveal-modal.fade - -webkit-transition: -webkit-transform .3s ease-out - -moz-transition: -moz-transform .3s ease-out - -o-transition: -o-transform .3s ease-out - transition: transform .3s ease-out + -webkit-transition: -webkit-transform .2s ease-out + -moz-transition: -moz-transform .2s ease-out + -o-transition: -o-transform .2s ease-out + transition: transform .2s ease-out -webkit-transform: translate(0, -25%) -ms-transform: translate(0, -25%) transform: translate(0, -25%) @@ -28,3 +83,77 @@ filter: alpha(opacity = 50) opacity: .5 + +.animate-repeat + -webkit-transform: translateZ(0) + transform: translateZ(0) + &.ng-move, &.ng-enter, &.ng-leave + -webkit-transition: all 300ms linear + transition: all 300ms linear + + &.ng-leave + opacity: 1 + &.ng-leave-active + opacity: 0 + + &.ng-enter + opacity: 0 + &.ng-enter-active + opacity: 1 + +product.animate-repeat + &.ng-leave + border-color: rgba(153, 153, 153, 1) + &.ng-leave-active + border-color: rgba(153, 153, 153, 0) + + &.ng-enter + border-color: rgba(153, 153, 153, 0) + &.ng-enter-active + border-color: rgba(153, 153, 153, 1) + +.animate-show + -webkit-animation-name: slideInDown + animation-name: slideInDown + -webkit-animation-duration: 0.5s + animation-duration: 0.5s + -webkit-animation-fill-mode: both + animation-fill-mode: both + // line-height: 20px + // opacity: 1 + +.animate-show.ng-hide-add, +.animate-show.ng-hide-remove + // display: block !important + +.animate-show.ng-hide + -webkit-animation-name: slideOutUp + animation-name: slideOutUp + -webkit-animation-duration: 0.15s + animation-duration: 0.15s + -webkit-animation-fill-mode: both + animation-fill-mode: both + // line-height: 0 + // opacity: 0 + // padding: 0 10px + +.row.animate-show ~ .row + -webkit-animation-name: fadeIn + animation-name: fadeIn + -webkit-animation-duration: 0.5s + animation-duration: 0.5s + -webkit-animation-fill-mode: both + animation-fill-mode: both + + +@mixin csstrans + -webkit-transition: all 300ms ease + -moz-transition: all 300ms ease + -ms-transition: all 300ms ease + -o-transition: all 300ms ease + transition: all 300ms ease + -webkit-transform-style: preserve-3d + + + + diff --git a/app/assets/stylesheets/darkswarm/big-input.sass b/app/assets/stylesheets/darkswarm/big-input.sass new file mode 100644 index 0000000000..133097a5ac --- /dev/null +++ b/app/assets/stylesheets/darkswarm/big-input.sass @@ -0,0 +1,64 @@ +@import typography +@import branding +@import animations +@import mixins + +//Big search used in active table search \\ + +@mixin big-input($input, $inputhvr, $inputactv) + @extend .avenir + @include csstrans + @include border-radius(0.5rem) + + // transition: all 0.5s ease + background: rgba(255,255,255,0.1) + border: 2px solid $input + font-size: 2rem + box-shadow: 0 + padding: 0.75rem 1rem 0.35rem 1rem + height: auto + width: 100% + margin-bottom: 0.5rem + box-shadow: none + color: $inputactv + + &:hover + @include box-shadow(0 1px 1px 0 rgba(255,255,255,0.25)) + border: 2px solid $inputhvr + color: $inputactv + + &:active, &:focus, &.active + border: 2px solid $inputactv + color: $inputactv + background: white + background: rgba(255,255,255,0.5) + text-shadow: 0 0 10px #ffffff + padding: 1.5rem 1rem 1rem 1rem + letter-spacing: 0.02rem + +@mixin big-input-static + outline: 0 + &:active, &:focus, &.active + padding: 0.75rem 1rem 0.35rem 1rem + letter-spacing: 0 + +@mixin placeholder($placeholder, $placeholderhvr) + ::-webkit-input-placeholder + color: $placeholder + :-moz-placeholder + color: $placeholder + ::-moz-placeholder + color: $placeholder + :-ms-input-placeholder + color: $placeholder + + &:hover + ::-webkit-input-placeholder + color: $placeholderhvr + :-moz-placeholder + color: $placeholderhvr + ::-moz-placeholder + color: $placeholderhvr + :-ms-input-placeholder + color: $placeholderhvr + diff --git a/app/assets/stylesheets/darkswarm/checkout.css.sass b/app/assets/stylesheets/darkswarm/checkout.css.sass index 9caf28ae43..d90c81f673 100644 --- a/app/assets/stylesheets/darkswarm/checkout.css.sass +++ b/app/assets/stylesheets/darkswarm/checkout.css.sass @@ -27,7 +27,6 @@ checkout // Logic to turn on & off the alerts for success against each fieldset label, label.alert, label.success, &.valid label.alert, &.dirty label.success - @include csstrans display: none &.dirty label.alert diff --git a/app/assets/stylesheets/darkswarm/footer.sass b/app/assets/stylesheets/darkswarm/footer.sass index 30f8caf8b4..9e3f9dac55 100644 --- a/app/assets/stylesheets/darkswarm/footer.sass +++ b/app/assets/stylesheets/darkswarm/footer.sass @@ -8,8 +8,6 @@ footer .row &, & * color: white - a, a > i - @include csstrans a, a * color: $clr-brick-light-bright &:hover, &:active, &:focus diff --git a/app/assets/stylesheets/darkswarm/home_tagline.css.sass b/app/assets/stylesheets/darkswarm/home_tagline.css.sass index 3d00cb7a96..2c109a2703 100644 --- a/app/assets/stylesheets/darkswarm/home_tagline.css.sass +++ b/app/assets/stylesheets/darkswarm/home_tagline.css.sass @@ -7,19 +7,24 @@ background-color: black background-image: url("/assets/home/tagline-bg.jpg") @include fullbg - height: 400px + height: 500px padding: 40px 0px - h1, h2, p + h1, h2, span, small, timer color: white + p + color: $clr-brick-light h1 - margin-bottom: 1em + margin-bottom: 3rem h2 font-size: 1.6875rem max-width: 610px margin: 0 auto + padding-bottom: 0.5rem a color: $clr-brick-bright &:hover, &:active, &:focus color: $clr-brick-light-bright - @include textsoftpress \ No newline at end of file + @include textsoftpress + a.button.primary + color: white \ No newline at end of file diff --git a/app/assets/stylesheets/darkswarm/hubs.css.sass b/app/assets/stylesheets/darkswarm/hubs.css.sass index 87ccb3ef50..43f2b8cbea 100644 --- a/app/assets/stylesheets/darkswarm/hubs.css.sass +++ b/app/assets/stylesheets/darkswarm/hubs.css.sass @@ -2,6 +2,8 @@ @import mixins #hubs - background: $clr-brick-ultra-light url("/assets/home/shopping-bg.jpg") - @include fullwidthbg + background-repeat: repeat + background-image: url("/assets/subtle_white_feathers.png") + // background: $clr-brick-ultra-light url("/assets/home/shopping-bg.jpg") + // @include fullwidthbg @include panepadding diff --git a/app/assets/stylesheets/darkswarm/menu.css.sass b/app/assets/stylesheets/darkswarm/menu.css.sass index 0460367e87..7be329824b 100644 --- a/app/assets/stylesheets/darkswarm/menu.css.sass +++ b/app/assets/stylesheets/darkswarm/menu.css.sass @@ -27,7 +27,6 @@ nav .top-bar-section ul li > a font-size: 0.75rem height: 45px - @include csstrans opacity: 0.8 &:hover, &:focus, &:active opacity: 1 @@ -47,7 +46,6 @@ nav @include box-shadow(inset 0 0 6px 2px rgba(0,0,0,0.5)) .off-canvas-wrap .tab-bar .menu-icon - @include csstrans @include box-shadow(none) .off-canvas-wrap.move-right .tab-bar .menu-icon span @@ -72,4 +70,6 @@ nav @media screen and (max-width: 1025px) section.right .nav-branded - padding: 0 1em \ No newline at end of file + padding: 0 1em + + \ No newline at end of file diff --git a/app/assets/stylesheets/darkswarm/mixins.sass b/app/assets/stylesheets/darkswarm/mixins.sass index edb41f9ead..b7207335bb 100644 --- a/app/assets/stylesheets/darkswarm/mixins.sass +++ b/app/assets/stylesheets/darkswarm/mixins.sass @@ -8,56 +8,9 @@ padding-top: 100px padding-bottom: 100px -@mixin big-input - border: 1px solid #999 - font-size: 18px - @extend .avenir - box-shadow: 0 - padding: 0.75em 1em - height: auto - margin-bottom: 1em - background: rgba(255,255,255,0.65) - &:active, &:hover, &:focus - background: rgba(255,255,255,1) - border-color: #888 - @mixin disabled color: $disabled-bright -@mixin csstrans - -webkit-transition: all 100ms ease-in-out - -moz-transition: all 100ms ease-in-out - -ms-transition: all 100ms ease-in-out - -o-transition: all 100ms ease-in-out - transition: all 100ms ease-in-out - -webkit-transform-style: preserve-3d - -@mixin animate-in - -webkit-animation: cssAnimation 100ms 1 ease-in - -moz-animation: cssAnimation 100ms 1 ease-in - -o-animation: cssAnimation 100ms 1 ease-in - - @-webkit-keyframes cssAnimation - from - -webkit-transform: rotate(180deg) scale(0.25) skew(0deg) translate(0px) - to - -webkit-transform: rotate(0deg) scale(1) skew(0deg) translate(0px) - - - @-moz-keyframes cssAnimation - from - -moz-transform: rotate(180deg) scale(0.25) skew(0deg) translate(0px) - to - -moz-transform: rotate(0deg) scale(1) skew(0deg) translate(0px) - - - @-o-keyframes cssAnimation - from - -o-transform: rotate(180deg) scale(0.25) skew(0deg) translate(0px) - to - -o-transform: rotate(0deg) scale(1) skew(0deg) translate(0px) - - @mixin box-shadow($box-shadow) -moz-box-shadow: $box-shadow -webkit-box-shadow: $box-shadow @@ -67,6 +20,18 @@ -webkit-border-radius: $border-radius border-radius: $border-radius +@mixin transform-translate($translate) + -ms-transform: $translate + -webkit-transform: $translate + transform: $translate + +@mixin transform-scale($scale) + -moz-transform: $scale + -webkit-transform: $scale + -o-transform: $scale + -ms-transform: $scale + transform: $scale + // Typography \\ @mixin avenir diff --git a/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass b/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass index 0560cb742c..1ed8fc5823 100644 --- a/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass +++ b/app/assets/stylesheets/darkswarm/modal-enterprises.css.sass @@ -90,7 +90,8 @@ .active_table_row .cta-container - padding-bottom: 0.75rem + // padding-bottom: 0.75rem + background: rgba(0,0,0,0.05) padding-top: 0.5rem // Generic styles for use diff --git a/app/assets/stylesheets/darkswarm/modals.css.sass b/app/assets/stylesheets/darkswarm/modals.css.sass index 7121e7ea69..ba46a984ae 100644 --- a/app/assets/stylesheets/darkswarm/modals.css.sass +++ b/app/assets/stylesheets/darkswarm/modals.css.sass @@ -10,7 +10,6 @@ dialog, .reveal-modal background-color: rgba(0,0,0,0.65) dialog .close-reveal-modal.outside, .reveal-modal .close-reveal-modal.outside - @include csstrans top: -2.5rem right: -2.5rem font-size: 2rem diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index 3407916bfc..6e6001ce6e 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -1,12 +1,17 @@ @import mixins @import variables @import branding +@import big-input .darkswarm - #search - font-size: 2em - @include big-input + // #search + @include placeholder(rgba(0,0,0,0.4), #777) + + input#search + @include big-input(rgba(0,0,0,0.3), #777, $clr-brick) + @include big-input-static + color: #666 display: block navigation @@ -98,7 +103,6 @@ background: $clr-brick-ultra-light product - @include csstrans border: 1px solid #989898 display: block margin-bottom: 1em !important @@ -111,22 +115,19 @@ .columns padding-top: 1em padding-bottom: 1em - line-height: 2.4em + line-height: 1em .row.summary, .row.variants - @include csstrans margin-left: 0 margin-right: 0 background: #f7f7f7 border-top: 1px solid #dfdfdf .row.summary - @include csstrans background: #fff line-height: 1 .summary-header - @include csstrans font-size: 1.15rem &, & * diff --git a/app/assets/stylesheets/darkswarm/tabs.css.sass b/app/assets/stylesheets/darkswarm/tabs.css.sass index a8e3ef4a22..76e4988d2f 100644 --- a/app/assets/stylesheets/darkswarm/tabs.css.sass +++ b/app/assets/stylesheets/darkswarm/tabs.css.sass @@ -20,13 +20,9 @@ .panel border-color: rgba(219, 88, 61, 0.5) background-color: rgba(255, 255, 255, 0) - // @include box-shadow( 0 1px 1px 0 rgba(255,255,255,1)) - - dl dd a @include avenir - @include csstrans background: transparent text-transform: uppercase line-height: 50px diff --git a/app/assets/stylesheets/darkswarm/taxons.css.sass b/app/assets/stylesheets/darkswarm/taxons.css.sass index d7d60fa31c..32ba3aba1b 100644 --- a/app/assets/stylesheets/darkswarm/taxons.css.sass +++ b/app/assets/stylesheets/darkswarm/taxons.css.sass @@ -2,12 +2,11 @@ @import mixins .fat-taxons - @include csstrans display: inline-block line-height: 1 margin-right: 0.5rem margin-bottom: 0.35rem - text-transform: uppercase + text-transform: capitalize font-weight: 300 font-size: 0.875rem background: rgba(235,235,235,0.5) @@ -23,8 +22,6 @@ &, &* display: inline-block color: #555 - // &:hover, &.hover, &:active - // background: rgba(255,255,255,0.5) .product-header render-svg @@ -40,7 +37,6 @@ height: 24px .summary-header - @include csstrans render-svg svg width: 18px diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass index 8dfe9eb9d9..ead907926e 100644 --- a/app/assets/stylesheets/darkswarm/typography.css.sass +++ b/app/assets/stylesheets/darkswarm/typography.css.sass @@ -34,6 +34,9 @@ small, .small margin-bottom: 0.5rem &, & * font-size: 0.875rem +.light + color: #999 + display: inline @mixin avenir font-family: "AvenirBla_IE", "AvenirBla" diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index a4f4eea834..53e58d110b 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -19,6 +19,17 @@ module Admin end + protected + + def build_resource_with_address + enterprise = build_resource_without_address + enterprise.address = Spree::Address.new + enterprise.address.country = Spree::Country.find_by_id(Spree::Config[:default_country_id]) + enterprise + end + alias_method_chain :build_resource, :address + + private # When an enterprise user creates another enterprise, it is granted management diff --git a/app/helpers/shared_helper.rb b/app/helpers/shared_helper.rb index f555a9f695..ff4310a8f0 100644 --- a/app/helpers/shared_helper.rb +++ b/app/helpers/shared_helper.rb @@ -3,6 +3,10 @@ module SharedHelper inject_json_ams "enterprises", Enterprise.all, Api::EnterpriseSerializer, active_distributors: @active_distributors end + def inject_taxons + inject_json_ams "taxons", Spree::Taxon.all, Api::TaxonSerializer + end + def inject_json(name, partial, opts = {}) render partial: "json/injection", locals: {name: name, partial: partial}.merge(opts) end diff --git a/app/models/distributor_shipping_method.rb b/app/models/distributor_shipping_method.rb new file mode 100644 index 0000000000..e765a23e50 --- /dev/null +++ b/app/models/distributor_shipping_method.rb @@ -0,0 +1,5 @@ +class DistributorShippingMethod < ActiveRecord::Base + self.table_name = "distributors_shipping_methods" + belongs_to :shipping_method, class_name: Spree::ShippingMethod + belongs_to :distributor, class_name: Enterprise, touch: true +end diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 5b922b15e4..2a4acc1d0a 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -14,8 +14,8 @@ class Enterprise < ActiveRecord::Base has_many :enterprise_roles, :dependent => :destroy has_many :users, through: :enterprise_roles has_and_belongs_to_many :payment_methods, join_table: 'distributors_payment_methods', class_name: 'Spree::PaymentMethod', foreign_key: 'distributor_id' - has_and_belongs_to_many :shipping_methods, join_table: 'distributors_shipping_methods', class_name: 'Spree::ShippingMethod', foreign_key: 'distributor_id' - + has_many :distributor_shipping_methods, foreign_key: :distributor_id + has_many :shipping_methods, through: :distributor_shipping_methods delegate :latitude, :longitude, :city, :state_name, :to => :address @@ -29,7 +29,6 @@ class Enterprise < ActiveRecord::Base validates_presence_of :address validates_associated :address - after_initialize :initialize_country before_validation :set_unused_address_fields after_validation :geocode_address @@ -159,20 +158,22 @@ class Enterprise < ActiveRecord::Base self.relatives.is_distributor end + def suppliers + self.relatives.is_primary_producer + end + def website strip_url read_attribute(:website) end + def facebook strip_url read_attribute(:facebook) end + def linkedin strip_url read_attribute(:linkedin) end - def suppliers - self.relatives.is_primary_producer - end - def distributed_variants Spree::Variant.joins(:product).merge(Spree::Product.in_distributor(self)).select('spree_variants.*') end @@ -192,6 +193,7 @@ class Enterprise < ActiveRecord::Base where('spree_products.id IN (?)', Spree::Product.in_distributor(self)). select('DISTINCT spree_taxons.*') end + # Return all taxons for all supplied products def supplied_taxons Spree::Taxon. @@ -200,17 +202,13 @@ class Enterprise < ActiveRecord::Base select('DISTINCT spree_taxons.*') end + private def strip_url(url) url.andand.sub /(https?:\/\/)?(www\.)?/, '' end - def initialize_country - self.address ||= Spree::Address.new - self.address.country = Spree::Country.find_by_id(Spree::Config[:default_country_id]) if self.address.new_record? - end - def set_unused_address_fields address.firstname = address.lastname = address.phone = 'unused' if address.present? end diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index 26bf8fc012..4db650f2e7 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -1,6 +1,6 @@ class EnterpriseRelationship < ActiveRecord::Base - belongs_to :parent, class_name: 'Enterprise' - belongs_to :child, class_name: 'Enterprise' + belongs_to :parent, class_name: 'Enterprise', touch: true + belongs_to :child, class_name: 'Enterprise', touch: true validates_presence_of :parent_id, :child_id validates_uniqueness_of :child_id, scope: :parent_id, message: "^That relationship is already established." diff --git a/app/models/spree/address_decorator.rb b/app/models/spree/address_decorator.rb index 28f292fbd3..8797a91599 100644 --- a/app/models/spree/address_decorator.rb +++ b/app/models/spree/address_decorator.rb @@ -2,6 +2,8 @@ Spree::Address.class_eval do has_one :enterprise belongs_to :country, class_name: "Spree::Country" + after_save :touch_enterprise + geocoded_by :full_address delegate :name, :to => :state, :prefix => true, :allow_nil => true @@ -15,6 +17,10 @@ Spree::Address.class_eval do private + def touch_enterprise + enterprise.andand.touch + end + # We have a hard-to-track-down bug around invalid addresses with all-nil fields finding # their way into the database. I don't know what the source of them is, so this patch # is designed to track them down. diff --git a/app/models/spree/classification_decorator.rb b/app/models/spree/classification_decorator.rb index 7e34e6890e..c69eb01fcf 100644 --- a/app/models/spree/classification_decorator.rb +++ b/app/models/spree/classification_decorator.rb @@ -1,4 +1,5 @@ Spree::Classification.class_eval do + belongs_to :product, :class_name => "Spree::Product", touch: true before_destroy :dont_destroy_if_primary_taxon def dont_destroy_if_primary_taxon diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 6e116d3e4d..5894e46cc2 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -4,7 +4,7 @@ Spree::Product.class_eval do # https://github.com/rails/rails/issues/7618 has_many :option_types, :through => :product_option_types, :dependent => :destroy - belongs_to :supplier, :class_name => 'Enterprise' + belongs_to :supplier, :class_name => 'Enterprise', touch: true belongs_to :primary_taxon, class_name: 'Spree::Taxon' has_many :product_distributions, :dependent => :destroy @@ -12,9 +12,10 @@ Spree::Product.class_eval do accepts_nested_attributes_for :product_distributions, :allow_destroy => true delegate_belongs_to :master, :unit_value, :unit_description - delegate :images_attributes=, to: :master + delegate :images_attributes=, :display_as=, to: :master - attr_accessible :supplier_id, :primary_taxon_id, :distributor_ids, :product_distributions_attributes, :group_buy, :group_buy_unit_size, :variant_unit, :variant_unit_scale, :variant_unit_name, :unit_value, :unit_description, :notes, :images_attributes + attr_accessible :supplier_id, :primary_taxon_id, :distributor_ids, :product_distributions_attributes, :group_buy, :group_buy_unit_size + attr_accessible :variant_unit, :variant_unit_scale, :variant_unit_name, :unit_value, :unit_description, :notes, :images_attributes, :display_as validates_presence_of :supplier validates_presence_of :primary_taxon @@ -27,6 +28,7 @@ Spree::Product.class_eval do after_initialize :set_available_on_to_now, :if => :new_record? after_save :update_units + after_touch :touch_distributors before_save :add_primary_taxon_to_taxons @@ -186,8 +188,12 @@ Spree::Product.class_eval do end end + def touch_distributors + Enterprise.distributing_product(self).each(&:touch) + end + def add_primary_taxon_to_taxons - taxons << primary_taxon unless taxons.find_by_id(primary_taxon) + taxons << primary_taxon unless taxons.include? primary_taxon end def self.all_variant_unit_option_types diff --git a/app/models/spree/shipping_method_decorator.rb b/app/models/spree/shipping_method_decorator.rb index 303ab7b96f..4a2cf75b47 100644 --- a/app/models/spree/shipping_method_decorator.rb +++ b/app/models/spree/shipping_method_decorator.rb @@ -1,8 +1,13 @@ Spree::ShippingMethod.class_eval do - has_and_belongs_to_many :distributors, join_table: 'distributors_shipping_methods', :class_name => 'Enterprise', association_foreign_key: 'distributor_id' + has_many :distributor_shipping_methods + has_many :distributors, through: :distributor_shipping_methods, class_name: 'Enterprise', foreign_key: 'distributor_id' + + after_save :touch_distributors attr_accessible :distributor_ids, :description attr_accessible :require_ship_address + validates :distributors, presence: { message: "^At least one hub must be selected" } + scope :managed_by, lambda { |user| if user.has_spree_role?('admin') scoped @@ -41,4 +46,10 @@ Spree::ShippingMethod.class_eval do def adjustment_label 'Shipping' end + + private + + def touch_distributors + distributors.each(&:touch) + end end diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 81977bc05a..12593215ba 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -51,11 +51,13 @@ Spree::Variant.class_eval do end def name_to_display - display_name || product.name + return product.name if display_name.blank? + display_name end def unit_to_display - display_as || options_text + return options_text if display_as.blank? + display_as end diff --git a/app/overrides/spree/admin/products/new/replace_form.html.haml.deface b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface index a4e073d40d..334739a4c2 100644 --- a/app/overrides/spree/admin/products/new/replace_form.html.haml.deface +++ b/app/overrides/spree/admin/products/new/replace_form.html.haml.deface @@ -30,10 +30,11 @@ %input.fullwidth{ id: 'product_unit_value_with_description', 'ng-model' => 'product.master.unit_value_with_description', :type => 'text', placeholder: "eg. 2", 'ng-disabled' => "!hasUnit(product)" } %input{ type: 'hidden', 'ng-value' => 'product.master.unit_value', name: 'product[unit_value]' } %input{ type: 'hidden', 'ng-value' => 'product.master.unit_description', name: 'product[unit_description]' } - .three.columns.omega{ 'ng-show' => "product.variant_unit_with_scale == 'items'" } - = f.field_container :unit_name do - = f.label :product_variant_unit_name, :unit_name - %input.fullwidth{ id: 'product_variant_unit_name','ng-model' => 'product.variant_unit_name', :name => 'product[variant_unit_name]', :placeholder => 'eg. bunches', :type => 'text' } + = render 'display_as', f: f + .three.columns.omega{ 'ng-show' => "product.variant_unit_with_scale == 'items'" } + = f.field_container :unit_name do + = f.label :product_variant_unit_name, t(:unit_name) + %input.fullwidth{ id: 'product_variant_unit_name','ng-model' => 'product.variant_unit_name', :name => 'product[variant_unit_name]', :placeholder => 'eg. bunches', :type => 'text' } .twelve.columns.alpha .six.columns.alpha = render 'spree/admin/products/primary_taxon_form', f: f diff --git a/app/overrides/spree/admin/shared/_head/replace_spree_title.html.haml.deface b/app/overrides/spree/admin/shared/_head/replace_spree_title.html.haml.deface index 757bfa797f..4b9bd4142d 100644 --- a/app/overrides/spree/admin/shared/_head/replace_spree_title.html.haml.deface +++ b/app/overrides/spree/admin/shared/_head/replace_spree_title.html.haml.deface @@ -1,4 +1,4 @@ / replace_contents "title" -= "OFN #{t(:administration)}:" -= t(controller.controller_name, :default => controller.controller_name.titleize) \ No newline at end of file += t(controller.controller_name, :default => controller.controller_name.titleize) += " - OFN #{t(:administration)}" \ No newline at end of file diff --git a/app/serializers/api/address_serializer.rb b/app/serializers/api/address_serializer.rb index a4dfcb0f5f..98344e0315 100644 --- a/app/serializers/api/address_serializer.rb +++ b/app/serializers/api/address_serializer.rb @@ -1,4 +1,7 @@ class Api::AddressSerializer < ActiveModel::Serializer + cached + delegate :cache_key, to: :object + attributes :id, :zipcode, :city, :state def state diff --git a/app/serializers/api/enterprise_serializer.rb b/app/serializers/api/enterprise_serializer.rb index a74606dd79..850c88ad15 100644 --- a/app/serializers/api/enterprise_serializer.rb +++ b/app/serializers/api/enterprise_serializer.rb @@ -1,12 +1,43 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer + def serializable_hash + cached_serializer_hash.merge uncached_serializer_hash + end + + private + + def cached_serializer_hash + Api::CachedEnterpriseSerializer.new(object, @options).serializable_hash + end + + def uncached_serializer_hash + Api::UncachedEnterpriseSerializer.new(object, @options).serializable_hash + end +end + +class Api::UncachedEnterpriseSerializer < ActiveModel::Serializer + attributes :orders_close_at, :active + + def orders_close_at + OrderCycle.first_closing_for(object).andand.orders_close_at + end + + def active + @options[:active_distributors].andand.include? object + end +end + +class Api::CachedEnterpriseSerializer < ActiveModel::Serializer + cached + delegate :cache_key, to: :object + 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, :icon, :path, - :pickup, :delivery, :active, :orders_close_at + :pickup, :delivery - has_many :distributed_taxons, key: :taxons, serializer: Api::TaxonSerializer - has_many :supplied_taxons, serializer: Api::TaxonSerializer + has_many :distributed_taxons, key: :taxons, serializer: Api::IdSerializer + has_many :supplied_taxons, serializer: Api::IdSerializer has_many :distributors, key: :hubs, serializer: Api::IdSerializer has_many :suppliers, key: :producers, serializer: Api::IdSerializer @@ -20,14 +51,6 @@ class Api::EnterpriseSerializer < ActiveModel::Serializer object.shipping_methods.where(:require_ship_address => true).present? end - def active - @options[:active_distributors].andand.include?(object) - end - - def orders_close_at - OrderCycle.first_closing_for(object).andand.orders_close_at - end - def email object.email.to_s.reverse end diff --git a/app/serializers/api/taxon_serializer.rb b/app/serializers/api/taxon_serializer.rb index a907532a6b..352a158e95 100644 --- a/app/serializers/api/taxon_serializer.rb +++ b/app/serializers/api/taxon_serializer.rb @@ -1,4 +1,7 @@ class Api::TaxonSerializer < ActiveModel::Serializer + cached + delegate :cache_key, to: :object + attributes :id, :name, :permalink, :icon def icon diff --git a/app/views/home/_fat.html.haml b/app/views/home/_fat.html.haml index 16dc3eaafb..e6158dbca0 100644 --- a/app/views/home/_fat.html.haml +++ b/app/views/home/_fat.html.haml @@ -38,7 +38,7 @@ %span{"active-table-hub-link" => "hub", change: "Change hub to", shop: "Shop at"} .row .columns.small-12 - %a.button.hub.expand{"bo-href" => "hub.path", + %a.button.hub{"bo-href" => "hub.path", "ng-class" => "{primary: hub.active, secondary: !hub.active}", "ofn-empties-cart" => "hub"} %i.ofn-i_033-open-sign{"bo-if" => "hub.active"} diff --git a/app/views/home/_filters.html.haml b/app/views/home/_filters.html.haml new file mode 100644 index 0000000000..78d755f1bc --- /dev/null +++ b/app/views/home/_filters.html.haml @@ -0,0 +1,23 @@ += render partial: 'shared/components/filter_controls' + +.row.animate-show{"ng-show" => "filtersActive"} + .small-12.columns + .row.filter-box + .small-12.large-9.columns + %h5.tdhead + .light Filter by + Type + %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-5 + %taxon-selector{objects: "hubs | hubs:query", + results: "activeTaxons"} + .small-12.large-3.columns + %h5.tdhead + .light Filter by + Delivery + %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-2 + %shipping-type-selector{results: "shippingTypes"} + .row.filter-box.animate-show{"ng-show" => "filtersActive && totalActive() > 0"} + .small-12.columns + %a.button.secondary.small.expand{"ng-click" => "clearAll()"} + %i.ofn-i_009-close + Clear all filters diff --git a/app/views/home/_hubs.html.haml b/app/views/home/_hubs.html.haml index a7c61f92c4..5669014bcf 100644 --- a/app/views/home/_hubs.html.haml +++ b/app/views/home/_hubs.html.haml @@ -1,27 +1,29 @@ = inject_enterprises #hubs.hubs{"ng-controller" => "HubsCtrl"} .row - .small-12.columns.text-center - %h1 Ready to shop? - %div - Select a - %ofn-modal{title: "food hub"} - = render partial: "modals/food_hub" - from the list below: + .small-12.columns + %h1 Find hubs in your area... + / %div + / Shop a + / %ofn-modal{title: "food hub"} + / = render partial: "modals/food_hub" + / from the list below: #active-table-search.row.pad-top .small-12.columns - %i.ofn-i_020-search + / %i.ofn-i_020-search %input{type: :text, "ng-model" => "query", - placeholder: "Search postcode, suburb or hub name...", + placeholder: "Search by Hub or Suburb name", "ng-debounce" => "150", "ofn-disable-enter" => true} + = render partial: "home/filters" + .row{bindonce: true} .small-12.columns .active_table - %hub.active_table_node.row{"ng-repeat" => "hub in filteredHubs = (hubs | hubs:query)", + %hub.active_table_node.row.animate-repeat{"ng-repeat" => "hub in filteredHubs = (hubs | hubs:query | taxons:activeTaxons | shipping:shippingTypes)", "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}", "scroll-after-load" => true, "ng-controller" => "HubNodeCtrl", @@ -30,6 +32,9 @@ = render partial: 'home/skinny' = render partial: 'home/fat' - .row{"ng-show" => "filteredHubs.length == 0"} - .columns.small-12.text-center - No results + .row{"ng-show" => "filteredHubs.length == 0"} + .columns.small-12 + %p.no-results + Sorry, no results found for + %strong {{query}}. + Try another search? diff --git a/app/views/home/_skinny.html.haml b/app/views/home/_skinny.html.haml index 768d16f2b6..e5567f3d38 100644 --- a/app/views/home/_skinny.html.haml +++ b/app/views/home/_skinny.html.haml @@ -15,4 +15,7 @@ Orders closed .columns.small-1.text-right - %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} + / This forces line-height to be triggered + %span   + %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} + diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index b441ac647f..ea283f1490 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -2,10 +2,16 @@ .row .small-12.text-center.columns %h1= image_tag "ofn_logo_beta.png", title: "Open Food Network (beta)" - %h2 An open marketplace that makes it easy to find, buy, sell and move sustainable local food. - - %ofn-modal{title: "Learn more"} - = render partial: "modals/learn_more" + %h2 We're crowdfunding right now! + %h5 + %timer{"end-time" => '1407679200000'} + {{days}} days, {{hours}} hrs, {{minutes}} mins & {{seconds}} secs to go + %p Help us make Open Food Network the best it can be: + %a.button.primary{href: "http://startsomegood.com/openfoodnetwork", target:"_blank"} Support now + / %h2 An open marketplace that makes it easy to find, buy, sell and move sustainable local food. + %br + %ofn-modal{title: "Learn more"} + = render partial: "modals/learn_more" = render partial: "home/hubs" diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 3d246acab8..f831d93d17 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -28,6 +28,7 @@ = inject_json "currentOrder", "current_order" = inject_json "user", "current_user" = inject_json "railsFlash", "flash" + = inject_taxons .off-canvas-wrap{offcanvas: true} .inner-wrap diff --git a/app/views/producers/_fat.html.haml b/app/views/producers/_fat.html.haml index b1eb7196aa..86ed3ad7b7 100644 --- a/app/views/producers/_fat.html.haml +++ b/app/views/producers/_fat.html.haml @@ -26,7 +26,7 @@ %h5 Shop for {{ producer.name }} products at: .row .columns.small-12 - %a.button.hub{"ng-repeat" => "hub in producer.hubs", + %a.button.hub{"ng-repeat" => "hub in producer.hubs | orderBy:'-active'", "bo-href" => "hub.path", "ofn-empties-cart" => "hub", "bo-class" => "{primary: hub.active, secondary: !hub.active}"} %i.ofn-i_033-open-sign{"bo-if" => "hub.active"} diff --git a/app/views/producers/_filters.html.haml b/app/views/producers/_filters.html.haml new file mode 100644 index 0000000000..63ccef2f0c --- /dev/null +++ b/app/views/producers/_filters.html.haml @@ -0,0 +1,18 @@ += render partial: 'shared/components/filter_controls' + +.row.animate-show{"ng-show" => "filtersActive"} + .small-12.columns + .row.filter-box + .small-12.columns + %h5.tdhead + .light Filter by + Type + %ul.small-block-grid-2.medium-block-grid-4.large-block-grid-6 + %taxon-selector{objects: "Producers.visible | filterProducers:query", + results: "activeTaxons"} + + .row.filter-box.animate-show{"ng-show" => "filtersActive && totalActive() > 0"} + .small-12.columns + %a.button.secondary.small.expand{"ng-click" => "clearAll()"} + %i.ofn-i_009-close + Clear all filters diff --git a/app/views/producers/_skinny.html.haml b/app/views/producers/_skinny.html.haml index a5608b8b3d..429fd33e4a 100644 --- a/app/views/producers/_skinny.html.haml +++ b/app/views/producers/_skinny.html.haml @@ -7,4 +7,6 @@ .columns.small-4 {{ producer.address.state | uppercase }} .columns.small-1.text-right + / This forces line-height to be triggered + %span   %i{"ng-class" => "{'ofn-i_005-caret-down' : !open(), 'ofn-i_006-caret-up' : open()}"} \ No newline at end of file diff --git a/app/views/producers/index.haml b/app/views/producers/index.haml deleted file mode 100644 index 5cde67a512..0000000000 --- a/app/views/producers/index.haml +++ /dev/null @@ -1,35 +0,0 @@ -= inject_enterprises -.producers{"ng-controller" => "ProducersCtrl"} - .row - .small-12.columns.text-center.pad-top - %h1 Producers - %div - Select a - %ofn-modal{title: "producer"} - = render partial: "modals/producers" - from the list below: - - #active-table-search.row.pad-top - .small-12.columns - %i.ofn-i_020-search - %input{type: :text, - "ng-model" => "query", - placeholder: "Search postcode, suburb or producer name...", - "ng-debounce" => "150", - "ofn-disable-enter" => true} - - .row{bindonce: true} - .small-12.columns - .active_table - %producer.active_table_node.row.animate-repeat{id: "{{producer.path}}", - "scroll-after-load" => true, - "ng-repeat" => "producer in filteredProducers = (Producers.visible | filterProducers:query)", - "ng-controller" => "ProducerNodeCtrl", - "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", - id: "{{producer.hash}}"} - - .small-12.columns - = render partial: 'producers/skinny' - = render partial: 'producers/fat' - -= render partial: "shared/footer" diff --git a/app/views/producers/index.html.haml b/app/views/producers/index.html.haml new file mode 100644 index 0000000000..1a2c827068 --- /dev/null +++ b/app/views/producers/index.html.haml @@ -0,0 +1,42 @@ += inject_enterprises +.producers.pad-top{"ng-controller" => "ProducersCtrl"} + .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 name", + "ng-debounce" => "150", + "ofn-disable-enter" => true} + + = render partial: "producers/filters" + + .row{bindonce: true} + .small-12.columns + .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-controller" => "ProducerNodeCtrl", + "ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}", + id: "{{producer.hash}}"} + + .small-12.columns + = 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/footer" diff --git a/app/views/shared/components/_filter_controls.html.haml b/app/views/shared/components/_filter_controls.html.haml new file mode 100644 index 0000000000..c8fccb0558 --- /dev/null +++ b/app/views/shared/components/_filter_controls.html.haml @@ -0,0 +1,10 @@ +.row + .small-12.columns + %a.button.primary.tiny.filterbtn{"ng-click" => "filtersActive = !filtersActive", + "ng-show" => "FilterSelectorsService.selectors.length > 0"} + {{ filterText(filtersActive) }} + %i.ofn-i_005-caret-down{"ng-show" => "!filtersActive"} + %i.ofn-i_006-caret-up{"ng-show" => "filtersActive"} + + %a.button.secondary.tiny.filterbtn.disabled{"ng-show" => "FilterSelectorsService.selectors.length == 0"} + No filters diff --git a/app/views/shop/products.rabl b/app/views/shop/products.rabl index b934272b9a..a337ec3b16 100644 --- a/app/views/shop/products.rabl +++ b/app/views/shop/products.rabl @@ -43,7 +43,7 @@ node :variants do |product| end child :taxons => :taxons do |taxon| - attributes :name + attributes :id end child :properties => :properties do |property| diff --git a/app/views/shop/products/_filters.html.haml b/app/views/shop/products/_filters.html.haml new file mode 100644 index 0000000000..7879bcc5d1 --- /dev/null +++ b/app/views/shop/products/_filters.html.haml @@ -0,0 +1,18 @@ += render partial: 'shared/components/filter_controls' + +.row.animate-show{"ng-show" => "filtersActive"} + .small-12.columns + .row.filter-box + .small-12.columns + %h5.tdhead + .light Filter by + Type + %ul.small-block-grid-2.medium-block-grid-3.large-block-grid-4 + %taxon-selector{objects: "Product.products | products:query", + results: "activeTaxons"} + + .row.filter-box.animate-show{"ng-show" => "filtersActive && totalActive() > 0"} + .small-12.columns + %a.button.secondary.small.expand{"ng-click" => "clearAll()"} + %i.ofn-i_009-close + Clear all filters diff --git a/app/views/shop/products/_form.html.haml b/app/views/shop/products/_form.html.haml index eee0e16332..2aaf7a4f5f 100644 --- a/app/views/shop/products/_form.html.haml +++ b/app/views/shop/products/_form.html.haml @@ -1,30 +1,42 @@ %products.small-12.columns{"ng-controller" => "ProductsCtrl", "ng-show" => "order_cycle.order_cycle_id != null", "infinite-scroll" => "incrementLimit()", "infinite-scroll-distance" => "1"} + = form_for :order, :url => populate_orders_path, html: {:class => "custom"} do .row - .small-6.columns + .small-12.medium-8.large-8.columns %input#search.text{"ng-model" => "query", - placeholder: "Search", - "ng-debounce" => "150", + placeholder: "Search by product or producer", + "ng-debounce" => "100", "ofn-disable-enter" => true} - .small-6.columns + + = render partial: "shop/products/filters" + + .small-12.medium-4.large-4.columns %input.button.primary.right{type: :submit, value: "Add to Cart"} - - %div{bindonce: true} + + %div.pad-top{bindonce: true} %product.animate-repeat{"ng-controller" => "ProductNodeCtrl", - "ng-repeat" => "product in Product.products | products:query | orderBy:ordering.order | limitTo: limit track by product.id"} + "ng-repeat" => "product in filteredProducts = (Product.products | products:query | taxons:activeTaxons | orderBy:ordering.order) track by product.id "} %div = render partial: "shop/products/summary" - %div{"bo-if" => "hasVariants"} + %div{"bo-if" => "product.hasVariants"} = render partial: "shop/products/variants" - .variants.row{"bo-if" => "!hasVariants"} + .variants.row{"bo-if" => "!product.hasVariants"} = render partial: "shop/products/master" %product{"ng-show" => "Product.loading"} .row.summary .small-12.columns.text-center Loading products + + %div{"ng-show" => "filteredProducts.length == 0 && !Product.loading"} + .row.summary + .small-12.columns + %p.no-results + Sorry, no results found for + %strong {{query}}. + Try another search? .row .small-12.columns %input.button.primary.right.add_to_cart{type: :submit, value: "Add to Cart"} diff --git a/app/views/shop/products/_summary.html.haml b/app/views/shop/products/_summary.html.haml index b21b535315..bded87c50c 100644 --- a/app/views/shop/products/_summary.html.haml +++ b/app/views/shop/products/_summary.html.haml @@ -1,17 +1,16 @@ .row.summary .small-1.columns - %product-modal - %img{"bo-src" => "product.master.images[0].small_url"} + %img{"bo-src" => "product.master.images[0].small_url", "ng-click" => "triggerProductModal()"} .small-4.columns.summary-header %render-svg{path: "{{product.primary_taxon.icon}}"} - %product-modal {{ product.name }} + %a{"ng-click" => "triggerProductModal()"}{{ product.name }} .small-5.columns %i.ofn-i_036-producers %producer-modal {{ enterprise.name }} .small-2.columns.summary-price.text-right.price - %span{"ng-if" => "hasVariants"} + %span{"ng-if" => "product.hasVariants"} %em from - {{ price() | currency }} + {{ product.price | currency }} diff --git a/app/views/shop/products/_variants.html.haml b/app/views/shop/products/_variants.html.haml index 5d6a59535e..2d281dafe8 100644 --- a/app/views/shop/products/_variants.html.haml +++ b/app/views/shop/products/_variants.html.haml @@ -1,5 +1,5 @@ .row.variants{bindonce: true, - "ng-repeat" => "variant in product.variants"} + "ng-repeat" => "variant in product.variants track by variant.id"} .small-1.columns %i.ofn-i_056-bulk{"bo-if" => "product.group_buy"} diff --git a/app/views/spree/admin/products/_display_as.html.haml b/app/views/spree/admin/products/_display_as.html.haml new file mode 100644 index 0000000000..33beec2b86 --- /dev/null +++ b/app/views/spree/admin/products/_display_as.html.haml @@ -0,0 +1,4 @@ +.three.columns.omega{ "ng-if" => "product.variant_unit_with_scale != 'items'" } + = f.field_container :display_as do + = f.label :product_display_as, t(:display_as) + %input#product_display_as.fullwidth{name: "product[display_as]", placeholder: "{{ placeholder_text }}", type: "text"} \ No newline at end of file diff --git a/app/views/spree/admin/products/bulk_edit.html.haml b/app/views/spree/admin/products/bulk_edit.html.haml index d21235cee6..d1cdd6324f 100644 --- a/app/views/spree/admin/products/bulk_edit.html.haml +++ b/app/views/spree/admin/products/bulk_edit.html.haml @@ -51,13 +51,13 @@ %li.column-list-item{ :class => "three columns alpha", 'ofn-toggle-column' => 'column', 'ng-repeat' => 'column in columns' } {{ column.name }} %hr - %div.loading{ 'ng-show' => 'loading' } + %div.sixteen.columns.alpha.loading{ 'ng-show' => 'loading' } %h4 Loading Products... - %div{ 'ng-show' => '!loading && products.length == 0' } + %div.sixteen.columns.alpha{ 'ng-show' => '!loading && products.length == 0' } %h4{ :style => 'color:red;' } No matching products found. - %div{ 'ng-show' => 'products.length == 500' } + %div.sixteen.columns.alpha{ 'ng-show' => '!loading && products.length == 500' } %h6 Search returned too many products to display (500+), please apply more search filters to reduce the number of matching products - %div{ 'ng-hide' => 'loading || products.length == 500 || products.length == 0' } + %div.sixteen.columns.alpha{ 'ng-hide' => 'loading || products.length == 500 || products.length == 0' } %div.quick_search{ :class => "five columns omega" } %input.search{ :class => "four columns alpha", 'ng-model' => 'query', :name => "quick_filter", :type => 'text', 'placeholder' => 'Quick Search' } %div.pagination{ :class => "seven columns omega" } diff --git a/app/views/spree/admin/shared/_hubs_sidebar.html.haml b/app/views/spree/admin/shared/_hubs_sidebar.html.haml index 8998e6d593..4fe9b1078a 100644 --- a/app/views/spree/admin/shared/_hubs_sidebar.html.haml +++ b/app/views/spree/admin/shared/_hubs_sidebar.html.haml @@ -1,10 +1,11 @@ - hubs_color = @hubs.count > 0 ? "blue" : "red" +- hubs_color = 'red' if (controller.action_name == 'create' || controller.action_name == 'update') && @object.errors.full_messages.include?("At least one hub must be selected") .sidebar_item.omega.four.columns#hubs .four.columns.alpha.header{ class: "#{hubs_color}" } - %span.four.columns.alpha.centered Distributors + %span.four.columns.alpha.centered Hubs .four.columns.alpha.list{ class: "#{hubs_color}" } - if @hubs.count > 0 - -# = hidden_field_tag "enterprise[hub_ids][]", [] + = f.hidden_field :distributor_ids, :multiple => true, value: nil - @hubs.each do |hub| %a.four.columns.alpha.list-item{ class: "#{cycle('odd','even')}", href: "#{main_app.edit_admin_enterprise_path(hub)}" } %span.three.columns.alpha diff --git a/config/environments/development.rb b/config/environments/development.rb index 62642f7809..efa229e33c 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -5,6 +5,7 @@ Openfoodnetwork::Application.configure do # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false + config.cache_store = :memory_store # Log error messages when you accidentally call methods on nil. config.whiny_nils = true diff --git a/config/environments/production.rb b/config/environments/production.rb index ee6fa16e8a..a55540883d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -37,7 +37,7 @@ Openfoodnetwork::Application.configure do # config.logger = SyslogLogger.new # Use a different cache store in production - # config.cache_store = :mem_cache_store + config.cache_store = :dalli_store # Enable serving of images, stylesheets, and JavaScripts from an asset server # config.action_controller.asset_host = "http://assets.example.com" diff --git a/config/environments/staging.rb b/config/environments/staging.rb index ee6fa16e8a..a55540883d 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -37,7 +37,7 @@ Openfoodnetwork::Application.configure do # config.logger = SyslogLogger.new # Use a different cache store in production - # config.cache_store = :mem_cache_store + config.cache_store = :dalli_store # Enable serving of images, stylesheets, and JavaScripts from an asset server # config.action_controller.asset_host = "http://assets.example.com" diff --git a/db/migrate/20140702053145_add_fields_to_distributors_shipping_methods.rb b/db/migrate/20140702053145_add_fields_to_distributors_shipping_methods.rb new file mode 100644 index 0000000000..0e16a13a13 --- /dev/null +++ b/db/migrate/20140702053145_add_fields_to_distributors_shipping_methods.rb @@ -0,0 +1,23 @@ +class AddFieldsToDistributorsShippingMethods < ActiveRecord::Migration + class DistributorShippingMethod < ActiveRecord::Base + self.table_name = "distributors_shipping_methods" + end + + def up + add_column :distributors_shipping_methods, :id, :primary_key + add_column :distributors_shipping_methods, :created_at, :datetime + add_column :distributors_shipping_methods, :updated_at, :datetime + + DistributorShippingMethod.reset_column_information + DistributorShippingMethod.update_all created_at: Time.now, updated_at: Time.now + + change_column :distributors_shipping_methods, :created_at, :datetime, null: false + change_column :distributors_shipping_methods, :updated_at, :datetime, null: false + end + + def down + remove_column :distributors_shipping_methods, :id + remove_column :distributors_shipping_methods, :created_at + remove_column :distributors_shipping_methods, :updated_at + end +end diff --git a/db/schema.rb b/db/schema.rb index 5932749993..a9eddf99be 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 => 20140613004344) do +ActiveRecord::Schema.define(:version => 20140702053145) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -163,9 +163,11 @@ ActiveRecord::Schema.define(:version => 20140613004344) do add_index "distributors_payment_methods", ["distributor_id"], :name => "index_distributors_payment_methods_on_distributor_id" add_index "distributors_payment_methods", ["payment_method_id"], :name => "index_distributors_payment_methods_on_payment_method_id" - create_table "distributors_shipping_methods", :id => false, :force => true do |t| - t.integer "distributor_id" - t.integer "shipping_method_id" + create_table "distributors_shipping_methods", :force => true do |t| + t.integer "distributor_id" + t.integer "shipping_method_id" + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false end add_index "distributors_shipping_methods", ["distributor_id"], :name => "index_distributors_shipping_methods_on_distributor_id" @@ -560,9 +562,9 @@ ActiveRecord::Schema.define(:version => 20140613004344) do t.string "email" t.text "special_instructions" t.integer "distributor_id" + t.integer "order_cycle_id" t.string "currency" t.string "last_ip_address" - t.integer "order_cycle_id" t.integer "cart_id" end diff --git a/public/404.html b/public/404.html index 9a48320a5f..77830da9f7 100644 --- a/public/404.html +++ b/public/404.html @@ -7,20 +7,31 @@ div.dialog { width: 25em; padding: 0 4em; - margin: 4em auto 0 auto; - border: 1px solid #ccc; - border-right-color: #999; - border-bottom-color: #999; + margin: 2em auto 0 auto; + } + a.go_home { + font-size: 100%; + color: black; + line-height: 1.5em; + text-decoration: none; + border-bottom: 1px dotted black; + padding: 0 0.2rem; + } + a.go_home:hover, a.go_home:focus, a.go_home:active{ + background: #8f301d; + color: white; + border-bottom: none; } - h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
-

The page you were looking for doesn't exist.

-

You may have mistyped the address or the page may have moved.

+ +

It seems the page you're looking for is in a grump. +

Return home

+

diff --git a/public/404.jpg b/public/404.jpg new file mode 100644 index 0000000000..836b4e0188 Binary files /dev/null and b/public/404.jpg differ diff --git a/public/422.html b/public/422.html index 83660ab187..b222475557 100644 --- a/public/422.html +++ b/public/422.html @@ -7,20 +7,32 @@ div.dialog { width: 25em; padding: 0 4em; - margin: 4em auto 0 auto; - border: 1px solid #ccc; - border-right-color: #999; - border-bottom-color: #999; + margin: 2em auto 0 auto; + } + a.go_home { + font-size: 100%; + color: black; + line-height: 1.5em; + text-decoration: none; + border-bottom: 1px dotted black; + padding: 0 0.2rem; + } + a.go_home:hover, a.go_home:focus, a.go_home:active{ + background: #8f301d; + color: white; + border-bottom: none; } - h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
-

The change you wanted was rejected.

-

Maybe you tried to change something you didn't have access to.

+ +

The change you wanted was rejected. Maybe you tried to change something you don't have access to. +

Return home

+

+ diff --git a/public/422.jpg b/public/422.jpg new file mode 100644 index 0000000000..1685c87f0d Binary files /dev/null and b/public/422.jpg differ diff --git a/public/500.html b/public/500.html index b80307fc16..30379bc44d 100644 --- a/public/500.html +++ b/public/500.html @@ -7,20 +7,39 @@ div.dialog { width: 25em; padding: 0 4em; - margin: 4em auto 0 auto; - border: 1px solid #ccc; - border-right-color: #999; - border-bottom-color: #999; + margin: 2em auto 0 auto; + } + a.go_home { + font-size: 100%; + color: black; + line-height: 1.5em; + text-decoration: none; + border-bottom: 1px dotted black; + padding: 0 0.2rem; + } + a.go_home:hover, a.go_home:focus, a.go_home:active{ + background: #8f301d; + color: white; + border-bottom: none; } - h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
-

We're sorry, but something went wrong.

-

We've been notified about this issue and we'll take a look at it shortly.

+ +

We're sorry, but something went wrong. +
Try refreshing the page, or +

Return home

+
Want to let us know what went wrong? Email us at: +

+ + hello [at] openfoodnetwork.org +

+

diff --git a/public/500.jpg b/public/500.jpg new file mode 100644 index 0000000000..2cfabee423 Binary files /dev/null and b/public/500.jpg differ diff --git a/spec/controllers/checkout_controller_spec.rb b/spec/controllers/checkout_controller_spec.rb index d8c6ee738c..e46b9688a2 100644 --- a/spec/controllers/checkout_controller_spec.rb +++ b/spec/controllers/checkout_controller_spec.rb @@ -40,9 +40,7 @@ describe CheckoutController do it "doesn't copy the previous shipping address from a pickup order" do old_order = create(:order, bill_address: create(:address), ship_address: create(:address)) - old_order.shipping_method.stub_chain(:andand, :require_ship_address).and_return(false) Spree::Order.stub_chain(:order, :where, :where, :limit, :detect).and_return(old_order) - controller.send(:find_last_used_addresses, "email").last.should == nil end @@ -81,6 +79,7 @@ describe CheckoutController do context "via xhr" do before do controller.stub(:current_distributor).and_return(distributor) + controller.stub(:current_order_cycle).and_return(order_cycle) controller.stub(:current_order).and_return(order) end diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index 50ce2aad7f..e481be4635 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -110,16 +110,17 @@ feature %q{ scenario "editing an existing enterprise" do @enterprise = create(:enterprise) + e2 = create(:enterprise) eg1 = create(:enterprise_group, name: 'eg1') eg2 = create(:enterprise_group, name: 'eg2') payment_method = create(:payment_method, distributors: []) - shipping_method = create(:shipping_method, distributors: []) + shipping_method = create(:shipping_method, distributors: [e2]) enterprise_fee = create(:enterprise_fee, enterprise: @enterprise ) login_to_admin_section click_link 'Enterprises' - click_link 'Edit Profile' + all("a", text:'Edit Profile').first.click fill_in 'enterprise_name', :with => 'Eaterprises' fill_in 'enterprise_description', :with => 'Connecting farmers and eaters' diff --git a/spec/features/consumer/authentication_spec.rb b/spec/features/consumer/authentication_spec.rb index 8853a07c3d..f8b405374e 100644 --- a/spec/features/consumer/authentication_spec.rb +++ b/spec/features/consumer/authentication_spec.rb @@ -11,7 +11,7 @@ feature "Authentication", js: true do fill_in "Email", with: user.email fill_in "Password", with: user.password click_login_button - page.should have_content "Select a producer from the list below" + page.should have_content "Find local producers" current_path.should == producers_path end end diff --git a/spec/features/consumer/producers_spec.rb b/spec/features/consumer/producers_spec.rb index 8e834aaeb3..98bef7f084 100644 --- a/spec/features/consumer/producers_spec.rb +++ b/spec/features/consumer/producers_spec.rb @@ -8,6 +8,8 @@ feature %q{ 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 @@ -16,7 +18,7 @@ feature %q{ it "shows all producers with expandable details" do page.should have_content producer.name expand_active_table_node producer.name - page.should have_content producer.supplied_taxons.join(', ') + page.should have_content producer.supplied_taxons.first.name.split.map(&:capitalize).join(' ') end it "doesn't show invisible producers" do diff --git a/spec/helpers/shared_helper_spec.rb b/spec/helpers/shared_helper_spec.rb index 76d2e6e859..4e7208c8fc 100644 --- a/spec/helpers/shared_helper_spec.rb +++ b/spec/helpers/shared_helper_spec.rb @@ -24,17 +24,27 @@ describe SharedHelper do helper.distributor_link_class(d1).should =~ /empties-cart/ end - describe "Injecting json" do - let(:enterprise) { create(:distributor_enterprise, facebook: "roger") } + describe "injecting json" do + let!(:enterprise) { create(:distributor_enterprise, facebook: "roger") } - it "Will inject via AMS" do + it "will inject via AMS" do helper.inject_json_ams("test", [enterprise], Api::EnterpriseSerializer).should match enterprise.name end it "injects enterprises" do - Enterprise.stub(:visible).and_return [enterprise] - helper.inject_enterprises().should match enterprise.name - helper.inject_enterprises().should match enterprise.facebook + helper.inject_enterprises.should match enterprise.name + helper.inject_enterprises.should match enterprise.facebook + end + + it "injects taxons" do + taxon = create(:taxon) + helper.inject_taxons.should match taxon.name + end + + it "injects taxons" do + taxon = create(:taxon) + helper.inject_taxons.should match taxon.name end end + end diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js index 4eacb45036..44654e7b32 100644 --- a/spec/javascripts/application_spec.js +++ b/spec/javascripts/application_spec.js @@ -4,6 +4,7 @@ //= require angular-sanitize //= require angular-mocks //= require angular-cookies +//= require angular-timer.min.js //= require angular-backstretch.js //= require lodash.underscore.js //= require angular-flash.min.js diff --git a/spec/javascripts/unit/darkswarm/filters/active_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/active_spec.js.coffee new file mode 100644 index 0000000000..337121b1ae --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/active_spec.js.coffee @@ -0,0 +1,28 @@ +describe 'filtering by active', -> + filterByActive = null + objects = [ + { + active: true + } + { + active: false + } + ] + + + beforeEach -> + module 'Darkswarm' + inject ($filter) -> + filterByActive = $filter('active') + + it "filters to active", -> + expect(filterByActive(objects, {closed: false, open: true})[0]).toBe objects[0] + + it "filters to inactive", -> + expect(filterByActive(objects, {closed: true, open: false})[0]).toBe objects[1] + + it "doesn't filter if needed", -> + expect(filterByActive(objects, {closed: false, open: false})).toBe objects + + it "filters to all", -> + expect(filterByActive(objects, {closed: true, open: true})).toBe objects diff --git a/spec/javascripts/unit/darkswarm/filters/by_producer_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/by_producer_spec.js.coffee new file mode 100644 index 0000000000..48b811a11f --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/by_producer_spec.js.coffee @@ -0,0 +1,21 @@ +describe 'filtering by producer', -> + filterByProducer = null + objects = [ + { + producer: + id: 1 + } + { + producer: + id: 2 + } + ] + + beforeEach -> + module 'Darkswarm' + inject ($filter) -> + filterByProducer = $filter('byProducer') + + it "filters by producer", -> + expect(filterByProducer(objects, 1)[0]).toBe objects[0] + expect(filterByProducer(objects, 2)[0]).toBe objects[1] diff --git a/spec/javascripts/unit/darkswarm/filters/shipping_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/shipping_spec.js.coffee new file mode 100644 index 0000000000..1bb436017a --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/shipping_spec.js.coffee @@ -0,0 +1,33 @@ +describe 'filtering by shipping method', -> + filterByShippingMethod = null + objects = [ + { + delivery: true + pickup: false + } + { + delivery: false + pickup: true + } + ] + + + beforeEach -> + module 'Darkswarm' + inject ($filter) -> + filterByShippingMethod = $filter('shipping') + + it "filters to pickup", -> + expect(filterByShippingMethod(objects, {pickup: true, delivery: false})[0]).toBe objects[1] + + it "filters to delivery", -> + expect(filterByShippingMethod(objects, {pickup: false, delivery: true})[0]).toBe objects[0] + + it "filters to both", -> + expect(filterByShippingMethod(objects, {pickup: true, delivery: true})).toBe objects + + it "filters to none", -> + expect(filterByShippingMethod(objects, {pickup: false, delivery: false})).toBe objects + + it "filters to none with empty", -> + expect(filterByShippingMethod(objects, {})).toBe objects diff --git a/spec/javascripts/unit/darkswarm/filters/taxons_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/taxons_spec.js.coffee new file mode 100644 index 0000000000..1313229ef6 --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/taxons_spec.js.coffee @@ -0,0 +1,38 @@ +describe 'filtering by taxons', -> + filterByTaxons = null + objects = [ + { + taxons: [] + supplied_taxons: [] + primary_taxon: + name: "frogs" + id: 1 + } + { + taxons: [ + {name: "kittens", id: 2} + {name: "puppies", id: 3} + ] + supplied_taxons: [] + } + ] + + beforeEach -> + module 'Darkswarm' + inject ($filter) -> + filterByTaxons = $filter('taxons') + + it "filters by nothing", -> + expect(filterByTaxons(objects, [])).toBe objects + + it "filters by primary taxon", -> + expect(filterByTaxons(objects, [1])[0]).toBe objects[0] + + it "filters by taxons", -> + expect(filterByTaxons(objects, [2])[0]).toBe objects[1] + + it "filters by multiple", -> + expect(filterByTaxons(objects, [1, 2])[0]).toBe objects[0] + expect(filterByTaxons(objects, [1, 2])[1]).toBe objects[1] + + diff --git a/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee index ebf480d9e8..59d4ae9826 100644 --- a/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee @@ -1,8 +1,11 @@ describe "Enterprises service", -> Enterprises = null CurrentHubMock = {} + taxons = [ + {id: 1, name: "test"} + ] enterprises = [ - {id: 1, type: "hub", producers: [{id: 2}]}, + {id: 1, type: "hub", producers: [{id: 2}], taxons: [{id: 1}]}, {id: 2, type: "producer", hubs: [{id: 1}]}, {id: 3, type: "producer", hubs: [{id: 1}]} ] @@ -12,6 +15,7 @@ describe "Enterprises service", -> $provide.value "CurrentHub", CurrentHubMock null angular.module('Darkswarm').value('enterprises', enterprises) + angular.module('Darkswarm').value('taxons', taxons) inject ($injector)-> Enterprises = $injector.get("Enterprises") @@ -29,3 +33,6 @@ describe "Enterprises service", -> 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] + + it "dereferences taxons", -> + expect(Enterprises.enterprises[0].taxons[0]).toBe taxons[0] diff --git a/spec/javascripts/unit/darkswarm/services/taxon_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/taxon_spec.js.coffee new file mode 100644 index 0000000000..b05c82a13d --- /dev/null +++ b/spec/javascripts/unit/darkswarm/services/taxon_spec.js.coffee @@ -0,0 +1,16 @@ +describe "Taxons service", -> + Taxons = null + taxons = [ + {id: 1, name: "test"} + {id: 2, name: "Roger"} + ] + + beforeEach -> + module('Darkswarm') + angular.module('Darkswarm').value 'taxons', taxons + + inject ($injector)-> + Taxons = $injector.get("Taxons") + + it "caches taxons in an id-referenced hash", -> + expect(Taxons.taxons_by_id[1]).toBe taxons[0] diff --git a/spec/lib/open_food_network/searcher_spec.rb b/spec/lib/open_food_network/searcher_spec.rb index c57a2c1c53..2fb83ed309 100644 --- a/spec/lib/open_food_network/searcher_spec.rb +++ b/spec/lib/open_food_network/searcher_spec.rb @@ -36,7 +36,7 @@ module OpenFoodNetwork d1 = create(:distributor_enterprise) p1 = create(:product, :supplier => s1) p2 = create(:product, :distributors => [d1]) - p3 = create(:product) + p3 = create(:product, :supplier => s0) # When we search by the supplier enterprise, we should see the supplied products searcher = Searcher.new(:enterprise_id => s1.id.to_s) diff --git a/spec/models/distributor_shipping_method_spec.rb b/spec/models/distributor_shipping_method_spec.rb new file mode 100644 index 0000000000..bd8d7d2973 --- /dev/null +++ b/spec/models/distributor_shipping_method_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe DistributorShippingMethod do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/enterprise_caching_spec.rb b/spec/models/enterprise_caching_spec.rb new file mode 100644 index 0000000000..72b356df21 --- /dev/null +++ b/spec/models/enterprise_caching_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Enterprise do + context "key-based caching invalidation" do + describe "is touched when a(n)" do + let(:enterprise) { create(:distributor_enterprise, updated_at: 1.week.ago) } + let(:taxon) { create(:taxon) } + + describe "with a supplied product" do + let(:product) { create(:simple_product, supplier: enterprise) } + let!(:classification) { create(:classification, taxon: taxon, product: product) } + it "touches enterprise when a classification on that product changes" do + expect{classification.save!}.to change{enterprise.updated_at} + end + end + + describe "with a distributed product" do + let(:product) { create(:simple_product) } + let!(:oc) { create(:simple_order_cycle, distributors: [enterprise], variants: [product.master]) } + let!(:classification) { create(:classification, taxon: taxon, product: product) } + it "touches enterprise when a classification on that product changes" do + expect{classification.save!}.to change{enterprise.reload.updated_at} + end + end + + describe "with relatives" do + let(:child_enterprise) { create(:supplier_enterprise) } + let!(:er) { create(:enterprise_relationship, parent: enterprise, child: child_enterprise) } + it "touches enterprise when enterprise relationship is updated" do + expect{er.save!}.to change {enterprise.reload.updated_at } + end + end + + describe "with shipping methods" do + let(:sm) { create(:shipping_method) } + before do + enterprise.shipping_methods << sm + end + it "touches enterprise when distributor_shipping_method is updated" do + expect { + enterprise.distributor_shipping_methods.first.save! + }.to change {enterprise.reload.updated_at} + end + + it "touches enterprise when shipping method is updated" do + expect{sm.save!}.to change {enterprise.reload.updated_at } + end + end + + it "touches enterprise when address is updated" do + expect{enterprise.address.save!}.to change {enterprise.reload.updated_at } + end + end + end +end diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 0fa57d19d3..3837bd23ee 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -66,13 +66,7 @@ describe Enterprise do it { should delegate(:city).to(:address) } it { should delegate(:state_name).to(:address) } end - - it "should default address country to system country" do - subject.address.country.should == Spree::Country.find_by_id(Spree::Config[:default_country_id]) - end - describe "scopes" do - describe 'active' do it 'find active enterprises' do d1 = create(:distributor_enterprise, visible: false) diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index bf13cf978c..0abf9ea431 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -133,6 +133,8 @@ module Spree it "returns product name if display_name is empty" do v = create(:variant, product: create(:product)) v.name_to_display.should == v.product.name + v1 = create(:variant, display_name: "", product: create(:product)) + v1.name_to_display.should == v1.product.name end end @@ -142,10 +144,13 @@ module Spree v.unit_to_display.should == "foo" end - it "returns options_text if display_as is empty" do + it "returns options_text if display_as is blank" do v = create(:variant) + v1 = create(:variant, display_as: "") v.stub(:options_text).and_return "ponies" + v1.stub(:options_text).and_return "ponies" v.unit_to_display.should == "ponies" + v1.unit_to_display.should == "ponies" end end diff --git a/spec/serializers/enterprise_serializer.rb b/spec/serializers/enterprise_serializer.rb index 4e9248be53..263f621598 100644 --- a/spec/serializers/enterprise_serializer.rb +++ b/spec/serializers/enterprise_serializer.rb @@ -11,7 +11,7 @@ describe Api::EnterpriseSerializer do it "includes distributed taxons" do enterprise.stub(:distributed_taxons).and_return [taxon] serializer = Api::EnterpriseSerializer.new enterprise - serializer.to_json.should match taxon.name + serializer.to_json.should match taxon.id.to_s end it "will render urls" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4f96a09070..3f3435eda5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -104,4 +104,25 @@ RSpec.configure do |config| config.include Paperclip::Shoulda::Matchers config.include JsonSpec::Helpers + + # Profiling + # + # This code shouldn't be run in normal circumstances. But if you want to know + # which parts of your code take most time, then you can activate the lines + # below. Keep in mind that it will slow down the execution time heaps. + # + # The PerfTools will write a binary file to the specified path which can then + # be examined by: + # + # bundle exec pprof.rb --text /tmp/rspec_profile + # + + #require 'perftools' + #config.before :suite do + # PerfTools::CpuProfiler.start("/tmp/rspec_profile") + #end + # + #config.after :suite do + # PerfTools::CpuProfiler.stop + #end end diff --git a/vendor/assets/javascripts/angular-timer.min.js b/vendor/assets/javascripts/angular-timer.min.js new file mode 100755 index 0000000000..9fdc966a93 --- /dev/null +++ b/vendor/assets/javascripts/angular-timer.min.js @@ -0,0 +1,8 @@ +/** + * angular-timer - v1.1.6 - 2014-07-01 7:37 AM + * https://github.com/siddii/angular-timer + * + * Copyright (c) 2014 Siddique Hameed + * Licensed MIT + */ +var timerModule=angular.module("timer",[]).directive("timer",["$compile",function(a){return{restrict:"EAC",replace:!1,scope:{interval:"=interval",startTimeAttr:"=startTime",endTimeAttr:"=endTime",countdownattr:"=countdown",finishCallback:"&finishCallback",autoStart:"&autoStart",maxTimeUnit:"="},controller:["$scope","$element","$attrs","$timeout",function(b,c,d,e){function f(){b.timeoutId&&clearTimeout(b.timeoutId)}function g(){b.maxTimeUnit&&"day"!==b.maxTimeUnit?"second"===b.maxTimeUnit?(b.seconds=Math.floor(b.millis/1e3),b.minutes=0,b.hours=0,b.days=0,b.months=0,b.years=0):"minute"===b.maxTimeUnit?(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4),b.hours=0,b.days=0,b.months=0,b.years=0):"hour"===b.maxTimeUnit?(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4%60),b.hours=Math.floor(b.millis/36e5),b.days=0,b.months=0,b.years=0):"month"===b.maxTimeUnit?(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4%60),b.hours=Math.floor(b.millis/36e5%24),b.days=Math.floor(b.millis/36e5/24%30),b.months=Math.floor(b.millis/36e5/24/30),b.years=0):"year"===b.maxTimeUnit&&(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4%60),b.hours=Math.floor(b.millis/36e5%24),b.days=Math.floor(b.millis/36e5/24%30),b.months=Math.floor(b.millis/36e5/24/30%12),b.years=Math.floor(b.millis/36e5/24/365)):(b.seconds=Math.floor(b.millis/1e3%60),b.minutes=Math.floor(b.millis/6e4%60),b.hours=Math.floor(b.millis/36e5%24),b.days=Math.floor(b.millis/36e5/24),b.months=0,b.years=0),b.secondsS=1==b.seconds?"":"s",b.minutesS=1==b.minutes?"":"s",b.hoursS=1==b.hours?"":"s",b.daysS=1==b.days?"":"s",b.monthsS=1==b.months?"":"s",b.yearsS=1==b.years?"":"s",b.sseconds=b.seconds<10?"0"+b.seconds:b.seconds,b.mminutes=b.minutes<10?"0"+b.minutes:b.minutes,b.hhours=b.hours<10?"0"+b.hours:b.hours,b.ddays=b.days<10?"0"+b.days:b.days,b.mmonths=b.months<10?"0"+b.months:b.months,b.yyears=b.years<10?"0"+b.years:b.years}"function"!=typeof String.prototype.trim&&(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),b.autoStart=d.autoStart||d.autostart,c.append(0===c.html().trim().length?a("{{millis}}")(b):a(c.contents())(b)),b.startTime=null,b.endTime=null,b.timeoutId=null,b.countdown=b.countdownattr&&parseInt(b.countdownattr,10)>=0?parseInt(b.countdownattr,10):void 0,b.isRunning=!1,b.$on("timer-start",function(){b.start()}),b.$on("timer-resume",function(){b.resume()}),b.$on("timer-stop",function(){b.stop()}),b.$on("timer-clear",function(){b.clear()}),b.$on("timer-set-countdown",function(a,c){b.countdown=c}),b.start=c[0].start=function(){b.startTime=b.startTimeAttr?new Date(b.startTimeAttr):new Date,b.endTime=b.endTimeAttr?new Date(b.endTimeAttr):null,b.countdown||(b.countdown=b.countdownattr&&parseInt(b.countdownattr,10)>0?parseInt(b.countdownattr,10):void 0),f(),h(),b.isRunning=!0},b.resume=c[0].resume=function(){f(),b.countdownattr&&(b.countdown+=1),b.startTime=new Date-(b.stoppedTime-b.startTime),h(),b.isRunning=!0},b.stop=b.pause=c[0].stop=c[0].pause=function(){var a=b.timeoutId;b.clear(),b.$emit("timer-stopped",{timeoutId:a,millis:b.millis,seconds:b.seconds,minutes:b.minutes,hours:b.hours,days:b.days})},b.clear=c[0].clear=function(){b.stoppedTime=new Date,f(),b.timeoutId=null,b.isRunning=!1},c.bind("$destroy",function(){f(),b.isRunning=!1}),b.countdownattr?(b.millis=1e3*b.countdownattr,b.addCDSeconds=c[0].addCDSeconds=function(a){b.countdown+=a,b.$digest(),b.isRunning||b.start()},b.$on("timer-add-cd-seconds",function(a,c){e(function(){b.addCDSeconds(c)})}),b.$on("timer-set-countdown-seconds",function(a,c){b.isRunning||b.clear(),b.countdown=c,b.millis=1e3*c,g()})):b.millis=0,g();var h=function(){b.millis=new Date-b.startTime;var a=b.millis%1e3;return b.endTimeAttr&&(b.millis=b.endTime-new Date,a=b.interval-b.millis%1e3),b.countdownattr&&(b.millis=1e3*b.countdown),b.millis<0?(b.stop(),b.millis=0,g(),void(b.finishCallback&&b.$eval(b.finishCallback))):(g(),b.timeoutId=setTimeout(function(){h(),b.$digest()},b.interval-a),b.$emit("timer-tick",{timeoutId:b.timeoutId,millis:b.millis}),void(b.countdown>0?b.countdown--:b.countdown<=0&&(b.stop(),b.finishCallback&&b.$eval(b.finishCallback))))};(void 0===b.autoStart||b.autoStart===!0)&&b.start()}]}}]);"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports=timerModule); \ No newline at end of file