Files
dmenu-plus-front/src/app/[name]/(Main)/cart/hooks/useCartPageState.ts
T
hamid zarghami 59e446b66e
Build and Deploy Docker Images / build_and_deploy (push) Has been cancelled
fix empty cart
2026-06-15 15:46:21 +03:30

143 lines
3.8 KiB
TypeScript

"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,
};
}