Skip to content

Commit

Permalink
Add to cart restrictions for stock levels.
Browse files Browse the repository at this point in the history
  • Loading branch information
by-tim committed Oct 29, 2021
1 parent 32ac16e commit 6379324
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 48 deletions.
10 changes: 10 additions & 0 deletions components/CartItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@
/>
</div>
</div>

<!-- Adjustment error -->
<div v-if="adjustmentError">
<p class="w-full text-error text-right pb-4">
{{ $t('cart.exceedsStockLevel') }}
</p>
</div>
</div>
</div>
</template>
Expand Down Expand Up @@ -124,6 +131,8 @@ export default {
itemEditorIsVisible: false,
quantity: 1,
maxQuantity: 99,
adjustmentError: false,
validateCartStock: false,
}
},
Expand All @@ -143,6 +152,7 @@ export default {
this.maxQuantity = maxQuantity
this.quantity = item.quantity
this.validateCartStock = $swell.settings.get('cart.validateStock')
},
computed: {
Expand Down
80 changes: 66 additions & 14 deletions components/QuickAdd.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,26 @@
</div>
</transition>

<!-- Error messages -->
<div v-if="addToCartError" class="relative px-4">
<div
class="
absolute
left-0
bottom-0
w-full
px-4
py-3
bg-primary-lighter
rounded
"
>
<span class="w-full label-sm text-error text-center">{{
$t('products.preview.quickAdd.outOfStock')
}}</span>
</div>
</div>

<!-- Adding in progress -->
<div v-if="cartIsUpdating" class="relative px-4">
<BaseButton
Expand Down Expand Up @@ -73,6 +93,7 @@ export default {
optionState: null,
quickAddIsActive: false,
quickAddIndex: 0,
addToCartError: null,
}
},
Expand Down Expand Up @@ -189,10 +210,17 @@ export default {
optionInputs.length === 1 ||
quickAddIndex + 1 >= optionInputs.length
) {
this.addToCart()
// Hide options when adding to cart
this.quickAddIsActive = false
// Check if product/variant is in stock before adding to cart
if (!this.checkHasStock()) {
this.addToCartError = 'Out of stock'
return
}
// Add to cart
this.addToCart()
} else {
// Move onto next option if available
this.quickAddIndex++
Expand All @@ -216,19 +244,43 @@ export default {
}
},
// Check if current variation has stock
checkHasStock() {
const { product, variation } = this
return (
!product.stockTracking ||
product.stockPurchasable ||
((variation.stockStatus !== 'out_of_stock' || variation.stockStatus) &&
variation.stockLevel > 0)
)
},
// Add product to cart with selected options
addToCart() {
// Emit event to show updating label
this.$emit('adding-to-cart')
// Emit event to hide quick add button if keep-alive is active
this.$emit('keep-alive', false)
this.$store.dispatch('addCartItem', {
productId: this.variation.id,
quantity: 1,
options: this.optionState,
})
async addToCart() {
try {
await this.$store.dispatch('addCartItem', {
productId: this.variation.id,
quantity: 1,
options: this.optionState,
})
// Close popup when product has been added to cart
this.$emit('click-close')
} catch (err) {
let errorMessage
switch (err.message) {
case 'invalid_stock':
errorMessage = this.$t('cart.exceedsStockLevel')
break
default:
break
}
this.$store.dispatch('showNotification', {
message: errorMessage,
type: 'error',
})
}
},
},
Expand Down
54 changes: 36 additions & 18 deletions components/QuickViewPopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@
</div>
</div>
</div>

