menu: on click on prduct open details as BS, also add redirect button to detials page if user click on redirect button

This commit is contained in:
2025-12-23 01:36:46 +03:00
parent fc1a75bc4b
commit 5c27303695
16 changed files with 224 additions and 78 deletions

View File

@@ -253,6 +253,8 @@ export function ProBottomSheet({
border: `1px solid ${themeName === "dark" ? "#424242" : "transparent"}`, border: `1px solid ${themeName === "dark" ? "#424242" : "transparent"}`,
transition: "all 0.3s ease", transition: "all 0.3s ease",
cursor: "pointer", cursor: "pointer",
width: 30,
height: 30,
}; };
const titleStyle: React.CSSProperties = { const titleStyle: React.CSSProperties = {

View File

@@ -6,6 +6,8 @@ import { useAppSelector } from "redux/hooks";
import { ProBlack2 } from "ThemeConstants"; import { ProBlack2 } from "ThemeConstants";
import ProTitle from "../ProTitle"; import ProTitle from "../ProTitle";
import useBreakPoint from "hooks/useBreakPoint"; import useBreakPoint from "hooks/useBreakPoint";
import NextIcon from "components/Icons/NextIcon";
const { Text } = Typography; const { Text } = Typography;
interface ProHeaderProps { interface ProHeaderProps {
@@ -41,7 +43,6 @@ const ProHeader: FunctionComponent<ProHeaderProps> = ({
top: 0, top: 0,
display: "flex", display: "flex",
zIndex: 1000, zIndex: 1000,
flexDirection: isRTL ? "row-reverse" : "row",
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "center", // This centers vertically alignItems: "center", // This centers vertically
backgroundColor: themeName === "light" ? "white" : ProBlack2, backgroundColor: themeName === "light" ? "white" : ProBlack2,
@@ -52,10 +53,9 @@ const ProHeader: FunctionComponent<ProHeaderProps> = ({
> >
<Button <Button
type="text" type="text"
icon={<BackIcon />} icon={isRTL ? <NextIcon /> : <BackIcon />}
style={{ style={{
flex: 0, // Don't allow this to grow flex: 0, // Don't allow this to grow
marginRight: "auto", // Push it to the start
}} }}
onClick={handleBack} onClick={handleBack}
/> />

View File

@@ -1,23 +1,17 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import { Card, Divider, Form, Space, Layout } from "antd"; import { Card, Divider, Space, Layout } from "antd";
import ArabicPrice from "components/ArabicPrice"; import ArabicPrice from "components/ArabicPrice";
import CartActionsButtons from "components/CartActionsButtons/CartActionsButtons.tsx"; import CartActionsButtons from "components/CartActionsButtons/CartActionsButtons.tsx";
import ImageWithFallback from "components/ImageWithFallback"; import ImageWithFallback from "components/ImageWithFallback";
import ProHeader from "components/ProHeader/ProHeader.tsx"; import ProHeader from "components/ProHeader/ProHeader.tsx";
import ProInputCard from "components/ProInputCard/ProInputCard.tsx";
import ProRatioGroups from "components/ProRatioGroups/ProRatioGroups.tsx";
import ProText from "components/ProText.tsx"; import ProText from "components/ProText.tsx";
import ProTitle from "components/ProTitle.tsx"; import ProTitle from "components/ProTitle.tsx";
import { import { selectCart } from "features/order/orderSlice.ts";
selectCart,
updateCollectionMethod,
} from "features/order/orderSlice.ts";
import styles from "pages/cart/cart.module.css"; import styles from "pages/cart/cart.module.css";
import YouMightAlsoLike from "pages/cart/components/youMayLike/YouMightAlsoLike.tsx"; import YouMightAlsoLike from "pages/cart/components/youMayLike/YouMightAlsoLike.tsx";
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import { colors } from "ThemeConstants.ts"; import { colors } from "ThemeConstants.ts";
import OrderSummary from "components/OrderSummary/OrderSummary.tsx";
import { useAppSelector } from "redux/hooks.ts"; import { useAppSelector } from "redux/hooks.ts";
import { FormInstance } from "antd"; import { FormInstance } from "antd";
@@ -25,7 +19,6 @@ import useBreakPoint from "hooks/useBreakPoint.ts";
import CarPlateCard from "pages/cart/components/CarPlateCard.tsx"; import CarPlateCard from "pages/cart/components/CarPlateCard.tsx";
import CartFooter from "pages/cart/components/cartFooter/CartFooter.tsx"; import CartFooter from "pages/cart/components/cartFooter/CartFooter.tsx";
import CouponCard from "pages/cart/components/CouponCard.tsx"; import CouponCard from "pages/cart/components/CouponCard.tsx";
import RewardWaiterCard from "pages/cart/components/RewardWaiterCard.tsx";
import SpecialRequestCard from "pages/cart/components/specialRequest/SpecialRequestCard.tsx"; import SpecialRequestCard from "pages/cart/components/specialRequest/SpecialRequestCard.tsx";
import TableNumberCard from "pages/cart/components/TableNumberCard.tsx"; import TableNumberCard from "pages/cart/components/TableNumberCard.tsx";
import TimeEstimateCard from "pages/cart/components/timeEstimate/TimeEstimateCard.tsx"; import TimeEstimateCard from "pages/cart/components/timeEstimate/TimeEstimateCard.tsx";
@@ -41,7 +34,7 @@ export default function CartMobileTabletLayout({
form, form,
}: CartMobileTabletLayoutProps) { }: CartMobileTabletLayoutProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const { items, collectionMethod, orderType } = useAppSelector(selectCart); const { items, orderType } = useAppSelector(selectCart);
const { isRTL } = useAppSelector((state) => state.locale); const { isRTL } = useAppSelector((state) => state.locale);
const { subdomain } = useParams(); const { subdomain } = useParams();
const { pickup_type } = useAppSelector((state) => state.order.restaurant); const { pickup_type } = useAppSelector((state) => state.order.restaurant);
@@ -52,8 +45,8 @@ export default function CartMobileTabletLayout({
const getMenuItemImageStyle = () => { const getMenuItemImageStyle = () => {
if (isMobile) { if (isMobile) {
return { return {
width: 90, width: 115,
height: 80, height: 96,
}; };
} }
return { return {
@@ -73,6 +66,10 @@ export default function CartMobileTabletLayout({
size={isMobile ? "middle" : isTablet ? "large" : "large"} size={isMobile ? "middle" : isTablet ? "large" : "large"}
style={{ width: "100%", gap: 16 }} style={{ width: "100%", gap: 16 }}
> >
{/* Table Number */}
{(orderType === OrderType.DineIn ||
orderType === OrderType.ToOffice) && <TableNumberCard />}
<div className={`${styles.cartContent} ${getResponsiveClass()}`}> <div className={`${styles.cartContent} ${getResponsiveClass()}`}>
<div className={styles.cartItems}> <div className={styles.cartItems}>
<Card hoverable className={styles.cartItem}> <Card hoverable className={styles.cartItem}>
@@ -170,7 +167,7 @@ export default function CartMobileTabletLayout({
style={{ style={{
margin: 0, margin: 0,
lineClamp: 1, lineClamp: 1,
padding: isMobile ? 3 : isTablet ? 8 : 10, padding: isMobile ? "3px 0" : isTablet ? 8 : 10,
fontSize: isMobile ? 14 : isTablet ? 18 : 20, fontSize: isMobile ? 14 : isTablet ? 18 : 20,
display: "-webkit-box", display: "-webkit-box",
WebkitBoxOrient: "vertical", WebkitBoxOrient: "vertical",
@@ -187,7 +184,7 @@ export default function CartMobileTabletLayout({
: "6.8em", : "6.8em",
fontWeight: 500, fontWeight: 500,
letterSpacing: "0.01em", letterSpacing: "0.01em",
width: "65%", width: "55%",
}} }}
> >
{item.description} {item.description}
@@ -196,7 +193,7 @@ export default function CartMobileTabletLayout({
<div <div
style={{ style={{
position: "absolute", position: "absolute",
bottom: index !== items.length - 1 ? 16 : 6, bottom: index !== items.length - 1 ? 16 : 3,
[isRTL ? "right" : "left"]: 0, [isRTL ? "right" : "left"]: 0,
}} }}
> >
@@ -218,7 +215,9 @@ export default function CartMobileTabletLayout({
/> />
<div <div
style={{ style={{
marginTop: 10, position: "absolute",
right: 3,
bottom: 3,
}} }}
> >
<CartActionsButtons item={item} /> <CartActionsButtons item={item} />
@@ -239,8 +238,6 @@ export default function CartMobileTabletLayout({
<SpecialRequestCard /> <SpecialRequestCard />
<CouponCard />
{/* Car Plate*/} {/* Car Plate*/}
{((orderType === OrderType.Pickup && pickup_type === "car") || {((orderType === OrderType.Pickup && pickup_type === "car") ||
orderType === OrderType.ScheduledOrder) && <CarPlateCard />} orderType === OrderType.ScheduledOrder) && <CarPlateCard />}
@@ -250,7 +247,7 @@ export default function CartMobileTabletLayout({
orderType === OrderType.ScheduledOrder) && <TimeEstimateCard />} orderType === OrderType.ScheduledOrder) && <TimeEstimateCard />}
{/* Collection Method */} {/* Collection Method */}
{orderType === OrderType.Pickup && ( {/* {orderType === OrderType.Pickup && (
<ProInputCard title={t("cart.collectionMethod")}> <ProInputCard title={t("cart.collectionMethod")}>
<Form.Item <Form.Item
name="collectionMethod" name="collectionMethod"
@@ -282,17 +279,13 @@ export default function CartMobileTabletLayout({
/> />
</Form.Item> </Form.Item>
</ProInputCard> </ProInputCard>
)} )} */}
{/* Reward Your Waiter */} {/* Reward Your Waiter */}
<RewardWaiterCard /> {/* <RewardWaiterCard /> */}
{/* Table Number */}
{(orderType === OrderType.DineIn ||
orderType === OrderType.ToOffice) && <TableNumberCard />}
{/* Invoice Summary */} {/* Invoice Summary */}
<OrderSummary /> {/* <OrderSummary /> */}
</Space> </Space>
</Layout.Content> </Layout.Content>
<CartFooter form={form} /> <CartFooter form={form} />

View File

@@ -6,9 +6,10 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: flex-start;
overflow-x: hidden; overflow-x: auto;
padding: 0; padding: 0;
scroll-behavior: smooth; scroll-behavior: smooth;
scrollbar-width: none;
} }
:global(.darkApp) .youMightAlsoLikeContainer path { :global(.darkApp) .youMightAlsoLikeContainer path {
@@ -56,7 +57,7 @@
outline-offset: 4px; outline-offset: 4px;
} }
.youMightAlsoLikeContainer { .youMightAlsoLikeContainer {
height: 130px !important; height: 150px !important;
} }
.itemDescriptionIconsContainer { .itemDescriptionIconsContainer {

View File

@@ -191,19 +191,19 @@ export default function YouMightAlsoLike() {
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
justifyContent: "space-between", justifyContent: "space-between",
width: isMobile ? "95px" : isTablet ? "120px" : "140px", width: isMobile ? "120px" : isTablet ? "120px" : "140px",
position: "relative", position: "relative",
overflow: "hidden", overflow: "hidden",
}} }}
> >
<div <div
style={{ style={{
width: isMobile ? 18 : 24, width: isMobile ? 28 : 24,
height: isMobile ? 18 : 24, height: isMobile ? 28 : 24,
borderRadius: "50%", borderRadius: "50%",
top: isMobile ? 50 : isTablet ? 60 : 80, top: isMobile ? 65 : isTablet ? 60 : 80,
position: "absolute", position: "absolute",
[isRTL ? "left" : "right"]: isMobile ? 15 : 20, [isRTL ? "left" : "right"]: isMobile ? 10 : 20,
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
justifyContent: "center", justifyContent: "center",
@@ -234,8 +234,8 @@ export default function YouMightAlsoLike() {
? styles.popularMenuItemImageTablet ? styles.popularMenuItemImageTablet
: styles.popularMenuItemImageDesktop : styles.popularMenuItemImageDesktop
}`} }`}
width={isMobile ? 73 : isTablet ? 90 : 110} width={isMobile ? 106 : isTablet ? 90 : 110}
height={isMobile ? 73 : isTablet ? 90 : 110} height={isMobile ? 96 : isTablet ? 90 : 110}
fallbackSrc={default_image} fallbackSrc={default_image}
/> />

View File

@@ -3,7 +3,7 @@ import InputCard from "components/InputCard";
import OrderSummary from "components/OrderSummary/OrderSummary"; import OrderSummary from "components/OrderSummary/OrderSummary";
import PaymentMethods from "components/PaymentMethods/PaymentMethods"; import PaymentMethods from "components/PaymentMethods/PaymentMethods";
import ProHeader from "components/ProHeader/ProHeader"; import ProHeader from "components/ProHeader/ProHeader";
import { selectCart } from "features/order/orderSlice"; import { selectCart, updateCollectionMethod } from "features/order/orderSlice";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useAppSelector } from "redux/hooks"; import { useAppSelector } from "redux/hooks";
import styles from "../address/address.module.css"; import styles from "../address/address.module.css";
@@ -13,11 +13,16 @@ import CheckoutButton from "./components/CheckoutButton";
import { GiftCard } from "./components/GiftCard"; import { GiftCard } from "./components/GiftCard";
import PhoneCard from "./components/phoneCard"; import PhoneCard from "./components/phoneCard";
import { OrderType } from "./hooks/types"; import { OrderType } from "./hooks/types";
import RewardWaiterCard from "pages/cart/components/RewardWaiterCard";
import ProInputCard from "components/ProInputCard/ProInputCard";
import ProRatioGroups from "components/ProRatioGroups/ProRatioGroups";
import CouponCard from "pages/cart/components/CouponCard";
export default function CheckoutPage() { export default function CheckoutPage() {
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const { phone, order, orderType } = useAppSelector(selectCart); const { phone, order, orderType, collectionMethod } =
useAppSelector(selectCart);
const { token } = useAppSelector((state) => state.auth); const { token } = useAppSelector((state) => state.auth);
return ( return (
<> <>
@@ -53,8 +58,47 @@ export default function CheckoutPage() {
{/* <RoomDetails /> {/* <RoomDetails />
<OfficeDetails /> */} <OfficeDetails /> */}
{/* <GiftDetails /> */} {/* <GiftDetails /> */}
<BriefMenu /> {/* <BriefMenu /> */}
<PaymentMethods /> <PaymentMethods />
<CouponCard />
{/* Collection Method */}
{orderType === OrderType.Pickup && (
<ProInputCard title={t("cart.collectionMethod")}>
<Form.Item
name="collectionMethod"
required
rules={[
{
required: true,
message: t("cart.pleaseSelectCollectionMethod"),
},
]}
>
<ProRatioGroups
options={[
{ label: t("cart.Cash"), value: "cod", price: "" },
{
label: t("cart.e-payment"),
value: "paymentgateway",
price: "",
},
]}
value={collectionMethod}
onRatioClick={(value) => {
if (value === "cod") {
updateCollectionMethod(value);
} else {
updateCollectionMethod(value);
}
}}
/>
</Form.Item>
</ProInputCard>
)}
{/* Reward Your Waiter */}
<RewardWaiterCard />
<OrderSummary /> <OrderSummary />
</Layout.Content> </Layout.Content>

View File

@@ -85,10 +85,6 @@ export function AddToCartButton({ item }: { item: Product }) {
message.warning(t("menu.restaurantIsClosed")); message.warning(t("menu.restaurantIsClosed"));
return; return;
} }
if (restaurant && !restaurant.isOpened) {
message.warning(t("menu.restaurantIsClosed"));
return;
}
if (basicCartItem && basicCartItem.uniqueId) { if (basicCartItem && basicCartItem.uniqueId) {
if (basicCartItem.quantity > 1) { if (basicCartItem.quantity > 1) {

View File

@@ -1,6 +1,7 @@
import { Button } from "antd"; import { Button } from "antd";
import BackIcon from "components/Icons/BackIcon"; import BackIcon from "components/Icons/BackIcon";
import NextIcon from "components/Icons/NextIcon";
import { useAppSelector } from "redux/hooks";
interface BackButtonProps { interface BackButtonProps {
navigateBack?: boolean; // true = use router.back(), false = just clear state navigateBack?: boolean; // true = use router.back(), false = just clear state
@@ -10,6 +11,7 @@ export default function BackButton({ navigateBack = true }: BackButtonProps) {
const handleBack = () => { const handleBack = () => {
if (navigateBack) window.history.back(); if (navigateBack) window.history.back();
}; };
const { isRTL } = useAppSelector((state) => state.locale);
return ( return (
<Button <Button
@@ -23,8 +25,14 @@ export default function BackButton({ navigateBack = true }: BackButtonProps) {
borderRadius: "50%", borderRadius: "50%",
}} }}
icon={ icon={
<div style={{ position: "relative", top: 2.5, right: 1 }}> <div
<BackIcon /> style={{
position: "relative",
top: 2.5,
[isRTL ? "left" : "right"]: 1,
}}
>
{isRTL ? <NextIcon /> : <BackIcon />}
</div> </div>
} }
onClick={handleBack} onClick={handleBack}

View File

@@ -6,6 +6,8 @@ import { useAppSelector } from "redux/hooks";
import { Product } from "utils/types/appTypes"; import { Product } from "utils/types/appTypes";
import styles from "./MenuList.module.css"; import styles from "./MenuList.module.css";
import ProductCard from "pages/menu/components/MenuList/ProductCard.tsx"; import ProductCard from "pages/menu/components/MenuList/ProductCard.tsx";
import { ProductBottomSheet } from "pages/product/components/ProductBottomSheet";
import { useState } from "react";
interface MenuListProps { interface MenuListProps {
data: data:
@@ -20,6 +22,7 @@ interface MenuListProps {
export function MenuList({ data, categoryRefs }: MenuListProps) { export function MenuList({ data, categoryRefs }: MenuListProps) {
const { isRTL } = useAppSelector((state) => state.locale); const { isRTL } = useAppSelector((state) => state.locale);
const products = data?.products; const products = data?.products;
const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
const { themeName } = useAppSelector((state) => state.theme); const { themeName } = useAppSelector((state) => state.theme);
@@ -80,12 +83,20 @@ export function MenuList({ data, categoryRefs }: MenuListProps) {
</ProTitle> </ProTitle>
<div className={styles.menuItemsGrid}> <div className={styles.menuItemsGrid}>
{categoryProducts.map((item: Product) => ( {categoryProducts.map((item: Product) => (
<ProductCard item={item} key={item.id} /> <ProductCard
item={item}
key={item.id}
setIsBottomSheetOpen={setIsBottomSheetOpen}
/>
))} ))}
</div> </div>
</div> </div>
); );
})} })}
<ProductBottomSheet
isOpen={isBottomSheetOpen}
onClose={() => setIsBottomSheetOpen(false)}
/>
</div> </div>
</> </>
); );

View File

@@ -11,24 +11,25 @@ import { useAppSelector } from "redux/hooks.ts";
import { ProductPreviewDialog } from "pages/menu/components/ProductPreviewDialog"; import { ProductPreviewDialog } from "pages/menu/components/ProductPreviewDialog";
import { useState } from "react"; import { useState } from "react";
import { AddToCartButton } from "../AddToCartButton/AddToCartButton"; import { AddToCartButton } from "../AddToCartButton/AddToCartButton";
import { useNavigate, useParams } from "react-router-dom";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
type Props = { type Props = {
item: Product; item: Product;
setIsBottomSheetOpen: (isOpen: boolean) => void;
}; };
export default function ProductCard({ item }: Props) { export default function ProductCard({ item, setIsBottomSheetOpen }: Props) {
const { isRTL } = useAppSelector((state) => state.locale); const { isRTL } = useAppSelector((state) => state.locale);
const { isMobile, isTablet, isDesktop } = useBreakPoint(); const { isMobile, isTablet, isDesktop } = useBreakPoint();
const { items } = useAppSelector((state) => state.order); const { items } = useAppSelector((state) => state.order);
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
const navigate = useNavigate(); // const navigate = useNavigate();
const { subdomain } = useParams(); // const { subdomain } = useParams();
const { t } = useTranslation(); const { t } = useTranslation();
// Handle dialog close // Handle dialog close
const handleDialogClose = () => { const handleDialogClose = () => {
setIsDialogOpen(false); setIsDialogOpen(false);
setIsBottomSheetOpen(false);
}; };
// Handle product click - open dialog on desktop, navigate on mobile/tablet // Handle product click - open dialog on desktop, navigate on mobile/tablet
@@ -36,9 +37,11 @@ export default function ProductCard({ item }: Props) {
localStorage.setItem("productId", item.id.toString()); localStorage.setItem("productId", item.id.toString());
if (isDesktop) { if (isDesktop) {
setIsDialogOpen(true); setIsDialogOpen(true);
} else {
navigate(`/${subdomain}/product/${item.id}`);
} }
setIsBottomSheetOpen(true);
// else {
// navigate(`/${subdomain}/product/${item.id}`);
// }
}; };
return ( return (

View File

@@ -45,10 +45,9 @@
gap: 5px; gap: 5px;
} }
.closeButton { .closeButton {
background: #DD41434d; background: #dd41434d;
color: #DD4143; color: #dd4143;
width: 62px !important; width: 62px !important;
height: 20px !important; height: 20px !important;
border: none; border: none;
@@ -503,11 +502,6 @@
color: #fff !important; color: #fff !important;
} }
.searchButtonContainer {
right: 20px;
border-radius: 50%;
}
.searchButton { .searchButton {
width: 32px !important; width: 32px !important;
height: 32px !important; height: 32px !important;
@@ -575,19 +569,24 @@
color: #666; color: #666;
} }
.searchButtonContainer {
right: 20px;
border-radius: 50%;
}
/* RTL support for back button */ /* RTL support for back button */
.backButtonContainer[dir="rtl"] { :global(.ant-app-rtl) .backButtonContainer {
left: auto; left: auto;
right: 20px; right: 20px;
} }
/* RTL support for search button */ /* RTL support for search button */
.searchButtonContainer[dir="rtl"] { :global(.ant-app-rtl) .searchButtonContainer {
right: auto;
left: 20px; left: 20px;
right: auto;
} }
:global(.rtl) .dineInIcon { :global(.ant-app-rtl) .dineInIcon {
margin-bottom: 1px; margin-bottom: 1px;
} }

View File

@@ -165,7 +165,7 @@ function MenuPage() {
</div> </div>
<div className={`${styles.pageContainer}`}> <div className={`${styles.pageContainer}`}>
<Space direction="vertical" style={{ width: "100%" }}> <Space orientation="vertical" style={{ width: "100%" }}>
<div> <div>
{restaurant?.loyalty_stamps && {restaurant?.loyalty_stamps &&
restaurant?.is_loyalty_enabled && <LoyaltyCard />} restaurant?.is_loyalty_enabled && <LoyaltyCard />}

View File

@@ -0,0 +1,68 @@
import { ProBottomSheet } from "components/ProBottomSheet/ProBottomSheet.tsx";
import ProductDetailPage from "../page";
import { Button } from "antd";
import { UploadOutlined } from "@ant-design/icons";
import { useAppSelector } from "redux/hooks";
import { useNavigate, useParams } from "react-router-dom";
interface ProductBottomSheetProps {
isOpen: boolean;
onClose: () => void;
}
export function ProductBottomSheet({
isOpen,
onClose,
}: ProductBottomSheetProps) {
const { themeName } = useAppSelector((state) => state.theme);
const { isRTL } = useAppSelector((state) => state.locale);
const { subdomain } = useParams();
const closeButtonStyle: React.CSSProperties = {
color: themeName === "dark" ? "#ffffff" : "#4D5154",
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: 8,
borderRadius: "50%",
backgroundColor: themeName === "dark" ? "rgba(54, 54, 54, 0.8)" : "#EDEEEE",
border: `1px solid ${themeName === "dark" ? "#424242" : "transparent"}`,
transition: "all 0.3s ease",
cursor: "pointer",
width: 30,
height: 30,
position: "absolute",
top: 40,
[isRTL ? "right" : "left"]: 16,
zIndex: 1000,
};
const navigate = useNavigate();
const productId = localStorage.getItem("productId");
const redirectToProductPage = () => {
if (productId) {
navigate(`/${subdomain}/product/${productId}`);
onClose();
}
};
return (
<ProBottomSheet
isOpen={isOpen}
onClose={onClose}
showCloseButton={true}
initialSnap={1}
height={"90vh"}
snapPoints={["90vh"]}
contentStyle={{ padding: 0 }}
>
<Button
type="text"
icon={<UploadOutlined />}
onClick={redirectToProductPage}
style={closeButtonStyle}
/>
<ProductDetailPage onClose={onClose} />
</ProBottomSheet>
);
}

View File

@@ -55,6 +55,9 @@ export default function ProductFooter({
product.extras.length, product.extras.length,
product?.theExtrasGroups?.length, product?.theExtrasGroups?.length,
]); ]);
const isBottomSheetView = useMemo(() => {
return window.location.href.includes("menu");
}, []);
const handleAddToCart = () => { const handleAddToCart = () => {
if (restaurant && !restaurant.isOpened) { if (restaurant && !restaurant.isOpened) {
@@ -114,7 +117,7 @@ export default function ProductFooter({
}), }),
); );
// Navigate back to menu - scroll position will be restored automatically // Navigate back to menu - scroll position will be restored automatically
if (!isDesktop) window.history.back(); if (!isDesktop && !isBottomSheetView ) window.history.back();
else { else {
onClose?.(); onClose?.();
} }

View File

@@ -25,7 +25,6 @@ export default function ProductDetailPage({
onClose?: () => void; onClose?: () => void;
}) { }) {
const { productId } = useParams(); const { productId } = useParams();
const { isRTL } = useAppSelector((state) => state.locale); const { isRTL } = useAppSelector((state) => state.locale);
const { restaurant } = useAppSelector((state) => state.order); const { restaurant } = useAppSelector((state) => state.order);
const { isDesktop, isTablet } = useBreakPoint(); const { isDesktop, isTablet } = useBreakPoint();
@@ -33,6 +32,14 @@ export default function ProductDetailPage({
const [viewportHeight, setViewportHeight] = useState<number>( const [viewportHeight, setViewportHeight] = useState<number>(
typeof window !== "undefined" ? window.innerHeight : 0, typeof window !== "undefined" ? window.innerHeight : 0,
); );
const isBottomSheetView = useMemo(() => {
return window.location.href.includes("menu");
}, []);
const currenctProductId = useMemo(() => {
// in case the prduct viewing from the bottom sheet, the product id is not passed in the url
return productId || localStorage.getItem("productId");
}, [localStorage.getItem("productId")]);
// Update viewport height when browser UI changes (mobile address bar, etc.) // Update viewport height when browser UI changes (mobile address bar, etc.)
useEffect(() => { useEffect(() => {
@@ -55,7 +62,10 @@ export default function ProductDetailPage({
window.removeEventListener("resize", updateViewportHeight); window.removeEventListener("resize", updateViewportHeight);
window.removeEventListener("orientationchange", updateViewportHeight); window.removeEventListener("orientationchange", updateViewportHeight);
if (window.visualViewport) { if (window.visualViewport) {
window.visualViewport.removeEventListener("resize", updateViewportHeight); window.visualViewport.removeEventListener(
"resize",
updateViewportHeight,
);
} }
}; };
}, []); }, []);
@@ -71,14 +81,15 @@ export default function ProductDetailPage({
// because in desktop we open the product dialog from the menu list // because in desktop we open the product dialog from the menu list
// and the product id is not passed in the url // and the product id is not passed in the url
const productIdLocalStorage = localStorage.getItem("productId"); const productIdLocalStorage = localStorage.getItem("productId");
if (!menuData?.products || (!productId && !productIdLocalStorage)) if (!menuData?.products || (!currenctProductId && !productIdLocalStorage))
return null; return null;
// Find the product with matching IDs // Find the product with matching IDs
return menuData.products.find( return menuData.products.find(
(p: Product) => p.id.toString() === (productId || productIdLocalStorage), (p: Product) =>
p.id.toString() === (currenctProductId || productIdLocalStorage),
); );
}, [menuData, productId]); }, [menuData, currenctProductId]);
// State for variant selections // State for variant selections
const [selectedVariants, setSelectedVariants] = useState< const [selectedVariants, setSelectedVariants] = useState<
@@ -205,14 +216,15 @@ export default function ProductDetailPage({
return ( return (
<div <div
style={{ style={{
height: viewportHeight > 0 height:
? `${viewportHeight - 195}px` viewportHeight > 0
: "calc(100dvh - 195px)", ? `${viewportHeight - 195}px`
: "calc(100dvh - 195px)",
overflow: "auto", overflow: "auto",
scrollbarWidth: "none", scrollbarWidth: "none",
}} }}
> >
{!isDesktop && ( {!isDesktop && !isBottomSheetView && (
<div className={styles.backButtonContainer}> <div className={styles.backButtonContainer}>
<BackButton /> <BackButton />
</div> </div>
@@ -371,6 +383,7 @@ export default function ProductDetailPage({
selectedGroups={selectedExtrasByGroup} selectedGroups={selectedExtrasByGroup}
quantity={quantity} quantity={quantity}
setQuantity={(quantity: number) => setQuantity(quantity)} setQuantity={(quantity: number) => setQuantity(quantity)}
onClose={onClose}
/> />
)} )}
</div> </div>

View File

@@ -315,6 +315,11 @@
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
} }
:global(.ant-app-rtl) .backButtonContainer {
left: auto;
right: 20px;
}
:global(.darkApp) .itemDescriptionIcons path { :global(.darkApp) .itemDescriptionIcons path {
fill: #ffffff !important; fill: #ffffff !important;
} }