useAddToCart.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. "use client";
  2. import { useCallback } from "react";
  3. import { useCustomToast } from "./useToast";
  4. import { useAppDispatch } from "@/store/hooks";
  5. import { addItem, clearCart } from "@/store/slices/cart-slice";
  6. import { isObject } from "@utils/type-guards";
  7. import {
  8. setCookie,
  9. getIsGuest,
  10. getGuestCartToken,
  11. deleteGuestCookie
  12. } from "@utils/cookie-tools";
  13. import { useGuestCartToken } from "./useGuestCartToken";
  14. import { IS_GUEST,GUEST_CART_TOKEN,GUEST_CART_ID } from "@/utils/constants";
  15. import { useMutation, useApolloClient } from "@apollo/client/react";
  16. import {
  17. CREATE_ADD_PRODUCT_IN_CART,
  18. REMOVE_CART_ITEM,
  19. UPDATE_CART_ITEM,
  20. } from "@/graphql";
  21. import { formatCartDetail } from "@/utils/cartDetailTools";
  22. import {handleApolloBusinessError} from "@/lib/ApolloErrorHandler";
  23. export const useAddProduct = () => {
  24. const dispatch = useAppDispatch();
  25. const { createGuestToken } = useGuestCartToken();
  26. const { showToast } = useCustomToast();
  27. const apolloClient = useApolloClient();
  28. const [mutateAsync, { loading: isCartLoading }] = useMutation(
  29. CREATE_ADD_PRODUCT_IN_CART,
  30. {
  31. onCompleted: (res) => {
  32. console.log('useAddToCart onCompleted run ----- 0');
  33. const responseData = res?.createAddProductInCart?.addProductInCart;
  34. if (!responseData?.success) {
  35. showToast(responseData?.message || "Error adding to cart", "danger");
  36. return;
  37. }
  38. if (responseData) {
  39. if (responseData.success) {
  40. /** 兜底代码 start*/
  41. // 游客加购,然后清空购物车,然后刷新页面,然后再加购,然后再刷新页面,购物车会失效,所以添加兜底代码
  42. const isGuest = getIsGuest();// true 是游客
  43. // 如果之前的cartToken有效,responseCartId和responseCartToken的值是一样的都是cart id; 否则两者不相等
  44. const responseCartId = responseData.id;
  45. const responseCartToken = responseData.cartToken;
  46. if(isGuest && responseCartId !== responseCartToken) {
  47. setCookie(GUEST_CART_TOKEN, responseCartToken,{encode: false});
  48. setCookie(GUEST_CART_ID, responseCartId);
  49. setCookie(IS_GUEST, String(isGuest));
  50. }
  51. /** 兜底代码 end*/
  52. const cartDetail = formatCartDetail(responseData);
  53. dispatch(addItem(cartDetail));
  54. showToast("Product added to cart successfully", "success");
  55. }
  56. }
  57. },
  58. onError: (err) => {
  59. handleApolloBusinessError(err,(error) => {
  60. showToast(error.message, "danger");
  61. });
  62. },
  63. },
  64. );
  65. const onAddToCart = async ({
  66. productId,
  67. quantity,
  68. variantId
  69. }: {
  70. productId: string;
  71. quantity: number;
  72. variantId?: number;
  73. token?: string;
  74. cartId?: number | string;
  75. }) => {
  76. // Ensure token exists - create if needed
  77. let token = getGuestCartToken(); // 从cookie获取token
  78. const isGuest = getIsGuest();
  79. if (!token && isGuest) {
  80. // 没有token,则创建一个并写入cookie
  81. token = await createGuestToken();
  82. if (!token) {
  83. showToast("Failed to create cart session", "danger");
  84. return;
  85. }
  86. }
  87. const param : { productId: number; quantity:number; variantId?: number; } = {
  88. productId: parseInt(productId),
  89. quantity,
  90. };
  91. if(variantId) {
  92. param.variantId = variantId;
  93. }
  94. const result = await mutateAsync({
  95. variables: param,
  96. });
  97. return {
  98. data: result.data,
  99. error: result.error,
  100. };
  101. };
  102. // 删除购物车中的产品
  103. const deleteProductFromCart = useCallback((cartItemId: number) => {
  104. return apolloClient.mutate({
  105. mutation: REMOVE_CART_ITEM,
  106. variables: {
  107. cartItemId: cartItemId
  108. },
  109. }).then((res) => {
  110. const resCatData = res.data?.createRemoveCartItem?.removeCartItem ?? null;
  111. if(resCatData && resCatData.itemsQty) {
  112. const cartDetail = formatCartDetail(resCatData);
  113. dispatch(addItem(cartDetail));
  114. showToast('Cart item removed successfully', "warning");
  115. }
  116. if(!resCatData || !resCatData?.itemsQty) {
  117. dispatch(clearCart());
  118. // @todo 可以优化为通过接口或者server action 删除cookie
  119. const isGuest = getIsGuest();
  120. if (isGuest) {
  121. deleteGuestCookie();
  122. }
  123. }
  124. return {
  125. data: resCatData?.itemsQty ? formatCartDetail(resCatData) : null,
  126. error: false,
  127. msg: ""
  128. };
  129. }).catch((err) => {
  130. return {
  131. data: null,
  132. error: true,
  133. msg: err.message,
  134. }
  135. });
  136. },[apolloClient]);
  137. // 修改购物车中产品的数量
  138. const editProductQtyFromCart = useCallback((cartItemId: number,quantity:number) => {
  139. if (quantity < 1) {
  140. showToast("Quantity must be at least 1", "warning");
  141. return {
  142. data: null,
  143. error: true,
  144. msg: "Quantity must be at least 1",
  145. };
  146. }
  147. return apolloClient.mutate({
  148. mutation: UPDATE_CART_ITEM,
  149. variables: {
  150. cartItemId: cartItemId,
  151. quantity: quantity
  152. },
  153. }).then((res) => {
  154. const resCatData = res.data?.createUpdateCartItem?.updateCartItem ?? null;
  155. if(resCatData) {
  156. const cartDetail = formatCartDetail(resCatData);
  157. dispatch(addItem(cartDetail));
  158. }
  159. return {
  160. data: resCatData ? formatCartDetail(resCatData) : null,
  161. error: false,
  162. msg: ""
  163. };
  164. }).catch((err) => {
  165. return {
  166. data: null,
  167. error: true,
  168. msg: err.message,
  169. }
  170. });
  171. },[apolloClient]);
  172. //--------Remove Cart Product Quantity--------//
  173. const [removeFromCart, { loading: isRemoveLoading }] = useMutation(
  174. REMOVE_CART_ITEM,
  175. {
  176. onCompleted: async (response) => {
  177. const responseData = response?.createRemoveCartItem?.removeCartItem;
  178. if (isObject(responseData)) {
  179. const message = "Cart item removed successfully";
  180. const cartDetail = formatCartDetail(responseData);
  181. dispatch(addItem(cartDetail));
  182. showToast(message as string, "warning");
  183. if (!responseData?.itemsQty) {
  184. dispatch(clearCart());
  185. const isGuest = getIsGuest();
  186. if (isGuest) {
  187. deleteGuestCookie();
  188. }
  189. }
  190. } else {
  191. showToast("Something went wrong", "warning");
  192. }
  193. },
  194. onError: (error) => {
  195. handleApolloBusinessError(error,(err) => {
  196. showToast(err.message, "danger");
  197. });
  198. },
  199. },
  200. );
  201. const onAddToRemove = async (productId: string) => {
  202. await removeFromCart({
  203. variables: {
  204. cartItemId: parseInt(productId),
  205. },
  206. });
  207. };
  208. //---------Update Cart Product Quantity--------//
  209. const [updateCartItem, { loading: isUpdateLoading }] = useMutation(
  210. UPDATE_CART_ITEM,
  211. {
  212. onCompleted: (response) => {
  213. const responseData = response?.createUpdateCartItem?.updateCartItem;
  214. if (isObject(responseData)) {
  215. const cartDetail = formatCartDetail(responseData);
  216. dispatch(addItem(cartDetail));
  217. } else {
  218. showToast("Something went wrong!", "warning");
  219. }
  220. },
  221. onError: (error) => {
  222. handleApolloBusinessError(error,(err) => {
  223. showToast(err.message, "danger");
  224. });
  225. },
  226. },
  227. );
  228. const onUpdateCart = async ({
  229. cartItemId,
  230. quantity,
  231. }: {
  232. cartItemId: number;
  233. quantity: number;
  234. }) => {
  235. if (quantity < 1) {
  236. showToast("Quantity must be at least 1", "warning");
  237. return;
  238. }
  239. await updateCartItem({
  240. variables: {
  241. cartItemId: cartItemId,
  242. quantity,
  243. },
  244. });
  245. };
  246. return {
  247. isCartLoading,
  248. onAddToCart,
  249. isRemoveLoading,
  250. onAddToRemove,
  251. onUpdateCart,
  252. isUpdateLoading,
  253. deleteProductFromCart,
  254. editProductQtyFromCart
  255. };
  256. };