<!-- Cart button & stock info -->
<div
v-if="variation"
Expand Down Expand Up @@ -303,6 +302,7 @@ export default {
pendingState: false,
optionState: null,
productPreviewIndex: 0,
showStockLevel: true,
activeDropdownUID: null,
selectedPurchaseOption: undefined,
}
Expand Down Expand Up @@ -337,6 +337,7 @@ export default {
// Set component data
this.product = product
this.optionState = optionState
this.showStockLevel = get(product, 'content.showStockLevel')
this.enableQuantity = get(product, 'content.enableQuantity')
this.maxQuantity = maxQuantity
},
Expand Down Expand Up @@ -440,6 +441,15 @@ export default {
},
methods: {
// Determine whether to disable Add to Cart button based on the variant's stock status
disableOnVariantStockStatus(stockStatus) {
return (
(stockStatus === 'out_of_stock' || !stockStatus) &&
this.product.stockTracking &&
!this.product.stockPurchasable
)
},
// Update an option value based on user input
setOptionValue({ option, value }) {
// Use $set to update the data object because options are dynamic
Expand All @@ -455,22 +465,30 @@ export default {
// Add product to cart with selected options
async addToCart() {
// Touch and validate all fields
this.$v.$touch()
if (this.$v.$invalid) return // return if invalid
this.$store.commit('setState', {
key: 'addedItem',
value: this.variation,
})
await this.$store.dispatch('addCartItem', {
productId: this.variation.id,
quantity: this.quantity || 1,
options: this.optionState,
purchaseOption: this.selectedPurchaseOption,
})
// Close popup when product has been added to cart
this.$emit('click-close')
try {
this.$v.$touch()
if (this.$v.$invalid) return // return if invalid
this.$store.commit('setState', {
key: 'addedItem',
value: this.variation,
})
await this.$store.dispatch('addCartItem', {
productId: this.variation.id,
quantity: this.quantity || 1,
options: this.optionState,
purchaseOption: this.selectedPurchaseOption,
})
// Close popup when product has been added to cart
this.$emit('click-close')
} catch (err) {
if (err.message === 'invalid_stock') {
this.$store.dispatch('showNotification', {
message: this.$t('cart.exceedsStockLevel'),
type: 'error',
})
}
}
},
},
Expand All @@ -492,7 +510,7 @@ export default {
<style lang="postcss">
.gradient {
@apply w-full h-12;
@apply w-full absolute top-0 h-6 transform -translate-y-full;
background: rgb(255, 255, 255);
background: linear-gradient(
0deg,
Expand Down
14 changes: 14 additions & 0 deletions components/StockStatus.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
<span class="label-xs-bold text-primary-dark">{{
$t(status.label)
}}</span>
<span
v-if="showStockLevel && stockLevel > 0"
class="label-xs-bold text-primary-dark"
>
• {{ $t('products.slug.stockRemaining', { n: stockLevel }) }}
</span>
</div>
<div class>
<a v-if="status.link" href="#" class="label-sm-bold">{{
Expand Down Expand Up @@ -54,6 +60,14 @@ export default {
type: String,
default: 'out_of_stock',
},
stockLevel: {
type: Number,
default: 0,
},
showStockLevel: {
type: Boolean,
default: false,
},
},
computed: {
Expand Down
1 change: 0 additions & 1 deletion components/TheFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@
>Swell</a
>
</p>

<!-- Secondary nav menu -->
<ul v-if="footer.showSecondaryMenu && secondaryMenu" class="my-3">
<li
Expand Down
7 changes: 7 additions & 0 deletions components/TheToastNotification.vue
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,13 @@ export default {
},
},
watch: {
// If no added item in notification, hide recently added product
addedItem(item) {
if (!item) this.product = null
}
},
mounted() {
this.onScroll()
window.addEventListener('scroll', this.onScroll)
Expand Down
5 changes: 5 additions & 0 deletions config/content/product.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
"label": "Enable social sharing",
"type": "toggle"
},
{
"id": "show_stock_level",
"label": "Show stock level",
"type": "toggle"
},
{
"id": "enable_quantity",
"label": "Enable product quantity",
Expand Down
33 changes: 32 additions & 1 deletion config/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,11 @@
"type": "short_text",
"format": "url",
"placeholder": "/categories/"
},
{
"id": "cart.validate_stock",
"label": "Validate stock when adding to cart",
"type": "toggle"
}
]
},
Expand Down Expand Up @@ -3186,6 +3191,20 @@
"type": "short_text",
"localized": true
},
{
"id": "lang.products._slug.stock_remaining",
"label": "Stock remaining",
"default": "{n} remaining",
"type": "short_text",
"localized": true
},
{
"id": "lang.products._slug.options.required",
"label": "Options field required message",
"default": "Please enter a value.",
"type": "short_text",
"localized": true
},
{
"id": "lang.products._slug.options.required",
"label": "Options field required message",
Expand Down Expand Up @@ -3274,6 +3293,11 @@
"type": "short_text",
"localized": true
},
{
"id": "lang.products.preview.quick_add.out_of_stock",
"label": "Out of stock notification",
"default": "This product is out of stock.",
},
{
"label": "Purchase Options: Subscription",
"type": "heading"
Expand Down Expand Up @@ -3778,7 +3802,7 @@
"default": "Account balance",
"type": "short_text",
"localized": true
},
},
{
"id": "lang.cart.checkout",
"label": "Checkout action",
Expand All @@ -3793,6 +3817,13 @@
"type": "short_text",
"localized": true
},
{
"id": "lang.cart.exceeds_stock_level",
"label": "Cart item stock restriction",
"default": "You cannot add more than what is available of this product.",
"type": "short_text",
"localized": true
},
{
"id": "lang.cart.item.edit",
"label": "Edit cart item",
Expand Down
Loading

0 comments on commit 6379324

Please sign in to comment.