sort foods

This commit is contained in:
hamid zarghami
2025-11-25 12:49:38 +03:30
parent 69dfb50267
commit 3d6a8c4877
7 changed files with 84 additions and 60 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
@@ -1,12 +1,14 @@
'use client';
import AnimatedBottomSheet from "@/components/bottomsheet/AnimatedBottomSheet";
import clsx from "clsx";
import React from "react";
type MenuSortingDrawerProps = {
visible: boolean;
onClose: () => void;
sortings: string[];
sortings: ReadonlyArray<string>;
activeIndex: number;
onSelect: (index: number) => void;
tMenu: (key: string) => string;
};
@@ -15,6 +17,7 @@ const MenuSortingDrawer = ({
visible,
onClose,
sortings,
activeIndex,
onSelect,
tMenu,
}: MenuSortingDrawerProps) => {
@@ -25,20 +28,23 @@ const MenuSortingDrawer = ({
inDelay={150}
onClick={onClose}
>
<div className="px-8.5 py-10 justify-between">
{sortings.map((value, index) => (
<div key={value}>
<div
<div className="px-8.5 py-10 flex flex-col gap-2">
{sortings.map((value, index) => {
const isActive = index === activeIndex;
return (
<button
key={value}
type="button"
onClick={() => onSelect(index)}
className="text-sm2 font-normal cursor-pointer"
className={clsx(
"flex items-center justify-between py-3 text-sm2 transition-colors",
isActive ? "text-primary font-semibold" : "text-foreground"
)}
>
{tMenu(`MenuSortingDrawer.Options.${value}`)}
</div>
{index < sortings.length - 1 && (
<hr className="border-white/40 dark:border-border mb-4 mt-4" />
)}
</div>
))}
<span>{tMenu(`MenuSortingDrawer.Options.${value}`)}</span>
</button>
);
})}
</div>
</AnimatedBottomSheet>
);
+43 -30
View File
@@ -1,5 +1,4 @@
'use client';
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import SearchBox from "@/components/input/SearchBox";
import TextAlignIcon from "@/components/icons/TextAlignIcon";
@@ -18,29 +17,18 @@ import MenuSortingDrawer from "@/app/[name]/(Main)/components/MenuSortingDrawer"
import { useGetCategories, useGetFoods } from "./hooks/useMenuData";
import type { Food, Category } from "./types/Types";
const sortings = [
"PopularityDescending",
"DateDescending",
"PriceAscending",
"RateDescending",
"PriceDescending"
];
const sortings = ["PopularityDescending", "RateDescending", "PriceAscending", "PriceDescending"] as const;
const MenuIndex = () => {
const { data: foodsData } = useGetFoods();
const { data: categoriesData } = useGetCategories();
const foods = useMemo<Food[]>(() => foodsData?.data || [], [foodsData?.data]);
const categories = useMemo<Category[]>(() => categoriesData?.data || [], [categoriesData?.data]);
const { t: tCommon } = useTranslation('common');
const { t: tMenu } = useTranslation('menu', {
keyPrefix: "Menu"
});
const { t: tMenu } = useTranslation('menu', { keyPrefix: "Menu" });
const { state: filterModal, toggle: toggleFilterModal } = useToggle();
const { state: sortingModal, toggle: toggleSortingModal } = useToggle();
const [, setSorting] = useQueryState('sortBy', { defaultValue: '0' });
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: '0' });
@@ -81,9 +69,15 @@ const MenuIndex = () => {
setSelectedCategory(String(index));
}, [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);
@@ -95,22 +89,36 @@ const MenuIndex = () => {
const filteredReceiptItems = useMemo(() => {
if (!foods.length) return [];
const lowerSearch = search.toLowerCase();
const selectedCatId = selectedCategory === '0' ? null : categories[+selectedCategory - 1]?.id;
const selectedCatId =
selectedCategory === "0" ? null : categories[+selectedCategory - 1]?.id;
return foods.filter(
(item) => {
const matchesCategory =
selectedCategory === '0' || item.category?.id === selectedCatId;
const itemName = item.name || item.title || item.foodName || '';
const matchesSearch = !search || itemName.toLowerCase().includes(lowerSearch);
return matchesCategory && matchesSearch;
const filtered = foods.filter((item) => {
const matchesCategory =
selectedCategory === "0" || item.category?.id === selectedCatId;
const itemName = item.name || item.title || item.foodName || "";
const matchesSearch =
!search || itemName.toLowerCase().includes(lowerSearch);
return matchesCategory && matchesSearch;
});
const sortingKey = sortings[Number(sorting)] || sortings[0];
return [...filtered].sort((a, b) => {
switch (sortingKey) {
case "PopularityDescending":
return (b.points ?? 0) - (a.points ?? 0);
case "RateDescending":
return (b.rate ?? 0) - (a.rate ?? 0);
case "PriceAscending":
return (a.price ?? 0) - (b.price ?? 0);
case "PriceDescending":
return (b.price ?? 0) - (a.price ?? 0);
default:
return 0;
}
);
}, [selectedCategory, search, foods, categories]);
});
}, [selectedCategory, search, foods, categories, sorting]);
return (
<div className="flex flex-col gap-4 items-center pt-8 mb-8" ref={wrapperRef}>
<div className="w-full">
@@ -132,8 +140,12 @@ const MenuIndex = () => {
<Candle2 className="stroke-foreground" size={16} />
<span className="text-xs leading-5 font-medium">{tMenu('MenuFilterDrawer.Label')}</span>
</button>
<button onClick={toggleSortingModal} className="rounded-lg h-8 bg-container p-1.5">
<button
onClick={toggleSortingModal}
className="rounded-xl h-8 bg-container ps-4 pe-2 py-1.5 inline-flex items-center gap-2"
>
<TextAlignIcon className="text-foreground" />
<span className="text-xs leading-5 font-medium">{activeSortingLabel}</span>
</button>
</div>
</div>
@@ -183,6 +195,7 @@ const MenuIndex = () => {
visible={sortingModal}
onClose={toggleSortingModal}
sortings={sortings}
activeIndex={activeSortingIndex}
onSelect={changeSorting}
tMenu={tMenu}
/>
+10 -13
View File
@@ -2,11 +2,12 @@ import PreferenceWrapper from '@/components/wrapper/PreferenceWrapper'
import React from 'react'
import type { Metadata, Viewport } from 'next'
type LayoutParams = { name: string }
export async function generateMetadata ({
params
}: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: any
params: Promise<LayoutParams>
}): Promise<Metadata> {
const { name } = await params
return {
@@ -16,21 +17,17 @@ export async function generateMetadata ({
}
}
export async function generateViewport ({}: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: any
}): Promise<Viewport> {
export async function generateViewport (): Promise<Viewport> {
return {
themeColor: '#F4F5F9'
}
}
type Props = {
children: React.ReactElement
}
function layout ({ children }: Props) {
export default function Layout ({
children
}: {
children: React.ReactNode
params: Promise<LayoutParams>
}): React.ReactNode {
return <PreferenceWrapper>{children}</PreferenceWrapper>
}
export default layout
+3
View File
@@ -1,4 +1,7 @@
export enum AUTH_STEP {
ENTER_NUMBER = 0,
ENTER_OTP = 1,
ENTER_CREATE_PASSWORD = 2,
ENTER_CURRENT_PASSWORD = 3,
ENTER_RESET_PASSWORD = 4
}
@@ -6,6 +6,7 @@ import { AUTH_PAGE_ELEMENT, AUTH_STEP } from '@/enums'
import { LoginOTPRequestType } from '@/app/auth/login/types/Types'
import Image from 'next/image'
import React, { useState } from 'react'
import type { ChangeEvent } from 'react'
import { useTranslation } from 'react-i18next'
import { useFormik } from 'formik'
import * as Yup from 'yup'
@@ -15,9 +16,14 @@ import { toast } from '@/components/Toast'
import StepOtp from './StepOtp'
import { extractErrorMessage } from '@/lib/func'
type StepEnterNumberProps = {
pending?: boolean
onChange?: (event: ChangeEvent<HTMLInputElement>) => void
value?: string
}
function StepEnterNumber() {
function StepEnterNumber (_props: StepEnterNumberProps = {}) {
void _props
const { t } = useTranslation('auth')
const { name } = useParams()
-1
View File
@@ -29,7 +29,6 @@
"Options": {
"PopularityDescending": "محبوب ترین",
"RateDescending": "بالاترین امتیاز",
"DateDescending": "جدیدترین",
"PriceAscending": "ارزان ترین",
"PriceDescending": "گران ترین"
}