diff --git a/src/app/[name]/(Main)/components/CategoryScroll.tsx b/src/app/[name]/(Main)/components/CategoryScroll.tsx index 63012b1..3bfe486 100644 --- a/src/app/[name]/(Main)/components/CategoryScroll.tsx +++ b/src/app/[name]/(Main)/components/CategoryScroll.tsx @@ -19,17 +19,7 @@ const SVG_MASK_STYLE = { WebkitMaskPosition: "center", } as const; -function CategoryImage({ - src, - size, - alt, - tintWithPrimary = true, -}: { - src: string; - size: number; - alt: string; - tintWithPrimary?: boolean; -}) { +function CategoryImage({ src, size, alt, tintWithPrimary = true }: { src: string; size: number; alt: string; tintWithPrimary?: boolean }) { const isSvg = src.endsWith(".svg"); const shouldUseSvgMask = isSvg && tintWithPrimary; @@ -93,21 +83,11 @@ type Props = { className?: string; }; -const CategoryScroll = ({ - categories, - selectedCategory, - onSelect, - variant = "large", - className, -}: Props) => { +const CategoryScroll = ({ categories, selectedCategory, onSelect, variant = "large", className }: Props) => { const segment = usePathname()?.split("/").filter(Boolean)[0]; - const usesColoredSvg = - segment != null && - COLORED_SVG_RESTAURANT_SLUGS.has(segment.toLowerCase()); + const usesColoredSvg = segment != null && COLORED_SVG_RESTAURANT_SLUGS.has(segment.toLowerCase()); const { renderer: Renderer, imageSize } = variantConfig[variant]; - const selectedParent = - categories.find((c) => c.id === selectedCategory) ?? - categories.find((c) => c.children?.some((ch) => ch.id === selectedCategory)); + const selectedParent = categories.find((c) => c.id === selectedCategory) ?? categories.find((c) => c.children?.some((ch) => ch.id === selectedCategory)); const children = selectedParent?.children ?? []; const handleSelect = (categoryId: string) => () => onSelect(categoryId); @@ -118,63 +98,31 @@ const CategoryScroll = ({ className={clsx( "w-full noscrollbar", children.length > 0 ? "pt-4! pb-1!" : "py-4!", - variant === "large" && "mt-4!", + // variant === "large" && "mt-4!", className, )} > {categories.map((item) => { - const isSelected = - item.id === selectedCategory || - item.children?.some((ch) => ch.id === selectedCategory); + const isSelected = item.id === selectedCategory || item.children?.some((ch) => ch.id === selectedCategory); return ( - - - - {item.title} - + + + {item.title} ); })} {children.length > 0 && ( - + {children.map((child) => { const isChildSelected = child.id === selectedCategory; return ( - - {child.avatarUrl && ( - - )} - - {child.title} - + + {child.avatarUrl && } + {child.title} ); })} diff --git a/src/app/[name]/(Main)/page.tsx b/src/app/[name]/(Main)/page.tsx index d612f14..550b01f 100644 --- a/src/app/[name]/(Main)/page.tsx +++ b/src/app/[name]/(Main)/page.tsx @@ -1,21 +1,21 @@ -'use client'; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import SearchBox from "@/components/input/SearchBox"; -import TextAlignIcon from "@/components/icons/TextAlignIcon"; -import VerticalScrollView from "@/components/listview/VerticalScrollView"; -import MenuItemRenderer from "@/components/listview/MenuItemRenderer"; -import MenuItem, { type MenuItemViewMode } from "@/components/listview/MenuItem"; -import { useQueryState } from "next-usequerystate"; -import clsx from "clsx"; -import { motion } from "framer-motion"; -import { useTranslation } from "react-i18next"; -import useToggle from "@/hooks/helpers/useToggle"; +"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 { useGetCategories, useGetProducts } from "./hooks/useMenuData"; -import type { Product, Category } from "./types/Types"; +import TextAlignIcon from "@/components/icons/TextAlignIcon"; +import SearchBox from "@/components/input/SearchBox"; +import MenuItem, { type MenuItemViewMode } 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 { RowHorizontal, RowVertical } from "iconsax-react"; +import { useQueryState } from "next-usequerystate"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useGetCategories, useGetProducts } from "./hooks/useMenuData"; +import type { Category, Product } from "./types/Types"; const sortings = ["PopularityDescending", "RateDescending", "PriceAscending", "PriceDescending"] as const; @@ -26,13 +26,13 @@ const MenuIndex = () => { const categories = useMemo(() => categoriesData?.data || [], [categoriesData?.data]); const isLoading = productsLoading || categoriesLoading; - const { t: tCommon } = useTranslation('common'); - const { t: tMenu } = useTranslation('menu', { keyPrefix: "Menu" }); + const { t: tCommon } = useTranslation("common"); + const { t: tMenu } = useTranslation("menu", { keyPrefix: "Menu" }); const { state: filterSortModal, toggle: toggleFilterSortModal } = useToggle(); - const [sorting, setSorting] = useQueryState('sortBy', { defaultValue: '0' }); - const [search, setSearch] = useQueryState("q", { defaultValue: '' }); - const [selectedCategory, setSelectedCategory] = useQueryState('category', { defaultValue: '0' }); - const [filterSearch, setFilterSearch] = useQueryState('ingredients', { defaultValue: '' }); + const [sorting, setSorting] = useQueryState("sortBy", { defaultValue: "0" }); + const [search, setSearch] = useQueryState("q", { defaultValue: "" }); + const [selectedCategory, setSelectedCategory] = useQueryState("category", { defaultValue: "0" }); + const [filterSearch, setFilterSearch] = useQueryState("ingredients", { defaultValue: "" }); const smallCategoriesRef: React.RefObject = useRef(null); const wrapperRef: React.RefObject = useRef(null); const [smallCategoriesVisible, setSmallCategoriesVisibility] = useState(false); @@ -76,38 +76,49 @@ const MenuIndex = () => { const parent = wrapperRef.current.parentElement?.parentElement; if (!parent) return; - parent.addEventListener('scroll', onScroll); + parent.addEventListener("scroll", onScroll); return () => { - parent.removeEventListener('scroll', onScroll); - } + parent.removeEventListener("scroll", onScroll); + }; }, [onScroll]); + const updateSearch = useCallback( + (e: React.ChangeEvent) => { + setSearch(e.target.value); + }, + [setSearch], + ); - const updateSearch = useCallback((e: React.ChangeEvent) => { - setSearch(e.target.value); - }, [setSearch]); - - const updateCategory = useCallback((categoryId: string) => { - setSelectedCategory(categoryId); - }, [setSelectedCategory]); + const updateCategory = useCallback( + (categoryId: string) => { + setSelectedCategory(categoryId); + }, + [setSelectedCategory], + ); const sortingIndex = Number(sorting); const activeSortingIndex = Number.isNaN(sortingIndex) ? 0 : sortingIndex; - const changeSorting = useCallback((index: number) => { - setSorting(() => String(index)); - }, [setSorting]); + const changeSorting = useCallback( + (index: number) => { + setSorting(() => String(index)); + }, + [setSorting], + ); - const changeFilterSearch = useCallback((value: string) => { - setFilterSearch(value); - }, [setFilterSearch]); + const changeFilterSearch = useCallback( + (value: string) => { + setFilterSearch(value); + }, + [setFilterSearch], + ); const filteredReceiptItems = useMemo(() => { if (!products.length) return []; const lowerSearch = search.toLowerCase(); const lowerFilterQuery = filterSearch ? filterSearch.toLowerCase() : ""; - const selectedCatId = selectedCategory === '0' ? null : selectedCategory; + const selectedCatId = selectedCategory === "0" ? null : selectedCategory; // اگر دستهٔ اصلی انتخاب شده، خودش + همهٔ زیردسته‌ها را در نظر بگیر تا غذاهای زیردسته هم بیایند const effectiveCategoryIds = (() => { @@ -120,24 +131,20 @@ const MenuIndex = () => { })(); const filtered = products.filter((item) => { - const matchesCategory = - !selectedCatId || - (item.category?.id != null && effectiveCategoryIds?.has(item.category.id)); + const matchesCategory = !selectedCatId || (item.category?.id != null && effectiveCategoryIds?.has(item.category.id)); const itemName = item.name ?? item.title; - const matchesSearch = - !search || itemName.toLowerCase().includes(lowerSearch); + const matchesSearch = !search || itemName.toLowerCase().includes(lowerSearch); // جستجو در عنوان و توضیحات (و در صورت وجود در محتویات) - const matchesFilterSearch = !filterSearch || + const matchesFilterSearch = + !filterSearch || (() => { const titleMatch = itemName.toLowerCase().includes(lowerFilterQuery); const description = item.description || item.desc || ""; const descMatch = description.toLowerCase().includes(lowerFilterQuery); if (titleMatch || descMatch) return true; if (item.content && Array.isArray(item.content) && item.content.length > 0) { - return item.content.some((content: string) => - content.toLowerCase().includes(lowerFilterQuery) - ); + return item.content.some((content: string) => content.toLowerCase().includes(lowerFilterQuery)); } return false; })(); @@ -168,30 +175,22 @@ const MenuIndex = () => { } return ( -
+
- - + +
-
- - {selectedCategory === '0' ? '' : categories.find(c => c.id === selectedCategory)?.title || ''} - +
+ {selectedCategory === "0" ? "" : categories.find((c) => c.id === selectedCategory)?.title || ""}
-
@@ -223,24 +217,15 @@ const MenuIndex = () => { - + transition={{ duration: 0.1 }} + className={clsx("fixed left-0 z-10 top-0 px-4 pt-16 bg-[#F4F5F9CC] dark:bg-background/70 backdrop-blur-[44px] right-0 xl:pr-72 xl:pt-20", ``)} + > + {filteredReceiptItems.length === 0 ? ( -
- {productsData ? 'کالایی یافت نشد' : 'در حال بارگذاری...'} -
+
{productsData ? "کالایی یافت نشد" : "در حال بارگذاری..."}
) : viewMode === "grid" ? (
{filteredReceiptItems.map((product) => ( @@ -264,7 +249,7 @@ const MenuIndex = () => { onClose={toggleFilterSortModal} searchQuery={filterSearch} onSearchChange={changeFilterSearch} - onApply={() => { }} + onApply={() => {}} sortings={sortings} activeSortIndex={activeSortingIndex} onSortChange={changeSorting}