This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { useGetFoods } from "@/app/[name]/(Main)/hooks/useMenuData";
|
||||
import { useGetProfile } from "@/app/[name]/(Profile)/profile/hooks/userProfileData";
|
||||
import Button from "@/components/button/PrimaryButton";
|
||||
import NotificationBellIcon from "@/components/icons/NotificationBellIcon";
|
||||
import MenuItem from "@/components/listview/MenuItem";
|
||||
@@ -17,49 +15,19 @@ import { useGetAbout } from "../about/hooks/useAboutData";
|
||||
import CartSkeleton from "./components/CartSkeleton";
|
||||
import CartSummary from "./components/CartSummary";
|
||||
import { useCart } from "./hook/useCart";
|
||||
import { useGetCartItems } from "./hooks/useCartData";
|
||||
import {
|
||||
cartItemsToFoods,
|
||||
guestCartItemsToFoods,
|
||||
hasCartEntries,
|
||||
} from "./lib/cartUtils";
|
||||
import { useCartPageState } from "./hooks/useCartPageState";
|
||||
|
||||
const CartPage = () => {
|
||||
const { t } = useTranslation("parallels", { keyPrefix: "Cart" });
|
||||
const router = useRouter();
|
||||
const { isSuccess } = useGetProfile();
|
||||
const { clearCart, items } = useCart();
|
||||
const { data: cartData, isPending: cartPending } = useGetCartItems(isSuccess);
|
||||
const { data: foodsResponse } = useGetFoods();
|
||||
const { clearCart } = useCart();
|
||||
const { data: aboutData } = useGetAbout();
|
||||
const { isLoading, isCartEmpty, hasItems, cartFoods } = useCartPageState();
|
||||
const [showClearConfirm, setShowClearConfirm] = React.useState(false);
|
||||
const [showPagerModal, setShowPagerModal] = React.useState(false);
|
||||
|
||||
const isPremium = aboutData?.data?.plan === "premium";
|
||||
|
||||
const cartFoods = React.useMemo(() => {
|
||||
const foods = foodsResponse?.data ?? [];
|
||||
if (isSuccess) {
|
||||
const apiItems = cartData?.data?.items ?? [];
|
||||
return cartItemsToFoods(apiItems, foods);
|
||||
}
|
||||
return guestCartItemsToFoods(items, foods);
|
||||
}, [isSuccess, cartData?.data?.items, items, foodsResponse?.data]);
|
||||
|
||||
const hasItems = React.useMemo(() => {
|
||||
if (isSuccess) {
|
||||
return (cartData?.data?.totalItems ?? 0) > 0 || hasCartEntries(items);
|
||||
}
|
||||
return hasCartEntries(items);
|
||||
}, [isSuccess, cartData?.data?.totalItems, items]);
|
||||
|
||||
const guestAwaitingMenu =
|
||||
!isSuccess && hasItems && cartFoods.length === 0 && !foodsResponse?.data;
|
||||
|
||||
const showInitialCartLoad = isSuccess && cartPending && !cartData && hasItems;
|
||||
|
||||
const isCartEmpty = !hasItems;
|
||||
|
||||
const handleClearCart = (e: React.MouseEvent) => {
|
||||
e?.preventDefault();
|
||||
e?.stopPropagation();
|
||||
@@ -76,7 +44,7 @@ const CartPage = () => {
|
||||
return (
|
||||
<div className="overflow-y-auto h-full noscrollbar flex flex-col bg-background">
|
||||
<div className="grid grid-cols-3 items-center mt-6">
|
||||
{isCartEmpty ? (
|
||||
{isCartEmpty || isLoading ? (
|
||||
<span></span>
|
||||
) : (
|
||||
<Trash
|
||||
@@ -98,7 +66,7 @@ const CartPage = () => {
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex-1 h-full">
|
||||
{showInitialCartLoad || guestAwaitingMenu ? (
|
||||
{isLoading ? (
|
||||
<CartSkeleton />
|
||||
) : isCartEmpty ? (
|
||||
<div className="flex flex-col items-center justify-center gap-4 h-full">
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
"use client";
|
||||
|
||||
import { useGetFoods } from "@/app/[name]/(Main)/hooks/useMenuData";
|
||||
import { useGetProfile } from "@/app/[name]/(Profile)/profile/hooks/userProfileData";
|
||||
import { hasAuthToken } from "@/lib/api/func";
|
||||
import { useReceiptStore } from "@/zustand/receiptStore";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
cartItemsToFoods,
|
||||
guestCartItemsToFoods,
|
||||
hasCartEntries,
|
||||
} from "../lib/cartUtils";
|
||||
import { useBulkCart, useGetCartItems } from "./useCartData";
|
||||
|
||||
function useReceiptHydrated() {
|
||||
const [hydrated, setHydrated] = useState(() =>
|
||||
useReceiptStore.persist.hasHydrated()
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const unsub = useReceiptStore.persist.onFinishHydration(() =>
|
||||
setHydrated(true)
|
||||
);
|
||||
setHydrated(useReceiptStore.persist.hasHydrated());
|
||||
return unsub;
|
||||
}, []);
|
||||
|
||||
return hydrated;
|
||||
}
|
||||
|
||||
export function useCartPageState() {
|
||||
const {
|
||||
isSuccess,
|
||||
isPending: profilePending,
|
||||
isFetched: profileFetched,
|
||||
} = useGetProfile();
|
||||
const localItems = useReceiptStore((state) => state.items);
|
||||
const clearLocalCart = useReceiptStore((state) => state.clear);
|
||||
const receiptHydrated = useReceiptHydrated();
|
||||
const [offlineSyncState, setOfflineSyncState] = useState<
|
||||
"idle" | "syncing" | "done"
|
||||
>("idle");
|
||||
|
||||
const {
|
||||
data: cartData,
|
||||
isPending: cartPending,
|
||||
isFetching: cartFetching,
|
||||
isFetched: cartFetched,
|
||||
} = useGetCartItems(isSuccess);
|
||||
const { mutate: mutateBulkCart, isPending: bulkSyncPending } = useBulkCart();
|
||||
const { data: foodsResponse, isPending: foodsPending } = useGetFoods();
|
||||
|
||||
const foods = foodsResponse?.data ?? [];
|
||||
const apiItems = cartData?.data?.items ?? [];
|
||||
const apiTotalItems = cartData?.data?.totalItems ?? 0;
|
||||
const apiHasItems =
|
||||
apiTotalItems > 0 || apiItems.some((item) => item.quantity > 0);
|
||||
const localHasItems = hasCartEntries(localItems);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!isSuccess ||
|
||||
!cartFetched ||
|
||||
apiHasItems ||
|
||||
!localHasItems ||
|
||||
offlineSyncState !== "idle" ||
|
||||
bulkSyncPending
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bulkItems = Object.entries(localItems)
|
||||
.filter(([, detail]) => detail?.quantity && detail.quantity > 0)
|
||||
.map(([foodId, detail]) => ({
|
||||
foodId: String(foodId),
|
||||
quantity: detail.quantity,
|
||||
}));
|
||||
|
||||
if (bulkItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOfflineSyncState("syncing");
|
||||
mutateBulkCart(
|
||||
{ items: bulkItems },
|
||||
{
|
||||
onSuccess: () => clearLocalCart(),
|
||||
onSettled: () => setOfflineSyncState("done"),
|
||||
}
|
||||
);
|
||||
}, [
|
||||
isSuccess,
|
||||
cartFetched,
|
||||
apiHasItems,
|
||||
localHasItems,
|
||||
localItems,
|
||||
bulkSyncPending,
|
||||
offlineSyncState,
|
||||
mutateBulkCart,
|
||||
clearLocalCart,
|
||||
]);
|
||||
|
||||
const cartFoods = useMemo(() => {
|
||||
if (isSuccess) {
|
||||
return cartItemsToFoods(apiItems, foods);
|
||||
}
|
||||
return guestCartItemsToFoods(localItems, foods);
|
||||
}, [isSuccess, apiItems, localItems, foods]);
|
||||
|
||||
const isAuthResolving =
|
||||
hasAuthToken() && (!profileFetched || profilePending);
|
||||
const isCartResolving = isSuccess && (!cartFetched || cartPending);
|
||||
const isCartRefetchingEmpty =
|
||||
isSuccess && cartFetched && cartFetching && !apiHasItems;
|
||||
const needsOfflineSync =
|
||||
isSuccess && cartFetched && localHasItems && !apiHasItems;
|
||||
const isOfflineSyncing = needsOfflineSync && offlineSyncState !== "done";
|
||||
const isGuestHydrating = !hasAuthToken() && !receiptHydrated;
|
||||
const guestAwaitingMenu =
|
||||
!isSuccess &&
|
||||
localHasItems &&
|
||||
cartFoods.length === 0 &&
|
||||
(foodsPending || foods.length === 0);
|
||||
|
||||
const isLoading =
|
||||
isAuthResolving ||
|
||||
isCartResolving ||
|
||||
isCartRefetchingEmpty ||
|
||||
isOfflineSyncing ||
|
||||
isGuestHydrating ||
|
||||
guestAwaitingMenu;
|
||||
|
||||
const hasItems = isSuccess ? apiHasItems : localHasItems;
|
||||
const isCartEmpty = !isLoading && !hasItems;
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
isCartEmpty,
|
||||
hasItems,
|
||||
cartFoods,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user