+
diff --git a/src/components/menu/SideMenu.tsx b/src/components/menu/SideMenu.tsx
index 3ab33f1..75e5eca 100644
--- a/src/components/menu/SideMenu.tsx
+++ b/src/components/menu/SideMenu.tsx
@@ -1,466 +1,425 @@
-'use client';
+"use client";
-import React, { useMemo, useRef, useState, useEffect } from 'react';
-import { motion, Variants } from 'framer-motion';
-import SideMenuItem from './SideMenuItem';
-import NoteBoardIcon from '../icons/NoteBoardIcon';
-import BlurredOverlayContainer from '../overlays/BlurredOverlayContainer';
-import NightModeSwitch from '../button/NightModeSwitch';
-import Button from '../button/PrimaryButton';
-import { useParams, usePathname, useRouter } from 'next/navigation';
-import clsx from 'clsx';
-import { useTranslation } from 'react-i18next';
-import { CalendarSearch, Cup, DirectboxReceive, DirectInbox, DocumentCopy, Game, Icon, Instagram, Like1, Login, LogoutCurve, Notification, Receipt1, Setting2, Share, TicketDiscount, Whatsapp } from 'iconsax-react';
-import TelegramIcon from '../icons/TelegramIcon';
-import Modal from '../utils/Modal';
-import LogoutPrompt from '@/features/general/LogoutPrompt';
-import useToggle from '@/hooks/helpers/useToggle';
-import { useGetAbout } from '@/app/[name]/(Main)/about/hooks/useAboutData';
-import Image from 'next/image';
-import { useGetProfile } from '@/app/[name]/(Profile)/profile/hooks/userProfileData';
-import { toast, toastLoginRequired } from '../Toast';
-import { glassSurface } from '@/lib/styles/glassSurface';
+import { useGetAbout } from "@/app/[name]/(Main)/about/hooks/useAboutData";
+import { useGetProfile } from "@/app/[name]/(Profile)/profile/hooks/userProfileData";
+import LogoutPrompt from "@/features/general/LogoutPrompt";
+import useToggle from "@/hooks/helpers/useToggle";
+import { glassSurface } from "@/lib/styles/glassSurface";
+import clsx from "clsx";
+import { motion, Variants } from "framer-motion";
+import {
+ CalendarSearch,
+ Cup,
+ DirectboxReceive,
+ DirectInbox,
+ DocumentCopy,
+ Game,
+ Icon,
+ Instagram,
+ Like1,
+ Login,
+ LogoutCurve,
+ MessageProgramming,
+ Notification,
+ Receipt1,
+ Setting2,
+ Share,
+ TicketDiscount,
+ Whatsapp,
+} from "iconsax-react";
+import Image from "next/image";
+import { useParams, usePathname, useRouter } from "next/navigation";
+import React, { useEffect, useMemo, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+import NightModeSwitch from "../button/NightModeSwitch";
+import Button from "../button/PrimaryButton";
+import NoteBoardIcon from "../icons/NoteBoardIcon";
+import TelegramIcon from "../icons/TelegramIcon";
+import BlurredOverlayContainer from "../overlays/BlurredOverlayContainer";
+import { toast, toastLoginRequired } from "../Toast";
+import Modal from "../utils/Modal";
+import SideMenuItem from "./SideMenuItem";
type MenuItemType = {
- href: string | undefined;
- title: string;
- icon: Icon;
- auth?: boolean;
- guestOnly?: boolean;
- premiumOnly?: boolean;
+ href: string | undefined;
+ title: string;
+ icon: Icon;
+ auth?: boolean;
+ guestOnly?: boolean;
+ premiumOnly?: boolean;
};
const menuItems: Array
> = [
- [
- { href: 'notifications', title: 'Notifications', icon: Notification },
- { href: 'transactions/discount/club', title: 'CustomerClub', icon: Cup, premiumOnly: true },
- { href: 'transactions/discount', title: 'Discounts', icon: TicketDiscount, premiumOnly: true },
- { href: 'order/history', title: 'Orders', icon: CalendarSearch, premiumOnly: true },
- { href: 'transactions', title: 'Transactions', icon: Receipt1, premiumOnly: true },
- { href: 'game', title: 'Games', icon: Game },
- { href: '?share', title: 'ShareWithFriends', icon: Like1 },
- { href: 'report', title: 'ReportProblem', icon: NoteBoardIcon },
- { href: '?installpwa', title: 'InstallApp', icon: DirectboxReceive }
- ],
- [],
- [
- { auth: true, href: 'profile/settings', title: 'Preferences', icon: Setting2 },
- { auth: true, href: '?logout', title: 'Logout', icon: LogoutCurve },
- { guestOnly: true, href: 'auth', title: 'LoginSignup', icon: Login }
- ]
+ [
+ { href: "notifications", title: "Notifications", icon: Notification },
+ { href: "transactions/discount/club", title: "CustomerClub", icon: Cup, premiumOnly: true },
+ { href: "transactions/discount", title: "Discounts", icon: TicketDiscount, premiumOnly: true },
+ { href: "order/history", title: "Orders", icon: CalendarSearch, premiumOnly: true },
+ { href: "transactions", title: "Transactions", icon: Receipt1, premiumOnly: true },
+ { href: "game", title: "Games", icon: Game },
+ { href: "ai", title: "AI", icon: MessageProgramming },
+ { href: "?share", title: "ShareWithFriends", icon: Like1 },
+ { href: "report", title: "ReportProblem", icon: NoteBoardIcon },
+ { href: "?installpwa", title: "InstallApp", icon: DirectboxReceive },
+ ],
+ [],
+ [
+ { auth: true, href: "profile/settings", title: "Preferences", icon: Setting2 },
+ { auth: true, href: "?logout", title: "Logout", icon: LogoutCurve },
+ { guestOnly: true, href: "auth", title: "LoginSignup", icon: Login },
+ ],
];
type Props = {
- menuState: boolean;
- toggleMenuState: React.MouseEventHandler | undefined;
- nightModeState: boolean;
- togglenightModeState: React.MouseEventHandler | undefined;
+ menuState: boolean;
+ toggleMenuState: React.MouseEventHandler | undefined;
+ nightModeState: boolean;
+ togglenightModeState: React.MouseEventHandler | undefined;
};
type BeforeInstallPromptEvent = Event & {
- prompt: () => Promise;
- userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
+ prompt: () => Promise;
+ userChoice: Promise<{ outcome: "accepted" | "dismissed" }>;
};
const isIOS = () => {
- if (typeof window === 'undefined') return false;
- return /iPad|iPhone|iPod/.test(navigator.userAgent) ||
- (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
+ if (typeof window === "undefined") return false;
+ return /iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
};
const isStandalone = () => {
- if (typeof window === 'undefined') return false;
+ if (typeof window === "undefined") return false;
- const isIOSStandalone =
- ('standalone' in window.navigator) &&
- (window.navigator as { standalone?: boolean }).standalone === true;
+ const isIOSStandalone = "standalone" in window.navigator && (window.navigator as { standalone?: boolean }).standalone === true;
- const isDisplayModeStandalone =
- window.matchMedia('(display-mode: standalone)').matches ||
- window.matchMedia('(display-mode: fullscreen)').matches;
+ const isDisplayModeStandalone = window.matchMedia("(display-mode: standalone)").matches || window.matchMedia("(display-mode: fullscreen)").matches;
- return isIOSStandalone || isDisplayModeStandalone;
+ return isIOSStandalone || isDisplayModeStandalone;
};
function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeState }: Props) {
+ const menuStateMemo = useMemo(() => menuState, [menuState]);
+ const closeRef = useRef(null);
+ const { state: logoutModal, toggle: toggleLogoutModal } = useToggle();
+ const { state: shareModal, toggle: toggleShareModal } = useToggle();
+ const { state: installPwaModal, toggle: toggleInstallPwaModal } = useToggle();
+ const [deferredPrompt, setDeferredPrompt] = useState(null);
+ const [isIOSDevice, setIsIOSDevice] = useState(false);
+ const [isPWAInstalled, setIsPWAInstalled] = useState(false);
+ const { data: aboutData } = useGetAbout();
+ const { data: profile, isSuccess } = useGetProfile();
+ const profileData = profile?.data;
+ const isLoggedIn = isSuccess && profileData;
+ const isPremium = aboutData?.data?.plan === "premium";
+ const router = useRouter();
- const menuStateMemo = useMemo(() => menuState, [menuState]);
- const closeRef = useRef(null);
- const { state: logoutModal, toggle: toggleLogoutModal } = useToggle();
- const { state: shareModal, toggle: toggleShareModal } = useToggle();
- const { state: installPwaModal, toggle: toggleInstallPwaModal } = useToggle();
- const [deferredPrompt, setDeferredPrompt] = useState(null);
- const [isIOSDevice, setIsIOSDevice] = useState(false);
- const [isPWAInstalled, setIsPWAInstalled] = useState(false);
- const { data: aboutData } = useGetAbout();
- const { data: profile, isSuccess } = useGetProfile();
- const profileData = profile?.data;
- const isLoggedIn = isSuccess && profileData;
- const isPremium = aboutData?.data?.plan === 'premium';
- const router = useRouter();
+ const params = useParams();
+ const { name } = params;
+ const pathname = usePathname();
+ const { t: tMenu } = useTranslation("common", {
+ keyPrefix: "SideMenu",
+ });
+ const { t: tShareModal } = useTranslation("common", {
+ keyPrefix: "ShareModal",
+ });
+ const { t: tInstallPwaModal } = useTranslation("common", {
+ keyPrefix: "InstallPwaModal",
+ });
+ const variants: Variants = {
+ hidden: { x: "100%", transition: { duration: 0.1, ease: "easeInOut" } },
+ visible: { x: 0, transition: { delay: 0.075, duration: 0.1, ease: "easeInOut" } },
+ };
- const params = useParams();
- const { name } = params;
- const pathname = usePathname();
- const { t: tMenu } = useTranslation('common', {
- keyPrefix: 'SideMenu'
- });
- const { t: tShareModal } = useTranslation('common', {
- keyPrefix: 'ShareModal'
- });
- const { t: tInstallPwaModal } = useTranslation('common', {
- keyPrefix: 'InstallPwaModal'
- });
+ useEffect(() => {
+ const ios = isIOS();
+ const standalone = isStandalone();
+ setIsIOSDevice(ios);
+ setIsPWAInstalled(standalone);
- const variants: Variants = {
- hidden: { x: '100%', transition: { duration: 0.1, ease: 'easeInOut' } },
- visible: { x: 0, transition: { delay: 0.075, duration: 0.1, ease: 'easeInOut' } }
+ const handleBeforeInstallPrompt = (e: Event) => {
+ e.preventDefault();
+ setDeferredPrompt(e as BeforeInstallPromptEvent);
};
- useEffect(() => {
- const ios = isIOS();
- const standalone = isStandalone();
- setIsIOSDevice(ios);
- setIsPWAInstalled(standalone);
-
- const handleBeforeInstallPrompt = (e: Event) => {
- e.preventDefault();
- setDeferredPrompt(e as BeforeInstallPromptEvent);
- };
-
- const handleAppInstalled = () => {
- setDeferredPrompt(null);
- setIsPWAInstalled(true);
- };
-
- window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
- window.addEventListener('appinstalled', handleAppInstalled);
-
- return () => {
- window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
- window.removeEventListener('appinstalled', handleAppInstalled);
- };
- }, []);
-
- const handleInstallPWA = async () => {
- if (!deferredPrompt) {
- return;
- }
-
- deferredPrompt.prompt();
- const { outcome } = await deferredPrompt.userChoice;
-
- if (outcome === 'accepted') {
- setDeferredPrompt(null);
- toggleInstallPwaModal();
- }
+ const handleAppInstalled = () => {
+ setDeferredPrompt(null);
+ setIsPWAInstalled(true);
};
- const getShareUrl = () => {
- if (typeof window === 'undefined') return '';
- return window.location.href;
+ window.addEventListener("beforeinstallprompt", handleBeforeInstallPrompt);
+ window.addEventListener("appinstalled", handleAppInstalled);
+
+ return () => {
+ window.removeEventListener("beforeinstallprompt", handleBeforeInstallPrompt);
+ window.removeEventListener("appinstalled", handleAppInstalled);
};
+ }, []);
- const copyToClipboard = async (showToast: boolean = true) => {
- try {
- const url = getShareUrl();
- await navigator.clipboard.writeText(url);
- if (showToast) {
- toast('لینک با موفقیت کپی شد', 'success');
- }
- return true;
- } catch {
- if (showToast) {
- toast('خطا در کپی کردن لینک', 'error');
- }
- return false;
- }
- };
-
- const handleCopyLink = async () => {
- await copyToClipboard();
- toggleShareModal();
- };
-
- const handleShareTelegram = () => {
- const url = encodeURIComponent(getShareUrl());
- window.open(`https://t.me/share/url?url=${url}`, '_blank');
- };
-
- const handleShareWhatsApp = () => {
- const url = encodeURIComponent(getShareUrl());
- const text = encodeURIComponent('اپلیکیشن ما را ببینید!');
- window.open(`https://wa.me/?text=${text}%20${url}`, '_blank');
- };
-
- const handleShareInstagram = async () => {
- const success = await copyToClipboard(false);
- if (success) {
- toast('لینک کپی شد. لطفاً آن را در اینستاگرام به اشتراک بگذارید', 'info');
- }
- };
-
- const handleNotificationClick = (e: React.MouseEvent) => {
- e.preventDefault();
- if (!isLoggedIn) {
- toastLoginRequired(() => router.push(`/${name}/auth`), 'info');
- } else {
- router.push(`/${name}/notifications`);
- }
- };
-
- const hrefOnClicks = [
- { href: '?logout', handler: toggleLogoutModal },
- { href: '?share', handler: toggleShareModal },
- { href: '?installpwa', handler: toggleInstallPwaModal },
- { href: 'notifications', handler: handleNotificationClick }
- ];
-
- const renderMenu = () => {
- return (
-
- {
- aboutData?.data?.logo && (
-
-
-
- )
- }
-
-
-
-
-
-
-
-
- {menuItems[2]
- .filter(item => {
- if (item.auth && !isSuccess) return false;
- if (item.guestOnly && isSuccess) return false;
- return true;
- })
- .map(({ icon: Icon, ...item }, index) => {
- const href = `/${name}/${item.href}`;
- const isActive = pathname === href;
- return (
- -
- i.href === item.href)?.handler
- : undefined
- }
- title={tMenu(item.title)}
- icon={
-
-
- }
- />
-
- )
- })}
- -
- >}>
-
-
-
-
-
-
- )
+ const handleInstallPWA = async () => {
+ if (!deferredPrompt) {
+ return;
}
+ deferredPrompt.prompt();
+ const { outcome } = await deferredPrompt.userChoice;
+
+ if (outcome === "accepted") {
+ setDeferredPrompt(null);
+ toggleInstallPwaModal();
+ }
+ };
+
+ const getShareUrl = () => {
+ if (typeof window === "undefined") return "";
+ return window.location.href;
+ };
+
+ const copyToClipboard = async (showToast: boolean = true) => {
+ try {
+ const url = getShareUrl();
+ await navigator.clipboard.writeText(url);
+ if (showToast) {
+ toast("لینک با موفقیت کپی شد", "success");
+ }
+ return true;
+ } catch {
+ if (showToast) {
+ toast("خطا در کپی کردن لینک", "error");
+ }
+ return false;
+ }
+ };
+
+ const handleCopyLink = async () => {
+ await copyToClipboard();
+ toggleShareModal();
+ };
+
+ const handleShareTelegram = () => {
+ const url = encodeURIComponent(getShareUrl());
+ window.open(`https://t.me/share/url?url=${url}`, "_blank");
+ };
+
+ const handleShareWhatsApp = () => {
+ const url = encodeURIComponent(getShareUrl());
+ const text = encodeURIComponent("اپلیکیشن ما را ببینید!");
+ window.open(`https://wa.me/?text=${text}%20${url}`, "_blank");
+ };
+
+ const handleShareInstagram = async () => {
+ const success = await copyToClipboard(false);
+ if (success) {
+ toast("لینک کپی شد. لطفاً آن را در اینستاگرام به اشتراک بگذارید", "info");
+ }
+ };
+
+ const handleNotificationClick = (e: React.MouseEvent) => {
+ e.preventDefault();
+ if (!isLoggedIn) {
+ toastLoginRequired(() => router.push(`/${name}/auth`), "info");
+ } else {
+ router.push(`/${name}/notifications`);
+ }
+ };
+
+ const hrefOnClicks = [
+ { href: "?logout", handler: toggleLogoutModal },
+ { href: "?share", handler: toggleShareModal },
+ { href: "?installpwa", handler: toggleInstallPwaModal },
+ { href: "notifications", handler: handleNotificationClick },
+ ];
+
+ const renderMenu = () => {
return (
- <>
-
-
-
+
+ {aboutData?.data?.logo && (
+
+
+
+ )}
+
+
- {/* Logout Modal */}
-
+
+
- {/* Share modal */}
-
-
-
- {tShareModal('Heading')}
-
-
- {tShareModal('Description')}
-
-
-
-
-
-
-
-
-
-
-
- {/* Install PWA modal */}
-
-
-
- {tInstallPwaModal('Heading')}
-
- {isPWAInstalled ? (
-
- {tInstallPwaModal('AlreadyInstalled')}
-
- ) : isIOSDevice ? (
-
-
- {tInstallPwaModal('IOSDescription')}
-
-
- -
-
- ۱
-
- {tInstallPwaModal('IOSStep1')}
-
- -
-
- ۲
-
- {tInstallPwaModal('IOSStep2')}
-
- -
-
- ۳
-
- {tInstallPwaModal('IOSStep3')}
-
-
-
- ) : (
- <>
-
- {tInstallPwaModal('Description')}
-
- {deferredPrompt ? (
-
- ) : (
-
- {tInstallPwaModal('ChromeFallback')}
-
- )}
- >
- )}
-
-
- >
+
+
+ {menuItems[2]
+ .filter((item) => {
+ if (item.auth && !isSuccess) return false;
+ if (item.guestOnly && isSuccess) return false;
+ return true;
+ })
+ .map(({ icon: Icon, ...item }, index) => {
+ const href = `/${name}/${item.href}`;
+ const isActive = pathname === href;
+ return (
+ -
+ i.href === item.href)?.handler : undefined}
+ title={tMenu(item.title)}
+ icon={
+
+ }
+ />
+
+ );
+ })}
+ -
+ >}>
+
+
+
+
+
+
);
+ };
+
+ return (
+ <>
+
+
+
+
+ {/* Logout Modal */}
+
+
+ {/* Share modal */}
+
+
+
+ {tShareModal("Heading")}
+
+ {tShareModal("Description")}
+
+
+
+
+
+
+
+
+
+
+ {/* Install PWA modal */}
+
+
+
+ {tInstallPwaModal("Heading")}
+
+ {isPWAInstalled ? (
+ {tInstallPwaModal("AlreadyInstalled")}
+ ) : isIOSDevice ? (
+
+
{tInstallPwaModal("IOSDescription")}
+
+ -
+ ۱
+ {tInstallPwaModal("IOSStep1")}
+
+ -
+ ۲
+ {tInstallPwaModal("IOSStep2")}
+
+ -
+ ۳
+ {tInstallPwaModal("IOSStep3")}
+
+
+
+ ) : (
+ <>
+ {tInstallPwaModal("Description")}
+ {deferredPrompt ? (
+
+ ) : (
+ {tInstallPwaModal("ChromeFallback")}
+ )}
+ >
+ )}
+
+ >
+ );
}
-export default SideMenu;
\ No newline at end of file
+export default SideMenu;
diff --git a/src/components/wrapper/ClientMenuRouteWrapper.tsx b/src/components/wrapper/ClientMenuRouteWrapper.tsx
index 5f0a409..1356849 100644
--- a/src/components/wrapper/ClientMenuRouteWrapper.tsx
+++ b/src/components/wrapper/ClientMenuRouteWrapper.tsx
@@ -43,7 +43,7 @@ function ClientMenuRouteWrapper({ children }: Props) {
};
const location = usePathname();
- const hideBottomNav = pathname.endsWith("/cart");
+ const hideBottomNav = pathname.endsWith("/cart") || pathname.endsWith("/ai");
const mainRef = useRef(null);
// Refs for the scroll-driven animation — kept outside React state so scroll
@@ -75,8 +75,8 @@ function ClientMenuRouteWrapper({ children }: Props) {
}
const SCROLL_RANGE = 150; // px of scroll to reach full compact state
- const LERP_FACTOR = 0.1; // per-frame smoothing (lower = smoother, higher = snappier)
- const MIN_SCALE = 0.82; // scale floor (18% smaller at full scroll)
+ const LERP_FACTOR = 0.1; // per-frame smoothing (lower = smoother, higher = snappier)
+ const MIN_SCALE = 0.82; // scale floor (18% smaller at full scroll)
const tick = () => {
const diff = targetProgressRef.current - currentProgressRef.current;
@@ -106,7 +106,7 @@ function ClientMenuRouteWrapper({ children }: Props) {
return (
-
+
{!hideBottomNav && (
-
+
)}
diff --git a/src/locales/fa/common.json b/src/locales/fa/common.json
index fe21ce0..32e34b4 100644
--- a/src/locales/fa/common.json
+++ b/src/locales/fa/common.json
@@ -15,6 +15,7 @@
"Orders": "لیست سفارشات",
"Transactions": "لیست تراکنش ها",
"Games": "بازی و سرگرمی ها",
+ "AI": "چی بخورم؟",
"ShareWithFriends": "معرفی به دوستان",
"ReportProblem": "گزارش اشکال",
"InstallApp": "نصب اپلیکیشن",
@@ -59,5 +60,29 @@
"IOSStep3": "در صفحه بعدی روی دکمه «Add» در گوشه بالا بزنید.",
"ChromeFallback": "اگر دکمه نصب را نمیبینید، یا اپلیکیشن قبلاً نصب شده است یا مرورگر هنوز امکان نصب را فعال نکرده است. کمی بعد دوباره تلاش کنید یا از منوی مرورگر گزینه نصب در صفحه اصلی را انتخاب کنید."
},
- "SearchPlaceholder": "جستجو"
+ "SearchPlaceholder": "جستجو",
+ "AI": {
+ "Heading": "چی بخورم؟",
+ "BrandFallback": "دیمنو",
+ "GuestName": "مهمان",
+ "Greeting": {
+ "morning": "صبح بخیر، {{name}}",
+ "noon": "ظهر بخیر، {{name}}",
+ "afternoon": "عصر بخیر، {{name}}",
+ "evening": "شب بخیر، {{name}}"
+ },
+ "InputPlaceholder": "هر چی دوست داری بپرس…",
+ "Suggestions": {
+ "vegetarian": "غذای گیاهی چی دارید؟",
+ "healthy": "غذای سالم چی دارید؟",
+ "tasty": "یه غذای خوشمزه پیشنهاد بده.",
+ "protein": "غذای پروتئینی پیشنهاد بده",
+ "bestsellers": "پرفروشترین غذاها چیه؟",
+ "spicy": "غذای تند پیشنهاد کن.",
+ "quick": "یه غذای سریع برای من انتخاب کن",
+ "dessert": "پیشنهادت برای دسر چیه؟",
+ "group": "برای ۴ نفر چی سفارش بدیم"
+ },
+ "MockReply": "این نسخه فقط رابط کاربری است؛ بهزودی میتونم براتون از منو پیشنهاد واقعی بدم."
+ }
}
\ No newline at end of file