Skip to content

Commit 6e3ab02

Browse files
committed
fix: Migrate useCart composable to TypeScript
Replaces useCart.js with useCart.ts, adding type safety and improving error handling. Introduces shared types for cart items, product, and button statuses in shared/types/index.ts to support the new TypeScript implementation.
1 parent c2fb117 commit 6e3ab02

File tree

3 files changed

+127
-79
lines changed

3 files changed

+127
-79
lines changed

app/composables/useCart.js

Lines changed: 0 additions & 79 deletions
This file was deleted.

app/composables/useCart.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { push } from 'notivue';
2+
3+
export const useCart = () => {
4+
const cart = useState<CartItem[]>('cart', () => []);
5+
const addToCartButtonStatus = ref<AddBtnStatus>('add');
6+
const removeFromCartButtonStatus = ref<RemoveBtnStatus>('remove');
7+
8+
const findItem = (variationId: number) => cart.value.find(i => i.variation?.node?.databaseId === variationId);
9+
10+
const updateCart = (next: CartItem[]) => {
11+
cart.value = next;
12+
if (process.client) localStorage.setItem('cart', JSON.stringify(next));
13+
};
14+
15+
const handleAddToCart = async (productId: number) => {
16+
try {
17+
addToCartButtonStatus.value = 'loading';
18+
const res = await $fetch<AddToCartResponse>('/api/cart/add', { method: 'POST', body: { productId } });
19+
const incoming = res.addToCart.cartItem;
20+
const idx = cart.value.findIndex(i => i.key === incoming.key);
21+
22+
if (idx > -1) {
23+
const merged = { ...cart.value[idx], ...incoming };
24+
if (typeof incoming.variation?.node?.stockQuantity === 'number') {
25+
merged.variation.node.stockQuantity = incoming.variation.node.stockQuantity;
26+
}
27+
updateCart([...cart.value.slice(0, idx), merged, ...cart.value.slice(idx + 1)]);
28+
} else {
29+
updateCart([...cart.value, incoming]);
30+
}
31+
32+
addToCartButtonStatus.value = 'added';
33+
setTimeout(() => (addToCartButtonStatus.value = 'add'), 2000);
34+
} catch {
35+
addToCartButtonStatus.value = 'add';
36+
push.error('Insufficient stock');
37+
}
38+
};
39+
40+
const changeQty = (key: string, quantity: number) => {
41+
try {
42+
$fetch('/api/cart/update', { method: 'POST', body: { items: [{ key, quantity }] } });
43+
if (quantity <= 0) updateCart(cart.value.filter(i => i.key !== key));
44+
else updateCart(cart.value.map(i => (i.key === key ? { ...i, quantity } : i)));
45+
} catch {
46+
push.error('Unable to update quantity');
47+
}
48+
};
49+
50+
const handleRemoveFromCart = async (key: string) => {
51+
try {
52+
removeFromCartButtonStatus.value = 'loading';
53+
changeQty(key, 0);
54+
} finally {
55+
removeFromCartButtonStatus.value = 'remove';
56+
}
57+
};
58+
59+
const increment = async (variationId: number) => {
60+
const item = findItem(variationId);
61+
if (!item) return handleAddToCart(variationId);
62+
const max = item.variation?.node?.stockQuantity ?? Infinity;
63+
if (item.quantity >= max) return push.error('Insufficient stock');
64+
changeQty(item.key, item.quantity + 1);
65+
};
66+
67+
const decrement = async (variationId: number) => {
68+
const item = findItem(variationId);
69+
if (item) changeQty(item.key, item.quantity - 1);
70+
};
71+
72+
onMounted(() => {
73+
if (!process.client) return;
74+
const stored = localStorage.getItem('cart');
75+
if (!stored) return;
76+
try {
77+
updateCart(JSON.parse(stored) as CartItem[]);
78+
} catch {
79+
updateCart([]);
80+
}
81+
});
82+
83+
return {
84+
cart,
85+
addToCartButtonStatus,
86+
removeFromCartButtonStatus,
87+
handleAddToCart,
88+
handleRemoveFromCart,
89+
increment,
90+
decrement,
91+
};
92+
};

shared/types/index.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
export type Money = string | number;
2+
3+
export interface VariationNode {
4+
databaseId: number;
5+
salePrice: Money;
6+
regularPrice: Money;
7+
stockQuantity?: number;
8+
image?: { sourceUrl: string };
9+
}
10+
11+
export interface Variation {
12+
node: VariationNode;
13+
attributes: Array<{ value: string }>;
14+
}
15+
16+
export interface ProductNode {
17+
sku: string;
18+
slug: string;
19+
name: string;
20+
}
21+
22+
export interface CartItem {
23+
key: string;
24+
quantity: number;
25+
product: { node: ProductNode };
26+
variation: Variation;
27+
}
28+
29+
export interface AddToCartResponse {
30+
addToCart: { cartItem: CartItem };
31+
}
32+
33+
export type AddBtnStatus = 'add' | 'loading' | 'added';
34+
35+
export type RemoveBtnStatus = 'remove' | 'loading';

0 commit comments

Comments
 (0)