stall 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/para/stall/inputs/variant-select.coffee +4 -3
  3. data/app/assets/javascripts/stall.coffee +3 -1
  4. data/app/assets/javascripts/stall/add-product-to-wish-list-button.coffee +99 -0
  5. data/app/assets/javascripts/stall/add-to-wish-list-button.coffee +17 -0
  6. data/app/assets/javascripts/stall/product-list-form.coffee +31 -0
  7. data/app/controllers/stall/cart_line_items_controller.rb +21 -0
  8. data/app/controllers/stall/curated_product_lists_controller.rb +6 -0
  9. data/app/controllers/stall/line_items_controller.rb +14 -7
  10. data/app/controllers/stall/products_breadcrumbs.rb +8 -1
  11. data/app/controllers/stall/products_controller.rb +12 -0
  12. data/app/controllers/stall/wish_list_line_items_controller.rb +52 -0
  13. data/app/controllers/stall/wish_lists_controller.rb +7 -0
  14. data/app/helpers/stall/add_to_cart_helper.rb +3 -2
  15. data/app/helpers/stall/add_to_wish_list_helper.rb +30 -0
  16. data/app/helpers/stall/products_helper.rb +13 -0
  17. data/app/models/stall/models/cart.rb +0 -6
  18. data/app/models/stall/models/customer.rb +9 -0
  19. data/app/models/stall/models/line_item.rb +13 -0
  20. data/app/models/stall/models/product.rb +1 -1
  21. data/app/models/stall/models/product_list.rb +4 -0
  22. data/app/models/stall/models/variant.rb +4 -0
  23. data/app/models/stall/models/wish_list.rb +18 -0
  24. data/app/models/wish_list.rb +3 -0
  25. data/app/services/stall/add_to_cart_service.rb +4 -47
  26. data/app/services/stall/add_to_product_list_service.rb +63 -0
  27. data/app/services/stall/add_to_wish_list_service.rb +17 -0
  28. data/app/services/stall/available_stocks_service.rb +1 -1
  29. data/app/services/stall/shipping_fee_calculator_service.rb +22 -9
  30. data/app/views/admin/products/_form.html.haml +5 -0
  31. data/app/views/checkout/steps/_informations.html.haml +1 -1
  32. data/app/views/stall/addresses/_fields.html.haml +4 -4
  33. data/app/views/stall/{line_items → cart_line_items}/_add_error.html.haml +0 -0
  34. data/app/views/stall/{line_items → cart_line_items}/_added.html.haml +2 -2
  35. data/app/views/stall/{line_items → cart_line_items}/_form.html.haml +1 -1
  36. data/app/views/stall/carts/_widget.html.haml +1 -1
  37. data/app/views/stall/carts/show.html.haml +4 -4
  38. data/app/views/stall/curated_product_lists/show.html.haml +1 -1
  39. data/app/views/stall/customers/_fields.html.haml +1 -1
  40. data/app/views/stall/customers/_sign_in.html.haml +2 -2
  41. data/app/views/stall/products/_list.html.haml +2 -0
  42. data/app/views/stall/products/_product.html.haml +2 -1
  43. data/app/views/stall/wish_list_line_items/_add_error.html.haml +17 -0
  44. data/app/views/stall/wish_list_line_items/_added.html.haml +19 -0
  45. data/app/views/stall/wish_list_line_items/_button.html.haml +3 -0
  46. data/app/views/stall/wish_list_line_items/_form.html.haml +12 -0
  47. data/app/views/stall/wish_lists/show.html.haml +21 -0
  48. data/config/locales/stall.fr.yml +26 -0
  49. data/db/migrate/20170425085606_add_weight_to_stall_products_and_variants.rb +6 -0
  50. data/db/migrate/20170426163450_add_vat_rate_to_stall_products.rb +5 -0
  51. data/db/migrate/20170522062334_change_variants_weight_default_to_nil.rb +11 -0
  52. data/lib/generators/stall/install/templates/initializer.rb +18 -0
  53. data/lib/stall.rb +1 -0
  54. data/lib/stall/addressable.rb +35 -4
  55. data/lib/stall/addresses/copy_source_to_target.rb +14 -10
  56. data/lib/stall/addresses/prefill_target_from_source.rb +10 -6
  57. data/lib/stall/config.rb +4 -0
  58. data/lib/stall/engine.rb +1 -0
  59. data/lib/stall/routes.rb +42 -19
  60. data/lib/stall/sellable.rb +1 -1
  61. data/lib/stall/shipping/calculator.rb +1 -1
  62. data/lib/stall/shipping/country_weight_table_calculator.rb +3 -2
  63. data/lib/stall/version.rb +1 -1
  64. data/lib/stall/wish_list_helper.rb +93 -0
  65. metadata +26 -6
  66. data/app/assets/javascripts/stall/cart-form.coffee +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d3c1c5916425f5f689fed5cb2167bc48e6046ea6
