mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Merge branch 'breakdowns' into laura_and_will
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
Darkswarm.directive 'mapSearch', ($timeout)->
|
||||
# Install a basic search field in a map
|
||||
restrict: 'E'
|
||||
require: '^googleMap'
|
||||
replace: true
|
||||
template: '<input id="pac-input"></input>'
|
||||
link: (scope, elem, attrs, ctrl)->
|
||||
$timeout =>
|
||||
map = ctrl.getMap()
|
||||
input = (document.getElementById("pac-input"))
|
||||
map.controls[google.maps.ControlPosition.TOP_LEFT].push input
|
||||
searchBox = new google.maps.places.SearchBox((input))
|
||||
|
||||
google.maps.event.addListener searchBox, "places_changed", ->
|
||||
places = searchBox.getPlaces()
|
||||
return if places.length is 0
|
||||
# For each place, get the icon, place name, and location.
|
||||
markers = []
|
||||
bounds = new google.maps.LatLngBounds()
|
||||
for place in places
|
||||
#map.setCenter place.geometry.location
|
||||
map.fitBounds place.geometry.viewport
|
||||
#map.fitBounds bounds
|
||||
|
||||
# Bias the SearchBox results towards places that are within the bounds of the
|
||||
# current map's viewport.
|
||||
google.maps.event.addListener map, "bounds_changed", ->
|
||||
bounds = map.getBounds()
|
||||
searchBox.setBounds bounds
|
||||
|
||||
@@ -8,4 +8,7 @@ Darkswarm.directive 'priceBreakdownPopup', ->
|
||||
restrict: 'EA'
|
||||
replace: true
|
||||
templateUrl: 'price_breakdown.html'
|
||||
scope: true
|
||||
scope: false
|
||||
|
||||
link: (scope, elem, attrs) ->
|
||||
scope.expanded = false unless scope.expanded?
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Darkswarm.directive "pricePercentage", ->
|
||||
restrict: 'E'
|
||||
replace: true
|
||||
templateUrl: 'price_percentage.html'
|
||||
scope:
|
||||
percentage: '='
|
||||
|
||||
link: (scope, elem, attrs) ->
|
||||
elem.find(".meter").css
|
||||
width: "#{scope.percentage}%"
|
||||
@@ -0,0 +1,6 @@
|
||||
Darkswarm.directive "shopVariant", ->
|
||||
restrict: 'E'
|
||||
replace: true
|
||||
templateUrl: 'shop_variant.html'
|
||||
scope:
|
||||
variant: '='
|
||||
@@ -1,4 +0,0 @@
|
||||
Darkswarm.factory 'Order', (order)->
|
||||
new class Order
|
||||
order: order
|
||||
|
||||
@@ -44,4 +44,5 @@ Darkswarm.factory 'Products', ($resource, Enterprises, Dereferencer, Taxons, Car
|
||||
product.price = Math.min.apply(null, prices)
|
||||
product.hasVariants = product.variants?.length > 0
|
||||
|
||||
product.primaryImage = product.images[0]?.small_url || "/assets/noimage/small.png"
|
||||
product.primaryImage = product.images[0]?.small_url if product.images
|
||||
product.primaryImageOrMissing = product.primaryImage || "/assets/noimage/small.png"
|
||||
|
||||
@@ -7,4 +7,5 @@ Darkswarm.factory 'Variants', ->
|
||||
extend: (variant)->
|
||||
variant.getPrice = ->
|
||||
variant.price * variant.line_item.quantity
|
||||
variant.basePricePercentage = Math.round(variant.base_price / variant.price * 100)
|
||||
variant
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,27 @@
|
||||
.joyride-tip-guide{"ng-class" => "{ in: tt_isOpen, fade: tt_animation }"}
|
||||
%span.joyride-nub.bottom
|
||||
.joyride-content-wrapper
|
||||
{{ variant.id }}
|
||||
|
||||
.collapsed{"ng-show" => "!expanded"}
|
||||
%price-percentage{percentage: 'variant.basePricePercentage'}
|
||||
%a{"ng-click" => "expanded = !expanded"} Full price breakdown
|
||||
|
||||
.expanded{"ng-show" => "expanded"}
|
||||
%ul
|
||||
%li
|
||||
Cost
|
||||
%span {{ variant.base_price | currency }}
|
||||
%li
|
||||
Admin fee
|
||||
%span {{ variant.fees.admin | currency }}
|
||||
%li
|
||||
Sales fee
|
||||
%span {{ variant.fees.sales | currency }}
|
||||
%li
|
||||
Packing fee
|
||||
%span {{ variant.fees.packing | currency }}
|
||||
%li
|
||||
Transport fee
|
||||
%span {{ variant.fees.transport | currency }}
|
||||
%a{"ng-click" => "expanded = !expanded"} Price graph
|
||||
\= {{ variant.price | currency }}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.progress
|
||||
.meter
|
||||
Cost
|
||||
Fees
|
||||
@@ -1,6 +1,6 @@
|
||||
.row
|
||||
.columns.small-12.large-6
|
||||
%img.product-img{"ng-src" => "{{product.master.images[0].large_url}}", "ng-if" => "product.master.images[0]"}
|
||||
%img.product-img{"ng-src" => "{{product.primaryImage}}", "ng-if" => "product.primaryImage"}
|
||||
.columns.small-12.large-6.product-header
|
||||
%h2
|
||||
%render-svg{path: "{{product.primary_taxon.icon}}"}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
.row.variants{bindonce: true,
|
||||
"ng-repeat" => "variant in product.variants track by variant.id"}
|
||||
|
||||
.variants.row
|
||||
.small-12.medium-4.large-4.columns.variant-name
|
||||
.table-cell
|
||||
.inline {{ variant.name_to_display }}
|
||||
.bulk-buy.inline{"bo-if" => "product.group_buy"}
|
||||
.bulk-buy.inline{"bo-if" => "variant.product.group_buy"}
|
||||
%i.ofn-i_056-bulk><
|
||||
%em><
|
||||
\ Bulk
|
||||
|
||||
-# WITHOUT GROUP BUY
|
||||
.small-5.medium-3.large-3.columns.text-right{"bo-if" => "!product.group_buy"}
|
||||
.small-5.medium-3.large-3.columns.text-right{"bo-if" => "!variant.product.group_buy"}
|
||||
|
||||
%input{type: :number,
|
||||
value: nil,
|
||||
min: 0,
|
||||
@@ -22,7 +21,7 @@
|
||||
|
||||
|
||||
-# WITH GROUP BUY
|
||||
.small-5.medium-3.large-3.columns.text-right{"bo-if" => "product.group_buy"}
|
||||
.small-5.medium-3.large-3.columns.text-right{"bo-if" => "variant.product.group_buy"}
|
||||
%span.bulk-input-container
|
||||
%span.bulk-input
|
||||
%input.bulk.first{type: :number,
|
||||
@@ -33,7 +32,7 @@
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
|
||||
name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}"}
|
||||
%span.bulk-input{"bo-if" => "product.group_buy"}
|
||||
%span.bulk-input{"bo-if" => "variant.product.group_buy"}
|
||||
%input.bulk.second{type: :number,
|
||||
min: 0,
|
||||
"ng-model" => "variant.line_item.max_quantity",
|
||||
@@ -51,8 +50,10 @@
|
||||
%i.ofn-i_009-close
|
||||
{{ variant.price | currency }}
|
||||
|
||||
/ %button.graph-button{popover: "This is the popover text", "popover-title" => "The title.", "popover-animation" => "true", "popover-trigger" =>"mouseenter", "popover-placement" => "top", "tabindex" => "-1"}
|
||||
/ %i.ofn-i-058-graph
|
||||
%button.graph-button{"price-breakdown" => "_",
|
||||
"variant" => "variant",
|
||||
"price-breakdown-animation" => "true"}
|
||||
%i.ofn-i-058-graph
|
||||
|
||||
.small-12.medium-2.large-2.columns.total-price.text-right
|
||||
.table-cell
|
||||
@@ -10,3 +10,7 @@
|
||||
img // https://github.com/zurb/foundation/issues/112
|
||||
max-width: none
|
||||
height: auto
|
||||
|
||||
#pac-input
|
||||
padding: 4px
|
||||
font-size: 2em
|
||||
|
||||
@@ -15,8 +15,12 @@ Spree::OrderPopulator.class_eval do
|
||||
attempt_cart_add(variant_id, from_hash[:quantity])
|
||||
end if from_hash[:products]
|
||||
|
||||
from_hash[:variants].each do |variant_id, args|
|
||||
attempt_cart_add(variant_id, args[:quantity], args[:max_quantity])
|
||||
from_hash[:variants].each do |variant_id, quantity|
|
||||
if quantity.is_a?(Hash)
|
||||
attempt_cart_add(variant_id, quantity[:quantity], quantity[:max_quantity])
|
||||
else
|
||||
attempt_cart_add(variant_id, quantity)
|
||||
end
|
||||
end if from_hash[:variants]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
class Api::VariantSerializer < ActiveModel::Serializer
|
||||
attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display,
|
||||
:on_demand, :price
|
||||
:on_demand, :price, :fees, :base_price
|
||||
|
||||
def price
|
||||
object.price_with_fees(options[:current_distributor], options[:current_order_cycle])
|
||||
end
|
||||
|
||||
def base_price
|
||||
1.00
|
||||
end
|
||||
|
||||
def fees
|
||||
{admin: 1.23, sales: 4.56, packing: 7.89, transport: 0.12}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# price_without_fees / price
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
%link{href: "https://fonts.googleapis.com/css?family=Open+Sans:400,700", rel: "stylesheet", type: "text/css"}/
|
||||
|
||||
= yield :scripts
|
||||
%script{src: "//maps.googleapis.com/maps/api/js?sensor=false"}
|
||||
%script{src: "//maps.googleapis.com/maps/api/js?libraries=places&sensor=false"}
|
||||
= stylesheet_link_tag "darkswarm/all"
|
||||
= javascript_include_tag "darkswarm/all"
|
||||
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
.map-container{"fill-vertical" => true}
|
||||
%map{"ng-controller" => "MapCtrl"}
|
||||
%google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"}
|
||||
%map-search
|
||||
%markers{models: "OfnMap.enterprises", fit: "true",
|
||||
coords: "'self'", icon: "'icon'", click: "'reveal'"}
|
||||
|
||||
@@ -20,12 +20,8 @@
|
||||
"ng-repeat" => "product in filteredProducts = (Products.products | products:query | taxons:activeTaxons | orderBy:ordering.order) track by product.id "}
|
||||
|
||||
= render partial: "shop/products/summary"
|
||||
|
||||
%span{"bo-if" => "product.hasVariants"}
|
||||
= render partial: "shop/products/variants"
|
||||
|
||||
.variants.row{"bo-if" => "!product.hasVariants"}
|
||||
= render partial: "shop/products/master"
|
||||
%shop-variant{variant: 'product.master', "bo-if" => "!product.hasVariants"}
|
||||
%shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants track by variant.id"}
|
||||
|
||||
%product{"ng-show" => "Products.loading"}
|
||||
.row.summary
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
.small-12.medium-4.large-4.columns.variant-name
|
||||
.table-cell
|
||||
.inline {{ product.master.name_to_display }}
|
||||
.bulk-buy.inline{"bo-if" => "product.group_buy"}
|
||||
%i.ofn-i_056-bulk><
|
||||
%em><
|
||||
\ Bulk
|
||||
|
||||
-# WITHOUT GROUP BUY
|
||||
.small-5.medium-3.large-3.columns.text-right{"bo-if" => "!product.group_buy"}
|
||||
%input{type: :number,
|
||||
min: 0,
|
||||
placeholder: "0",
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
|
||||
name: "variants[{{product.master.id}}]",
|
||||
"ng-model" => "product.master.line_item.quantity",
|
||||
id: "variants_{{product.master.id}}"}
|
||||
|
||||
-# WITH GROUP BUY
|
||||
.small-5.medium-3.large-3.columns.text-right{"bo-if" => "product.group_buy"}
|
||||
%span.bulk-input-container
|
||||
%span.bulk-input
|
||||
%input.bulk.first{type: :number,
|
||||
min: 0,
|
||||
"ng-model" => "product.master.line_item.quantity",
|
||||
placeholder: "min",
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
|
||||
name: "variants[{{product.master.id}}]",
|
||||
id: "variants_{{product.master.id}}"}
|
||||
|
||||
%span.bulk-input{"bo-if" => "product.group_buy"}
|
||||
%input.bulk.second{type: :number,
|
||||
min: 0,
|
||||
"ng-model" => "product.master.line_item.max_quantity",
|
||||
placeholder: "max",
|
||||
"ofn-disable-scroll" => true,
|
||||
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
|
||||
name: "variant_attributes[{{product.master.id}}][max_quantity]"}
|
||||
|
||||
.small-3.medium-1.large-1.columns.variant-unit
|
||||
.table-cell
|
||||
%em {{ product.master.unit_to_display }}
|
||||
|
||||
.small-4.medium-2.large-2.columns.variant-price
|
||||
.table-cell
|
||||
%i.ofn-i_009-close
|
||||
{{ product.master.price | currency }}
|
||||
-#%button.graph-button{"price-breakdown" => "_",
|
||||
-#"variant" => "product.master",
|
||||
-#"price-breakdown-animation" => "true"}
|
||||
-#%i.ofn-i-058-graph
|
||||
|
||||
.small-12.medium-2.large-2.columns.total-price.text-right
|
||||
.table-cell
|
||||
%strong
|
||||
{{ product.master.getPrice() | currency }}
|
||||
@@ -1,6 +1,6 @@
|
||||
.product-thumb
|
||||
%a{"ng-click" => "triggerProductModal()"}
|
||||
%img{"bo-src" => "product.primaryImage", "ng-click" => "triggerProductModal()"}
|
||||
%img{"bo-src" => "product.primaryImageOrMissing", "ng-click" => "triggerProductModal()"}
|
||||
|
||||
.row.summary
|
||||
.small-9.medium-10.large-11.columns.summary-header
|
||||
|
||||
@@ -8,7 +8,6 @@ module.exports = function(config) {
|
||||
APPLICATION_SPEC,
|
||||
'app/assets/javascripts/shared/jquery-1.8.0.js', // TODO: Can we link to Rails' jquery?
|
||||
'app/assets/javascripts/shared/jquery.timeago.js',
|
||||
'app/assets/javascripts/shared/mm-foundation-tpls-0.2.0-SNAPSHOT.js',
|
||||
'app/assets/javascripts/shared/angular-local-storage.js',
|
||||
'app/assets/javascripts/shared/bindonce.min.js',
|
||||
'app/assets/javascripts/shared/ng-infinite-scroll.min.js',
|
||||
|
||||
@@ -114,7 +114,8 @@ describe ShopController do
|
||||
it "scopes variants for a product to the order cycle and distributor" do
|
||||
controller.stub(:current_order_cycle).and_return order_cycle
|
||||
controller.stub(:current_distributor).and_return d
|
||||
Spree::Product.any_instance.should_receive(:variants_for).with(order_cycle, d)
|
||||
Spree::Product.any_instance.should_receive(:variants_for).with(order_cycle, d).and_return(m = double())
|
||||
m.stub(:in_stock).and_return []
|
||||
xhr :get, :products
|
||||
end
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
//= require angular-backstretch.js
|
||||
//= require lodash.underscore.js
|
||||
//= require angular-flash.min.js
|
||||
//= require shared/mm-foundation-tpls-0.2.2.min.js
|
||||
//= require moment
|
||||
|
||||
angular.module('templates', [])
|
||||
|
||||
@@ -3,6 +3,7 @@ describe 'ProductsCtrl', ->
|
||||
scope = null
|
||||
event = null
|
||||
Products = null
|
||||
Cart = {}
|
||||
|
||||
beforeEach ->
|
||||
module('Darkswarm')
|
||||
@@ -15,7 +16,7 @@ describe 'ProductsCtrl', ->
|
||||
|
||||
inject ($controller) ->
|
||||
scope = {}
|
||||
ctrl = $controller 'ProductsCtrl', {$scope: scope, Products: Products, OrderCycle: OrderCycle}
|
||||
ctrl = $controller 'ProductsCtrl', {$scope: scope, Products: Products, OrderCycle: OrderCycle, Cart: Cart}
|
||||
|
||||
it 'fetches products from Products', ->
|
||||
expect(scope.Products.products).toEqual ['testy mctest']
|
||||
|
||||
@@ -2,7 +2,7 @@ describe "filtering Groups", ->
|
||||
filterGroups = null
|
||||
groups = [{
|
||||
name: "test"
|
||||
long_description: "roger"
|
||||
description: "roger"
|
||||
enterprises: [{
|
||||
name: "kittens"
|
||||
}, {
|
||||
@@ -10,7 +10,7 @@ describe "filtering Groups", ->
|
||||
}]
|
||||
}, {
|
||||
name: "blankness"
|
||||
long_description: "in the sky"
|
||||
description: "in the sky"
|
||||
enterprises: [{
|
||||
name: "ponies"
|
||||
}, {
|
||||
|
||||
@@ -6,11 +6,8 @@ describe 'filtering urls', ->
|
||||
inject ($filter) ->
|
||||
filter = $filter('stripUrl')
|
||||
|
||||
it "removes http and www", ->
|
||||
expect(filter("http://www.footle.com")).toEqual "footle.com"
|
||||
it "removes http", ->
|
||||
expect(filter("http://footle.com")).toEqual "footle.com"
|
||||
|
||||
it "removes https and www", ->
|
||||
expect(filter("https://www.footle.com")).toEqual "footle.com"
|
||||
|
||||
it "removes just www", ->
|
||||
expect(filter("www.footle.com")).toEqual "footle.com"
|
||||
it "removes https", ->
|
||||
expect(filter("https://www.footle.com")).toEqual "www.footle.com"
|
||||
|
||||
@@ -55,6 +55,12 @@ describe 'Products service', ->
|
||||
$httpBackend.flush()
|
||||
expect(Cart.line_items[0].variant).toBe Products.products[0].variants[0]
|
||||
|
||||
it "sets primaryImageOrMissing when no images are provided", ->
|
||||
$httpBackend.expectGET("/shop/products").respond([product])
|
||||
$httpBackend.flush()
|
||||
expect(Products.products[0].primaryImage).toBeUndefined()
|
||||
expect(Products.products[0].primaryImageOrMissing).toEqual "/assets/noimage/small.png"
|
||||
|
||||
describe "determining the price to display for a product", ->
|
||||
it "displays the product price when the product does not have variants", ->
|
||||
$httpBackend.expectGET("/shop/products").respond([product])
|
||||
@@ -66,4 +72,3 @@ describe 'Products service', ->
|
||||
$httpBackend.expectGET("/shop/products").respond([product])
|
||||
$httpBackend.flush()
|
||||
expect(Products.products[0].price).toEqual 22
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ describe 'Variants service', ->
|
||||
beforeEach ->
|
||||
variant =
|
||||
id: 1
|
||||
base_price: 80.5
|
||||
price: 100
|
||||
module 'Darkswarm'
|
||||
inject ($injector)->
|
||||
Variants = $injector.get("Variants")
|
||||
@@ -19,3 +21,5 @@ describe 'Variants service', ->
|
||||
it "will return the same object as passed", ->
|
||||
expect(Variants.register(variant)).toBe variant
|
||||
|
||||
it "initialises base price percentage", ->
|
||||
expect(Variants.register(variant).basePricePercentage).toEqual 81
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
require 'spec_helper'
|
||||
|
||||
# TODO this seems to be redundant
|
||||
describe Cart do
|
||||
|
||||
describe "associations" do
|
||||
|
||||
@@ -448,9 +448,9 @@ describe Enterprise do
|
||||
linkedin: "https://linkedin.com")
|
||||
}
|
||||
|
||||
it "strips http and www from url fields" do
|
||||
distributor.website.should == "google.com"
|
||||
distributor.facebook.should == "facebook.com/roger"
|
||||
it "strips http from url fields" do
|
||||
distributor.website.should == "www.google.com"
|
||||
distributor.facebook.should == "www.facebook.com/roger"
|
||||
distributor.linkedin.should == "linkedin.com"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -56,6 +56,7 @@ describe ProductDistribution do
|
||||
adjustment.label.should == "Product distribution by #{distributor.name} for Pear"
|
||||
adjustment.amount.should == 1.23
|
||||
|
||||
# TODO ROB this has an intermittent failure
|
||||
# And it should have some associated metadata
|
||||
md = adjustment.metadata
|
||||
md.enterprise.should == distributor
|
||||
|
||||
@@ -24,6 +24,7 @@ module UIComponentHelper
|
||||
within ".login-modal" do
|
||||
find("a", text: text).click
|
||||
end
|
||||
sleep 0.2
|
||||
end
|
||||
|
||||
def open_login_modal
|
||||
|
||||
Reference in New Issue
Block a user