Enhance addon handling with quantity controls and unselecting

Added support for managing addon quantities including increment, decrement, and conditional status toggling. Improved UX by allowing product unselection and refined expandable product details behavior. These changes enhance flexibility and clarity in addon operations.
This commit is contained in:
Jepp9350
2025-02-26 14:49:52 +01:00
parent 8d878d788a
commit 6cefd1fa53
2 changed files with 137 additions and 31 deletions
+60 -26
View File
@@ -3,6 +3,8 @@ import { ref, defineProps, defineEmits } from 'vue';
import { Colors } from "@/ThemeConfig.vue";
import { showPopper, popperBoxProductAddonHtml, removePopperIfOpen } from "@/components/displays/PopperDefault.vue";
const expandIcon = ref(null);
const emit = defineEmits(['add-to-cart']);
const props = defineProps({
@@ -15,6 +17,10 @@ const props = defineProps({
isSelected: Boolean,
selectedAddons: Array,
toggleProductAddon: Function,
getProductAddonQuantity: Function,
addProductAddon: Function,
subtractProductAddon: Function,
unselect: Function
});
const isAddonSelected = (productId, addonId) => {
@@ -44,6 +50,13 @@ const emitAddToCart = (id) => {
emit('add-to-cart', id);
};
const toggleIsSelected = () => {
if (props.isSelected)
{
props.unselect()
}
};
</script>
@@ -68,37 +81,52 @@ const emitAddToCart = (id) => {
<!-- Expandable product details -->
<div v-if="isSelected" class="column is-12-desktop">
<!-- Addons -->
<div class="buttons is-centered mt-3 mb-0 pl-6" v-if="addons.length > 0">
<button
class="button is-small is-fullwidth"
<div class="buttons has-addons is-centered mt-3 mb-0 pl-6" v-if="addons.length > 0">
<div
class="buttons has-addons is-small is-fullwidth is-flex-wrap-nowrap"
v-for="addon in addons"
:key="addon.id"
:class="{ 'is-link': isAddonSelected(props.id, addon.option_id), 'is-light': !isAddonSelected(props.id, addon.option_id) }"
@click="toggleProductAddon(props.id, addon.option_id)"
@mouseenter="showAddonPopper(addon, $event.target)"
@mouseleave="hideAddonPopper(addon, $event.target)"
>
<!-- Addon ( + ) -->
<span class="icon is-small" style="margin-right: auto; margin-left: 1rem;">
<i class="fas fa-plus fa-xs"></i>
</span>
<!-- Addon name -->
<span style="font-size: 0.7rem;">
{{ addon.name }}
</span>
<!-- Price separator -->
<span style="font-size: 0.7rem;">
&nbsp;/&nbsp;
</span>
<!-- Addon price -->
<span style="font-size: 0.7rem;">
{{ getAddonPrice(addon.id) }} Kr.
</span>
<button
class="button is-small"
@click="addProductAddon(
props.id,
addon.option_id
)"
>
<span class="icon is-small">
<i class="fas fa-plus"></i>
</span>
</button>
<!-- quantity (if any), Addon name, price -->
<button
class="button is-small"
:class="{ 'is-link': isAddonSelected(props.id, addon.option_id), 'is-light': !isAddonSelected(props.id, addon.option_id) }"
@click="toggleProductAddon(props.id, addon.option_id)"
@mouseenter="showAddonPopper(addon, $event.target)"
@mouseleave="hideAddonPopper(addon, $event.target)"
>
<template v-if="isAddonSelected(props.id, addon.option_id)">
{{ getProductAddonQuantity(props.id, addon.option_id) }} x
</template>
{{ addon.name }} / {{ getAddonPrice(addon.id) }} Kr.
</button>
<!-- Addon ( - ) -->
<span class="icon is-small" style="margin-left: auto; margin-right: 1rem;">
<i class="fas fa-minus fa-xs"></i>
</span>
</button>
<button
class="button is-small"
:disabled="!isAddonSelected(props.id, addon.option_id)"
@click="subtractProductAddon(
props.id,
addon.option_id
)"
>
<span class="icon is-small">
<i class="fas fa-minus"></i>
</span>
</button>
</div>
</div>
<!-- White space, to accommodate the design requirements -->
<p>&nbsp;</p>
@@ -117,12 +145,18 @@ const emitAddToCart = (id) => {
</div>
<!-- Expander -->
<div class="column is-12-desktop">
<p class="has-text-centered pl-6">
<p
class="has-text-centered pl-6"
@click="toggleIsSelected"
@mouseover="expandIcon.classList.add('has-text-link')"
@mouseleave="expandIcon.classList.remove('has-text-link')"
>
<span class="icon is-small" style="color: #c7c7c7;">
<i
class="fas fa-angle-down fa-lg"
:class="{ 'fa-rotate-180': isSelected }"
aria-hidden="true"
ref="expandIcon"
></i>
</span>
</p>
@@ -184,14 +184,15 @@ const isProductAddonTrue = (productId, addonId) => {
return productAddons.value.find((productAddon) => productAddon.product_id === productId && productAddon.addon_id === addonId && productAddon.status === true) !== undefined;
};
// Set the product addon
const setProductAddon = (productId, addonId, status) => {
const setProductAddon = (productId, addonId, status, quantity = 1) => {
// Set the product addon status
const productAddon = productAddons.value.find((productAddon) => productAddon.product_id === productId && productAddon.addon_id === addonId);
if (productAddon === undefined) {
productAddons.value.push({
product_id: productId,
addon_id: addonId,
status: status
quantity: status ? quantity : 0,
status: quantity === 0 ? false : status
});
} else {
productAddon.status = status;
@@ -208,12 +209,42 @@ const addAddonsToOrderMiddleware = async (product_id, quantity = 1, related_item
// If the product addons are set, add them to the order (If the product is added)
var product_addons = productAddons.value.filter((productAddon) => productAddon.product_id === product_id && productAddon.status === true);
for (let i = 0; i < product_addons.length; i++) {
// Get the addons quantity (If the addon quantity is 0, use the product quantity, otherwise multiply the addon quantity with the product quantity)
let addon_quantity = product_addons[i].quantity === 0 ? quantity : product_addons[i].quantity * quantity;
// Get the products addon, and show the fake create order item
showFakeCreateOrderItem(product_addons[i].addon_id, quantity, 0, related_item_id);
showFakeCreateOrderItem(
product_addons[i].addon_id,
addon_quantity,
0,
related_item_id
);
}
// Add the addons to the order
for (let i = 0; i < product_addons.length; i++) {
await createOrderItem(getOrderId(), product_addons[i].addon_id, quantity, related_item_id);
let addon_quantity = product_addons[i].quantity === 0 ? quantity : product_addons[i].quantity * quantity;
await createOrderItem(
getOrderId(),
product_addons[i].addon_id,
addon_quantity,
related_item_id
);
}
};
const getProductAddonQuantity = (productId, addonId) => {
// Get the product addon quantity (If the product addon is not set, return 0)
const productAddon = productAddons.value.find((productAddon) => productAddon.product_id === productId && productAddon.addon_id === addonId);
return productAddon === undefined ? 0 : productAddon.quantity;
};
const setProductAddonQuantity = (productId, addonId, quantity) => {
console.log(productId, addonId, quantity);
// If the quantity is 0, set the product addon status to false
if (quantity === 0 || quantity < 0) {
setProductAddon(productId, addonId, false, 0);
} else {
// Set the product addon quantity
setProductAddon(productId, addonId, true, quantity);
}
};
@@ -287,6 +318,43 @@ const selectProduct = (productId) => {
const getAddonPrice = (productId, addonId) => {
return products.value.find((product) => product.id === productId).addons.find((addon) => addon.option_id === addonId).price;
};
const addProductAddon = (productId, addonId) => {
const product = products.value.find((product) => product.id === productId);
const addon = product.addons.find((addon) => addon.option_id === addonId);
const productAddon = productAddons.value.find((productAddon) => productAddon.product_id === productId && productAddon.addon_id === addonId);
if (productAddon === undefined) {
productAddons.value.push({
product_id: productId,
addon_id: addonId,
quantity: 1,
status: true
});
} else {
productAddon.quantity += 1;
productAddon.status = true;
}
};
const subtractProductAddon = (productId, addonId) => {
const productAddon = productAddons.value.find((productAddon) => productAddon.product_id === productId && productAddon.addon_id === addonId);
if (productAddon !== undefined) {
if (productAddon.quantity > 0) {
productAddon.quantity -= 1;
}
// If the quantity is 0, set the product addon status to false
if (productAddon.quantity === 0) {
productAddon.status = false;
}
}
};
const unselectProduct = () => {
// Wait for the selection from the click to be done
setTimeout(() => {
selectedProduct.value = null;
}, 100);
};
</script>
<template>
@@ -446,8 +514,12 @@ const getAddonPrice = (productId, addonId) => {
:isSelected="selectedProduct === product.id"
:selectedAddons="productAddons"
:toggleProductAddon="toggleProductAddon"
@click="selectProduct(parseInt(product.id))"
:getProductAddonQuantity="getProductAddonQuantity"
:addProductAddon="addProductAddon"
:subtractProductAddon="subtractProductAddon"
@click="!isProductSelected(product.id) ? selectProduct(product.id) : void 0"
@addToCart="addProductWithAddonsToOrder(product.id)"
:unselect="unselectProduct"
/>
</WhiteBox>
</div>