This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
"use client";
|
"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 Button from "@/components/button/PrimaryButton";
|
||||||
import NotificationBellIcon from "@/components/icons/NotificationBellIcon";
|
import NotificationBellIcon from "@/components/icons/NotificationBellIcon";
|
||||||
import MenuItem from "@/components/listview/MenuItem";
|
import MenuItem from "@/components/listview/MenuItem";
|
||||||
@@ -17,49 +15,19 @@ import { useGetAbout } from "../about/hooks/useAboutData";
|
|||||||
import CartSkeleton from "./components/CartSkeleton";
|
import CartSkeleton from "./components/CartSkeleton";
|
||||||
import CartSummary from "./components/CartSummary";
|
import CartSummary from "./components/CartSummary";
|
||||||
import { useCart } from "./hook/useCart";
|
import { useCart } from "./hook/useCart";
|
||||||
import { useGetCartItems } from "./hooks/useCartData";
|
import { useCartPageState } from "./hooks/useCartPageState";
|
||||||
import {
|
|
||||||
cartItemsToFoods,
|
|
||||||
guestCartItemsToFoods,
|
|
||||||
hasCartEntries,
|
|
||||||
} from "./lib/cartUtils";
|
|
||||||
|
|
||||||
const CartPage = () => {
|
const CartPage = () => {
|
||||||
const { t } = useTranslation("parallels", { keyPrefix: "Cart" });
|
const { t } = useTranslation("parallels", { keyPrefix: "Cart" });
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { isSuccess } = useGetProfile();
|
const { clearCart } = useCart();
|
||||||
const { clearCart, items } = useCart();
|
|
||||||
const { data: cartData, isPending: cartPending } = useGetCartItems(isSuccess);
|
|
||||||
const { data: foodsResponse } = useGetFoods();
|
|
||||||
const { data: aboutData } = useGetAbout();
|
const { data: aboutData } = useGetAbout();
|
||||||
|
const { isLoading, isCartEmpty, hasItems, cartFoods } = useCartPageState();
|
||||||
const [showClearConfirm, setShowClearConfirm] = React.useState(false);
|
const [showClearConfirm, setShowClearConfirm] = React.useState(false);
|
||||||
const [showPagerModal, setShowPagerModal] = React.useState(false);
|
const [showPagerModal, setShowPagerModal] = React.useState(false);
|
||||||
|
|
||||||
const isPremium = aboutData?.data?.plan === "premium";
|
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) => {
|
const handleClearCart = (e: React.MouseEvent) => {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
@@ -76,7 +44,7 @@ const CartPage = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="overflow-y-auto h-full noscrollbar flex flex-col bg-background">
|
<div className="overflow-y-auto h-full noscrollbar flex flex-col bg-background">
|
||||||
<div className="grid grid-cols-3 items-center mt-6">
|
<div className="grid grid-cols-3 items-center mt-6">
|
||||||
{isCartEmpty ? (
|
{isCartEmpty || isLoading ? (
|
||||||
<span></span>
|
<span></span>
|
||||||
) : (
|
) : (
|
||||||
<Trash
|
<Trash
|
||||||
@@ -98,7 +66,7 @@ const CartPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 flex-1 h-full">
|
<div className="mt-8 flex-1 h-full">
|
||||||
{showInitialCartLoad || guestAwaitingMenu ? (
|
{isLoading ? (
|
||||||
<CartSkeleton />
|
<CartSkeleton />
|
||||||
) : isCartEmpty ? (
|
) : isCartEmpty ? (
|
||||||
<div className="flex flex-col items-center justify-center gap-4 h-full">
|
<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