4
- data.tar.gz: 31301c3457145a56633b53d3b44c12a49ebd03db
3
+ metadata.gz: ac018d181d2997364eeca462e80758d50ab4c9e2
4
+ data.tar.gz: 4e3396a2466fab227f83d9d201a237e22147acc3
5
5
  SHA512:
6
- metadata.gz: 125ee1c9e7804ebd54017e364dde2e578a729dd18e9b0283f6d3b1ee71b34e861177febbf509b7387c430b54fae86ed093911f16ab150554ec84148d126cd332
7
- data.tar.gz: 2d1e7e7b43e0bc1b1f22710c00f652231529e8e2205613acdf22b0acbff30c371f95fd88e436c093451d35a5186941eed0d189458044e155f133e49381eed391
6
+ metadata.gz: c612280b92d9cbff047f8e7e0adf1f8dcc8abea245f7cd4cbf37170a68261c0d732f53266c11ba666fbf103c3b3998f267ce970fbb553b40880f88a2e52ee21e
7
+ data.tar.gz: b35431b6aa8856a8b3dc21686dff0346d0b724ec443176f044d022e0495563584a7e6d8ff537b227be52f91aed929b9e23bbacfa85ff7fb1c827024d7749cb51
@@ -1,4 +1,4 @@
1
- class VariantSelectInput extends Vertebra.View
1
+ class Stall.VariantSelectInput extends Vertebra.View
2
2
  events:
3
3
  'change [data-variant-select-property]': 'onInputChanged'
4
4
 
@@ -57,6 +57,7 @@ class VariantSelectInput extends Vertebra.View
57
57
  price = if variant then variant.price else @originalPrice
58
58
  @$priceTarget.html(price)
59
59
 
60
- $(document).on 'page:change turbolinks:load', ->
60
+
61
+ Stall.onDomReady ->
61
62
  $('[data-variant-select-input]').each (i, el) ->
62
- new VariantSelectInput(el: el)
63
+ new Stall.VariantSelectInput(el: el)
@@ -4,7 +4,9 @@
4
4
  #= require_self
5
5
  #
6
6
  #= require stall/add-to-cart-form
7
- #= require stall/cart-form
7
+ #= require stall/add-to-wish-list-button
8
+ #= require stall/add-product-to-wish-list-button
9
+ #= require stall/product-list-form
8
10
  #= require stall/addresses-fields
9
11
  #= require stall/remote-sign-in-form
10
12
  #= require stall/products-filters
