Merge remote-tracking branch 'origin/main'

This commit is contained in:
2025-10-06 11:14:27 +03:00
12 changed files with 433 additions and 573 deletions

View File

@@ -4,6 +4,8 @@
:root {
--background: #f7f7f7;
--foreground: #181818;
--primary-light: #fff;
--primary-dark: #0a0a0a;
--primary: #ffb700;
--primary2: #ffc600;
--secondary: #09237d;
@@ -304,4 +306,4 @@ label {
> label.ant-form-item-required::before {
position: relative !important;
top: 3px !important;
}
}

View File

@@ -1,231 +0,0 @@
import {
LogoutOutlined,
QuestionOutlined,
SettingOutlined,
UserOutlined,
} from "@ant-design/icons";
import {
Dropdown,
Flex,
FloatButton,
Layout,
MenuProps,
message,
theme,
Tooltip,
Typography,
} from "antd";
import { ReactNode, useEffect, useRef, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import dayjs from "dayjs";
import { logout } from "features/auth/authSlice.ts";
import { useTranslation } from "react-i18next";
import { useAppDispatch } from "redux/hooks.ts";
import { PATH_AUTH } from "utils/constants.ts";
import LangSelect from "./components/langSelect/LangSelect.tsx";
import HeaderNav from "./HeaderNav.tsx";
import SideNav from "./SideNav.tsx";
import "./styles.css";
const { Content } = Layout;
const { Title } = Typography;
type AppLayoutProps = {
withSidebar?: boolean;
children: ReactNode;
};
export const AppLayout = ({ children, withSidebar = true }: AppLayoutProps) => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const {
token: { borderRadius },
} = theme.useToken();
const [navFill, setNavFill] = useState(false);
const navigate = useNavigate();
const nodeRef = useRef(null);
const floatBtnRef = useRef(null);
const location = useLocation();
const isMenu = location.pathname === "/menu";
const items: MenuProps["items"] = [
{
key: "user-profile-link",
label: t("profile"),
icon: <UserOutlined />,
},
{
key: "user-settings-link",
label: t("settings"),
icon: <SettingOutlined />,
},
{
key: "user-help-link",
label: t("help center"),
icon: <QuestionOutlined />,
},
{
type: "divider",
},
{
key: "user-logout-link",
label: t("logout"),
icon: <LogoutOutlined />,
danger: true,
onClick: () => {
message.open({
type: "loading",
content: t("msg-success-logout"),
});
dispatch(logout());
setTimeout(() => {
navigate(PATH_AUTH.signin);
}, 1000);
},
},
];
useEffect(() => {
window.addEventListener("scroll", () => {
if (window.scrollY > 5) {
setNavFill(true);
} else {
setNavFill(false);
}
});
}, []);
return (
<>
<Layout
style={{
minHeight: "100vh",
// backgroundColor: 'white',
}}
>
{withSidebar && (
<SideNav
trigger={null}
style={{
overflow: "auto",
background: "none",
border: "none",
transition: "all .2s",
}}
/>
)}
<HeaderNav
style={{
padding: "0px 20px 0px 20px",
background: navFill ? "rgba(255, 255, 255, .5)" : "none",
backdropFilter: navFill ? "blur(8px)" : "none",
boxShadow: navFill ? "0 0 8px 2px rgba(0, 0, 0, 0.05)" : "none",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
height: "7vh",
zIndex: 10,
transition: "all .25s",
}}
>
<Flex gap="middle" align="start" style={{ gap: 25 }}>
<Tooltip title={dayjs().format("DD/MM/YYYY")}>
<Title level={3} style={{ position: "relative", top: -2 }}>
{dayjs().format("DD/MM/YYYY")}{" "}
</Title>
</Tooltip>
<Tooltip title={t("language")}>
<LangSelect />
</Tooltip>
</Flex>
{/* <Flex align="center">
<Input.Search
placeholder="search"
style={{
width: isMobile ? "100%" : "400px",
marginLeft: isMobile ? 0 : ".5rem",
}}
size="middle"
/>
</Flex> */}
<Flex align="end" gap="small">
{/* <Tooltip title="Apps">
<Button icon={<AppstoreOutlined />} type="text" size="large" />
</Tooltip>
<Tooltip title="Messages">
<Button icon={<MessageOutlined />} type="text" size="large" />
</Tooltip> */}
{/* <Tooltip title="Theme">
<Switch
className=" hidden sm:inline py-1"
checkedChildren={<MoonOutlined />}
unCheckedChildren={<SunOutlined />}
checked={mytheme === "light" ? true : false}
onClick={() => dispatch(toggleTheme())}
/>
</Tooltip> */}
{/* <Badge
count={2}
size="small"
style={{
top: 15,
right: 5,
left: 5,
}}
>
<BellOutlined
style={{
fontSize: 20,
marginTop: 15,
marginLeft: 15,
marginRight: 15,
cursor: "pointer",
}}
onClick={() => setNotificationsDrawerOpen(true)}
/>
</Badge> */}
<Dropdown menu={isMenu ? {} : { items }} trigger={["click"]}>
<Flex>
{!isMenu && (
<img
src={"/avatar.png"}
alt="user profile photo"
height={36}
width={36}
style={{
borderRadius,
objectFit: "cover",
position: "relative",
top: 5,
}}
/>
)}
</Flex>
</Dropdown>
</Flex>
</HeaderNav>
<Content>
<div ref={nodeRef} style={{ background: "none" }}>
{children}
</div>
<div ref={floatBtnRef}>
<FloatButton.BackTop />
</div>
</Content>
{/* <FooterNav
style={{
textAlign: "center",
marginLeft: collapsed ? 0 : "200px",
background: "none",
}}
/> */}
</Layout>
</>
);
};

View File

@@ -0,0 +1,80 @@
import { MoonOutlined, SunOutlined, UserOutlined } from "@ant-design/icons";
import { Button, Flex, Image, Layout, Switch, Tooltip } from "antd";
import { ReactNode } from "react";
import { toggleTheme } from "features/theme/themeSlice.ts";
import useBreakPoint from "hooks/useBreakPoint.ts";
import { useTranslation } from "react-i18next";
import { useAppDispatch, useAppSelector } from "redux/hooks.ts";
import { colors, ProBlack2 } from "ThemeConstants.ts";
import LangSelect from "./components/langSelect/LangSelect.tsx";
import HeaderNav from "./HeaderNav.tsx";
import "./styles.css";
const { Content } = Layout;
type AppLayoutProps = {
children: ReactNode;
};
export const AppLayout = ({ children }: AppLayoutProps) => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const { themeName } = useAppSelector((state) => state.theme);
const { isDesktop } = useBreakPoint();
return (
<>
<Layout
style={{
minHeight: "100vh",
}}
>
{isDesktop && (
<HeaderNav className="header-nav">
<Flex align="center" gap="middle">
<Tooltip>
<Button
type="text"
icon={<UserOutlined />}
className="user-icon"
/>
</Tooltip>
</Flex>
<Flex align="center" gap="middle">
<Tooltip title={t("language")}>
<div style={{ position: "relative", top: -26 }}>
<LangSelect />
</div>
</Tooltip>
<Tooltip title="Theme">
<Switch
checkedChildren={<MoonOutlined />}
unCheckedChildren={<SunOutlined />}
checked={themeName === "light" ? true : false}
onClick={() => dispatch(toggleTheme())}
style={{
background:
themeName === "dark" ? colors.primary : ProBlack2,
borderRadius: "12px",
}}
/>
</Tooltip>
<Image
src="/me.png"
alt="user profile photo"
height={40}
width={40}
className="user-profile-image"
preview={false}
/>
</Flex>
</HeaderNav>
)}
<Content>{children}</Content>
</Layout>
</>
);
};

