fix: cart connect to backend (#39)
Release package / release (push) Failing after 8m34s

Co-authored-by: TakahashiNg <83152264+TakahashiNguyen@users.noreply.github.com>
Reviewed-on: #39
This commit was merged in pull request #39.
This commit is contained in:
2026-05-13 08:56:58 +00:00
parent ae8134fd64
commit 877c7be84b
7 changed files with 183 additions and 288 deletions
+144 -82
View File
@@ -1,131 +1,193 @@
"use client";
import { gql } from "@apollo/client";
import { useMutation, useQuery } from "@apollo/client/react";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import { CartItemEntity, MenuItemEntity } from "./types";
import { cartClient, eateryClient } from "./apollo-clients";
import {
CartEntity,
CartItemEntity,
addMenuItemMutation,
allEateriesQuery,
createCartMutation,
getCartQuery,
} from "./types";
interface CartContextValue {
items: CartItemEntity[];
totalItems: number;
totalPrice: number;
addToCart: (product: MenuItemEntity) => void;
addToCart: (product: CartItemEntity) => void;
increaseQty: (id: string) => void;
decreaseQty: (id: string) => void;
removeFromCart: (id: string) => void;
setQuantity: (id: string, quantity: number) => void;
}
const STORAGE_KEY = "coffee-shop-cart";
const CART_ID = "cartId";
const CartContext = createContext<CartContextValue | null>(null);
const GET_CART_ITEMS = gql`
query getCart($cartId: String!) {
getCart(cartId: $cartId) {
Id
userId
eateryId
items {
productId
quantity
priceAtTimeOfAdding
subTotal
}
totalAmount
paymentQrUrl
}
}
`;
const GET_EATERY = gql`
query GetEateryMenu {
allEateries {
id
}
}
`;
const CREATE_CART = gql`
mutation createCart($eateryId: String!) {
createCart(eateryId: $eateryId)
}
`;
const ADD_ITEM = gql`
mutation addItem(
$cartId: String!
$menuItemId: String!
$quantity: BigInteger!
) {
addItem(cartId: $cartId, menuItemId: $menuItemId, quantity: $quantity) {
Id
userId
eateryId
items {
productId
quantity
priceAtTimeOfAdding
subTotal
}
totalAmount
paymentQrUrl
}
}
`;
export function CartProvider({ children }: { children: React.ReactNode }) {
const [items, setItems] = useState<CartItemEntity[]>([]);
const [cart, setCart] = useState<CartEntity>(null!);
const [cartId, setCartId] = useState<string | null>(null);
const { data: eateryData } = useQuery<allEateriesQuery>(GET_EATERY, {
client: eateryClient,
});
const [createCart] = useMutation<createCartMutation>(CREATE_CART, {
client: cartClient,
});
const { data, loading, error } = useQuery<getCartQuery>(GET_CART_ITEMS, {
client: cartClient,
variables: { cartId },
skip: !cartId,
});
const [addMenuItem] = useMutation<addMenuItemMutation>(ADD_ITEM, {
client: cartClient,
});
useEffect(() => {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return;
const parsed = JSON.parse(raw) as CartItemEntity[];
if (Array.isArray(parsed)) {
setItems(parsed.filter((i) => i && i.productId && i.quantity > 0));
const createCartFunc = async () => {
if (eateryData && eateryData.allEateries?.length > 0) {
try {
const firstEateryId = eateryData.allEateries[0].id;
const { data: mutationResult } = await createCart({
variables: { eateryId: firstEateryId },
});
const newCartId = mutationResult!.createCart;
if (newCartId) {
localStorage.setItem(CART_ID, newCartId);
setCartId(newCartId);
}
} catch (err) {
console.error("Lỗi khi tạo giỏ hàng:", err);
}
}
} catch {
localStorage.removeItem(STORAGE_KEY);
};
if (error) createCartFunc();
else {
const localCartId = localStorage.getItem(CART_ID);
if (localCartId) setCartId(localCartId);
}
}, []);
}, [eateryData, createCart, data, loading]);
useEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(items));
}, [items]);
if (data?.getCart) setCart(data.getCart);
}, [data]);
const addToCart = (product: MenuItemEntity) => {
setItems((prev) => {
const index = prev.findIndex((i) => i.productId === product.id);
if (index === -1) {
return [
...prev,
{
productId: product.id!,
name: product.name,
description: product.description,
priceAtTimeOfAdding: product.price,
quantity: 1,
},
];
}
const addToCart = async (product: CartItemEntity) => {
if (!cartId) return;
const next = [...prev];
next[index] = { ...next[index], quantity: next[index].quantity + 1 };
return next;
const { data: result } = await addMenuItem({
variables: {
cartId,
menuItemId: product.productId!,
quantity: product.quantity,
},
});
if (result) setCart(result.addItem);
};
const increaseQty = (id: string) => {
setItems((prev) =>
prev.map((item) =>
item.productId === id ? { ...item, quantity: item.quantity + 1 } : item,
),
);
const setQuantity = async (id: string, quantity: number) => {
if (!cartId) return;
const { data: result } = await addMenuItem({
variables: {
cartId,
menuItemId: id,
quantity,
},
});
if (result) setCart(result.addItem);
};
const decreaseQty = (id: string) => {
setItems((prev) =>
prev
.map((item) =>
item.productId === id
? { ...item, quantity: Math.max(0, item.quantity - 1) }
: item,
)
.filter((item) => item.quantity > 0),
);
};
const removeFromCart = (id: string) => setQuantity(id, 0);
const removeFromCart = (id: string) => {
setItems((prev) => prev.filter((item) => item.productId !== id));
};
const increaseQty = (id: string) =>
setQuantity(id, cart.items.find((i) => i.productId == id)!.quantity + 1);
const setQuantity = (id: string, quantity: number) => {
const safeQty = Number.isFinite(quantity)
? Math.max(0, Math.floor(quantity))
: 0;
if (safeQty === 0) {
removeFromCart(id);
return;
}
setItems((prev) =>
prev.map((item) =>
item.productId === id ? { ...item, quantity: safeQty } : item,
),
);
};
const decreaseQty = (id: string) =>
setQuantity(id, cart.items.find((i) => i.productId == id)!.quantity - 1);
const totalItems = useMemo(
() => items.reduce((sum, item) => sum + item.quantity, 0),
[items],
);
const totalPrice = useMemo(
() =>
items.reduce(
(sum, item) => sum + item.priceAtTimeOfAdding * item.quantity,
0,
),
[items],
() => cart?.items.reduce((sum, item) => sum + item.quantity, 0),
[cart],
);
const value = useMemo(
() => ({
items,
items: cart?.items,
totalItems,
totalPrice,
totalPrice: cart?.totalAmount,
addToCart,
increaseQty,
decreaseQty,
removeFromCart,
setQuantity,
}),
[items, totalItems, totalPrice],
[cart?.items, cart?.totalAmount],
);
return <CartContext.Provider value={value}>{children}</CartContext.Provider>;