stall 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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