"use client"; import CategoryScroll from "@/app/[name]/(Main)/components/CategoryScroll"; import MenuFilterDrawer from "@/app/[name]/(Main)/components/MenuFilterDrawer"; import MenuSkeleton from "@/app/[name]/(Main)/components/MenuSkeleton"; import MenuSortingDrawer from "@/app/[name]/(Main)/components/MenuSortingDrawer"; import TextAlignIcon from "@/components/icons/TextAlignIcon"; import SearchBox from "@/components/input/SearchBox"; import MenuItem from "@/components/listview/MenuItem"; import MenuItemRenderer from "@/components/listview/MenuItemRenderer"; import VerticalScrollView from "@/components/listview/VerticalScrollView"; import useToggle from "@/hooks/helpers/useToggle"; import clsx from "clsx"; import { motion } from "framer-motion"; import { Candle2 } from "iconsax-react"; import { useQueryState } from "next-usequerystate"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useGetCategories, useGetFoods } from "../hooks/useMenuData"; import type { Category, Food } from "../types/Types"; const sortings = ["PopularityDescending", "RateDescending", "PriceAscending", "PriceDescending"] as const; const MenuIndex = () => { const { data: foodsData, isLoading: foodsLoading } = useGetFoods(); const { data: categoriesData, isLoading: categoriesLoading } = useGetCategories(); const foods = useMemo(() => foodsData?.data || [], [foodsData?.data]); const categories = useMemo(() => categoriesData?.data || [], [categoriesData?.data]); const isLoading = foodsLoading || categoriesLoading; const { t: tCommon } = useTranslation("common"); const { t: tMenu } = useTranslation("menu", { keyPrefix: "Menu" }); const { state: filterModal, toggle: toggleFilterModal } = useToggle(); const { state: sortingModal, toggle: toggleSortingModal } = useToggle(); const [sorting, setSorting] = useQueryState("sortBy", { defaultValue: "0" }); const [search, setSearch] = useQueryState("q", { defaultValue: "" }); const [selectedCategory, setSelectedCategory] = useQueryState("category", { defaultValue: "0", }); const [selectedIngredients, setSelectedIngredients] = useQueryState("ingredients", { defaultValue: "" }); const [selectedDeliveryId, setSelectedDeliveryId] = useQueryState("delivery", { defaultValue: "0" }); const smallCategoriesRef: React.RefObject = useRef(null); const wrapperRef: React.RefObject = useRef(null); const [smallCategoriesVisible, setSmallCategoriesVisibility] = useState(false); const [isInitialMount, setIsInitialMount] = useState(true); useEffect(() => { if (isInitialMount) { setSearch(null); setSelectedIngredients(null); setSelectedDeliveryId(null); setSorting(null); setIsInitialMount(false); } }, [isInitialMount, setSearch, setSelectedIngredients, setSelectedDeliveryId, setSorting]); useEffect(() => { if (categoriesData?.data && selectedCategory === "0") { setSelectedCategory(categoriesData?.data?.[0]?.id); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedCategory, categoriesData]); useEffect(() => { if (isLoading || !wrapperRef.current) return; const scrollContainer = wrapperRef.current.parentElement?.parentElement; if (!scrollContainer) return; const handleScroll = () => { if (!smallCategoriesRef.current) return; setSmallCategoriesVisibility(scrollContainer.scrollTop >= smallCategoriesRef.current.offsetTop); }; scrollContainer.addEventListener("scroll", handleScroll, { passive: true }); handleScroll(); return () => { scrollContainer.removeEventListener("scroll", handleScroll); }; }, [isLoading]); const updateSearch = useCallback( (e: React.ChangeEvent) => { setSearch(e.target.value); }, [setSearch], ); const updateCategory = useCallback( (categoryId: string) => { setSelectedCategory(categoryId); }, [setSelectedCategory], ); const sortingIndex = Number(sorting); const activeSortingIndex = Number.isNaN(sortingIndex) ? 0 : sortingIndex; const activeSortingKey = sortings[activeSortingIndex] ?? sortings[0]; const activeSortingLabel = tMenu(`MenuSortingDrawer.Options.${activeSortingKey}`); const changeSorting = (index: number) => { setSorting(() => String(index)); toggleSortingModal(); }; const changeSelectedIngredients = useCallback( (value: string) => { setSelectedIngredients(value); }, [setSelectedIngredients], ); const changeSelectedDelivery = useCallback( (value: string) => { setSelectedDeliveryId(() => value); }, [setSelectedDeliveryId], ); const filteredReceiptItems = useMemo(() => { if (!foods.length) return []; const lowerSearch = search.toLowerCase(); const lowerIngredients = selectedIngredients ? selectedIngredients.toLowerCase() : ""; const selectedCatId = selectedCategory === "0" ? null : selectedCategory; const filtered = foods.filter((item) => { const matchesCategory = !selectedCatId || item.category === selectedCatId; const itemName = item.title; const matchesSearch = !search || itemName.toLowerCase().includes(lowerSearch); const matchesIngredients = !selectedIngredients || (() => { if (item.content && Array.isArray(item.content) && item.content.length > 0) { return item.content.some((content: string) => content.toLowerCase().includes(lowerIngredients)); } return item.desc.toLowerCase().includes(lowerIngredients); })(); const matchesDelivery = selectedDeliveryId === "0" || (selectedDeliveryId === "pickup" && item.pickupServe) || (selectedDeliveryId === "inPlace" && item.inPlaceServe); return matchesCategory && matchesSearch && matchesIngredients && matchesDelivery; }); const sortingKey = sortings[Number(sorting)] || sortings[0]; return [...filtered].sort((a, b) => { switch (sortingKey) { case "PopularityDescending": case "RateDescending": return 0; case "PriceAscending": return (a.price ?? 0) - (b.price ?? 0); case "PriceDescending": return (b.price ?? 0) - (a.price ?? 0); default: return 0; } }); }, [selectedCategory, search, selectedIngredients, selectedDeliveryId, foods, sorting]); if (isLoading) { return ; } return (
{selectedCategory === "0" ? "" : categories.find((c) => c.id === selectedCategory)?.title || ""}
{filteredReceiptItems.length === 0 ? (
{foodsData ? "غذایی یافت نشد" : "در حال بارگذاری..."}
) : ( filteredReceiptItems.map((food) => ( )) )}
{}} tMenu={tMenu} />
); }; export default MenuIndex;