reason: locale removed from url
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
'use client'
|
||||
|
||||
import MinusIcon from '@/components/icons/MinusIcon'
|
||||
import PlusIcon from '@/components/icons/PlusIcon'
|
||||
import { useReceiptStore } from '@/zustand/receiptStore'
|
||||
import { motion } from 'framer-motion'
|
||||
import { ArrowLeft, Clock, Cup, Heart, TruckTick } from 'iconsax-react'
|
||||
import Image from 'next/image'
|
||||
import React from 'react'
|
||||
|
||||
type Props = object
|
||||
|
||||
function FoodPage({ }: Props) {
|
||||
const quantity = useReceiptStore(state => state.items[0]?.quantity || 0);
|
||||
const increment = useReceiptStore(state => state.increment);
|
||||
const decrement = useReceiptStore(state => state.decrement);
|
||||
|
||||
const price = 0; // TODO: Replace with actual price if needed
|
||||
|
||||
return (
|
||||
<div className='w-full -translate-y-2 pt-6'>
|
||||
<div className="relative w-full rounded-2xl overflow-hidden">
|
||||
<Image
|
||||
className='w-full'
|
||||
src={'/assets/images/food-preview-hq.png'}
|
||||
alt='Food image'
|
||||
height={100}
|
||||
width={100}
|
||||
unoptimized
|
||||
priority
|
||||
/>
|
||||
<div className="absolute top-4 left-0 pe-5.5 ps-7 w-full inline-flex justify-between items-center">
|
||||
<div className="bg-white rounded-[34px] py-1.5 px-4 w-min text-xs text-disabled2 font-bold">
|
||||
خوراک
|
||||
</div>
|
||||
<button>
|
||||
<ArrowLeft size={24} className='stroke-white' />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative px-6 pt-6 pb-7.5 bg-white w-full rounded-3xl overflow-hidden -translate-y-6">
|
||||
<div className="w-full inline-flex justify-between items-center">
|
||||
<h5 className="text-base font-bold">
|
||||
کباب چوبی مخصوص
|
||||
</h5>
|
||||
<button className='p-2 bg-[#EAECF0] rounded-lg'>
|
||||
<Heart size={24} className='fill-black' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 text-xs">
|
||||
<div className='flex items-center gap-1'>
|
||||
<Clock size={14} className='stroke-disabled-text' />
|
||||
<span className='text-disabled-text'>زمان پخت و آماده سازی: 25 دقیقه</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-2 mt-2'>
|
||||
<Cup size={14} className='stroke-disabled-text' />
|
||||
<span className='text-disabled-text'>1500 امتیاز برای هر بار خرید</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-2 mt-2'>
|
||||
<TruckTick size={14} className='stroke-disabled-text' />
|
||||
<span className='text-disabled-text'>ارسال با پیک</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 text-xs">
|
||||
<p>محتویات:</p>
|
||||
<p>گوشت، سبزیجات، پیاز، ادویه جات معطر و ....</p>
|
||||
</div>
|
||||
|
||||
<div className='mt-12 flex justify-between items-center'>
|
||||
<span dir='ltr'>{price} T</span>
|
||||
<motion.div
|
||||
whileTap={{ scale: 1.05 }}
|
||||
className="bg-background active:drop-shadow-xs max-w-[115px] rounded-md w-full h-8 inline-flex p-1 justify-between items-center gap-2"
|
||||
>
|
||||
{quantity <= 0 ? (
|
||||
<button
|
||||
onClick={() => increment(0)}
|
||||
className="inline-flex w-full justify-center items-center gap-2"
|
||||
>
|
||||
<PlusIcon />
|
||||
<span className="text-sm2 pt-0.5 font-medium">افزودن</span>
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
onClick={() => increment(0)}
|
||||
className="bg-white hover:bg-white/60 active:bg-white/30 rounded-sm p-2"
|
||||
>
|
||||
<PlusIcon />
|
||||
</button>
|
||||
<motion.div
|
||||
key={quantity}
|
||||
initial={{ scale: 1 }}
|
||||
animate={{ scale: [1.2, 0.95, 1] }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="text-sm2 pt-0.5 font-semibold"
|
||||
>
|
||||
{quantity}
|
||||
</motion.div>
|
||||
<button
|
||||
onClick={() => decrement(0)}
|
||||
className="bg-white hover:bg-white/60 active:bg-white/30 rounded-sm p-2"
|
||||
>
|
||||
<MinusIcon />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FoodPage
|
||||
@@ -0,0 +1,214 @@
|
||||
'use client'
|
||||
import AnimatedBottomSheet from '@/components/bottomsheet/AnimatedBottomSheet';
|
||||
import EqualizerIcon from '@/components/icons/EqualizerIcon';
|
||||
import TabContainer from '@/components/tab/TabContainer';
|
||||
import { TabHeader } from '@/components/tab/TabHeader';
|
||||
import Comment from '@/components/utils/Comment';
|
||||
import RateBar from '@/components/utils/RateBar';
|
||||
import { AboutDataModel } from '@/lib/api/info/getAboutData';
|
||||
import { ArrowDown2, CallCalling, Clock, Gallery, InfoCircle, Instagram, Location, MouseCircle, Star1, Whatsapp } from 'iconsax-react';
|
||||
import { useQueryState } from 'next-usequerystate';
|
||||
import Image from 'next/image';
|
||||
import React, { useCallback, useState } from 'react'
|
||||
|
||||
const sortings = [
|
||||
'جدیدترین',
|
||||
'قدیمی ترین'
|
||||
]
|
||||
|
||||
function AboutPage({ data }: Readonly<{ data: AboutDataModel; }>) {
|
||||
const [sortingModal, setSortingModal] = useState(false);
|
||||
const [sorting, setSorting] = useQueryState('sortBy', { defaultValue: '0' });
|
||||
|
||||
const toggleSortingModal = useCallback(() => {
|
||||
setSortingModal((state) => !state);
|
||||
}, []);
|
||||
|
||||
const changeSorting = (index: number) => {
|
||||
setSorting(() => String(index));
|
||||
setSortingModal(false);
|
||||
}
|
||||
|
||||
const firstTab = () => {
|
||||
return (
|
||||
<section aria-labelledby="about-title" className='py-4'>
|
||||
<section
|
||||
className="bg-container rounded-container shadow-container p-4">
|
||||
<div className="flex justify-between items-center border-b-[1.5px] border-gray-200 pb-[25px]">
|
||||
<div className="">
|
||||
<h2 className='text-sm2 font-bold leading-5'>{data.title_fa}</h2>
|
||||
<p className="text-sm2 leading-5 mt-4">تاسیس: {data.foundationDate}</p>
|
||||
<p className="text-sm2 leading-5 mt-2">نوع محصولات: {data.offering}</p>
|
||||
</div>
|
||||
<div className="rounded-normal overflow-clip">
|
||||
<Image
|
||||
alt='logo'
|
||||
src={'/assets/images/food-preview.png'}
|
||||
width={88}
|
||||
height={88}
|
||||
unoptimized
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-[23px]">
|
||||
<h3 className="text-sm2 font-bold leading-5">درباره مجموعه</h3>
|
||||
<p className="text-sm2 leading-5 mt-3 border-spacing-48" style={{ wordSpacing: '0.01em' }}>
|
||||
{data.description}
|
||||
</p>
|
||||
<div className='inline-flex gap-2 mt-[23px] items-center'>
|
||||
<Gallery size={20} className='stroke-disabled-text' />
|
||||
<span className='text-sm2 text-disabled-text font-medium pt-0.5'>عکس های رستوران</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
className="bg-container rounded-container shadow-container pt-3 pb-3.5 px-[16px] mt-4 grid grid-cols-3 items-center">
|
||||
<h2 className='text-sm2 font-medium leading-5'>ارتباط</h2>
|
||||
<div className='col-span-1 text-center flex justify-center gap-4'>
|
||||
{data.contacts.phone &&
|
||||
<a href={`tel://${data.contacts.phone}`} className='bg-[#EAEDF5] p-2 rounded-normal'>
|
||||
<CallCalling className='stroke-foreground' size={24} />
|
||||
</a>
|
||||
}
|
||||
{data.contacts.telegram &&
|
||||
<a href={`https://t.me/${data.contacts.telegram}`} className='bg-[#EAEDF5] p-2 rounded-normal'>
|
||||
<MouseCircle variant='Linear' className='stroke-foreground' size={24} />
|
||||
</a>
|
||||
}
|
||||
{data.contacts.whatsapp &&
|
||||
<a href={`https://whatsapp.com/?phone=${data.contacts.whatsapp}`} className='bg-[#EAEDF5] p-2 rounded-normal'>
|
||||
<Whatsapp className='stroke-foreground' size={24} />
|
||||
</a>
|
||||
}
|
||||
{data.contacts.instagram &&
|
||||
<a href={`https://instagram.com/${data.contacts.instagram}`} className='bg-[#EAEDF5] p-2 rounded-normal'>
|
||||
<Instagram className='stroke-foreground' size={24} />
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="bg-container rounded-container shadow-container px-4 pt-6 pb-6 mt-4">
|
||||
<h2 className='text-sm2 font-medium leading-5'>آدرس</h2>
|
||||
<p className='text-sm2 mt-[9px] leading-5'>{data.address}</p>
|
||||
<div className='inline-flex gap-2 mt-[23px] items-center '>
|
||||
<Location size={20} className='stroke-disabled-text' />
|
||||
<span className='text-sm2 text-disabled-text '>عکس های رستوران</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
aria-label='ساعات کاری'
|
||||
className="bg-container rounded-container shadow-container py-6 px-4 mt-4 flex justify-between items-center">
|
||||
<div className="flex items-center gap-2 justify-start">
|
||||
<Clock size={16} className='stroke-disabled-text' />
|
||||
<div className='text-sm2 font-medium leading-5 mt-0.5 text-[#8C90A3]'>باز</div>
|
||||
<div className='size-1.5 bg-[#D9D9D9] rounded-full'></div>
|
||||
<div className='text-sm2 mt-0.5'>امروز از ساعت 10:30 تا 17:30 </div>
|
||||
</div>
|
||||
<div className="">
|
||||
<ArrowDown2 size={16} className='stroke-[#292D32]' />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
{data.open.map((v, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{ boxShadow: '0px 0px 14px 0px #0000000F' }}
|
||||
className="bg-[#F6F6FA] rounded-container leading-5 py-6 px-4 mt-4 flex justify-between items-center">
|
||||
<div className="text-sm2">
|
||||
{v.day}
|
||||
</div>
|
||||
<div className='text-sm2 font-light'>
|
||||
{v.close} - {v.open}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</section>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
const secondTab = () => {
|
||||
|
||||
return (
|
||||
<section aria-labelledby="reviews-title" className='py-4'>
|
||||
<section
|
||||
aria-label='امتیاز کاربران'
|
||||
className="bg-container rounded-container shadow-container p-4 py-6 grid grid-cols-2 items-center">
|
||||
<div className="text-center font-bold text-5xl">
|
||||
4.1
|
||||
</div>
|
||||
<div className="">
|
||||
<RateBar className='mt-1' content='5' percentage='55' />
|
||||
<RateBar className='mt-1' content='4' percentage='32' />
|
||||
<RateBar className='mt-1' content='3' percentage='70' />
|
||||
<RateBar className='mt-1' content='2' percentage='12' />
|
||||
<RateBar className='mt-1' content='1' percentage='89' />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="bg-container rounded-container shadow-container pt-3 pb-3.5 px-[16px] mt-4">
|
||||
<div className="flex justify-between items-center border-b-[1.5px] border-border pb-2">
|
||||
<h2 className='text-sm2 font-medium leading-5'>نظرات کاربران</h2>
|
||||
<button onClick={toggleSortingModal} className="rounded-xl h-8 bg-white ps-4 pe-2 py-1.5 inline-flex items-center justify-between gap-[7px]">
|
||||
<EqualizerIcon />
|
||||
<span className="text-xs leading-5 font-medium">{sortings[+sorting]}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="">
|
||||
|
||||
<article>
|
||||
<Comment
|
||||
className='pt-8'
|
||||
user='علیرضا عابدزاده'
|
||||
rating={5.0}
|
||||
date='18 آذر 1401'
|
||||
text='لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است'
|
||||
tags={['پیتزا قارچ مخصوص', 'چیز برگر ژیوان']}
|
||||
/>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
<AnimatedBottomSheet title="مرتب کردن بر اساس" visible={sortingModal} inDelay={150} onClick={toggleSortingModal}>
|
||||
<div className="px-8.5 py-10 justify-between">
|
||||
{sortings.map((v, i) => {
|
||||
return (
|
||||
<div key={i}>
|
||||
<div onClick={() => changeSorting(i)} className="text-sm2 font-medium cursor-pointer">
|
||||
{v}
|
||||
</div>
|
||||
{i < sortings.length - 1 && <hr className="text-white/40 mb-4 mt-4" />}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</AnimatedBottomSheet>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='pt-8 h-full overflow-y-auto noscrollbar'>
|
||||
<TabContainer>
|
||||
<TabHeader
|
||||
viewRenderer={firstTab()}
|
||||
title='درباره ما' icon={<InfoCircle size={24} />}>
|
||||
</TabHeader>
|
||||
<TabHeader
|
||||
viewRenderer={secondTab()}
|
||||
title='امتیازات و نظرات' icon={<Star1 size={24} />}>
|
||||
</TabHeader>
|
||||
</TabContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default AboutPage
|
||||
@@ -0,0 +1,36 @@
|
||||
import React from 'react'
|
||||
import { Metadata } from 'next';
|
||||
import { getAboutData } from '@/lib/api/info/getAboutData';
|
||||
|
||||
export type AboutPageProps = { name: string }
|
||||
|
||||
type Params = { name: string };
|
||||
|
||||
export const revalidate = 60
|
||||
|
||||
export const dynamicParams = false // or false, to 404 on unknown paths
|
||||
|
||||
export async function generateMetadata({ params }: { params: Promise<Params> }): Promise<Metadata> {
|
||||
const { name } = await params;
|
||||
const data = await getAboutData(name);
|
||||
|
||||
return {
|
||||
title: data.metadata.title || `About | ${name}`,
|
||||
description: data.metadata.description || `This is the about-us page for ${name}.`,
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return [
|
||||
{ name: 'zhivan' },
|
||||
{ name: 'boote' },
|
||||
];
|
||||
}
|
||||
|
||||
async function layout({ children }: Readonly<{ children: React.ReactNode; params: Promise<Params> }>) {
|
||||
return (
|
||||
<>{children}</>
|
||||
)
|
||||
}
|
||||
|
||||
export default layout
|
||||
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import AboutPage from './AboutPage';
|
||||
import { getAboutData } from '@/lib/api/info/getAboutData';
|
||||
|
||||
type Params = { name: string };
|
||||
|
||||
export default async function Page({ params }: { params: Promise<Params> }) {
|
||||
const { name } = await params;
|
||||
const data = await getAboutData(name);
|
||||
|
||||
return (
|
||||
<><AboutPage data={data} /></>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
'use client';
|
||||
|
||||
import { EmojiHappy, Microphone2, Paperclip2 } from 'iconsax-react';
|
||||
import React from 'react'
|
||||
|
||||
type Props = object
|
||||
|
||||
function ChatIndex({ }: Props) {
|
||||
|
||||
const submitMessage = async (formData: FormData) => {
|
||||
console.log(formData.get('textMessage'))
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="flex flex-col h-full pt-6">
|
||||
<h1 className="font-medium">علی مصلحی</h1>
|
||||
|
||||
<div className="bg-container rounded-[30px] mt-6 flex flex-col flex-1 px-6 pt-7 pb-8 overflow-hidden">
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<ul className="grid gap-6 pb-6 ">
|
||||
<li className='bg-[#F6F7FA] rounded-[30px] rounded-tr-none p-6'>
|
||||
<article>
|
||||
<p className='text-xs2'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است
|
||||
</p>
|
||||
|
||||
<span dir='ltr' className='text-[11px] float-end text-disabled-text mt-2.5'>
|
||||
10:07 | 1403/09/30
|
||||
</span>
|
||||
</article>
|
||||
</li>
|
||||
|
||||
<li className='bg-[#EBEDF5] rounded-[30px] rounded-tl-none p-6'>
|
||||
<article>
|
||||
<h6 className='text-xs font-medium'>
|
||||
سیما فرهادی
|
||||
</h6>
|
||||
<p className='text-xs2 mt-2.5'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است،
|
||||
</p>
|
||||
|
||||
<span dir='ltr' className='text-[11px] float-end text-disabled-text mt-2.5'>
|
||||
10:07 | 1403/09/30
|
||||
</span>
|
||||
</article>
|
||||
</li>
|
||||
|
||||
<li className='bg-[#F6F7FA] rounded-[30px] rounded-tr-none p-6'>
|
||||
<article>
|
||||
<p className='text-xs2'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است
|
||||
</p>
|
||||
|
||||
<span dir='ltr' className='text-[11px] float-end text-disabled-text mt-2.5'>
|
||||
10:07 | 1403/09/30
|
||||
</span>
|
||||
</article>
|
||||
</li>
|
||||
|
||||
<li className='bg-[#EBEDF5] rounded-[30px] rounded-tl-none p-6'>
|
||||
<article>
|
||||
<h6 className='text-xs font-medium'>
|
||||
سیما فرهادی
|
||||
</h6>
|
||||
<p className='text-xs2 mt-2.5'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است،
|
||||
</p>
|
||||
|
||||
<span dir='ltr' className='text-[11px] float-end text-disabled-text mt-2.5'>
|
||||
10:07 | 1403/09/30
|
||||
</span>
|
||||
</article>
|
||||
</li>
|
||||
|
||||
<li className='bg-[#F6F7FA] rounded-[30px] rounded-tr-none p-6'>
|
||||
<article>
|
||||
<p className='text-xs2'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است
|
||||
</p>
|
||||
|
||||
<span dir='ltr' className='text-[11px] float-end text-disabled-text mt-2.5'>
|
||||
10:07 | 1403/09/30
|
||||
</span>
|
||||
</article>
|
||||
</li>
|
||||
|
||||
<li className='bg-[#EBEDF5] rounded-[30px] rounded-tl-none p-6'>
|
||||
<article>
|
||||
<h6 className='text-xs font-medium'>
|
||||
سیما فرهادی
|
||||
</h6>
|
||||
<p className='text-xs2 mt-2.5'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است،
|
||||
</p>
|
||||
|
||||
<span dir='ltr' className='text-[11px] float-end text-disabled-text mt-2.5'>
|
||||
10:07 | 1403/09/30
|
||||
</span>
|
||||
</article>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<section aria-labelledby="ارسال پیام" className="w-full">
|
||||
<form
|
||||
action={submitMessage}
|
||||
className="focus-within:outline-blue-400 outline outline-transparent transition-colors duration-100 flex items-center gap-0 bg-transparent border border-[#D0D0D0] h-12 rounded-xl py-2.5 px-2"
|
||||
>
|
||||
<button type="button" className="active:bg-neutral-100 transition-colors duration-150 rounded-full p-1">
|
||||
<Microphone2 className="stroke-foreground" size={24} />
|
||||
</button>
|
||||
<input
|
||||
name="textMessage"
|
||||
role="textbox"
|
||||
autoComplete="off"
|
||||
className="w-full text-[11px] h-full outline-none ms-1"
|
||||
type="text"
|
||||
placeholder="پیام خود را بنویسید"
|
||||
/>
|
||||
<button type="button" className="active:bg-neutral-100 transition-colors duration-150 rounded-full p-1.5">
|
||||
<EmojiHappy className="stroke-disabled2" size={20} />
|
||||
</button>
|
||||
<button type="button" className="active:bg-neutral-100 transition-colors duration-150 rounded-full p-1.5">
|
||||
<Paperclip2 className="stroke-disabled2" size={20} />
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChatIndex
|
||||
@@ -0,0 +1,140 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
const users = [
|
||||
{ id: 0, name: '1 علی مصلحی', table: 12, avatar: '/assets/images/user-avatar.png' },
|
||||
{ id: 1, name: '2 علی مصلحی', table: 10, avatar: '/assets/images/user-avatar.png' },
|
||||
{ id: 2, name: '3 علی مصلحی', table: 11, avatar: '/assets/images/user-avatar.png' },
|
||||
{ id: 3, name: '4 علی مصلحی', table: 5, avatar: '/assets/images/user-avatar.png' },
|
||||
{ id: 4, name: '5 علی مصلحی', table: 2, avatar: '/assets/images/user-avatar.png' },
|
||||
{ id: 5, name: '6 علی مصلحی', table: 1, avatar: '/assets/images/user-avatar.png' },
|
||||
];
|
||||
|
||||
function ChatIndex() {
|
||||
const containerRef = useRef<HTMLElement>(null);
|
||||
const [positions, setPositions] = useState<{ top: number; left: number }[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (containerRef.current) {
|
||||
const maxW = containerRef.current.offsetWidth - 50;
|
||||
const maxH = containerRef.current.offsetHeight - 80;
|
||||
const elementSize = 110; // approximate size of each user element
|
||||
const padding = 80; // minimum distance between elements
|
||||
|
||||
const positions: { top: number; left: number }[] = [];
|
||||
|
||||
const isTooClose = (top: number, left: number) => {
|
||||
return positions.some(p => {
|
||||
const dx = p.left - left;
|
||||
const dy = p.top - top;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
return distance < padding;
|
||||
});
|
||||
};
|
||||
|
||||
users.forEach(() => {
|
||||
let top = 0;
|
||||
let left = 0;
|
||||
let tries = 0;
|
||||
|
||||
do {
|
||||
top = Math.random() * (maxH - elementSize);
|
||||
left = Math.random() * (maxW - elementSize);
|
||||
tries++;
|
||||
} while (isTooClose(top, left) && tries < 100);
|
||||
|
||||
positions.push({ top, left });
|
||||
});
|
||||
|
||||
setPositions(positions);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<section ref={containerRef} className="h-full relative">
|
||||
<section className="relative h-full w-full mt-7">
|
||||
<motion.div
|
||||
className='size-[1145px] rounded-full fixed top-1/2 left-1/2 -translate-1/2'
|
||||
style={{ background: 'linear-gradient(180deg, #EAECF0 4.52%, #DBDEE8 95.02%)', willChange: 'transform' }}
|
||||
>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
animate={{ scale: [1, 0.95, 1] }}
|
||||
transition={{ repeat: Infinity, duration: 13 }}
|
||||
className='size-[824px] rounded-full fixed top-1/2 left-1/2 -translate-1/2'
|
||||
style={{ background: 'linear-gradient(180deg, #EBF0FC 4.52%, #EBF0FC 95.02%)', willChange: 'transform' }}
|
||||
>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
animate={{ scale: [0.9, 1] }}
|
||||
transition={{ repeat: Infinity, duration: 4, repeatType: 'reverse' }}
|
||||
className='size-[617px] rounded-full fixed top-1/2 left-1/2 -translate-1/2'
|
||||
style={{ background: 'linear-gradient(180deg, #E6EBFA 0%, #DFE2EC 100%)', willChange: 'transform' }}
|
||||
>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
animate={{ scale: [1.02, 0.95] }}
|
||||
transition={{ repeat: Infinity, duration: 2, repeatType: 'reverse' }}
|
||||
className='size-[425px] rounded-full fixed top-1/2 left-1/2 -translate-1/2'
|
||||
style={{ background: 'linear-gradient(180deg, #E1E5EF 0%, #D8DDE8 60.2%, #D2D7E3 100%)', willChange: 'transform' }}
|
||||
>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
animate={{ scale: [1.05, 1] }}
|
||||
transition={{ repeat: Infinity, duration: 2, repeatType: 'reverse' }}
|
||||
className='size-[292px] rounded-full fixed top-1/2 left-1/2 -translate-1/2'
|
||||
style={{ background: 'linear-gradient(180deg, #EAEDF5 0%, #E4E7F0 46.52%, #DDDFEB 100%)', willChange: 'transform' }}
|
||||
>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
animate={{ scale: [1, 1.05] }}
|
||||
transition={{ repeat: Infinity, duration: 2, repeatType: 'reverse' }}
|
||||
|
||||
className='size-[174px] rounded-full fixed top-1/2 left-1/2 -translate-1/2'
|
||||
style={{ background: 'linear-gradient(180deg, #F3F7FF 0%, #F2F5FC 100%)', willChange: 'transform' }}
|
||||
>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
animate={{ scale: [0, 1.2], opacity: [1, 0] }}
|
||||
transition={{ repeat: Infinity, duration: 2 }}
|
||||
|
||||
className='h-full w-dvh rounded-full fixed top-1/2 left-1/2 -translate-1/2 outline outline-solid outline-blue-400'
|
||||
style={{ willChange: 'transform, opacity' }}
|
||||
>
|
||||
</motion.div>
|
||||
|
||||
<div>
|
||||
{positions.length === users.length &&
|
||||
users.map((v, i) => (
|
||||
<Link
|
||||
href={`chat/${v.id}`}
|
||||
prefetch={false}
|
||||
key={v.id}
|
||||
style={{ position: 'absolute', top: positions[i].top, left: positions[i].left }}
|
||||
className="justify-items-center"
|
||||
>
|
||||
<Image height={46} width={46} alt="user avatar" src={v.avatar} />
|
||||
<h6 className="mt-2 text-xs font-medium">{v.name}</h6>
|
||||
<div className="mt-[3px] text-xs2 font-medium bg-disabled-text text-accent rounded-[5px] px-[5px] py-[3px]">
|
||||
میز {v.table}
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChatIndex;
|
||||
@@ -0,0 +1,21 @@
|
||||
import ClientSideWrapper from "@/components/wrapper/ClientSideWrapper";
|
||||
import ClientMenuRouteWrapper from "@/components/wrapper/ClientMenuRouteWrapper.tsx";
|
||||
|
||||
export const metadata = {
|
||||
title: 'Menu',
|
||||
}
|
||||
|
||||
export default function MenuLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
|
||||
return (
|
||||
<ClientSideWrapper>
|
||||
<ClientMenuRouteWrapper>
|
||||
{children}
|
||||
</ClientMenuRouteWrapper>
|
||||
</ClientSideWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import LoadingOverlay from '@/components/overlays/LoadingOverlay'
|
||||
import React from 'react'
|
||||
|
||||
function Loading() {
|
||||
return (
|
||||
<LoadingOverlay />
|
||||
)
|
||||
}
|
||||
|
||||
export default Loading
|
||||
@@ -0,0 +1,16 @@
|
||||
export const metadata = {
|
||||
title: 'Orders',
|
||||
}
|
||||
|
||||
export default function MenuLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
'use client';
|
||||
|
||||
import Button from '@/components/button/PrimaryButton';
|
||||
import CalendarIcon from '@/components/icons/CalendarIcon';
|
||||
import LocationPinIcon from '@/components/icons/LocationPinIcon';
|
||||
import PerformantTabRenderer from '@/components/tab/PerformantTabRenderer';
|
||||
import TabContainer from '@/components/tab/TabContainer'
|
||||
import { TabHeader } from '@/components/tab/TabHeader';
|
||||
import { Receipt2, ReceiptItem } from 'iconsax-react';
|
||||
import Image from 'next/image';
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const currentOrders = [
|
||||
{
|
||||
id: 0,
|
||||
address: 'خیابان دانشگاه روبروی بیمارستان خوانساری ساختمان مهر...',
|
||||
date: 'دوشنبه، 28 مرداد 1403',
|
||||
price: '560,000',
|
||||
items: [
|
||||
{ id: 0, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 1, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 2, quantity: 2, image: '/assets/images/food-preview.png' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
address: '1...',
|
||||
date: 'دوشنبه، 28 مرداد 1403',
|
||||
price: '560,000',
|
||||
items: [
|
||||
{ id: 0, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 1, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 2, quantity: 2, image: '/assets/images/food-preview.png' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
address: '2...',
|
||||
date: 'دوشنبه، 28 مرداد 1403',
|
||||
price: '560,000',
|
||||
items: [
|
||||
{ id: 0, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 1, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 2, quantity: 2, image: '/assets/images/food-preview.png' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
address: '3...',
|
||||
date: 'دوشنبه، 28 مرداد 1403',
|
||||
price: '560,000',
|
||||
items: [
|
||||
{ id: 0, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 1, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 2, quantity: 2, image: '/assets/images/food-preview.png' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
address: '2...',
|
||||
date: 'دوشنبه، 28 مرداد 1403',
|
||||
price: '560,000',
|
||||
items: [
|
||||
{ id: 0, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 1, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 2, quantity: 2, image: '/assets/images/food-preview.png' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
address: '3...',
|
||||
date: 'دوشنبه، 28 مرداد 1403',
|
||||
price: '560,000',
|
||||
items: [
|
||||
{ id: 0, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 1, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 2, quantity: 2, image: '/assets/images/food-preview.png' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
address: '2...',
|
||||
date: 'دوشنبه، 28 مرداد 1403',
|
||||
price: '560,000',
|
||||
items: [
|
||||
{ id: 0, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 1, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 2, quantity: 2, image: '/assets/images/food-preview.png' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
address: '3...',
|
||||
date: 'دوشنبه، 28 مرداد 1403',
|
||||
price: '560,000',
|
||||
items: [
|
||||
{ id: 0, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 1, quantity: 1, image: '/assets/images/food-preview.png' },
|
||||
{ id: 2, quantity: 2, image: '/assets/images/food-preview.png' },
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
function OrdersIndex() {
|
||||
|
||||
const { t } = useTranslation('orders');
|
||||
|
||||
const categories = [
|
||||
{ id: 0, title: t('Tab.ActiveOrders'), icon: <ReceiptItem size={22} /> },
|
||||
{ id: 1, title: t('Tab.PreviousOrders'), icon: <Receipt2 size={22} /> }
|
||||
]
|
||||
|
||||
const firstTab = () => {
|
||||
return currentOrders.map((order) => {
|
||||
return (
|
||||
<div key={order.id} className='w-full px-[17px] mb-4 pt-8 pb-6 bg-white rounded-3xl'>
|
||||
<div className="text-sm2">
|
||||
<div className='flex gap-2'>
|
||||
<LocationPinIcon size={16} />
|
||||
<p>{order.address}</p>
|
||||
</div>
|
||||
<div className='mt-2 flex gap-2'>
|
||||
<CalendarIcon size={16} />
|
||||
<p>{order.date}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className='mx-4 mt-6 text-disabled3 border-2' />
|
||||
|
||||
<div className='flex justify-between mt-6'>
|
||||
<div className='flex justify-start gap-[9px]'>
|
||||
{order.items.map((item) => {
|
||||
return (
|
||||
<span key={item.id} className='w-8 h-8 relative'>
|
||||
<Image
|
||||
priority
|
||||
className='rounded-lg'
|
||||
src={item.image}
|
||||
width={32}
|
||||
height={32}
|
||||
alt="order image"
|
||||
/>
|
||||
<span className="absolute -bottom-3.5 -right-0.5 text-xs2 bg-white border border-border rounded-full w-[18px] h-[18px] text-center leading-4.5">
|
||||
{item.quantity}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className='font-medium text-sm' dir='ltr'>
|
||||
{order.price} T
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button className='mt-6 font-medium!'>{t('Card.TrackOrder')}</Button>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className='pt-8'>
|
||||
<TabContainer>
|
||||
<TabHeader
|
||||
viewRenderer={<PerformantTabRenderer rowHeight={320}>{firstTab()}</PerformantTabRenderer>}
|
||||
title={categories[0].title} icon={categories[0].icon}>
|
||||
</TabHeader>
|
||||
<TabHeader
|
||||
viewRenderer={<PerformantTabRenderer><div>empty</div><div></div></PerformantTabRenderer>}
|
||||
title={categories[1].title} icon={categories[1].icon}>
|
||||
</TabHeader>
|
||||
</TabContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrdersIndex
|
||||
@@ -0,0 +1,370 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import SearchBox from "@/components/input/SearchBox";
|
||||
import CategoryItemRenderer from "@/components/listview/CategoryItemRenderer";
|
||||
import HorizontalScrollView from "@/components/listview/HorizontalScrollView";
|
||||
import Image from "next/image";
|
||||
import EqualizerIcon from "@/components/icons/EqualizerIcon";
|
||||
import TextAlignIcon from "@/components/icons/TextAlignIcon";
|
||||
import VerticalScrollView from "@/components/listview/VerticalScrollView";
|
||||
import MenuItemRenderer from "@/components/listview/MenuItemRenderer";
|
||||
import React from "react";
|
||||
import MenuItem from "@/components/listview/MenuItem";
|
||||
import Button from "@/components/button/PrimaryButton";
|
||||
import AnimatedBottomSheet from "@/components/bottomsheet/AnimatedBottomSheet";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { TicketPercentIcon } from "lucide-react";
|
||||
import { MedalStar } from 'iconsax-react'
|
||||
import { useQueryState } from 'next-usequerystate'
|
||||
import clsx from "clsx";
|
||||
import CategorySmallItemRenderer from "@/components/listview/CategorySmallItemRenderer";
|
||||
import { motion } from "framer-motion";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ComboBox, { ComboboxOption } from "@/components/combobox/Combobox";
|
||||
|
||||
const categories = new Array(13).fill({ title: "خوراک", icon: "" });
|
||||
|
||||
const foods = [
|
||||
{
|
||||
id: 1,
|
||||
category: 0,
|
||||
price: "69.000",
|
||||
name: "کباب چوبی ژیوان )سیخ چوبی(",
|
||||
contains: "فیله گوساله / سس سفید / قارچ و سیب زمینی/ پنیر پارمزان"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
category: 1,
|
||||
price: "49.000",
|
||||
name: "برگر گوشت گوساله",
|
||||
contains: "گوشت گوساله / پنیر چدار / کاهو / گوجه"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
category: 2,
|
||||
price: "29.000",
|
||||
name: "ساندویچ مرغ",
|
||||
contains: "مرغ / کاهو / گوجه / سس مایونز"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
category: 0,
|
||||
price: "89.000",
|
||||
name: "کباب ماهی",
|
||||
contains: "ماهی / سس لیمو / قارچ و سیب زمینی"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
category: 1,
|
||||
price: "39.000",
|
||||
name: "سالاد فصل",
|
||||
contains: "کاهو / گوجه / خیار / پنیر فتا"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
category: 3,
|
||||
price: "59.000",
|
||||
name: "پاستا گوشت",
|
||||
contains: "گوشت گوساله / پاستا / سس گوجه"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
category: 2,
|
||||
price: "49.000",
|
||||
name: "ساندویچ تن ماهی",
|
||||
contains: "تن ماهی / کاهو / گوجه / سس مایونز"
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
category: 0,
|
||||
price: "79.000",
|
||||
name: "کباب گوشت گوسفندی",
|
||||
contains: "گوشت گوسفندی / سس سفید / قارچ و سیب زمینی"
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
category: 1,
|
||||
price: "29.000",
|
||||
name: "برگر مرغ",
|
||||
contains: "مرغ / پنیر چدار / کاهو / گوجه"
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
category: 3,
|
||||
price: "69.000",
|
||||
name: "سالاد میگو",
|
||||
contains: "میگو / کاهو / گوجه / خیار"
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
category: 2,
|
||||
price: "39.000",
|
||||
name: "ساندویچ شاورما",
|
||||
contains: "مرغ / کاهو / گوجه / سس تارتار"
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
category: 0,
|
||||
price: "99.000",
|
||||
name: "کباب گوشت بوقلمون",
|
||||
contains: "گوشت بوقلمون / سس سفید / قارچ و سیب زمینی"
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
category: 1,
|
||||
price: "59.000",
|
||||
name: "برگر ماهی",
|
||||
contains: "ماهی / پنیر چدار / کاهو / گوجه"
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
category: 3,
|
||||
price: "49.000",
|
||||
name: "سالاد مرغ",
|
||||
contains: "مرغ / کاهو / گوجه / خیار"
|
||||
}
|
||||
];
|
||||
|
||||
const sortings = [
|
||||
"PopularityDescending",
|
||||
"DateDescending",
|
||||
"PriceAscending",
|
||||
"RateDescending",
|
||||
"PriceDescending"
|
||||
]
|
||||
|
||||
const MenuIndex = () => {
|
||||
const { t: tCommon } = useTranslation('common');
|
||||
const { t: tMenu } = useTranslation('menu', {
|
||||
keyPrefix: "Menu"
|
||||
});
|
||||
const contents: Array<ComboboxOption> = [
|
||||
{ id: '0', title: tMenu('MenuFilterDrawer.SelectContent.Options.Normal'), label: '' },
|
||||
{ id: '1', title: tMenu('MenuFilterDrawer.SelectContent.Options.Vegan'), label: '' },
|
||||
]
|
||||
|
||||
const shippings: Array<ComboboxOption> = [
|
||||
{ id: '0', title: tMenu('MenuFilterDrawer.SelectDelivery.Options.Courier'), label: '' },
|
||||
]
|
||||
|
||||
const [filterModal, setFilterModal] = useState(false);
|
||||
const [sortingModal, setSortingModal] = useState(false);
|
||||
const [, 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' });
|
||||
const [selectedDeliveryId, setSelectedDeliveryId] = useQueryState('delivery', { defaultValue: '0' });
|
||||
const smallCategoriesRef: React.RefObject<HTMLDivElement | null> = useRef(null);
|
||||
const wrapperRef: React.RefObject<HTMLDivElement | null> = useRef(null);
|
||||
const [smallCategoriesVisible, setSmallCategoriesVisibility] = useState(false);
|
||||
|
||||
const onScroll = useCallback(() => {
|
||||
if (!wrapperRef?.current?.parentElement?.scrollTop || !smallCategoriesRef.current?.offsetTop) return;
|
||||
|
||||
if (wrapperRef.current.parentElement.scrollTop >= smallCategoriesRef.current.offsetTop) {
|
||||
setSmallCategoriesVisibility(true);
|
||||
} else {
|
||||
setSmallCategoriesVisibility(false);
|
||||
}
|
||||
}, [wrapperRef, smallCategoriesRef]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wrapperRef.current) return;
|
||||
|
||||
const parent = wrapperRef.current.parentElement;
|
||||
if (!parent) return;
|
||||
|
||||
parent.addEventListener('scroll', onScroll);
|
||||
|
||||
return () => {
|
||||
parent.removeEventListener('scroll', onScroll);
|
||||
}
|
||||
}, [onScroll]);
|
||||
|
||||
|
||||
const updateSearch = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(e.target.value);
|
||||
}, [setSearch]);
|
||||
|
||||
const updateCategory = useCallback((id: number) => {
|
||||
setSelectedCategory(String(id));
|
||||
}, [setSelectedCategory]);
|
||||
|
||||
const toggleFilterModal = useCallback(() => {
|
||||
setFilterModal((state) => !state);
|
||||
}, []);
|
||||
|
||||
const toggleSortingModal = useCallback(() => {
|
||||
setSortingModal((state) => !state);
|
||||
}, []);
|
||||
|
||||
const changeSorting = (index: number) => {
|
||||
setSorting(() => String(index));
|
||||
}
|
||||
|
||||
const changeSelectedIngredients = (e: React.MouseEvent<HTMLDivElement, MouseEvent>, index: number) => {
|
||||
setSelectedIngredients(() => contents[index]?.id || contents[0].id);
|
||||
}
|
||||
|
||||
const changeSelectedDelivery = (e: React.MouseEvent<HTMLDivElement, MouseEvent>, index: number) => {
|
||||
setSelectedDeliveryId(() => contents[index]?.id || contents[0].id);
|
||||
}
|
||||
|
||||
const filteredReceiptItems = useMemo(() => {
|
||||
const lowerSearch = search.toLowerCase();
|
||||
return foods.filter(
|
||||
(item) =>
|
||||
item.category === +selectedCategory &&
|
||||
item.name.toLowerCase().includes(lowerSearch)
|
||||
);
|
||||
}, [selectedCategory, search]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 items-center pt-8 mb-8" ref={wrapperRef}>
|
||||
<div className="w-full">
|
||||
<SearchBox value={search} placeholder={tCommon('SearchPlaceholder')} onChange={updateSearch} />
|
||||
<HorizontalScrollView className="w-full noscrollbar mt-4! py-4!">
|
||||
{categories.map((item, index) => (
|
||||
<CategoryItemRenderer
|
||||
key={index}
|
||||
className={clsx(
|
||||
index === +selectedCategory && "bg-white!",
|
||||
)}
|
||||
onClick={() => updateCategory(index)}
|
||||
>
|
||||
<Image
|
||||
priority
|
||||
src="/assets/images/food-image.png"
|
||||
width={32}
|
||||
height={32}
|
||||
alt="category image"
|
||||
/>
|
||||
<span className="text-xs text-black">{item.title}</span>
|
||||
</CategoryItemRenderer>
|
||||
))}
|
||||
</HorizontalScrollView>
|
||||
</div>
|
||||
|
||||
<section className="w-full">
|
||||
<div className="flex justify-between items-center relative" ref={smallCategoriesRef} >
|
||||
<span className="text-base font-medium">
|
||||
{categories[+selectedCategory]?.title}
|
||||
</span>
|
||||
<div className="inline-flex gap-2 justify-around items-center">
|
||||
<button onClick={toggleFilterModal} className="rounded-xl h-8 bg-white ps-4 pe-2 py-1.5 inline-flex items-center justify-between gap-[7px]">
|
||||
<EqualizerIcon />
|
||||
<span className="text-xs leading-5 font-medium">{tMenu('MenuFilterDrawer.Label')}</span>
|
||||
</button>
|
||||
<button onClick={toggleSortingModal} className="rounded-lg h-8 bg-white p-1.5">
|
||||
<TextAlignIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ y: -200 }}
|
||||
animate={{ y: smallCategoriesVisible ? 0 : -200, opacity: smallCategoriesVisible ? 1 : 0 }}
|
||||
transition={{ duration: .1 }}
|
||||
className={clsx(
|
||||
'fixed left-0 z-10 top-0 px-4 pt-16 bg-[#F4F5F9CC] backdrop-blur-[44px] right-0',
|
||||
``
|
||||
)}>
|
||||
<HorizontalScrollView className="w-full noscrollbar py-4!">
|
||||
{categories.map((item, index) => (
|
||||
<CategorySmallItemRenderer
|
||||
key={index}
|
||||
className={clsx(
|
||||
index === +selectedCategory && "bg-white!",
|
||||
)}
|
||||
onClick={() => updateCategory(index)}
|
||||
>
|
||||
<Image
|
||||
priority
|
||||
src="/assets/images/food-image.png"
|
||||
width={24}
|
||||
height={24}
|
||||
alt="category image"
|
||||
/>
|
||||
<span className="text-xs text-black">{item.title}</span>
|
||||
</CategorySmallItemRenderer>
|
||||
))}
|
||||
</HorizontalScrollView>
|
||||
</motion.div>
|
||||
|
||||
<VerticalScrollView className="mt-5! overflow-y-auto h-full">
|
||||
{filteredReceiptItems.map((food) => (
|
||||
<MenuItemRenderer key={food.id}>
|
||||
<MenuItem food={food} />
|
||||
</MenuItemRenderer>
|
||||
))}
|
||||
</VerticalScrollView>
|
||||
</section>
|
||||
|
||||
<AnimatedBottomSheet title={tMenu('MenuFilterDrawer.Heading')} visible={filterModal} outDelay={150} onClick={toggleFilterModal}>
|
||||
<div className="ps-8.5 pe-[31px] mt-[89px]">
|
||||
<ComboBox
|
||||
title={tMenu('MenuFilterDrawer.SelectContent.Label')}
|
||||
options={contents}
|
||||
selectedId={selectedIngredients}
|
||||
onSelectionChange={changeSelectedIngredients} />
|
||||
<ComboBox
|
||||
className="relative mt-9.5"
|
||||
title={tMenu('MenuFilterDrawer.SelectDelivery.Label')}
|
||||
options={shippings}
|
||||
selectedId={selectedDeliveryId}
|
||||
onSelectionChange={changeSelectedDelivery} />
|
||||
<div
|
||||
className="flex w-full mt-[23px] h-11 items-center justify-between gap-2 px-3 py-4 relative bg-white/29 rounded-xl border border-solid border-neutral-200 cursor-pointer focus:outline-none focus:ring-2 focus:ring-black"
|
||||
>
|
||||
<span className="inline-flex items-center gap-2.5 text-sm2">
|
||||
<MedalStar size="16" color="#333333" />
|
||||
{tMenu('MenuFilterDrawer.PlusPoint')}
|
||||
</span>
|
||||
<Switch className="w-12 h-6" />
|
||||
</div>
|
||||
<div
|
||||
className="flex w-full mt-[23px] h-11 items-center justify-between gap-2 px-3 py-4 relative bg-white/29 rounded-xl border border-solid border-neutral-200 cursor-pointer focus:outline-none focus:ring-2 focus:ring-black"
|
||||
>
|
||||
<span className="inline-flex items-center gap-2.5 text-sm2">
|
||||
<TicketPercentIcon size={16} />
|
||||
{tMenu('MenuFilterDrawer.DiscountState')}
|
||||
</span>
|
||||
<Switch />
|
||||
</div>
|
||||
</div>
|
||||
<hr className="text-white/40 mt-12" />
|
||||
<div className="px-9 pt-6 flex justify-between gap-[22px]">
|
||||
<div className="w-full">
|
||||
<Button>
|
||||
{tMenu('MenuFilterDrawer.ButtonOk')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Button className="bg-disabled! text-disabled-text!">
|
||||
{tMenu('MenuFilterDrawer.ButtonCancel')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</AnimatedBottomSheet>
|
||||
|
||||
<AnimatedBottomSheet title={tMenu('MenuSortingDrawer.Heading')} visible={sortingModal} inDelay={150} onClick={toggleSortingModal}>
|
||||
<div className="px-8.5 py-10 justify-between">
|
||||
{sortings.map((v, i) => {
|
||||
return (
|
||||
<div key={i}>
|
||||
<div onClick={() => changeSorting(i)} className="text-sm2 font-medium cursor-pointer">
|
||||
{tMenu('MenuSortingDrawer.Options.' + v)}
|
||||
</div>
|
||||
{i < sortings.length - 1 && <hr className="text-white/40 mb-4 mt-4" />}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</AnimatedBottomSheet>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MenuIndex;
|
||||
@@ -0,0 +1,130 @@
|
||||
'use client';
|
||||
|
||||
import ToggleButton from '@/components/button/ToggleButton';
|
||||
import TabContainer, { TabContainerClassNames, TabContainerRenderType } from '@/components/tab/TabContainer';
|
||||
import { TabHeader } from '@/components/tab/TabHeader';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import RateSelectionBar from '@/components/utils/RateSelectionBar';
|
||||
import clsx from 'clsx';
|
||||
import { useParams } from 'next/navigation';
|
||||
import React from 'react'
|
||||
|
||||
type Props = object
|
||||
|
||||
type Params = {
|
||||
orderId: string;
|
||||
}
|
||||
|
||||
function RatingOrderIndex({ }: Props) {
|
||||
const params: Params = useParams();
|
||||
console.log(params)
|
||||
|
||||
const badPoitns = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-x-4.5 gap-y-4 mt-8">
|
||||
<ToggleButton name='minus1'>
|
||||
اماده سازی زمانبر
|
||||
</ToggleButton>
|
||||
<ToggleButton name='minus2'>
|
||||
بهداشت محیطی بد
|
||||
</ToggleButton>
|
||||
<ToggleButton name='minus3'>
|
||||
کیفیت منو پایین
|
||||
</ToggleButton>
|
||||
<ToggleButton name='minus4'>
|
||||
تنوع پایین منو
|
||||
</ToggleButton>
|
||||
<ToggleButton name='minus5'>
|
||||
برخورد نامناسب کارکنان
|
||||
</ToggleButton>
|
||||
<ToggleButton name='minus6'>
|
||||
قیمت نامناسب
|
||||
</ToggleButton>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const goodPoitns = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
<div className="grid grid-cols-2 gap-x-4.5 gap-y-4 mt-8">
|
||||
<ToggleButton name='plus1'>
|
||||
اماده سازی سریع
|
||||
</ToggleButton>
|
||||
<ToggleButton name='plus2'>
|
||||
بهداشت محیطی خوب
|
||||
</ToggleButton>
|
||||
<ToggleButton name='plus3'>
|
||||
کیفیت منو خوب
|
||||
</ToggleButton>
|
||||
<ToggleButton name='plus4'>
|
||||
تنوع بالا منو
|
||||
</ToggleButton>
|
||||
<ToggleButton name='plus5'>
|
||||
برخورد مناسب کارکنان
|
||||
</ToggleButton>
|
||||
<ToggleButton name='plus6'>
|
||||
قیمت مناسب
|
||||
</ToggleButton>
|
||||
</div>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const submitForm = (formData: FormData) => {
|
||||
console.log(formData.get('rating'))
|
||||
}
|
||||
|
||||
return (
|
||||
<section className='flex flex-col h-full pt-6'>
|
||||
<h1 className='font-medium'>امتیاز شما به رستوران</h1>
|
||||
|
||||
<form action={submitForm} className='bg-container flex flex-col h-min justify-between rounded-[30px] pt-10 px-6 pb-7.5 mt-4'>
|
||||
|
||||
<div>
|
||||
<div className='w-full'>
|
||||
<RateSelectionBar name='rating'/>
|
||||
</div>
|
||||
|
||||
<div className='mt-10'>
|
||||
<TabContainer
|
||||
defaultIndex={1}
|
||||
changeType={TabContainerRenderType.VISIBILITY}
|
||||
className={{
|
||||
...TabContainerClassNames,
|
||||
wrapper: 'p-2!',
|
||||
scrollView: clsx(
|
||||
'border-none! rounded-xl! gap-0! h-full bg-[#EAECF0]! grid! grid-cols-2 px-2! py-2! pt-2!', TabContainerClassNames.scrollView
|
||||
),
|
||||
title: 'text-sm2! font-normal mt-1',
|
||||
header: 'rounded-lg h-7! w-full',
|
||||
headerActive: 'bg-primary',
|
||||
titleActive: 'text-accent'
|
||||
}}>
|
||||
<TabHeader title='نقاط ضعف' viewRenderer={badPoitns()}></TabHeader>
|
||||
<TabHeader title='نقاط قوت' viewRenderer={goodPoitns()}></TabHeader>
|
||||
</TabContainer>
|
||||
<div className="w-full mt-6">
|
||||
<label htmlFor='userOpinion'>نظر شما</label>
|
||||
<br />
|
||||
<textarea name='userOpinion' id='userOpinion' placeholder='نظر شما' className='mt-2 w-full h-21 px-4 py-2.5 text-xs text-[#888888] rounded-xl border border-[#D0D0D0]' />
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 mt-6">
|
||||
<Button type='submit' className='mt-auto rounded-xl font-normal cursor-pointer'>ثبت بازخورد</Button>
|
||||
<Button type='submit' className='mt-auto rounded-xl bg-white cursor-pointer hover:bg-neutral-100 border border-foreground text-foreground font-normal'>بازگشت</Button>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default RatingOrderIndex
|
||||
@@ -0,0 +1,90 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import React from 'react'
|
||||
|
||||
function TransactionsIndex() {
|
||||
return (
|
||||
<section className='pt-6'>
|
||||
<h1 className='font-medium'>کدهای تخفیف</h1>
|
||||
|
||||
<section
|
||||
aria-label="درباره کدهای تخفیف"
|
||||
className='mt-3 bg-container rounded-normal grid grid-cols-4 items-center'
|
||||
>
|
||||
<div className='col-span-3 py-9.5 pe-7 ps-5 relative'>
|
||||
<div className='w-full h-full absolute top-0 left-0 overflow-clip'>
|
||||
<svg width="301" height="146" viewBox="0 0 301 146" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.15">
|
||||
<path d="M262.479 152.795C202.8 122.128 120.111 132.267 77.763 175.315C35.4154 218.362 49.3247 278.14 109.004 308.807C168.683 339.474 251.373 329.335 293.721 286.287C336.069 243.24 322.159 183.462 262.479 152.795Z" stroke="#AFAFAF" />
|
||||
<path d="M298.808 115.866C239.129 85.1988 156.44 95.3379 114.092 138.385C71.7445 181.433 85.6538 241.21 145.333 271.877C205.012 302.544 287.702 292.405 330.05 249.358C372.398 206.31 358.488 146.533 298.808 115.866Z" stroke="#AFAFAF" />
|
||||
<path d="M347.543 66.3263C287.863 35.6595 205.174 45.7986 162.826 88.8461C120.479 131.894 134.388 191.671 194.067 222.338C253.747 253.005 336.437 242.866 378.784 199.819C421.132 156.771 407.222 96.9933 347.543 66.3263Z" stroke="#AFAFAF" />
|
||||
<path d="M416.657 -3.92933C356.977 -34.5961 274.288 -24.457 231.941 18.5905C189.593 61.638 203.502 121.415 263.182 152.082C322.861 182.749 405.551 172.61 447.899 129.563C490.246 86.5153 476.336 26.7377 416.657 -3.92933Z" stroke="#AFAFAF" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 className='font-bold text-base'>کدهای تخفیف شما</h2>
|
||||
<p className='text-xs mt-[7px]'>کد تخفیف مورد نظر را کپی کنید و در بخش پرداختها استفاده کنید</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className='col-span-1 h-full text-end py-4 px-5.5 flex flex-col justify-between rounded-e-normal'
|
||||
style={{
|
||||
background: 'linear-gradient(165.51deg, #000000 -8.44%, #C3C7DD 100%)',
|
||||
}}
|
||||
>
|
||||
<p className='text-sm2 text-white leading-tight'>
|
||||
<span className='font-medium'>Your</span><br />
|
||||
<span className='font-bold'>Coupon</span>
|
||||
</p>
|
||||
|
||||
<svg className='w-full' height="20" viewBox="0 0 72 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.0033 0H0.845703V20H4.0033V0Z" fill="white" />
|
||||
<path d="M9.266 0H6.1084V20H9.266V0Z" fill="white" />
|
||||
<path d="M12.4236 0H11.3711V20H12.4236V0Z" fill="white" />
|
||||
<path d="M16.6344 0H14.5293V20H16.6344V0Z" fill="white" />
|
||||
<path d="M19.7916 0H17.6865V20H19.7916V0Z" fill="white" />
|
||||
<path d="M24.0018 0H22.9492V20H24.0018V0Z" fill="white" />
|
||||
<path d="M28.2113 0H25.0537V20H28.2113V0Z" fill="white" />
|
||||
<path d="M30.3182 0H29.2656V20H30.3182V0Z" fill="white" />
|
||||
<path d="M33.4744 0H32.4219V20H33.4744V0Z" fill="white" />
|
||||
<path d="M36.6324 0H34.5273V20H36.6324V0Z" fill="white" />
|
||||
<path d="M42.9478 0H40.8428V20H42.9478V0Z" fill="white" />
|
||||
<path d="M47.1576 0H44V20H47.1576V0Z" fill="white" />
|
||||
<path d="M49.2625 0H48.21V20H49.2625V0Z" fill="white" />
|
||||
<path d="M55.5785 0H52.4209V20H55.5785V0Z" fill="white" />
|
||||
<path d="M57.6824 0H56.6299V20H57.6824V0Z" fill="white" />
|
||||
<path d="M62.9455 0H58.7354V20H62.9455V0Z" fill="white" />
|
||||
<path d="M66.1031 0H63.998V20H66.1031V0Z" fill="white" />
|
||||
<path d="M71.3666 0H68.209V20H71.3666V0Z" fill="white" />
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-6 space-y-6" aria-label="لیست کدهای تخفیف">
|
||||
{[1, 2, 3].map((_, i) => (
|
||||
<article
|
||||
key={i}
|
||||
className='bg-container rounded-normal flex justify-between items-center'
|
||||
>
|
||||
<div className='py-4 pe-7 ps-5 border-e-2 w-full border-border border-dashed'>
|
||||
<h3 className='font-bold text-sm2'>مبلغ تخفیف: 50,000 تومان</h3>
|
||||
<p className='text-xs mt-[7px]'>قابل استفاده برای همهی منوها و محصولات</p>
|
||||
<Button
|
||||
className='w-fit px-10 py-1.5 mt-4 text-xs rounded-lg font-light'
|
||||
aria-label="کپی کد تخفیف"
|
||||
>
|
||||
کپی کد
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<p className='text-sm2 transform -rotate-90 text-[#B2B2B2] font-medium' aria-label="کد تخفیف">
|
||||
F12BB587
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default TransactionsIndex
|
||||
@@ -0,0 +1,54 @@
|
||||
import { DataTableDemo } from '@/components/ui/datatable'
|
||||
import { MoneyRecive, MoneySend, Wallet } from 'iconsax-react'
|
||||
import { ArrowDownLeft, ArrowUpRight } from 'lucide-react'
|
||||
import React from 'react'
|
||||
|
||||
function TransactionsReviewIndex() {
|
||||
return (
|
||||
<section className='flex flex-col h-svh pb-10 pt-6 gap-4'>
|
||||
<h1 className="font-medium text-base">لیست تراکنش ها</h1>
|
||||
|
||||
<div className="grid grid-cols-3 gap-2 h-28">
|
||||
<div className="w-full h-full flex flex-col items-center justify-center p-2.5 text-center bg-container rounded-xl">
|
||||
<div className="size-8 bg-background rounded-full grid items-center">
|
||||
<MoneySend className='stroke-primary justify-self-center' size={20} />
|
||||
</div>
|
||||
<span className='text-xs2 pt-1'>مجموع واریز ها</span>
|
||||
<span className='text-xs font-medium mt-1'>140,000,000 ریال</span>
|
||||
<span className='text-xs2 text-green-60mt-0.5 0 bg-green-100 w-min px-1 py-px items-center justify-between rounded-md flex'>
|
||||
<ArrowUpRight size={10} className='stroke-green-600' />
|
||||
<div className="mt-0.5">30%</div>
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full h-full flex flex-col items-center justify-between p-2.5 text-center bg-container rounded-xl">
|
||||
<div className="size-8 bg-background rounded-full grid items-center">
|
||||
<MoneyRecive className='stroke-primary justify-self-center' size={20} />
|
||||
</div>
|
||||
<span className='text-xs2 pt-1'>مجموع برداشت ها</span>
|
||||
<span className='text-xs font-medium mt-1'>140,000,000 ریال</span>
|
||||
<span className='text-xs2 text-green-60mt-0.5 0 bg-green-100 w-min px-1 py-px items-center justify-between rounded-md flex'>
|
||||
<ArrowUpRight size={10} className='stroke-green-600' />
|
||||
<div className="mt-0.5">30%</div>
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full h-full flex flex-col items-center justify-between p-2.5 text-center bg-container rounded-xl">
|
||||
<div className="size-8 bg-background rounded-full grid items-center">
|
||||
<Wallet className='stroke-primary justify-self-center' size={20} />
|
||||
</div>
|
||||
<span className='text-xs2 pt-1'>موجودی کیف پول</span>
|
||||
<span className='text-xs font-medium mt-1'>140,000,000 ریال</span>
|
||||
<span className='text-xs2 text-red-600 mt-0.5 bg-red-100 w-min px-1 py-px items-center justify-between rounded-md flex'>
|
||||
<ArrowDownLeft size={10} className='stroke-red-600' />
|
||||
<div className="mt-0.5">30%</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full h-full bg-white rounded-3xl mt-0'>
|
||||
<DataTableDemo />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default TransactionsReviewIndex
|
||||
Reference in New Issue
Block a user