@@ -0,0 +1,99 @@
1
+ class Stall.AddProductToWishListButton extends Vertebra.View
2
+ events:
3
+ 'click': 'onButtonClicked'
4
+
5
+ onButtonClicked: ->
6
+ if @$el.is('[data-included]') then @remove() else @add()
7
+
8
+ add: ->
9
+ @setLoading(true)
10
+
11
+ data =
12
+ product_id: @$el.data('product-id')
13
+
14
+ $.post(@$el.data('url'), data).then(@onResponse)
15
+
16
+ remove: ->
17
+ @setLoading(true)
18
+
19
+ data =
20
+ _method: 'delete'
21
+
22
+ $.post(@$el.data('url'), data).then(@onResponse)
23
+
24
+ onResponse: (resp) =>
25
+ @popover?.destroy()
26
+ @setLoading(false)
27
+ @setNewElement($(resp))
28
+
29
+ setNewElement: ($el) ->
30
+ @$el.tooltip('hide')
31
+ @$el.replaceWith($el)
32
+ @setElement($el)
33
+ # Open popover if provided
34
+ if (content = $el.data('popover-content')) then @openPopover(content)
35
+
36
+ openPopover: (content) ->
37
+ @popover = new Stall.WishListFormPopover(content: content)
38
+ @popover.renderTo(@$el)
39
+ @listenTo(@popover, 'send', => @setLoading(true))
40
+ @listenTo(@popover, 'added', @onResponse)
41
+
42
+ setLoading: (state) ->
43
+ @popover?.hide()
44
+ @$el.toggleClass('loading', state)
45
+ @$('[data-wish-list-icon]').toggleClass('hidden', state)
46
+ @$('[data-wish-list-loading-spinner]').toggleClass('hidden', !state)
47
+
48
+ class Stall.WishListFormPopover extends Vertebra.View
49
+ events:
50
+ 'ajax:beforeSend [data-add-to-wish-list-form]': 'onBeforeSend'
51
+ 'ajax:success [data-add-to-wish-list-form]': 'onItemAdded'
52
+ 'click [data-cancel-button]': 'onCancelClicked'
53
+
54
+ initialize: (options = {}) ->
55
+ @content = options.content
56
+
57
+ renderTo: ($parent) ->
58
+ $parent.popover
59
+ title: false
60
+ content: @content
61
+ html: true
62
+ trigger: 'manual'
63
+ placement: 'auto'
64
+ container: 'body'
65
+
66
+ @popover = $parent.data('bs.popover')
67
+ @popover.show()
68
+
69
+ @setElement(@popover.$tip)
70
+ @initializeForm()
71
+
72
+ initializeForm: ->
73
+ @$('[data-variant-select-input]').each (i, el) ->
74
+ new Stall.VariantSelectInput(el: el)
75
+
76
+ hide: ->
77
+ @popover?.hide()
78
+
79
+ onBeforeSend: ->
80
+ @trigger('send')
81
+
82
+ onItemAdded: (e, resp) ->
83
+ @trigger('added', resp)
84
+
85
+ onCancelClicked: ->
86
+ console.log 'onCancelClicked', this
87
+ @destroy()
88
+
89
+ destroy: ->
90
+ return if @destroyed
91
+ @popover.destroy()
92
+ @popover = null
93
+ super()
94
+ @destroyed = true
95
+
96
+
97
+ Stall.onDomReady ->
98
+ $('[data-add-to-wish-list="product"]').each (i, el) ->
99
+ new Stall.AddProductToWishListButton(el: el)
@@ -0,0 +1,17 @@
1
+ class Stall.AddToWishListButton extends Vertebra.View
2
+ events:
3
+ 'click': 'add'
4
+
5
+ initialize: ->
6
+ @url = @$el.data('url')
7
+
8
+ add: ->
9
+ data = @$el.closest('form').serialize()
10
+ $.post(@url, data).then(@onResponse)
11
+
12
+ onResponse: (resp) =>
13
+ $(resp).modal()
14
+
15
+ Stall.onDomReady ->
16
+ $('[data-add-to-wish-list="line-item-form"]').each (i, el) ->
17
+ new Stall.AddToWishListButton(el: el)
@@ -0,0 +1,31 @@
1
+ class Stall.ProductListForm extends Vertebra.View
2
+ events:
3
+ 'change [data-quantity-field]': 'formUpdated'
4
+ 'cocoon:after-remove': 'formUpdated'
5
+ 'ajax:success': 'updateSuccess'
6
+
7
+ initialize: ->
8
+ @clean()
9
+
10
+ clean: ->
11
+ @$('[data-product-list-update-button]').hide(0)
12
+ # Backwards compatibility with app overriden cart forms
13
+ @$('[data-cart-update-button]').hide(0)
14
+
15
+ formUpdated: (e) ->
16
+ @$el.submit()
17
+
18
+ updateSuccess: (e, resp) ->
19
+ @updateProductListFormWith(resp)
20
+
21
+ updateProductListFormWith: (markup) =>
22
+ $form = $(markup).find('[data-product-list-form], [data-cart-form]')
23
+ @$el.html($form.html())
24
+ @clean()
25
+
26
+
27
+ Stall.onDomReady ->
28
+ # Backwards compatibility with app overriden cart forms
29
+ if ($product_listForm = $('[data-product-list-form], [data-cart-form]')).length
30
+ product_listForm = new Stall.ProductListForm(el: $product_listForm)
31
+ $product_listForm.data('stall.product-list-form', product_listForm)
@@ -0,0 +1,21 @@
1
+ module Stall
2
+ class CartLineItemsController < Stall::LineItemsController
3
+ def create
4
+ super do |success|
5
+ if success
6
+ @widget_partial = render_to_string(partial: 'stall/carts/widget', locals: { cart: product_list })
7
+ end
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def product_list
14
+ @product_list ||= ProductList.find_by_token(params[:cart_id]) || current_cart
15
+ end
16
+
17
+ def service
18
+ @service ||= Stall.config.service_for(:add_to_cart).new(product_list, params)
19
+ end
20
+ end
21
+ end
@@ -5,6 +5,12 @@ module Stall
5
5
  def show
