sort foods
This commit is contained in:
+1
-1
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>
|
||||
);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
"Options": {
|
||||
"PopularityDescending": "محبوب ترین",
|
||||
"RateDescending": "بالاترین امتیاز",
|
||||
"DateDescending": "جدیدترین",
|
||||
"PriceAscending": "ارزان ترین",
|
||||
"PriceDescending": "گران ترین"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user