Compare commits

...

3 Commits

20 changed files with 291 additions and 96 deletions

View File

@@ -162,7 +162,8 @@
"openingHours": "ساعات العمل: {{openingTime}} - {{closingTime}}",
"restaurantIsClosed": "المطعم مغلق",
"address": "العنوان",
"openingTimes": "ساعات العمل"
"openingTimes": "ساعات العمل",
"customizable": "قابل للتخصيص"
},
"cart": {
"title": "السلة",

View File

@@ -174,7 +174,8 @@
"pay": "Pay",
"payDescription": "Pay for your order",
"address": "Address",
"openingTimes": "Opening Times"
"openingTimes": "Opening Times",
"customizable": "Customizable"
},
"cart": {
"title": "Cart",

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
/* Import Ant Design reset */
@import "antd/dist/reset.css";
/* Import Nunito Sans from Google Fonts */
@import url("https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&display=swap");
/* Import Outfit from Google Fonts */
@import url("https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap");
:root {
--background: #f7f7f7;

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ import InputCard from "components/InputCard";
import OrderSummary from "components/OrderSummary/OrderSummary";
import PaymentMethods from "components/PaymentMethods/PaymentMethods";
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 { useAppSelector } from "redux/hooks";
import styles from "../address/address.module.css";
@@ -13,11 +13,17 @@ import CheckoutButton from "./components/CheckoutButton";
import { GiftCard } from "./components/GiftCard";
import PhoneCard from "./components/phoneCard";
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() {
const { t } = useTranslation();
const [form] = Form.useForm();
const { phone, order, orderType } = useAppSelector(selectCart);
const { phone, order, orderType, collectionMethod } =
useAppSelector(selectCart);
const { token } = useAppSelector((state) => state.auth);
return (
<>
<Form
@@ -31,7 +37,7 @@ export default function CheckoutPage() {
<ProHeader>{t("checkout.title")}</ProHeader>
<Layout.Content className={styles.checkoutContainer}>
<AddressSummary />
<PhoneCard />
{!token && <PhoneCard />}
{orderType === OrderType.ToRoom && (
<InputCard
title={t("address.roomNo")}
@@ -52,8 +58,47 @@ export default function CheckoutPage() {
{/* <RoomDetails />
<OfficeDetails /> */}
{/* <GiftDetails /> */}
<BriefMenu />
{/* <BriefMenu /> */}
<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 />
</Layout.Content>

View File

@@ -44,7 +44,9 @@ export function AddToCartButton({ item }: { item: Product }) {
(!cartItem.extrasgroupnew || cartItem.extrasgroupnew.length === 0),
);
const handleClick = () => {
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
e.preventDefault();
if (restaurant && !restaurant.isOpened) {
message.warning(t("menu.restaurantIsClosed"));
return;
@@ -76,7 +78,9 @@ export function AddToCartButton({ item }: { item: Product }) {
}
};
const handleMinusClick = () => {
const handleMinusClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
e.preventDefault();
if (restaurant && !restaurant.isOpened) {
message.warning(t("menu.restaurantIsClosed"));
return;
@@ -115,7 +119,9 @@ export function AddToCartButton({ item }: { item: Product }) {
}
};
const handlePlusClick = () => {
const handlePlusClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
e.preventDefault();
if (restaurant && !restaurant.isOpened) {
message.warning(t("menu.restaurantIsClosed"));
return;
@@ -163,9 +169,8 @@ export function AddToCartButton({ item }: { item: Product }) {
position: "absolute",
bottom: 3,
[isRTL ? "left" : "right"]: 1,
backgroundColor: "var(--secondary-background)",
background: "#FAFAFA",
borderRadius: 888,
opacity: 0.8,
}}
>
<Button
@@ -215,7 +220,7 @@ export function AddToCartButton({ item }: { item: Product }) {
height: 28,
position: "absolute",
bottom: 1,
[isRTL ? "left" : "right"]: 3,
[isRTL ? "left" : "right"]: 2,
minWidth: 28,
}}
/>
@@ -236,7 +241,7 @@ export function AddToCartButton({ item }: { item: Product }) {
size="small"
icon={
hasOptions ? (
<NextIcon iconColor="#fff" iconSize={16} />
<NextIcon iconColor="#302E3E" iconSize={16} />
) : (
<PlusOutlined title="add" />
)
@@ -244,13 +249,15 @@ export function AddToCartButton({ item }: { item: Product }) {
onClick={handleClick}
className={styles.addButton}
style={{
backgroundColor: colors.primary,
color: "#302E3E",
width: 28,
height: 28,
position: "absolute",
bottom: 16,
[isRTL ? "left" : "right"]: 7,
minWidth: 28,
boxShadow:
"0px 1px 2px 0px #8585851A, 0px 3px 3px 0px #85858517, -1px 7px 4px 0px #8585850D, -1px 13px 5px 0px #85858503, -2px 20px 6px 0px #85858500",
}}
/>
</div>

View File

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

View File

@@ -1,4 +1,4 @@
import { Badge, Button } from "antd";
import { Badge, Button, message } from "antd";
import ArabicPrice from "components/ArabicPrice";
import BackIcon from "components/Icons/BackIcon";
import CartIcon from "components/Icons/cart/CartIcon";
@@ -47,10 +47,18 @@ export function MenuFooter() {
>
<Link
to={
orderType === OrderType.Pay
? `/${subdomain}/pay`
: `/${subdomain}/cart`
totalItems === 0
? "#"
: orderType === OrderType.Pay
? `/${subdomain}/pay`
: `/${subdomain}/cart`
}
onClick={(e) => {
if (totalItems === 0) {
e.preventDefault();
message.warning(t("cart.emptyCartMessage"));
}
}}
style={{
width: "100%",
padding: "0 16px",
@@ -60,6 +68,9 @@ export function MenuFooter() {
backgroundColor: colors.primary,
height: 48,
borderRadius: "999px",
opacity: totalItems === 0 ? 0.5 : 1,
pointerEvents: totalItems === 0 ? "none" : "auto",
cursor: totalItems === 0 ? "not-allowed" : "pointer",
}}
>
<Button

View File

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

View File

@@ -11,19 +11,25 @@ import { useAppSelector } from "redux/hooks.ts";
import { ProductPreviewDialog } from "pages/menu/components/ProductPreviewDialog";
import { useState } from "react";
import { AddToCartButton } from "../AddToCartButton/AddToCartButton";
import { useTranslation } from "react-i18next";
type Props = {
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 { isMobile, isTablet, isDesktop } = useBreakPoint();
const { items } = useAppSelector((state) => state.order);
const [isDialogOpen, setIsDialogOpen] = useState(false);
// const navigate = useNavigate();
// const { subdomain } = useParams();
const { t } = useTranslation();
// Handle dialog close
const handleDialogClose = () => {
setIsDialogOpen(false);
setIsBottomSheetOpen(false);
};
// Handle product click - open dialog on desktop, navigate on mobile/tablet
@@ -32,6 +38,7 @@ export default function ProductCard({ item }: Props) {
if (isDesktop) {
setIsDialogOpen(true);
}
setIsBottomSheetOpen(true);
// else {
// navigate(`/${subdomain}/product/${item.id}`);
// }
@@ -48,7 +55,7 @@ export default function ProductCard({ item }: Props) {
key={item.id}
style={{
borderRadius: 8,
height: item.description ? 148 : 120,
height: 156,
overflow: "hide",
width: "100%",
boxShadow: "none",
@@ -61,7 +68,7 @@ export default function ProductCard({ item }: Props) {
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
padding: item.description ? "12px 12px 8px 12px" : "12px",
padding: item.description ? "16px 16px 8px 16px" : "16px",
overflow: "hide",
boxShadow: "none",
},
@@ -123,8 +130,8 @@ export default function ProductCard({ item }: Props) {
<div
style={{
position: "absolute",
bottom: 12,
[isRTL ? "right" : "left"]: 12,
bottom: 16,
[isRTL ? "right" : "left"]: 16,
}}
>
{item.original_price !== item.price && (
@@ -134,7 +141,7 @@ export default function ProductCard({ item }: Props) {
style={{
fontSize: "1rem",
fontWeight: 700,
color: colors.primary,
color: "#333333",
textDecoration: "line-through",
marginRight: isRTL ? 0 : 10,
marginLeft: isRTL ? 10 : 0,
@@ -145,9 +152,13 @@ export default function ProductCard({ item }: Props) {
price={item.price}
strong
style={{
fontSize: "1rem",
fontWeight: 700,
color: colors.primary,
color: "#333333",
fontFamily: "Outfit",
fontWeight: 600,
fontStyle: "SemiBold",
fontSize: "14px",
lineHeight: "140%",
letterSpacing: "0%",
}}
/>
<div
@@ -188,13 +199,29 @@ export default function ProductCard({ item }: Props) {
? styles.popularMenuItemImageTablet
: styles.popularMenuItemImageDesktop
}`}
width={92}
height={92}
width={91}
height={96}
/>
<AddToCartButton item={item} />
</Badge>
</div>
</div>
<ProText
style={{
fontWeight: 400,
fontStyle: "Regular",
fontSize: "12px",
lineHeight: "140%",
letterSpacing: "0%",
textAlign: "center",
position: "absolute",
bottom: 19,
[isRTL ? "left" : "right"]: 26,
color: "#A4A3AA",
}}
>
{t("menu.customizable")}
</ProText>
</Card>
</div>

View File

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

View File

@@ -165,7 +165,7 @@ function MenuPage() {
</div>
<div className={`${styles.pageContainer}`}>
<Space direction="vertical" style={{ width: "100%" }}>
<Space orientation="vertical" style={{ width: "100%" }}>
<div>
{restaurant?.loyalty_stamps &&
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?.theExtrasGroups?.length,
]);
const isBottomSheetView = useMemo(() => {
return window.location.href.includes("menu");
}, []);
const handleAddToCart = () => {
if (restaurant && !restaurant.isOpened) {
@@ -114,7 +117,7 @@ export default function ProductFooter({
}),
);
// Navigate back to menu - scroll position will be restored automatically
if (!isDesktop) window.history.back();
if (!isDesktop && !isBottomSheetView ) window.history.back();
else {
onClose?.();
}

View File

@@ -25,7 +25,6 @@ export default function ProductDetailPage({
onClose?: () => void;
}) {
const { productId } = useParams();
const { isRTL } = useAppSelector((state) => state.locale);
const { restaurant } = useAppSelector((state) => state.order);
const { isDesktop, isTablet } = useBreakPoint();
@@ -33,6 +32,14 @@ export default function ProductDetailPage({
const [viewportHeight, setViewportHeight] = useState<number>(
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.)
useEffect(() => {
@@ -55,7 +62,10 @@ export default function ProductDetailPage({
window.removeEventListener("resize", updateViewportHeight);
window.removeEventListener("orientationchange", updateViewportHeight);
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
// and the product id is not passed in the url
const productIdLocalStorage = localStorage.getItem("productId");
if (!menuData?.products || (!productId && !productIdLocalStorage))
if (!menuData?.products || (!currenctProductId && !productIdLocalStorage))
return null;
// Find the product with matching IDs
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
const [selectedVariants, setSelectedVariants] = useState<
@@ -205,14 +216,15 @@ export default function ProductDetailPage({
return (
<div
style={{
height: viewportHeight > 0
? `${viewportHeight - 195}px`
: "calc(100dvh - 195px)",
height:
viewportHeight > 0
? `${viewportHeight - 195}px`
: "calc(100dvh - 195px)",
overflow: "auto",
scrollbarWidth: "none",
}}
>
{!isDesktop && (
{!isDesktop && !isBottomSheetView && (
<div className={styles.backButtonContainer}>
<BackButton />
</div>
@@ -371,6 +383,7 @@ export default function ProductDetailPage({
selectedGroups={selectedExtrasByGroup}
quantity={quantity}
setQuantity={(quantity: number) => setQuantity(quantity)}
onClose={onClose}
/>
)}
</div>

View File

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