6
6
  @curated_product_list = CuratedProductList.friendly.find(params[:id])
7
7
  search_products_among(@curated_product_list.products)
8
+
9
+ # Also select curated list product positions to allow distinct call to
10
+ # work when ordering results by position
11
+ @products = @products.select('stall_products.*, stall_curated_list_products.position')
12
+
13
+ add_breadcrumb(@curated_product_list)
8
14
  end
9
15
  end
10
16
  end
@@ -1,23 +1,30 @@
1
1
  module Stall
2
2
  class LineItemsController < Stall::ApplicationController
3
3
  def create
4
- service = Stall.config.service_for(:add_to_cart).new(cart, params)
5
-
6
4
  if service.call
7
5
  @quantity = params[:line_item][:quantity].to_i
8
6
  @line_item = service.line_item
9
- @widget_partial = render_to_string(partial: 'stall/carts/widget', locals: { cart: cart })
10
- render partial: 'added'
7
+ # Allow subclasses to hook into successful product list add
8
+ yield(true) if block_given?
9
+ # We do not render if the yield bock already has done it
10
+ render partial: 'added' unless response_body
11
11
  else
12
12
  @line_item = service.line_item
13
- render partial: 'add_error'
13
+ # Allow subclasses to hook into failed product list add
14
+ yield(false) if block_given?
15
+ # We do not render if the yield bock already has done it
16
+ render partial: 'add_error' unless response_body
14
17
  end
15
18
  end
16
19
 
17
20
  private
18
21
 
19
- def cart
20
- @cart ||= ProductList.find_by_token(params[:cart_id]) || current_cart
22
+ def product_list
23
+ fail NotImplementedError, 'Override #product_list in subclass'
24
+ end
25
+
26
+ def service
27
+ fail NotImplementedError, 'Override #service in subclass'
21
28
  end
22
29
  end
23
30
  end
@@ -3,7 +3,14 @@ module Stall
3
3
  private
4
4
 
5
5
  def add_product_breadcrumbs
6
- add_product_category_breadcrumbs(@product.product_category)
6
+ if @curated_product_list
7
+ add_breadcrumb(@curated_product_list)
8
+ elsif @manufacturer
9
+ add_breadcrumb(@manufacturer)
10
+ else
11
+ add_product_category_breadcrumbs(@product.product_category)
12
+ end
13
+
7
14
  add_breadcrumb(@product)
8
15
  end
9
16
 
@@ -3,6 +3,8 @@ module Stall
3
3
  include ProductsSearch
4
4
  include ProductsBreadcrumbs
5
5
 
6
+ before_action :load_parent_data
7
+
6
8
  def index
7
9
  search_products_among(Product.all)
8
10
 
@@ -16,5 +18,15 @@ module Stall
16
18
 
17
19
  add_product_breadcrumbs
18
20
  end
21
+
22
+ private
23
+
24
+ def load_parent_data
25
+ if params[:curated_product_list_id]
26
+ @curated_product_list = CuratedProductList.find(params[:curated_product_list_id])
27
+ elsif params[:manufacturer_id]
28
+ @manufacturer = Manufacturer.find(params[:manufacturer_id])
29
+ end
30
+ end
19
31
  end
20
32
  end
