spree_group_buy 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +3 -0
- data/.rubocop.yml +24 -0
- data/.travis.yml +49 -0
- data/Appraisals +20 -0
- data/Gemfile +11 -0
- data/LICENSE +26 -0
- data/README.md +51 -0
- data/Rakefile +21 -0
- data/app/.gitkeep +0 -0
- data/app/controllers/.gitkeep +0 -0
- data/app/controllers/spree/admin/group_buys_controller.rb +15 -0
- data/app/controllers/spree/admin/orders_controller_decorator.rb +17 -0
- data/app/controllers/spree/api/v2/storefront/cart_controller_decorator.rb +9 -0
- data/app/controllers/spree/products_controller_decorator.rb +20 -0
- data/app/mailers/spree/order_mailer_decorator.rb +10 -0
- data/app/models/.gitkeep +0 -0
- data/app/models/spree/group_buy.rb +72 -0
- data/app/models/spree/line_item_decorator.rb +18 -0
- data/app/models/spree/order_decorator.rb +68 -0
- data/app/models/spree/product_decorator.rb +8 -0
- data/app/models/spree/stock/packer_decorator.rb +27 -0
- data/app/models/spree/stock/prioritizer_decorator.rb +18 -0
- data/app/services/.gitkeep +0 -0
- data/app/services/spree/cart/group_buy_add_item.rb +66 -0
- data/app/views/.gitkeep +0 -0
- data/app/views/spree/admin/group_buys/_form.html.erb +32 -0
- data/app/views/spree/admin/group_buys/edit.html.erb +13 -0
- data/app/views/spree/admin/group_buys/index.html.erb +49 -0
- data/app/views/spree/admin/group_buys/new.html.erb +15 -0
- data/app/views/spree/admin/orders/index.html.erb +271 -0
- data/app/views/spree/admin/shared/_main_menu.html.erb +49 -0
- data/app/views/spree/admin/shared/_product_tabs.html.erb +56 -0
- data/bin/rails +8 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +8 -0
- data/db/migrate/20200824192531_create_spree_group_buys.rb +15 -0
- data/db/migrate/20200826140754_add_group_buy_to_line_items.rb +5 -0
- data/db/migrate/20201005210530_add_group_buy_to_orders.rb +5 -0
- data/gemfiles/spree_3_7.gemfile +9 -0
- data/gemfiles/spree_4_0.gemfile +8 -0
- data/gemfiles/spree_4_1.gemfile +9 -0
- data/gemfiles/spree_master.gemfile +8 -0
- data/lib/generators/spree_group_buy/install/install_generator.rb +26 -0
- data/lib/generators/templates/vendor/assets/javascripts/spree/frontend/views/spree/products/cart_form.js +388 -0
- data/lib/generators/templates/vendor/assets/javascripts/spree/frontend/views/spree/shared/product_added_modal.js +28 -0
- data/lib/spree_group_buy.rb +4 -0
- data/lib/spree_group_buy/engine.rb +20 -0
- data/lib/spree_group_buy/factories.rb +6 -0
- data/lib/spree_group_buy/version.rb +17 -0
- data/spree_group_buy.gemspec +32 -0
- metadata +197 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<nav>
|
|
2
|
+
<% if can? :admin, Spree::Order %>
|
|
3
|
+
<ul class="nav nav-sidebar border-bottom">
|
|
4
|
+
<%= tab *Spree::BackendConfiguration::ORDER_TABS, icon: 'shopping-cart' %>
|
|
5
|
+
</ul>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<% if can? :admin, Spree::Order %>
|
|
9
|
+
<ul class="nav nav-sidebar border-bottom">
|
|
10
|
+
<%= tab :group_buy_orders, url: '/admin/orders?q[group_buy_eq]=1', icon: 'shopping-cart' %>
|
|
11
|
+
</ul>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
14
|
+
<% if can?(:admin, Spree::ReturnAuthorization) || can?(:admin, Spree::CustomerReturn) %>
|
|
15
|
+
<ul class="nav nav-sidebar border-bottom">
|
|
16
|
+
<%= main_menu_tree Spree.t(:returns), icon: "transfer", sub_menu: "returns", url: "#sidebar-returns" %>
|
|
17
|
+
</ul>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<% if can? :admin, Spree::Product %>
|
|
21
|
+
<ul class="nav nav-sidebar border-bottom">
|
|
22
|
+
<%= main_menu_tree Spree.t(:products), icon: "th-large", sub_menu: "product", url: "#sidebar-product" %>
|
|
23
|
+
</ul>
|
|
24
|
+
<% end %>
|
|
25
|
+
|
|
26
|
+
<% if can? :admin, Spree::Admin::ReportsController %>
|
|
27
|
+
<ul class="nav nav-sidebar border-bottom">
|
|
28
|
+
<%= tab *Spree::BackendConfiguration::REPORT_TABS, icon: 'file' %>
|
|
29
|
+
</ul>
|
|
30
|
+
<% end %>
|
|
31
|
+
|
|
32
|
+
<% if can? :admin, Spree::Promotion %>
|
|
33
|
+
<ul class="nav nav-sidebar border-bottom">
|
|
34
|
+
<%= main_menu_tree Spree.t(:promotions), icon: "gift", sub_menu: "promotion", url: "#sidebar-promotions" %>
|
|
35
|
+
</ul>
|
|
36
|
+
<% end %>
|
|
37
|
+
|
|
38
|
+
<% if Spree.user_class && can?(:admin, Spree.user_class) %>
|
|
39
|
+
<ul class="nav nav-sidebar border-bottom">
|
|
40
|
+
<%= tab *Spree::BackendConfiguration::USER_TABS, url: spree.admin_users_path, icon: 'user' %>
|
|
41
|
+
</ul>
|
|
42
|
+
<% end %>
|
|
43
|
+
|
|
44
|
+
<% if can? :admin, current_store %>
|
|
45
|
+
<ul class="nav nav-sidebar border-bottom">
|
|
46
|
+
<%= main_menu_tree Spree.t(:configurations), icon: "wrench", sub_menu: "configuration", url: "#sidebar-configuration" %>
|
|
47
|
+
</ul>
|
|
48
|
+
<% end %>
|
|
49
|
+
</nav>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<% content_for :page_title do %>
|
|
2
|
+
<%= link_to Spree.t(:products), admin_products_path %> /
|
|
3
|
+
<%= @product.name %>
|
|
4
|
+
<% end %>
|
|
5
|
+
|
|
6
|
+
<% content_for :sidebar do %>
|
|
7
|
+
<ul class="nav flex-column nav-pills" data-hook="admin_product_tabs">
|
|
8
|
+
<%= content_tag :li do %>
|
|
9
|
+
<%= link_to_with_icon 'edit',
|
|
10
|
+
Spree.t(:details),
|
|
11
|
+
edit_admin_product_url(@product),
|
|
12
|
+
class: "nav-link #{'active' if current == :details}" %>
|
|
13
|
+
|
|
14
|
+
<% end if can?(:admin, Spree::Product) %>
|
|
15
|
+
|
|
16
|
+
<%= content_tag :li do %>
|
|
17
|
+
<%= link_to_with_icon 'picture',
|
|
18
|
+
Spree.t(:images),
|
|
19
|
+
spree.admin_product_images_url(@product),
|
|
20
|
+
class: "nav-link #{'active' if current == :images}" %>
|
|
21
|
+
|
|
22
|
+
<% end if can?(:admin, Spree::Image) && !@product.deleted? %>
|
|
23
|
+
|
|
24
|
+
<%= content_tag :li do %>
|
|
25
|
+
<%= link_to_with_icon 'th-large',
|
|
26
|
+
Spree.t(:variants),
|
|
27
|
+
spree.admin_product_variants_url(@product),
|
|
28
|
+
class: "nav-link #{'active' if current == :variants}" %>
|
|
29
|
+
|
|
30
|
+
<% end if can?(:admin, Spree::Variant) && !@product.deleted? %>
|
|
31
|
+
|
|
32
|
+
<%= content_tag :li do %>
|
|
33
|
+
<%= link_to_with_icon 'list-alt',
|
|
34
|
+
Spree.t(:properties),
|
|
35
|
+
spree.admin_product_product_properties_url(@product),
|
|
36
|
+
class: "nav-link #{'active' if current == :properties}" %>
|
|
37
|
+
|
|
38
|
+
<% end if can?(:admin, Spree::ProductProperty) && !@product.deleted? %>
|
|
39
|
+
|
|
40
|
+
<%= content_tag :li do %>
|
|
41
|
+
<%= link_to_with_icon 'home',
|
|
42
|
+
Spree.t(:stock),
|
|
43
|
+
stock_admin_product_url(@product),
|
|
44
|
+
class: "nav-link #{'active' if current == :stock}" %>
|
|
45
|
+
|
|
46
|
+
<% end if can?(:admin, Spree::StockItem) && !@product.deleted? %>
|
|
47
|
+
|
|
48
|
+
<%= content_tag :li do %>
|
|
49
|
+
<%= link_to_with_icon 'money',
|
|
50
|
+
Spree.t(:group_buys),
|
|
51
|
+
admin_product_group_buys_url(@product),
|
|
52
|
+
class: "nav-link #{'active' if current == :group_buys}" %>
|
|
53
|
+
|
|
54
|
+
<% end if can?(:admin, Spree::GroupBuy) && !@product.deleted? %>
|
|
55
|
+
</ul>
|
|
56
|
+
<% end %>
|
data/bin/rails
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# This command will automatically be run when you run "rails" from the root of your extension
|
|
3
|
+
|
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/spree_group_buy/engine', __FILE__)
|
|
6
|
+
|
|
7
|
+
require 'rails/all'
|
|
8
|
+
require 'rails/engine/commands'
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreateSpreeGroupBuys < ActiveRecord::Migration[6.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :spree_group_buys do |t|
|
|
4
|
+
t.references :product
|
|
5
|
+
t.integer :quantity, null: false
|
|
6
|
+
t.decimal :price, precision: 8, scale: 2
|
|
7
|
+
t.string :currency
|
|
8
|
+
t.datetime :expires_at
|
|
9
|
+
t.integer :engaged_count, default: 0, null: false
|
|
10
|
+
t.string :state
|
|
11
|
+
t.timestamps
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module SpreeGroupBuy
|
|
2
|
+
module Generators
|
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
|
4
|
+
class_option :auto_run_migrations, type: :boolean, default: false
|
|
5
|
+
|
|
6
|
+
source_root File.expand_path('../../../templates', __FILE__)
|
|
7
|
+
|
|
8
|
+
def copy_javascripts
|
|
9
|
+
directory 'vendor'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_migrations
|
|
13
|
+
run 'bundle exec rake railties:install:migrations FROM=spree_group_buy'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def run_migrations
|
|
17
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
|
|
18
|
+
if run_migrations
|
|
19
|
+
run 'bundle exec rails db:migrate'
|
|
20
|
+
else
|
|
21
|
+
puts 'Skipping rails db:migrate, don\'t forget to run it!'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
//= require spree/api/storefront/cart
|
|
2
|
+
|
|
3
|
+
var ADD_TO_CART_FORM_SELECTOR = '.add-to-cart-form'
|
|
4
|
+
var VARIANT_ID_SELECTOR = '[name="variant_id"]'
|
|
5
|
+
var OPTION_VALUE_SELECTOR = '.product-variants-variant-values-radio'
|
|
6
|
+
var ADD_TO_CART_SELECTOR = '.add-to-cart-button'
|
|
7
|
+
var ADD_TO_CART_DEAL_SELECTOR = '.add-to-cart-deal-button'
|
|
8
|
+
var GROUP_BUY_SELECTOR = '.group_buy_options_radio'
|
|
9
|
+
|
|
10
|
+
var AVAILABILITY_TEMPLATES = {
|
|
11
|
+
notAvailableInCurrency: '.availability-template-not-available-in-currency',
|
|
12
|
+
inStock: '.availability-template-in-stock',
|
|
13
|
+
backorderable: '.availability-template-backorderable',
|
|
14
|
+
outOfStock: '.availability-template-out-of-stock'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
function CartForm($, $cartForm) {
|
|
19
|
+
this.constructor = function() {
|
|
20
|
+
this.initialize()
|
|
21
|
+
this.bindEventHandlers()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.initialize = function() {
|
|
25
|
+
this.selectedOptionValueIds = []
|
|
26
|
+
this.variants = JSON.parse($cartForm.attr('data-variants'))
|
|
27
|
+
this.withOptionValues = Boolean($cartForm.find(OPTION_VALUE_SELECTOR).length)
|
|
28
|
+
|
|
29
|
+
this.$addToCart = $cartForm.find(ADD_TO_CART_SELECTOR)
|
|
30
|
+
this.$addToCartDeal = $cartForm.find(ADD_TO_CART_DEAL_SELECTOR)
|
|
31
|
+
this.$price = $cartForm.find('.price.selling')
|
|
32
|
+
this.$variantIdInput = $cartForm.find(VARIANT_ID_SELECTOR)
|
|
33
|
+
|
|
34
|
+
this.initializeForm()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.initializeForm = function() {
|
|
38
|
+
if (this.withOptionValues) {
|
|
39
|
+
var $optionValue = this.firstCheckedOptionValue()
|
|
40
|
+
this.applyCheckedOptionValue($optionValue, true)
|
|
41
|
+
} else {
|
|
42
|
+
this.updateAddToCart()
|
|
43
|
+
this.triggerVariantImages()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.bindEventHandlers = function() {
|
|
48
|
+
$cartForm.on('click', OPTION_VALUE_SELECTOR, this.handleOptionValueClick)
|
|
49
|
+
// $cartForm.on('click', GROUP_BUY_SELECTOR, this.handleGroupBuyClick)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.handleOptionValueClick = function(event) {
|
|
53
|
+
this.applyCheckedOptionValue($(event.currentTarget))
|
|
54
|
+
}.bind(this)
|
|
55
|
+
|
|
56
|
+
this.handleGroupBuyClick = function(event) {
|
|
57
|
+
var checkboxes = $cartForm.find(GROUP_BUY_SELECTOR)
|
|
58
|
+
var current_val = parseInt($(event.currentTarget).val())
|
|
59
|
+
$cartForm.find(GROUP_BUY_SELECTOR)
|
|
60
|
+
.each(function(_index, ov) {
|
|
61
|
+
var $ov = $(ov)
|
|
62
|
+
var id = parseInt($ov.val())
|
|
63
|
+
if(id != current_val) {
|
|
64
|
+
$ov.prop('checked', false)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
})
|
|
68
|
+
}.bind(this)
|
|
69
|
+
|
|
70
|
+
this.applyCheckedOptionValue = function($optionValue, initialUpdate) {
|
|
71
|
+
this.saveCheckedOptionValue($optionValue)
|
|
72
|
+
this.showAvailableVariants()
|
|
73
|
+
this.updateAddToCart()
|
|
74
|
+
// we don't want to remove availability status on initial page load
|
|
75
|
+
if (!initialUpdate) this.updateVariantAvailability()
|
|
76
|
+
this.updateVariantPrice()
|
|
77
|
+
this.updateVariantId()
|
|
78
|
+
|
|
79
|
+
if (this.shouldTriggerVariantImage($optionValue)) {
|
|
80
|
+
this.triggerVariantImages()
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.saveCheckedOptionValue = function($optionValue) {
|
|
85
|
+
var optionTypeIndex = $optionValue.data('option-type-index')
|
|
86
|
+
|
|
87
|
+
this.selectedOptionValueIds.splice(
|
|
88
|
+
optionTypeIndex,
|
|
89
|
+
this.selectedOptionValueIds.length,
|
|
90
|
+
parseInt($optionValue.val())
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.showAvailableVariants = function() {
|
|
95
|
+
var availableOptionValueIds = this.availableOptionValueIds()
|
|
96
|
+
var selectedOptionValueIdsCount = this.selectedOptionValueIds.length
|
|
97
|
+
|
|
98
|
+
this.optionTypes().each(function(index, optionType) {
|
|
99
|
+
if (index < selectedOptionValueIdsCount) return
|
|
100
|
+
|
|
101
|
+
$(optionType)
|
|
102
|
+
.find(OPTION_VALUE_SELECTOR)
|
|
103
|
+
.each(function(_index, ov) {
|
|
104
|
+
var $ov = $(ov)
|
|
105
|
+
var id = parseInt($ov.val())
|
|
106
|
+
|
|
107
|
+
$ov.prop('checked', false)
|
|
108
|
+
$ov.prop('disabled', !availableOptionValueIds.includes(id))
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.optionTypes = function() {
|
|
114
|
+
return $cartForm.find('.product-variants-variant')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.availableOptionValueIds = function() {
|
|
118
|
+
var selectedOptionValueIds = this.selectedOptionValueIds
|
|
119
|
+
|
|
120
|
+
return this.variants.reduce(function(acc, variant) {
|
|
121
|
+
var optionValues = variant.option_values.map(function(ov) {
|
|
122
|
+
return ov.id
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
var isPossibleVariantFound = selectedOptionValueIds.every(function(ov) {
|
|
126
|
+
return optionValues.includes(ov)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
if (isPossibleVariantFound) {
|
|
130
|
+
return acc.concat(optionValues)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return acc
|
|
134
|
+
}, [])
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.firstCheckedOptionValue = function() {
|
|
138
|
+
return $cartForm.find(OPTION_VALUE_SELECTOR + '[data-option-type-index=0]' + ':checked')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.shouldTriggerVariantImage = function($optionValue) {
|
|
142
|
+
return $optionValue.data('is-color') || !this.firstCheckedOptionValue().data('is-color')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.triggerVariantImages = function() {
|
|
146
|
+
var checkedVariantId
|
|
147
|
+
var variant = this.selectedVariant()
|
|
148
|
+
|
|
149
|
+
if (variant) {
|
|
150
|
+
checkedVariantId = variant.id
|
|
151
|
+
} else {
|
|
152
|
+
checkedVariantId = this.firstCheckedOptionValue().data('variant-id')
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Wait for listeners to attach.
|
|
156
|
+
setTimeout(function() {
|
|
157
|
+
$cartForm.trigger({
|
|
158
|
+
type: 'variant_id_change',
|
|
159
|
+
triggerId: $cartForm.attr('data-variant-change-trigger-identifier'),
|
|
160
|
+
variantId: checkedVariantId + ''
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.selectedVariant = function() {
|
|
166
|
+
var self = this
|
|
167
|
+
|
|
168
|
+
if (!this.withOptionValues) {
|
|
169
|
+
return this.variants.find(function(variant) {
|
|
170
|
+
return variant.id === parseInt(self.$variantIdInput.val())
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (this.variants.length === 1 && this.variants[0].is_master) {
|
|
175
|
+
return this.variants[0]
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return this.variants.find(function(variant) {
|
|
179
|
+
var optionValueIds = variant.option_values.map(function(ov) {
|
|
180
|
+
return ov.id
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
return self.areArraysEqual(optionValueIds, self.selectedOptionValueIds)
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this.areArraysEqual = function(array1, array2) {
|
|
188
|
+
return this.sortArray(array1).join(',') === this.sortArray(array2).join(',')
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this.sortArray = function(array) {
|
|
192
|
+
return array.concat().sort(function(a, b) {
|
|
193
|
+
if (a < b) return -1
|
|
194
|
+
if (a > b) return 1
|
|
195
|
+
|
|
196
|
+
return 0
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this.updateAddToCart = function() {
|
|
201
|
+
var variant = this.selectedVariant()
|
|
202
|
+
|
|
203
|
+
this.$addToCart.prop('disabled', variant ? !variant.purchasable : true)
|
|
204
|
+
this.$addToCartDeal.prop('disabled', variant ? !variant.purchasable : true)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
this.availabilityMessage = function(variant) {
|
|
208
|
+
if (!variant.is_product_available_in_currency) {
|
|
209
|
+
return $(AVAILABILITY_TEMPLATES.notAvailableInCurrency).html()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (variant.in_stock) {
|
|
213
|
+
return $(AVAILABILITY_TEMPLATES.inStock).html()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (variant.backorderable) {
|
|
217
|
+
return $(AVAILABILITY_TEMPLATES.backorderable).html()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return $(AVAILABILITY_TEMPLATES.outOfStock).html()
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
this.updateVariantAvailability = function() {
|
|
224
|
+
var variant = this.selectedVariant()
|
|
225
|
+
|
|
226
|
+
if (!variant) {
|
|
227
|
+
return $cartForm
|
|
228
|
+
.find('.add-to-cart-form-general-availability .add-to-cart-form-general-availability-value')
|
|
229
|
+
.html('')
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return $cartForm
|
|
233
|
+
.find('.add-to-cart-form-general-availability .add-to-cart-form-general-availability-value')
|
|
234
|
+
.html(this.availabilityMessage(variant))
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.updateVariantPrice = function() {
|
|
238
|
+
var variant = this.selectedVariant()
|
|
239
|
+
|
|
240
|
+
if (!variant) return
|
|
241
|
+
|
|
242
|
+
this.$price.html(variant.display_price)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.updateVariantId = function() {
|
|
246
|
+
var variant = this.selectedVariant()
|
|
247
|
+
var variantId = (variant && variant.id) || ''
|
|
248
|
+
|
|
249
|
+
this.$variantIdInput.val(variantId)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.constructor()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function countdown() {
|
|
256
|
+
var countdown = document.getElementById("countdown");
|
|
257
|
+
|
|
258
|
+
if(!countdown) { return }
|
|
259
|
+
|
|
260
|
+
var expireDate = $('#expires_at').text();
|
|
261
|
+
|
|
262
|
+
// Set the date we're counting down to
|
|
263
|
+
var countDownDate = new Date(expireDate).getTime();
|
|
264
|
+
|
|
265
|
+
// Update the count down every 1 second
|
|
266
|
+
var x = setInterval(function() {
|
|
267
|
+
|
|
268
|
+
// Get today's date and time
|
|
269
|
+
var now = new Date().getTime();
|
|
270
|
+
|
|
271
|
+
// Find the distance between now and the count down date
|
|
272
|
+
var distance = countDownDate - now;
|
|
273
|
+
|
|
274
|
+
// Time calculations for days, hours, minutes and seconds
|
|
275
|
+
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
|
|
276
|
+
var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
277
|
+
var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
|
|
278
|
+
var seconds = Math.floor((distance % (1000 * 60)) / 1000);
|
|
279
|
+
|
|
280
|
+
// Display the result in the element with id="demo"
|
|
281
|
+
countdown.innerHTML = days + "d " + hours + "h "
|
|
282
|
+
+ minutes + "m " + seconds + "s ";
|
|
283
|
+
|
|
284
|
+
// If the count down is finished, write some text
|
|
285
|
+
if (distance < 0) {
|
|
286
|
+
clearInterval(x);
|
|
287
|
+
countdown.innerHTML = "EXPIRED";
|
|
288
|
+
}
|
|
289
|
+
}, 1000);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
Spree.ready(function($) {
|
|
293
|
+
Spree.variantById = function($cartForm, variantId) {
|
|
294
|
+
var cartFormVariants = JSON.parse($cartForm.attr('data-variants'))
|
|
295
|
+
return (
|
|
296
|
+
cartFormVariants.find(function(variant) {
|
|
297
|
+
return variant.id.toString() === variantId
|
|
298
|
+
}) || null
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
countdown();
|
|
303
|
+
|
|
304
|
+
$('#add-to-cart-button').on('click', function(event) {
|
|
305
|
+
var $cartForm = $(ADD_TO_CART_FORM_SELECTOR);
|
|
306
|
+
var checkboxes = $cartForm.find(GROUP_BUY_SELECTOR);
|
|
307
|
+
if(checkboxes.length > 0) {
|
|
308
|
+
$(checkboxes[0]).prop('checked', false);
|
|
309
|
+
}
|
|
310
|
+
$(ADD_TO_CART_SELECTOR).prop('disabled', true);
|
|
311
|
+
$cartForm.submit();
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
$('#add-to-cart-deal-button').on('click', function(event) {
|
|
315
|
+
var $cartForm = $(ADD_TO_CART_FORM_SELECTOR);
|
|
316
|
+
var checkboxes = $cartForm.find(GROUP_BUY_SELECTOR);
|
|
317
|
+
if(checkboxes.length > 0) {
|
|
318
|
+
$(checkboxes[0]).prop('checked', true);
|
|
319
|
+
}
|
|
320
|
+
$(ADD_TO_CART_DEAL_SELECTOR).prop('disabled', true);
|
|
321
|
+
$cartForm.submit();
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
$('#product-details').on('submit', ADD_TO_CART_FORM_SELECTOR, function(event) {
|
|
325
|
+
var variantId
|
|
326
|
+
var quantity
|
|
327
|
+
var group_buy_id
|
|
328
|
+
var group_buy_price
|
|
329
|
+
var group_buy_display_price
|
|
330
|
+
var $cartForm = $(event.currentTarget)
|
|
331
|
+
var $addToCart = $cartForm.find(ADD_TO_CART_SELECTOR)
|
|
332
|
+
var $addToCartDeal = $cartForm.find(ADD_TO_CART_DEAL_SELECTOR)
|
|
333
|
+
var $groupBuyChecked = $cartForm.find(GROUP_BUY_SELECTOR+':checked')
|
|
334
|
+
|
|
335
|
+
event.preventDefault()
|
|
336
|
+
// $addToCart.prop('disabled', true)
|
|
337
|
+
variantId = $cartForm.find(VARIANT_ID_SELECTOR).val()
|
|
338
|
+
quantity = parseInt($cartForm.find('[name="quantity"]').val())
|
|
339
|
+
|
|
340
|
+
if($groupBuyChecked.length) {
|
|
341
|
+
group_buy_id = $groupBuyChecked.val()
|
|
342
|
+
group_buy_price = $groupBuyChecked.data('price')
|
|
343
|
+
group_buy_display_price = $groupBuyChecked.data('display-price')
|
|
344
|
+
} else {
|
|
345
|
+
group_buy_id = group_buy_price = group_buy_display_price = undefined
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
Spree.ensureCart(function() {
|
|
349
|
+
SpreeAPI.Storefront.addToCart(
|
|
350
|
+
variantId,
|
|
351
|
+
quantity,
|
|
352
|
+
{groupBuyId: group_buy_id, groupBuyPrice: group_buy_price}, // options hash - you can pass additional parameters here, your backend
|
|
353
|
+
// needs to be aware of those, see API docs:
|
|
354
|
+
// https://github.com/spree/spree/blob/master/api/docs/v2/storefront/index.yaml#L42
|
|
355
|
+
function(response) {
|
|
356
|
+
$addToCart.prop('disabled', false)
|
|
357
|
+
$addToCartDeal.prop('disabled', false)
|
|
358
|
+
Spree.fetchCart()
|
|
359
|
+
Spree.showProductAddedModal(JSON.parse(
|
|
360
|
+
$cartForm.attr('data-product-summary')
|
|
361
|
+
), Spree.variantById($cartForm, variantId), group_buy_display_price)
|
|
362
|
+
$cartForm.trigger({
|
|
363
|
+
type: 'product_add_to_cart',
|
|
364
|
+
variant: Spree.variantById($cartForm, variantId),
|
|
365
|
+
quantity_increment: quantity,
|
|
366
|
+
cart: response.attributes
|
|
367
|
+
})
|
|
368
|
+
},
|
|
369
|
+
function(error) {
|
|
370
|
+
if (typeof error === 'string' && error !== '') {
|
|
371
|
+
document.querySelector('#no-product-available .no-product-available-text').innerText = error
|
|
372
|
+
}
|
|
373
|
+
document.getElementById('overlay').classList.add('shown')
|
|
374
|
+
document.getElementById('no-product-available').classList.add('shown')
|
|
375
|
+
window.scrollTo(0, 0)
|
|
376
|
+
$addToCart.prop('disabled', false)
|
|
377
|
+
$addToCartDeal.prop('disabled', false)
|
|
378
|
+
} // failure callback for 422 and 50x errors
|
|
379
|
+
)
|
|
380
|
+
})
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
$(ADD_TO_CART_FORM_SELECTOR).each(function(_cartFormIndex, cartFormElement) {
|
|
384
|
+
var $cartForm = $(cartFormElement)
|
|
385
|
+
|
|
386
|
+
CartForm($, $cartForm)
|
|
387
|
+
})
|
|
388
|
+
})
|