View File

@@ -1 +1,2 @@
export { AppLayout } from './App.tsx';
export { AppLayout } from './AppLayout.tsx';

View File

@@ -1,3 +1,44 @@
.header-nav {
padding: 0 1rem;
background: #fff;
backdrop-filter: blur(10px);
display: flex;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
align-items: center;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 1;
gap: 4px;
transition: all 0.25s;
height: 8vh;
}
.user-icon {
font-size: 16px;
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
background: var(--primary-light);
}
.user-profile-image {
border-radius: 12px;
object-fit: cover;
border: 2px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
position: relative;
top: -20px;
}
:where(.darkApp) .user-profile-image {
border: 2px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.trigger {
padding: 0 8px;
font-size: 18px;
@@ -64,3 +105,11 @@
right: 30px !important;
width: 32px;
}
:where(.darkApp) .header-nav {
background: var(--primary-dark);
}
:where(.darkApp) .user-icon {
background: var(--primary-dark);
}

View File

@@ -54,7 +54,7 @@
}
@media (min-width: 1025px) {
.menuSections{
.menuSections {
padding: 0;
}
@@ -630,28 +630,6 @@
}
}
/* Skeleton Loading Styles */
.skeletonContainer {
animation: skeletonPulse 1.5s ease-in-out infinite;
}
.skeletonContainer .ant-skeleton {
transition: all 0.3s ease;
}
.skeletonContainer .ant-skeleton-image {
border-radius: 8px;
overflow: hidden;
}
.skeletonContainer .ant-skeleton-input {
border-radius: 6px;
}
.skeletonContainer .ant-skeleton-button {
border-radius: 20px;
}
@keyframes skeletonPulse {
0% {
opacity: 1;
@@ -673,72 +651,6 @@
}
}
.skeletonContainer .ant-skeleton-active .ant-skeleton-input,
.skeletonContainer .ant-skeleton-active .ant-skeleton-image,
.skeletonContainer .ant-skeleton-active .ant-skeleton-button {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200px 100%;
animation: skeletonShimmer 1.5s infinite;
}
/* Mobile-specific skeleton styles */
@media (max-width: 768px) {
.skeletonContainer .ant-skeleton {
margin-bottom: 12px;
}
.skeletonContainer .ant-skeleton-image {
border-radius: 4px;
}
.skeletonContainer .ant-skeleton-input {
border-radius: 4px;
}
.skeletonContainer .ant-skeleton-button {
border-radius: 16px;
}
}
/* Dark theme skeleton styles */
:global(.darkApp) .skeletonContainer .ant-skeleton-active .ant-skeleton-input,
:global(.darkApp) .skeletonContainer .ant-skeleton-active .ant-skeleton-image,
:global(.darkApp) .skeletonContainer .ant-skeleton-active .ant-skeleton-button {
background: linear-gradient(90deg, #181818 25%, #363636 50%, #181818 75%);
background-size: 200px 100%;
animation: skeletonShimmer 1.5s infinite;
}
:global(.darkApp) .skeletonContainer .ant-skeleton-input,
:global(.darkApp) .skeletonContainer .ant-skeleton-image,
:global(.darkApp) .skeletonContainer .ant-skeleton-button {
background-color: #181818;
border-color: #363636;
}
/* Tablet-specific skeleton styles */
@media (min-width: 769px) and (max-width: 1024px) {
.skeletonContainer {
padding: 24px;
}
.skeletonContainer .ant-skeleton {
margin-bottom: 16px;
}
.skeletonContainer .ant-skeleton-image {
border-radius: 6px;
}
.skeletonContainer .ant-skeleton-input {
border-radius: 5px;
}
.skeletonContainer .ant-skeleton-button {
border-radius: 18px;
}
}
.menuItemsGridSticky {
height: 60;
}

View File

@@ -48,13 +48,16 @@ export function MenuList({ data, categoryRefs }: MenuListProps) {
}
// Group products by category
const productsByCategory = products?.reduce((acc, product) => {
if (product.categoryId && !acc[product?.categoryId]) {
acc[product?.categoryId] = [];
}
acc[product?.categoryId || 0].push(product);
return acc;
}, {} as Record<number, Product[]>);
const productsByCategory = products?.reduce(
(acc, product) => {
if (product.categoryId && !acc[product?.categoryId]) {
acc[product?.categoryId] = [];
}
acc[product?.categoryId || 0].push(product);
return acc;
},
{} as Record<number, Product[]>,
);
return (
<>
@@ -170,14 +173,14 @@ export function MenuList({ data, categoryRefs }: MenuListProps) {
style={{
display: "-webkit-box",
WebkitBoxOrient: "vertical",
WebkitLineClamp: xs ? 2 : 4,
WebkitLineClamp: 2,
overflow: "hidden",
textOverflow: "ellipsis",
wordWrap: "break-word",
overflowWrap: "break-word",
lineHeight: "1.5rem",
maxHeight: xs ? "3em" : "6.6em",
fontSize: xs ? "1rem" : 18,
maxHeight: "3em",
fontSize: "1rem",
letterSpacing: "0.01em",
}}
>
@@ -231,8 +234,8 @@ export function MenuList({ data, categoryRefs }: MenuListProps) {
xs
? styles.popularMenuItemImageMobile
: md
? styles.popularMenuItemImageTablet
: styles.popularMenuItemImageDesktop
? styles.popularMenuItemImageTablet
: styles.popularMenuItemImageDesktop
}`}
width={90}
height={90}

View File

@@ -18,6 +18,24 @@
gap: 16px;
}
.cover {
width: 100%;
height: "auto";
object-fit: cover;
}
.logo {
position: absolute;
left: 33px;
top: -64px;
width: 72px;
height: 72px;
border-radius: 50%;
border: 3px solid var(--background);
z-index: 10;
overflow: hidden;
}
.leftShape {
position: absolute;
top: 133px;
@@ -49,6 +67,12 @@
/* Enhanced responsive categories container */
@media (min-width: 769px) and (max-width: 1024px) {
.logo {
left: 40px;
width: 80px;
height: 80px;
}
.categoriesContainer {
height: 160px;
padding: 0 24px;
@@ -65,17 +89,23 @@
}
.leftShape {
top: 170px;
top: 150px;
left: -3px;
}
.rightShape {
top: 170px;
top: 150px;
left: 116px;
}
}
@media (min-width: 1025px) {
.logo {
left: 33px;
top: 141px;
width: 150px !important;
height: 150px !important;
}
.categoriesContainer {
height: 180px;
padding: 0 24px;
@@ -94,6 +124,16 @@
max-width: 100vw !important;
margin: 0 !important;
}
.leftShape {
top: 170px;
left: -10px;
}
.rightShape {
top: 170px;
left: 185px;
}
}
.menuSection:first-child h3 {

View File

@@ -1,4 +1,5 @@
import { Card, Grid, Skeleton } from "antd";
import { Card, Skeleton } from "antd";
import useBreakPoint from "hooks/useBreakPoint";
import styles from "./MenuSkeleton.module.css";
interface MenuSkeletonProps {
@@ -12,17 +13,12 @@ interface MenuSkeletonProps {
| "menu-only";
}
const { useBreakpoint } = Grid;
const MenuSkeleton = ({
categoryCount = 6,
categoryCount = 8,
itemCount = 8,
variant = "default",
}: MenuSkeletonProps) => {
const { xs, md } = useBreakpoint();
const isMobile = xs;
const isTablet = !xs && !md;
// const isDesktop = md;
const { isMobile, isTablet } = useBreakPoint();
const getCategoryCardStyle = () => {
if (isMobile) {
@@ -106,82 +102,90 @@ const MenuSkeleton = ({
}
};
const getGridClass = () => {
if (isMobile) {
return styles.menuItemsGridMobile;
} else if (isTablet) {
return styles.menuItemsGridTablet;
} else {
return styles.menuItemsGridDesktop;
}
};
return (
<div className={`${styles.pageContainer} ${styles.skeletonContainer}`}>
<>
{/* Restaurant Header Skeleton */}
<div className={styles.restaurantHeader}>
{/* Cover Image Skeleton */}
<Skeleton.Image
active
className={styles.cover}
style={{
width: "100vw",
height: isMobile ? 182 : isTablet ? 200 : 220,
borderRadius: 0,
}}
/>
{/* Logo Skeleton */}
<Skeleton.Image
active
style={{
position: "absolute",
left: isMobile ? "33px" : "40px",
top: isMobile ? "133px" : isTablet ? "170px" : "170px",
left: isMobile ? "33px" : isTablet ? "40px" : "36px",
top: isMobile ? "133px" : isTablet ? "150px" : "130px",
borderRadius: "50%",
width: isMobile ? "72px" : isTablet ? "80px" : "80px",
height: isMobile ? "72px" : isTablet ? "80px" : "80px",
width: isMobile ? "72px" : isTablet ? "80px" : "150px",
height: isMobile ? "72px" : isTablet ? "80px" : "150px",
border: "3px solid var(--background)",
zIndex: 10,
overflow: "hidden",
}}
/>
{/* Decorative Shapes Skeleton */}
{/* Decorative Shap es Skeleton */}
<div className={styles.leftShape}></div>
<div className={styles.rightShape}></div>
</div>
{/* Restaurant Info Skeleton */}
<div className={styles.restaurantInfoSkeleton}>
<div
className={
styles.restaurantDescriptionSkeleton +
" " +
"restaurant-description-skeleton"
}
>
<Skeleton
active
paragraph={{
rows: 1,
width: ["100%"],
}}
/>
</div>
</div>
{/* Loyalty Card Skeleton */}
<div className={styles.loyaltySkeleton}>
<Card className={styles.loyaltyCardSkeleton}>
<div className={`${styles.pageContainer} ${styles.skeletonContainer}`}>
{/* Restaurant Info Skeleton */}
<div className={styles.restaurantInfoSkeleton}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "0.5rem",
}}
className={
styles.restaurantDescriptionSkeleton +
" " +
"restaurant-description-skeleton"
}
>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<Skeleton
active
paragraph={{
rows: 1,
width: ["100%"],
}}
/>
</div>
</div>
{/* Loyalty Card Skeleton */}
<div className={styles.loyaltySkeleton}>
<Card className={styles.loyaltyCardSkeleton}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "0.5rem",
}}
>
<div
style={{ display: "flex", alignItems: "center", gap: "8px" }}
>
<Skeleton.Image
active
style={{
width: "24px",
height: "24px",
borderRadius: "4px",
}}
/>
<Skeleton.Input
active
style={{
width: "120px",
height: "16px",
}}
/>
</div>
<Skeleton.Image
active
style={{
@@ -190,150 +194,134 @@ const MenuSkeleton = ({
borderRadius: "4px",
}}
/>
<Skeleton.Input
</div>
<br />
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<div style={{ display: "flex", gap: "4px" }}>
{Array.from({ length: 5 }).map((_, index) => (
<Skeleton.Image
key={index}
active
style={{
width: "32px",
height: "32px",
borderRadius: "50%",
}}
/>
))}
</div>
<Skeleton.Button
active
style={{
width: "120px",
height: "16px",
width: "80px",
height: "32px",
borderRadius: "16px",
}}
/>
</div>
<Skeleton.Image
active
style={{
width: "24px",
height: "24px",
borderRadius: "4px",
}}
/>
</div>
<br />
</Card>
</div>
{/* Categories Skeleton */}
{(variant === "default" ||
variant === "minimal" ||
variant === "detailed" ||
variant === "categories-only") && (
<div
style={{
padding: isMobile ? "0 1rem" : isTablet ? "0 24px" : "0 24px",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
gap: 8,
overflow: "hidden",
marginBottom: isMobile ? "4rem" : isTablet ? "8px" : "16px",
}}
>
<div style={{ display: "flex", gap: "4px" }}>
{Array.from({ length: 5 }).map((_, index) => (
<Skeleton.Image
key={index}
active
style={{
width: "32px",
height: "32px",
borderRadius: "50%",
}}
/>
))}
</div>
<Skeleton.Button
active
style={{
width: "80px",
height: "32px",
borderRadius: "16px",
}}
/>
</div>
</Card>
</div>
{/* Categories Skeleton */}
{(variant === "default" ||
variant === "minimal" ||
variant === "detailed" ||
variant === "categories-only") && (
<div
style={{
padding: isMobile ? "0 1rem" : isTablet ? "0 24px" : "0 24px",
display: "flex",
gap: 8,
overflow: "hidden",
marginBottom: isMobile ? "4rem" : isTablet ? "8px" : "16px",
}}
>
{Array.from({
length:
variant === "minimal"
? Math.min(categoryCount, 4)
: categoryCount,
}).map((_, index) => (
<div
key={index}
style={{
marginRight: isMobile ? "3px" : "8px",
}}
>
<Card
style={{ borderRadius: 8 }}
styles={{
body: {
...getCategoryCardStyle(),
padding: 0,
},
{Array.from({
length:
variant === "minimal"
? Math.min(categoryCount, 4)
: categoryCount,
}).map((_, index) => (
<div
key={index}
style={{
marginRight: isMobile ? "3px" : "8px",
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "space-between",
gap: "0px",
width: "100%",
height: "100%",
<Card
style={{ borderRadius: 8 }}
styles={{
body: {
...getCategoryCardStyle(),
padding: 0,
},
}}
>
<Skeleton.Image
active
style={{
...getCategoryImageStyle(),
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
}}
/>
<div
style={{
flex: 1,
padding: isMobile ? "4px" : "8px",
textAlign: "center",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "space-between",
gap: "0px",
width: "100%",
height: "100%",
}}
>
<Skeleton
<Skeleton.Image
active
paragraph={{ rows: 0 }}
style={{
margin: 0,
padding: isMobile ? 3 : 8,
...getCategoryImageStyle(),
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
}}
/>
<div
style={{
flex: 1,
padding: isMobile ? "4px" : "8px",
textAlign: "center",
width: "100%",
}}
>
<Skeleton
active
paragraph={{ rows: 0 }}
style={{
margin: 0,
padding: isMobile ? 3 : 8,
}}
/>
</div>
</div>
</div>
</Card>
</div>
))}
</div>
)}
</Card>
</div>
))}
</div>
)}
{/* Menu Items Skeleton */}
{(variant === "default" ||
variant === "minimal" ||
variant === "detailed" ||
variant === "menu-only") && (
<div
className={styles.menuSections}
style={{
padding: isMobile ? "0 1rem" : isTablet ? "0 24px" : "0 24px",
}}
>
{Array.from({ length: variant === "minimal" ? 1 : 3 }).map(
(_, sectionIndex) => (
<div key={sectionIndex} className={styles.menuSection}>
{/* Section Header Skeleton */}
{/* <div
{/* Menu Items Skeleton */}
{(variant === "default" ||
variant === "minimal" ||
variant === "detailed" ||
variant === "menu-only") && (
<div
className={styles.menuSections}
style={{
padding: isMobile ? "0 1rem" : isTablet ? "0 24px" : "0 24px",
}}
>
{Array.from({ length: variant === "minimal" ? 1 : 3 }).map(
(_, sectionIndex) => (
<div key={sectionIndex} className={styles.menuSection}>
{/* Section Header Skeleton */}
{/* <div
style={{
margin: isMobile ? "20px 0 15px 0" : isTablet ? "30px 0 20px 0" : "40px 0 25px 0",
padding: isTablet ? "0 12px" : isDesktop ? "0 16px" : "0"
@@ -349,78 +337,81 @@ const MenuSkeleton = ({
/>
</div> */}
<div className={`${styles.menuItemsGrid} ${getGridClass()}`}>
{Array.from({
length:
variant === "minimal"
? Math.min(itemCount, 4)
: itemCount,
}).map((_, itemIndex) => (
<Card
key={itemIndex}
className="responsive-card"
style={{ borderRadius: 8 }}
styles={{
body: {
...getMenuItemCardStyle(),
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
},
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-around",
gap: isMobile ? 12 : 16,
position: "relative",
<div
className={`${styles.menuItemsGrid} ${styles.menuItemsGrid}`}
>
{Array.from({
length:
variant === "minimal"
? Math.min(itemCount, 4)
: itemCount,
}).map((_, itemIndex) => (
<Card
key={itemIndex}
className="responsive-card"
style={{ borderRadius: 8 }}
styles={{
body: {
...getMenuItemCardStyle(),
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
},
}}
>
<div
style={{
lineHeight: 1,
height: "100%",
flex: 1,
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
justifyContent: "space-around",
gap: isMobile ? 12 : 16,
position: "relative",
}}
>
{/* Item Description Skeleton */}
<Skeleton
active
paragraph={{
rows: isMobile ? 1 : 2,
width: ["100%", "90%", "70%"],
}}
<div
style={{
marginBottom: isMobile ? 8 : isTablet ? 16 : 20,
lineHeight: 1,
height: "100%",
flex: 1,
display: "flex",
flexDirection: "column",
justifyContent: "center",
}}
/>
{/* Action Icons Skeleton */}
</div>
>
{/* Item Description Skeleton */}
<Skeleton
active
paragraph={{
rows: isMobile ? 1 : 2,
width: ["100%", "90%", "70%"],
}}
style={{
marginBottom: isMobile ? 8 : isTablet ? 16 : 20,
}}
/>
{/* Action Icons Skeleton */}
</div>
<div style={{ position: "relative" }}>
{/* Item Image Skeleton */}
<Skeleton.Image
active
style={{
...getMenuItemImageStyle(),
}}
/>{" "}
<div style={{ position: "relative" }}>
{/* Item Image Skeleton */}
<Skeleton.Image
active
style={{
...getMenuItemImageStyle(),
}}
/>{" "}
</div>
</div>
</div>
</Card>
))}
</Card>
))}
</div>
</div>
</div>
)
)}
</div>
)}
</div>
),
)}
</div>
)}
</div>
</>
);
};

View File

@@ -570,6 +570,10 @@
height: 40px !important;
border-radius: 12px !important;
}
.contentWrapper{
margin-top: 16px;
}
}
/* Desktop devices (min-width: 1025px) */

View File

@@ -41,7 +41,7 @@ function MenuPage() {
restaurantDetails?.restaurant.id,
{
skip: !restaurantDetails?.restaurant.id,
}
},
);
const { categoryRefs } = useScrollHandler();
const { xs, md } = useBreakpoint();
@@ -58,7 +58,7 @@ function MenuPage() {
/>
{isLoading ? (
<MenuSkeleton categoryCount={6} itemCount={8} variant="default" />
<MenuSkeleton categoryCount={10} itemCount={8} variant="default" />
) : (
<div className={styles.menuContainer}>
<div className={styles.restaurantHeader}>

View File

@@ -1,6 +1,7 @@
import { Grid } from "antd";
import { Loader } from "components/Loader/Loader";
import { PrivateRoute } from "components/privateRoute/PrivateRoute";
import { AppLayout } from "layouts";
import HeaderMenuDrawer from "layouts/app/HeaderMenuDrawer";
import AddressPage from "pages/address/page";
import CartPage from "pages/cart/page";
@@ -73,7 +74,15 @@ export const router = createHashRouter([
{
path: "/:id/cart",
element: <PageWrapper children={<CartPage />} />,
element: (
<PageWrapper
children={
<AppLayout>
<CartPage />
</AppLayout>
}
/>
),
errorElement: <ErrorPage />,
},