Skip to content

Commit 7ee98ca

Browse files
committed
feat(storefront): New useCartSidebar composable optionally abstracting discount and freight
1 parent b4a195c commit 7ee98ca

File tree

3 files changed

+116
-5
lines changed

3 files changed

+116
-5
lines changed

packages/storefront/src/lib/composables/use-cart-item.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type Props = {
1010
item?: CartItem;
1111
product?: ProductItem;
1212
pictureSize?: string;
13-
} & ({ item: CartItem } | { product: ProductItem });
13+
} & ({ item: CartItem } | { product: ProductItem })
1414

1515
export const useCartItem = (props: Props) => {
1616
const parsedItem = computed(() => {
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { ref, computed, watch } from 'vue';
2+
import { useDebounceFn } from '@vueuse/core';
3+
import config from '@cloudcommerce/config';
4+
import { fetchModule, availableExtraDiscount } from '@@sf/state/modules-info';
5+
import { shoppingCart, totalItems } from '@@sf/state/shopping-cart';
6+
import { getPriceWithDiscount } from '@@sf/composables/use-prices';
7+
import { utm, sessionCoupon } from '@@sf/scripts/session-utm';
8+
9+
export type Props = {
10+
canAutoLoadDiscounts?: boolean;
11+
}
12+
13+
export const useCartSidebar = (props: Props = {}) => {
14+
const { lang } = config.get();
15+
const { canAutoLoadDiscounts = true } = props;
16+
const coupon = ref<string | null>(sessionCoupon);
17+
const isLoadingDiscount = ref(false);
18+
const amountDiscount = ref(0);
19+
const discountLabel = ref('');
20+
const discountedSubtotal = computed(() => {
21+
if (amountDiscount.value) {
22+
return shoppingCart.subtotal - amountDiscount.value;
23+
}
24+
const discount = availableExtraDiscount.value;
25+
if (discount) {
26+
return getPriceWithDiscount(shoppingCart.subtotal, discount);
27+
}
28+
return shoppingCart.subtotal;
29+
});
30+
let discountLoadId = 0;
31+
const debouncedLoadDiscounts = useDebounceFn(async () => {
32+
const execId = Date.now();
33+
discountLoadId = execId;
34+
try {
35+
const response = await fetchModule('apply_discount', {
36+
method: 'POST',
37+
body: {
38+
domain: globalThis.location?.hostname,
39+
lang,
40+
utm,
41+
discount_coupon: coupon.value !== null ? coupon.value : undefined,
42+
amount: {
43+
discount: 0,
44+
subtotal: shoppingCart.subtotal,
45+
total: shoppingCart.subtotal,
46+
},
47+
items: shoppingCart.items,
48+
},
49+
});
50+
if (execId !== discountLoadId) return;
51+
if (response.ok) {
52+
const data = await response.json();
53+
amountDiscount.value = 0;
54+
data.result.forEach(({ response: appRes }) => {
55+
if (appRes?.discount_rule) {
56+
amountDiscount.value += appRes.discount_rule.extra_discount.value;
57+
discountLabel.value = appRes.discount_rule.label || '';
58+
} else {
59+
amountDiscount.value = 0;
60+
}
61+
});
62+
}
63+
} catch {
64+
if (execId !== discountLoadId) return;
65+
}
66+
isLoadingDiscount.value = false;
67+
}, 400);
68+
const loadDiscounts = () => {
69+
isLoadingDiscount.value = true;
70+
debouncedLoadDiscounts();
71+
};
72+
if (!import.meta.env.SSR && canAutoLoadDiscounts) {
73+
watch(shoppingCart, async () => {
74+
amountDiscount.value = 0;
75+
if (totalItems.value < 2 && !coupon.value) return;
76+
loadDiscounts();
77+
}, {
78+
immediate: true,
79+
});
80+
watch(coupon, async (newCoupon, oldCoupon) => {
81+
if (oldCoupon === undefined && !newCoupon) return;
82+
amountDiscount.value = 0;
83+
loadDiscounts();
84+
}, {
85+
immediate: true,
86+
});
87+
}
88+
89+
const hasShippingCalculator = ref(false);
90+
const isShippingOpenOnce = ref(false);
91+
const unwatchShippingCalculator = watch(hasShippingCalculator, () => {
92+
if (!hasShippingCalculator.value) return;
93+
unwatchShippingCalculator();
94+
isShippingOpenOnce.value = true;
95+
});
96+
const amountFreight = ref<number | null>(null);
97+
const amountTotal = computed(() => {
98+
if (amountFreight.value === null) return null;
99+
return discountedSubtotal.value + amountFreight.value;
100+
});
101+
102+
return {
103+
coupon,
104+
isLoadingDiscount,
105+
amountDiscount,
106+
discountLabel,
107+
discountedSubtotal,
108+
loadDiscounts,
109+
amountFreight,
110+
amountTotal,
111+
};
112+
};
113+
114+
export default useCartSidebar;

packages/storefront/src/lib/composables/use-shipping-calculator.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@ import {
2727
i19upTo,
2828
i19workingDays,
2929
} from '@@i18n';
30-
import {
31-
fetchModule,
32-
availableExtraDiscount,
33-
} from '@@sf/state/modules-info';
30+
import { fetchModule, availableExtraDiscount } from '@@sf/state/modules-info';
3431
import { getPriceWithDiscount } from '@@sf/composables/use-prices';
3532

3633
export type ShippedItem = Exclude<CalculateShippingParams['items'], undefined>[0] & {

0 commit comments

Comments
 (0)