create order without scoll
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
hamid zarghami
2026-07-01 10:10:15 +03:30
parent 0539d4bc48
commit 3c6d3f009a
6 changed files with 824 additions and 561 deletions
@@ -1,9 +1,9 @@
import { type FC, useMemo, useState } from 'react'
import type { FormikProps } from 'formik'
import { Add, Minus, Trash } from 'iconsax-react'
import { Add } from 'iconsax-react'
import Input from '@/components/Input'
import type { Food } from '@/pages/food/types/Types'
import { formatFaNumber, formatPrice } from '@/helpers/func'
import { formatPrice } from '@/helpers/func'
import { getFoodUnitPrice } from '../hooks/useCreateOrderForm'
import type { CreateOrderFormValues } from '../hooks/useCreateOrderForm'
@@ -11,25 +11,16 @@ interface CreateOrderItemsSectionProps {
formik: FormikProps<CreateOrderFormValues>
foods: Food[]
onAddFood: (foodId: string) => void
onUpdateQuantity: (foodId: string, quantity: number) => void
onRemoveItem: (foodId: string) => void
}
const CreateOrderItemsSection: FC<CreateOrderItemsSectionProps> = ({
formik,
foods,
onAddFood,
onUpdateQuantity,
onRemoveItem,
}) => {
const [search, setSearch] = useState('')
const [selectedCategoryId, setSelectedCategoryId] = useState<string | null>(null)
const foodsMap = useMemo(
() => new Map(foods.map((food) => [food.id, food])),
[foods]
)
const categories = useMemo(() => {
const categoryMap = new Map<string, string>()
foods.forEach((food) => {
@@ -54,19 +45,11 @@ const CreateOrderItemsSection: FC<CreateOrderItemsSectionProps> = ({
return result
}, [foods, search, selectedCategoryId])
const selectedItems = formik.values.items
.map((item) => {
const food = foodsMap.get(item.foodId)
if (!food) return null
return { ...item, food }
})
.filter(Boolean)
return (
<div className='w-full bg-white rounded-4xl p-8'>
<div className='text-lg font-light'>آیتمهای سفارش</div>
<div className='w-full h-full bg-white rounded-4xl p-5 flex flex-col min-h-0'>
<div className='text-sm font-light flex-shrink-0'>انتخاب غذا</div>
<div className='mt-6 border border-border rounded-xl p-4'>
<div className='mt-3 flex-1 min-h-0 flex flex-col border border-border rounded-xl p-3'>
<Input
variant='search'
className='bg-[#EEF0F7]'
@@ -76,11 +59,11 @@ const CreateOrderItemsSection: FC<CreateOrderItemsSectionProps> = ({
/>
{categories.length > 0 && (
<div className='mt-3 flex gap-2 overflow-x-auto pb-1'>
<div className='mt-2 flex gap-1.5 overflow-x-auto flex-shrink-0 pb-0.5'>
<button
type='button'
onClick={() => setSelectedCategoryId(null)}
className={`flex-shrink-0 px-3 h-8 rounded-full text-xs transition-colors ${
className={`flex-shrink-0 px-2.5 h-7 rounded-full text-[11px] transition-colors ${
selectedCategoryId === null
? 'bg-primary text-white'
: 'bg-[#EEF0F7] text-description hover:bg-[#E4E7F0]'
@@ -93,7 +76,7 @@ const CreateOrderItemsSection: FC<CreateOrderItemsSectionProps> = ({
key={category.id}
type='button'
onClick={() => setSelectedCategoryId(category.id)}
className={`flex-shrink-0 px-3 h-8 rounded-full text-xs transition-colors whitespace-nowrap ${
className={`flex-shrink-0 px-2.5 h-7 rounded-full text-[11px] transition-colors whitespace-nowrap ${
selectedCategoryId === category.id
? 'bg-primary text-white'
: 'bg-[#EEF0F7] text-description hover:bg-[#E4E7F0]'
@@ -105,9 +88,9 @@ const CreateOrderItemsSection: FC<CreateOrderItemsSectionProps> = ({
</div>
)}
<div className='mt-3 space-y-2 max-h-56 overflow-y-auto'>
<div className='mt-2 flex-1 min-h-0 overflow-y-auto space-y-1'>
{filteredFoods.length === 0 ? (
<div className='text-center py-6 text-xs text-description'>
<div className='text-center py-8 text-xs text-description'>
غذایی یافت نشد
</div>
) : (
@@ -115,37 +98,41 @@ const CreateOrderItemsSection: FC<CreateOrderItemsSectionProps> = ({
const inCart = formik.values.items.some(
(item) => item.foodId === food.id
)
const cartQty = formik.values.items.find(
(item) => item.foodId === food.id
)?.quantity
return (
<div
key={food.id}
className='flex items-center gap-3 p-2 rounded-xl hover:bg-[#F8F9FC] transition-colors'
className='flex items-center gap-2 p-1.5 rounded-lg hover:bg-[#F8F9FC] transition-colors'
>
{food.images?.[0] ? (
<img
src={food.images[0]}
alt={food.title}
className='w-11 h-11 rounded-lg object-cover flex-shrink-0'
className='w-9 h-9 rounded-lg object-cover flex-shrink-0'
/>
) : (
<div className='w-11 h-11 rounded-lg bg-gray-100 flex-shrink-0' />
<div className='w-9 h-9 rounded-lg bg-gray-100 flex-shrink-0' />
)}
<div className='flex-1 min-w-0'>
<div className='text-sm truncate'>{food.title}</div>
<div className='text-xs text-description mt-0.5'>
<div className='text-xs truncate'>{food.title}</div>
<div className='text-[11px] text-description'>
{formatPrice(getFoodUnitPrice(food))}
</div>
</div>
<button
type='button'
onClick={() => onAddFood(food.id)}
className={`flex items-center gap-1 px-3 h-8 rounded-lg text-xs transition-colors ${
className={`flex items-center gap-1 px-2.5 h-7 rounded-lg text-[11px] transition-colors flex-shrink-0 ${
inCart
? 'bg-primary/10 text-primary'
: 'bg-primary text-white'
}`}
>
<Add size={14} color={inCart ? '#000' : '#fff'} />
{inCart ? 'افزودن' : 'انتخاب'}
<Add size={12} color={inCart ? '#000' : '#fff'} />
{inCart ? `×${cartQty}` : 'انتخاب'}
</button>
</div>
)
@@ -154,85 +141,9 @@ const CreateOrderItemsSection: FC<CreateOrderItemsSectionProps> = ({
</div>
</div>
<div className='mt-6'>
<div className='text-sm mb-3'>سبد سفارش</div>
{selectedItems.length === 0 ? (
<div className='border border-dashed border-border rounded-xl py-10 text-center text-xs text-description'>
هنوز آیتمی انتخاب نشده است
</div>
) : (
<div className='space-y-3'>
{selectedItems.map((item) => {
if (!item) return null
const unitPrice = getFoodUnitPrice(item.food)
return (
<div
key={item.foodId}
className='flex items-center gap-3 p-3 border border-border rounded-xl'
>
{item.food.images?.[0] ? (
<img
src={item.food.images[0]}
alt={item.food.title}
className='w-12 h-12 rounded-lg object-cover flex-shrink-0'
/>
) : (
<div className='w-12 h-12 rounded-lg bg-gray-100 flex-shrink-0' />
)}
<div className='flex-1 min-w-0'>
<div className='text-sm truncate'>{item.food.title}</div>
<div className='text-xs text-description mt-1'>
{formatPrice(unitPrice)}
</div>
</div>
<div className='flex items-center gap-2'>
<button
type='button'
onClick={() =>
onUpdateQuantity(item.foodId, item.quantity - 1)
}
className='w-8 h-8 rounded-lg border border-border flex items-center justify-center'
>
<Minus size={14} color='#000' />
</button>
<span className='w-6 text-center text-sm'>
{formatFaNumber(item.quantity)}
</span>
<button
type='button'
onClick={() =>
onUpdateQuantity(item.foodId, item.quantity + 1)
}
className='w-8 h-8 rounded-lg border border-border flex items-center justify-center'
>
<Add size={14} color='#000' />
</button>
</div>
<div className='text-sm font-medium min-w-[90px] text-left'>
{formatPrice(unitPrice * item.quantity)}
</div>
<button
type='button'
onClick={() => onRemoveItem(item.foodId)}
className='text-red-500 p-1'
>
<Trash size={18} color='#EF4444' />
</button>
</div>
)
})}
</div>
)}
{formik.touched.items && formik.errors.items && typeof formik.errors.items === 'string' && (
<div className='text-xs text-red-500 mt-2'>{formik.errors.items}</div>
)}
</div>
{formik.touched.items && formik.errors.items && typeof formik.errors.items === 'string' && (
<div className='text-xs text-red-500 mt-2 flex-shrink-0'>{formik.errors.items}</div>
)}
</div>
)
}