Files
web-menu-react-version-/src/pages/menu/page.tsx

276 lines
10 KiB
TypeScript

import { StarFilled } from "@ant-design/icons";
import { Button, Image, Select, Space } from "antd";
import { FloatingButton } from "components/FloatingButton/FloatingButton";
import LogoContainerIcon from "components/Icons/LogoContainerIcon";
import ImageWithFallback from "components/ImageWithFallback";
import LoyaltyCard from "components/LoyaltyCard/LoyaltyCard";
import ProText from "components/ProText";
import { useScrollHandler } from "contexts/ScrollHandlerContext";
import useBreakPoint from "hooks/useBreakPoint";
import { useRestaurant } from "hooks/useRestaurant";
import { OrderType } from "pages/checkout/hooks/types.ts";
import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
import {
useGetMenuQuery,
useGetRestaurantDetailsQuery,
} from "redux/api/others";
import { useAppSelector } from "redux/hooks";
import { default_image } from "utils/constants";
import { enumToSelectOptions } from "utils/helpers/helperFunctions.ts";
import BackButton from "./components/BackButton";
import { CartButton } from "./components/CartButton/CartButton";
import { CategoriesList } from "./components/CategoriesList/CategoriesList";
import LocalStorageHandler from "./components/LocalStorageHandler";
import { MenuFooter } from "./components/MenuFooter/MenuFooter";
import { MenuList } from "./components/MenuList/MenuList";
import MenuSkeleton from "./components/MenuSkeleton/MenuSkeleton";
import ScrollEventHandler from "./components/ScrollEventHandler";
import SearchButton from "./components/SearchButton";
import styles from "./menu.module.css";
import NextIcon from "components/Icons/NextIcon";
import { OpeningTimesBottomSheet } from "components/CustomBottomSheet/OpeningTimesBottomSheet";
import { useEffect, useState } from "react";
import BackIcon from "components/Icons/BackIcon";
import { OrderTypesBottomSheet } from "components/CustomBottomSheet/OrderTypesBottomSheet";
import { useNavigate } from "react-router-dom";
function MenuPage() {
const { subdomain } = useParams();
const navigate = useNavigate();
const { isRTL } = useAppSelector((state) => state.locale);
const { orderType } = useAppSelector((state) => state.order);
const { token } = useAppSelector((state) => state.auth);
const { t } = useTranslation();
const { data: restaurant, isLoading: isLoadingRestaurant } =
useGetRestaurantDetailsQuery(subdomain, {
skip: !subdomain,
});
const { data: menuData, isLoading: isLoadingMenu } = useGetMenuQuery(
restaurant?.restautantId,
{
skip: !restaurant?.restautantId,
},
);
const { categoryRefs } = useScrollHandler();
const { isMobile, isTablet, isDesktop } = useBreakPoint();
const isLoading = isLoadingRestaurant || isLoadingMenu;
const [isOpeningTimesOpen, setIsOpeningTimesOpen] = useState(false);
const [isOrderTypesOpen, setIsOrderTypesOpen] = useState(false);
const orderTypeOptions = enumToSelectOptions(OrderType, t, "orderTypes");
// Automatically load restaurant taxes when restaurant data is available
useRestaurant(restaurant);
// Handle browser back button with same logic as BackButton
useEffect(() => {
if (!token || !subdomain) {
return; // No custom route needed, allow normal back navigation
}
// Add a history entry to intercept back navigation
window.history.pushState(null, "", window.location.href);
const handlePopState = () => {
// If token exists, navigate to subdomain instead of going back
navigate(`/${subdomain}`, { replace: true });
};
window.addEventListener("popstate", handlePopState);
return () => {
window.removeEventListener("popstate", handlePopState);
};
}, [token, subdomain, navigate]);
return (
<>
<LocalStorageHandler restaurantID={restaurant?.restautantId || ""} />
{isLoading ? (
<MenuSkeleton categoryCount={10} itemCount={30} variant="default" />
) : (
<div className={styles.menuContainer}>
<div className={styles.restaurantHeader}>
<ImageWithFallback
src={restaurant?.restaurant_cover}
fallbackSrc={default_image}
alt={t("menu.restaurantCover")}
className={styles.cover}
width={"100%"}
height={isMobile ? 182 : isTablet ? 200 : 220}
preview={false}
loadingContainerStyle={{
width: "100vw",
}}
/>
<Image
src={restaurant?.restaurant_logo}
alt={t("menu.restaurantLogo")}
className={styles.logo}
width={"100%"}
preview={false}
/>
<LogoContainerIcon className={styles.logoContainerIcon} />
<div
className={`${styles.headerFloatingBtn} ${styles.backButtonContainer}`}
>
<BackButton
{...(token ? { customRoute: `/${subdomain}` } : {})}
/>
</div>
<div
className={`${styles.headerFloatingBtn} ${styles.orderTypeSelectContainer} order-type-select-container`}
>
{orderType !== OrderType.Redeem && (
<div className={styles.frameSelect}>
<div className={styles.divSelect}>
<Select
value={orderType}
options={orderTypeOptions}
open={false}
onOpenChange={() => false}
onClick={(e) => {
e.stopPropagation();
setIsOrderTypesOpen(true);
}}
variant="borderless"
size="small"
className={styles.orderTypeSelect}
classNames={{
popup: { root: "order-type-select-dropdown" },
}}
listHeight={150}
/>
</div>
</div>
)}
{orderType === OrderType.Redeem && (
<div className={styles.frame}>
<div className={styles.div}>
<div className={styles.pickup}>{t("menu.balance")}</div>
<div className={styles.elementMin}>
60{" "}
{isRTL
? restaurant?.local_currency
: restaurant?.global_currency}
</div>
</div>
</div>
)}
</div>
<SearchButton />
</div>
<div className={`${styles.headerContainer}`}>
<div className={styles.contentWrapper}>
<div className={styles.restaurantHeaderTitleContainer}>
<ProText className={styles.restaurantTitle}>
{isRTL ? restaurant?.nameAR : restaurant?.restautantName}
</ProText>
<Button
className={
restaurant?.isOpened
? styles.openButton
: styles.closeButton
}
icon={
!isRTL ? (
<NextIcon
iconColor={restaurant?.isOpened ? "#278655" : "#DD4143"}
iconSize={9}
/>
) : (
<BackIcon
iconColor={restaurant?.isOpened ? "#278655" : "#DD4143"}
iconSize={9}
/>
)
}
iconPlacement="end"
onClick={() => setIsOpeningTimesOpen(true)}
>
{restaurant?.isOpened ? t("menu.open") : t("menu.close")}
</Button>
</div>
<div className={styles.ratingContainer}>
<StarFilled className={styles.ratingStar} />
<ProText className={styles.ratingScore}>4.5</ProText>
<ProText className={styles.ratingCount}>(2567) </ProText>
<ProText
className={`${styles.itemDescription} ${styles.restaurantDescription} responsive-text`}
>
{isRTL ? restaurant?.descriptionAR : restaurant?.description}
</ProText>
</div>
{/* <ProText className={styles.openingHours}>
<TimeIcon className={styles.timeIcon} />
{restaurant?.openingTime} - {restaurant?.closingTime}
</ProText> */}
</div>
</div>
<Button
style={{
height: 32,
borderRadius: 6,
paddingTop: 6,
paddingRight: 16,
paddingBottom: 6,
paddingLeft: 16,
gap: 8,
opacity: 1,
margin: 16,
backgroundColor: "#EBEBEC",
border: "none",
}}
>
<ProText
style={{
fontWeight: 500,
fontStyle: "Medium",
fontSize: 14,
lineHeight: "140%",
letterSpacing: "0%",
color: "#09237D",
}}
>
{t("menu.callWaiter")}
</ProText>
</Button>
<div className={`${styles.pageContainer}`}>
<Space orientation="vertical" style={{ width: "100%" }}>
<div>
{restaurant?.loyalty_stamps &&
restaurant?.is_loyalty_enabled && <LoyaltyCard />}
<CategoriesList categories={menuData?.categories || []} />
</div>
<MenuList data={menuData} categoryRefs={categoryRefs} />
</Space>
</div>
{restaurant && restaurant.isOpened && <MenuFooter />}
<ScrollEventHandler />
<FloatingButton />
{isDesktop && <CartButton />}
<OpeningTimesBottomSheet
isOpen={isOpeningTimesOpen}
onClose={() => setIsOpeningTimesOpen(false)}
/>
<OrderTypesBottomSheet
isOpen={isOrderTypesOpen}
onClose={() => setIsOrderTypesOpen(false)}
/>
</div>
)}
</>
);
}
export default MenuPage;