@@ -0,0 +1,52 @@
1
+ module Stall
2
+ class WishListLineItemsController < Stall::LineItemsController
3
+ include Stall::AddToWishListHelper
4
+
5
+ def create
6
+ if !service.line_item_params? && (product_id = params[:product_id]).present?
7
+ product = Product.find(product_id)
8
+
9
+ if product.variants.length > 1
10
+ form = render_to_string(
11
+ partial: 'stall/wish_list_line_items/form', locals: {
12
+ wish_list: product_list, line_item: LineItem.new, product: product
13
+ }
14
+ )
15
+
16
+ add_to_wish_list_button(product, {
17
+ wish_list: product_list, popover_content: form
18
+ })
19
+ else
20
+ service.add(product.variants.first)
21
+ add_to_wish_list_button(product, wish_list: product_list)
22
+ end
23
+ else
24
+ super do |valid|
25
+ if (product = @line_item.sellable.try(:product))
26
+ add_to_wish_list_button(product, wish_list: product_list)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def destroy
33
+ line_item = product_list.line_items.find(params[:id])
34
+ product = line_item.sellable.try(:product)
35
+ product_list.line_items.destroy(line_item)
36
+
37
+ if product
38
+ add_to_wish_list_button(product, wish_list: product_list)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def product_list
45
+ @product_list ||= ProductList.find_by_token(params[:cart_id]) || current_wish_list
46
+ end
47
+
48
+ def service
49
+ @service ||= Stall.config.service_for(:add_to_wish_list).new(product_list, params)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,7 @@
1
+ module Stall
2
+ class WishListsController < Stall::ApplicationController
3
+ def show
4
+ @wish_list = current_customer.wish_lists.find_by_token(params[:id])
5
+ end
6
+ end
7
+ end
@@ -1,9 +1,10 @@
1
1
  module Stall
2
2
  module AddToCartHelper
3
3
  def add_to_cart_form_for(sellable, cart: nil)
4
- render partial: 'stall/line_items/form', locals: {
4
+ render partial: 'stall/cart_line_items/form', locals: {
5
5
  cart: (cart || current_cart),
6
- line_item: LineItem.new(sellable: sellable)
6
+ line_item: LineItem.new(sellable: sellable),
7
+ product: sellable
7
8
  }
8
9
  end
9
10
  end
@@ -0,0 +1,30 @@
1
+ module Stall
2
+ module AddToWishListHelper
3
+ def add_to_wish_list_button(product, variant: nil, wish_list: current_wish_list, popover_content: nil)
4
+ included = wish_list.includes_product?(product)
5
+
6
+ url = if included
7
+ line_item = wish_list.line_item_for_product(product)
8
+ wish_list_line_item_path(current_wish_list, line_item)
9
+ else
10
+ wish_list_line_items_path(current_wish_list)
11
+ end
12
+
13
+ title = if included
14
+ t('stall.wish_list_line_items.form.remove')
15
+ else
16
+ t('stall.wish_list_line_items.form.add')
17
+ end
18
+
19
+ render partial: 'stall/wish_list_line_items/button', locals: {
20
+ product: product,
21
+ variant: variant,
22
+ cart: wish_list,
23
+ included: included,
24
+ url: url,
25
+ title: title,
26
+ popover_content: popover_content
27
+ }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module Stall
2
+ module ProductsHelper
3
+ def product_path(*args)
4
+ if @curated_product_list
5
+ curated_product_list_product_path(@curated_product_list, *args)
6
+ elsif @manufacturer
7
+ manufacturer_product_path(@manufacturer, *args)
8
+ else
9
+ Rails.application.routes.url_helpers.product_path(*args)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -12,12 +12,6 @@ module Stall
12
12
  attr_accessor :terms
13
13
  end
14
14
 
15
- def total_weight
16
- line_items.reduce(0) do |total, line_item|
17
- total + (line_item.weight || Stall.config.default_product_weight)
18
- end
19
- end
20
-
21
15
  def active?
22
16
  !paid?
23
17
  end
@@ -14,12 +14,21 @@ module Stall
14
14
  before_validation :ensure_user_email
15
15
 
16
16
  has_many :product_lists, dependent: :destroy
17
+ has_many :wish_lists, class_name: 'WishList'
17
18
  has_many :credit_notes, dependent: :destroy
18
19
  has_many :carts, dependent: :nullify
19
20
 
20
21
  validates :email, presence: true,
21
22
  format: { with: /\A[^@\s]+@([^@\s]+\.)+[^@\W]+\z/ }
22
23
 
24
+ def name
25
+ if billing_address && (billing_address.first_name.present? || billing_address.last_name.present?)
26
+ [billing_address.last_name, billing_address.first_name].join(' ')
27
+ else
28
+ email
29
+ end
30
+ end
31
+
23
32
  def user_or_default
24
33
  user || build_user
25
34
  end