Files
dmenu-admin/src/pages/food/Create.tsx
T
2025-11-26 10:49:39 +03:30

387 lines
20 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { type FC, useState } from 'react'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { TickCircle } from 'iconsax-react'
import { toast } from 'react-toastify'
import Input from '@/components/Input'
import Select from '@/components/Select'
import Textarea from '@/components/Textarea'
import Button from '@/components/Button'
import { Checkbox } from '@/components/ui/checkbox'
import CreateFoodSidebar from './components/CreateFoodSidebar'
import type { CreateFoodType } from './types/Types'
import { useCreateFood, useGetCategories } from './hooks/useFoodData'
import { useMultipleUpload } from '../uploader/hooks/useUploaderData'
import { Pages } from '@/config/Pages'
import type { ErrorType } from '@/helpers/types'
const CreateFood: FC = () => {
const { data } = useGetCategories();
const [isActive, setIsActive] = useState<boolean>(true)
const [isSpecial, setIsSpecial] = useState<boolean>(false)
const [imageFiles, setImageFiles] = useState<File[]>([])
const { mutate: createFood, isPending } = useCreateFood()
const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload()
const categories = data?.data?.map(category => ({
label: category.title,
value: category.id
})) || []
const formik = useFormik<CreateFoodType>({
initialValues: {
title: '',
desc: '',
content: [],
categoryId: '',
price: 0,
discount: 0,
points: 0,
prepareTime: 0,
stock: 0,
stockDefault: 0,
breakfast: false,
noon: false,
dinner: false,
mon: false,
tue: false,
wed: false,
thu: false,
fri: false,
sat: false,
sun: false,
pickupServe: false,
inPlaceServe: false,
isActive: true,
images: []
},
validationSchema: Yup.object().shape({
title: Yup.string().required('نام غذا الزامی است'),
desc: Yup.string().required('توضیحات غذا الزامی است'),
categoryId: Yup.string().required('دسته‌بندی الزامی است'),
price: Yup.number().required('قیمت الزامی است').min(0, 'قیمت باید مثبت باشد'),
prepareTime: Yup.number().required('زمان آماده‌سازی الزامی است').min(0, 'زمان آماده‌سازی باید مثبت باشد'),
stock: Yup.number().required('موجودی الزامی است').min(0, 'موجودی باید مثبت باشد'),
stockDefault: Yup.number().required('موجودی پیش‌فرض الزامی است').min(0, 'موجودی پیش‌فرض باید مثبت باشد'),
points: Yup.number().min(0, 'امتیاز باید مثبت باشد'),
discount: Yup.number().min(0, 'تخفیف باید مثبت باشد'),
content: Yup.array().of(Yup.string())
}),
onSubmit: (values) => {
const submitFood = (imageUrls: string[] = []) => {
const foodData: CreateFoodType = {
...values,
isActive,
images: imageUrls,
content: values.content.filter(item => item && item.trim().length > 0)
}
createFood(foodData, {
onSuccess: () => {
toast.success('غذا با موفقیت ایجاد شد')
window.location.href = Pages.foods.list
},
onError: (error: ErrorType) => {
toast.error(error?.response?.data?.error?.message[0] || 'خطا در ایجاد غذا')
}
})
}
if (imageFiles.length > 0) {
multipleUpload(imageFiles, {
onSuccess: (response) => {
const imageUrls = response?.data?.map(item => item.url) || []
submitFood(imageUrls)
},
onError: (error: ErrorType) => {
toast.error(error?.response?.data?.error?.message[0] || 'خطا در آپلود تصاویر')
}
})
} else {
submitFood()
}
}
})
return (
<div className='w-full mt-4'>
<div className='flex w-full justify-between items-center'>
<div className='text-lg font-light'>
غذای جدید
</div>
<div>
<Button
className='px-5'
onClick={() => formik.handleSubmit()}
isloading={isPending || isUploading}
>
<div className='flex gap-2 items-center'>
<TickCircle className='size-5' color='white' />
<div>ثبت غذا</div>
</div>
</Button>
</div>
</div>
<div className='flex gap-6 mt-6'>
<div className='flex-1'>
<div className='bg-white py-8 xl:px-10 px-4 rounded-3xl'>
<Input
label='نام غذا'
name='title'
placeholder=''
value={formik.values.title}
onChange={formik.handleChange}
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
/>
<div className='mt-5'>
<Select
items={categories}
label='دسته بندی'
placeholder='انتخاب کنید'
name='categoryId'
value={formik.values.categoryId}
onChange={(e) => {
const value = e.target.value
formik.setFieldValue('categoryId', value)
}}
error_text={formik.touched.categoryId && formik.errors.categoryId ? String(formik.errors.categoryId) : ''}
/>
</div>
<div className='grid grid-cols-3 gap-4 mt-5'>
<Input
name='price'
label='قیمت'
placeholder='تومان'
type='number'
seprator={true}
value={formik.values.price}
onChange={(e) => formik.setFieldValue('price', Number(e.target.value))}
error_text={formik.touched.price && formik.errors.price ? formik.errors.price : ''}
/>
<Input
name='discount'
label='تخفیف'
placeholder='تومان'
type='number'
seprator={true}
value={formik.values.discount}
onChange={(e) => formik.setFieldValue('discount', Number(e.target.value))}
error_text={formik.touched.discount && formik.errors.discount ? formik.errors.discount : ''}
/>
<Input
name='points'
label='امتیاز'
placeholder=''
type='number'
value={formik.values.points}
onChange={(e) => formik.setFieldValue('points', Number(e.target.value))}
error_text={formik.touched.points && formik.errors.points ? formik.errors.points : ''}
/>
</div>
<div className='grid grid-cols-3 gap-4 mt-5'>
<Input
name='prepareTime'
label='زمان آماده سازی'
placeholder='دقیقه'
type='number'
value={formik.values.prepareTime}
onChange={(e) => formik.setFieldValue('prepareTime', Number(e.target.value))}
error_text={formik.touched.prepareTime && formik.errors.prepareTime ? formik.errors.prepareTime : ''}
/>
<Input
name='stock'
label='موجودی'
placeholder=''
type='number'
value={formik.values.stock}
onChange={(e) => formik.setFieldValue('stock', Number(e.target.value))}
error_text={formik.touched.stock && formik.errors.stock ? formik.errors.stock : ''}
/>
<Input
name='stockDefault'
label='موجودی پیش‌فرض'
placeholder=''
type='number'
value={formik.values.stockDefault}
onChange={(e) => formik.setFieldValue('stockDefault', Number(e.target.value))}
error_text={formik.touched.stockDefault && formik.errors.stockDefault ? formik.errors.stockDefault : ''}
/>
</div>
<div className='mt-5'>
<Textarea
name='desc'
label='توضیحات غذا'
placeholder=''
value={formik.values.desc}
onChange={formik.handleChange}
error_text={formik.touched.desc && formik.errors.desc ? formik.errors.desc : ''}
/>
</div>
<div className='mt-5'>
<div className='text-sm mb-2'>محتوای غذا</div>
<div className='space-y-2'>
{formik.values.content.map((item, index) => (
<div key={index} className='flex gap-2 items-center'>
<Input
placeholder='متن محتوا'
value={item}
onChange={(e) => {
const newContent = [...formik.values.content]
newContent[index] = e.target.value
formik.setFieldValue('content', newContent)
}}
/>
<Button
type='button'
className='px-3 bg-red-500 hover:bg-red-600 w-auto'
onClick={() => {
const newContent = formik.values.content.filter((_, i) => i !== index)
formik.setFieldValue('content', newContent)
}}
>
حذف
</Button>
</div>
))}
<Button
type='button'
className='w-full bg-gray-200 text-gray-700 hover:bg-gray-300'
onClick={() => {
formik.setFieldValue('content', [...formik.values.content, ''])
}}
>
افزودن محتوا
</Button>
</div>
</div>
<div className='mt-6'>
<div className='text-sm mb-3'>وعدههای غذایی</div>
<div className='flex gap-6 flex-wrap'>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.breakfast}
onCheckedChange={(checked) => formik.setFieldValue('breakfast', checked)}
/>
<label className='text-xs cursor-pointer'>صبحانه</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.noon}
onCheckedChange={(checked) => formik.setFieldValue('noon', checked)}
/>
<label className='text-xs cursor-pointer'>ناهار</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.dinner}
onCheckedChange={(checked) => formik.setFieldValue('dinner', checked)}
/>
<label className='text-xs cursor-pointer'>شام</label>
</div>
</div>
</div>
<div className='mt-6'>
<div className='text-sm mb-3'>روزهای هفته</div>
<div className='flex gap-6 flex-wrap'>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.sat}
onCheckedChange={(checked) => formik.setFieldValue('sat', checked)}
/>
<label className='text-xs cursor-pointer'>شنبه</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.sun}
onCheckedChange={(checked) => formik.setFieldValue('sun', checked)}
/>
<label className='text-xs cursor-pointer'>یکشنبه</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.mon}
onCheckedChange={(checked) => formik.setFieldValue('mon', checked)}
/>
<label className='text-xs cursor-pointer'>دوشنبه</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.tue}
onCheckedChange={(checked) => formik.setFieldValue('tue', checked)}
/>
<label className='text-xs cursor-pointer'>سهشنبه</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.wed}
onCheckedChange={(checked) => formik.setFieldValue('wed', checked)}
/>
<label className='text-xs cursor-pointer'>چهارشنبه</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.thu}
onCheckedChange={(checked) => formik.setFieldValue('thu', checked)}
/>
<label className='text-xs cursor-pointer'>پنجشنبه</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.fri}
onCheckedChange={(checked) => formik.setFieldValue('fri', checked)}
/>
<label className='text-xs cursor-pointer'>جمعه</label>
</div>
</div>
</div>
<div className='mt-6'>
<div className='text-sm mb-3'>گزینههای سرویس</div>
<div className='flex gap-6 flex-wrap'>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.pickupServe}
onCheckedChange={(checked) => formik.setFieldValue('pickupServe', checked)}
/>
<label className='text-xs cursor-pointer'> بیرونبر</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.inPlaceServe}
onCheckedChange={(checked) => formik.setFieldValue('inPlaceServe', checked)}
/>
<label className='text-xs cursor-pointer'>سرو در محل</label>
</div>
</div>
</div>
</div>
</div>
<CreateFoodSidebar
isActive={isActive}
isSpecial={isSpecial}
onChangeActive={setIsActive}
onChangeSpecial={setIsSpecial}
onChangeImages={setImageFiles}
/>
</div>
</div>
)
}
export default CreateFood