Skip to content

Commit ccd48f5

Browse files
committed
Cart quantity change
1 parent 46378d6 commit ccd48f5

File tree

3 files changed

+111
-3
lines changed

3 files changed

+111
-3
lines changed

components/Cart/CartContents.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
:key="product.key"
1616
:product="product"
1717
@remove="handleRemoveProduct"
18+
@update-quantity="handleUpdateQuantity"
1819
/>
1920
</section>
2021
<CommonButton v-if="showCheckoutButton" link-to="/checkout" center-button>
@@ -73,6 +74,20 @@ onMounted(async () => {
7374
isLoading.value = false;
7475
}
7576
});
77+
78+
/**
79+
* Handles updating the quantity of a cart item.
80+
*
81+
* @param {{ key: string, quantity: number }} payload
82+
*/
83+
const handleUpdateQuantity = async ({ key, quantity }) => {
84+
try {
85+
await cart.updateCartItemQuantity(key, quantity);
86+
} catch (error) {
87+
console.error("Error updating cart item quantity:", error);
88+
// Optionally, add user notification here
89+
}
90+
};
7691
</script>
7792

7893
<style scoped>

components/Cart/CartItem.vue

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@
2020
<div class="item">
2121
<span class="block mt-2 font-extrabold">Quantity: <br /></span>
2222
<span class="item-content">
23-
{{ product.quantity }}
23+
<QuantityInput
24+
:model-value="localQuantity"
25+
:min="1"
26+
:loading="isUpdating"
27+
@update:modelValue="onQuantityChange"
28+
/>
2429
</span>
2530
</div>
2631
<div class="item">
@@ -46,10 +51,20 @@
4651
* @emits CartItem#remove - Emitted when the remove button is clicked.
4752
*/
4853
49-
import { ref } from "vue";
54+
import { ref, watch } from "vue";
5055
import { formatPrice } from "@/utils/functions";
56+
import QuantityInput from "@/components/common/QuantityInput.vue";
5157
5258
const isRemoving = ref(false);
59+
const isUpdating = ref(false);
60+
const localQuantity = ref(props.product.quantity);
61+
62+
watch(
63+
() => props.product.quantity,
64+
(newVal) => {
65+
localQuantity.value = newVal;
66+
}
67+
);
5368
5469
const props = defineProps({
5570
product: {
@@ -58,7 +73,7 @@ const props = defineProps({
5873
},
5974
});
6075
61-
const emit = defineEmits(["remove"]);
76+
const emit = defineEmits(["remove", "update-quantity"]);
6277
6378
/**
6479
* Emits a "remove" event with the product's key as the payload.
@@ -67,6 +82,16 @@ const emitRemove = () => {
6782
isRemoving.value = true;
6883
emit("remove", props.product.key);
6984
};
85+
86+
const onQuantityChange = (newQuantity) => {
87+
if (newQuantity === props.product.quantity || isUpdating.value) return;
88+
isUpdating.value = true;
89+
emit("update-quantity", { key: props.product.key, quantity: newQuantity });
90+
// UI disables controls while updating; parent resets isUpdating via prop or reactivity
91+
setTimeout(() => {
92+
isUpdating.value = false;
93+
}, 1000);
94+
};
7095
</script>
7196

7297
<style scoped>

components/common/QuantityInput.vue

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<template>
2+
<div class="flex items-center space-x-2">
3+
<button
4+
class="px-2 py-1 bg-gray-200 rounded disabled:opacity-50"
5+
:disabled="loading || value <= min"
6+
@click="updateValue(value - 1)"
7+
aria-label="Decrease quantity"
8+
>
9+
-
10+
</button>
11+
<input
12+
type="number"
13+
:value="value"
14+
:min="min"
15+
:max="max"
16+
class="w-12 text-center border rounded"
17+
:disabled="loading"
18+
@input="onInput"
19+
/>
20+
<button
21+
class="px-2 py-1 bg-gray-200 rounded disabled:opacity-50"
22+
:disabled="loading || (max !== null && value >= max)"
23+
@click="updateValue(value + 1)"
24+
aria-label="Increase quantity"
25+
>
26+
+
27+
</button>
28+
</div>
29+
</template>
30+
31+
<script setup>
32+
import { computed } from "vue";
33+
34+
const props = defineProps({
35+
modelValue: {
36+
type: Number,
37+
required: true,
38+
},
39+
min: {
40+
type: Number,
41+
default: 1,
42+
},
43+
max: {
44+
type: Number,
45+
default: null,
46+
},
47+
loading: {
48+
type: Boolean,
49+
default: false,
50+
},
51+
});
52+
53+
const emit = defineEmits(["update:modelValue"]);
54+
55+
const value = computed(() => props.modelValue);
56+
57+
function updateValue(newValue) {
58+
if (props.loading) return;
59+
if (props.max !== null && newValue > props.max) return;
60+
if (newValue < props.min) return;
61+
emit("update:modelValue", newValue);
62+
}
63+
64+
function onInput(e) {
65+
const newValue = Number(e.target.value);
66+
updateValue(newValue);
67+
}
68+
</script>

0 commit comments

Comments
 (0)