219 lines
7.5 KiB
TypeScript
219 lines
7.5 KiB
TypeScript
import { type FC, useState } from 'react'
|
||
import { useFormik } from 'formik'
|
||
import * as Yup from 'yup'
|
||
import { toast } from 'react-toastify'
|
||
import Table from '@/components/Table'
|
||
import Filters from '@/components/Filters'
|
||
import CategoryForm from './components/CategoryForm'
|
||
import CategoryTableColumns from './components/CategoryTableColumns'
|
||
import type { Category, CreateCategoryType } from './types/Types'
|
||
import type { ErrorType } from '@/helpers/types'
|
||
import {
|
||
useGetCategories,
|
||
useCreateCategory,
|
||
useUpdateCategory,
|
||
useDeleteCategory
|
||
} from './hooks/useFoodData'
|
||
import { useSingleUpload } from '../uploader/hooks/useUploaderData'
|
||
import { extractErrorMessage } from '@/config/func'
|
||
|
||
type CategoryFormValues = {
|
||
title: string
|
||
icon: File[]
|
||
}
|
||
|
||
const CategoryFood: FC = () => {
|
||
const [isActive, setIsActive] = useState<boolean>(true)
|
||
const [iconFiles, setIconFiles] = useState<File[]>([])
|
||
const [selectedIconUrl, setSelectedIconUrl] = useState<string>('')
|
||
const [editingCategory, setEditingCategory] = useState<Category | null>(null)
|
||
const [filters, setFilters] = useState<{ search?: string | null }>({})
|
||
|
||
const { data: categoriesData, isLoading } = useGetCategories()
|
||
const { mutate: createCategory, isPending: isCreating } = useCreateCategory()
|
||
const { mutate: updateCategory, isPending: isUpdating } = useUpdateCategory()
|
||
const { mutate: deleteCategory, isPending: isDeleting } = useDeleteCategory()
|
||
const { mutate: singleUpload, isPending: isUploading } = useSingleUpload()
|
||
|
||
const categories = categoriesData?.data || []
|
||
|
||
const filteredCategories = categories.filter((category) => {
|
||
if (filters.search) {
|
||
return category.title.toLowerCase().includes(filters.search.toLowerCase())
|
||
}
|
||
return true
|
||
})
|
||
|
||
const formik = useFormik<CategoryFormValues>({
|
||
initialValues: {
|
||
title: '',
|
||
icon: []
|
||
},
|
||
validationSchema: Yup.object().shape({
|
||
title: Yup.string().required('عنوان دستهبندی الزامی است')
|
||
}),
|
||
onSubmit: (values) => {
|
||
const submitCategory = (avatarUrl?: string) => {
|
||
const categoryData: CreateCategoryType = {
|
||
title: values.title,
|
||
isActive,
|
||
avatarUrl: avatarUrl || selectedIconUrl || editingCategory?.avatarUrl || ''
|
||
}
|
||
|
||
if (editingCategory) {
|
||
updateCategory(
|
||
{ id: editingCategory.id, params: categoryData },
|
||
{
|
||
onSuccess: () => {
|
||
toast.success('دستهبندی با موفقیت بهروزرسانی شد')
|
||
resetForm()
|
||
},
|
||
onError: (error: ErrorType) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
}
|
||
)
|
||
} else {
|
||
createCategory(categoryData, {
|
||
onSuccess: () => {
|
||
toast.success('دستهبندی با موفقیت ایجاد شد')
|
||
resetForm()
|
||
},
|
||
onError: (error: ErrorType) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
if (selectedIconUrl) {
|
||
submitCategory(selectedIconUrl)
|
||
} else if (iconFiles.length > 0) {
|
||
singleUpload(iconFiles[0], {
|
||
onSuccess: (response) => {
|
||
const avatarUrl = response?.data?.url || ''
|
||
submitCategory(avatarUrl)
|
||
},
|
||
onError: (error: ErrorType) => {
|
||
toast.error(extractErrorMessage(error, 'خطا در آپلود تصویر'))
|
||
}
|
||
})
|
||
} else {
|
||
submitCategory()
|
||
}
|
||
}
|
||
})
|
||
|
||
const resetForm = () => {
|
||
formik.resetForm()
|
||
setIsActive(true)
|
||
setIconFiles([])
|
||
setSelectedIconUrl('')
|
||
setEditingCategory(null)
|
||
}
|
||
|
||
const handleEdit = (category: Category) => {
|
||
formik.setValues({
|
||
title: category.title,
|
||
icon: []
|
||
})
|
||
setIsActive(category.isActive)
|
||
setIconFiles([])
|
||
setSelectedIconUrl(category.avatarUrl || '')
|
||
setEditingCategory(category)
|
||
}
|
||
|
||
const handleIconUrlChange = (url: string) => {
|
||
setSelectedIconUrl(url)
|
||
setIconFiles([])
|
||
formik.setFieldValue('icon', [])
|
||
}
|
||
|
||
const handleIconFileChange = (files: File[]) => {
|
||
setIconFiles(files)
|
||
setSelectedIconUrl('')
|
||
}
|
||
|
||
const handleDelete = (id: string) => {
|
||
deleteCategory(id, {
|
||
onSuccess: () => {
|
||
toast.success('دستهبندی با موفقیت حذف شد')
|
||
},
|
||
onError: (error: ErrorType) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
})
|
||
}
|
||
|
||
const handleStatusChange = (id: string, value: boolean) => {
|
||
const category = categories.find((cat) => cat.id === id)
|
||
if (category) {
|
||
const categoryData: CreateCategoryType = {
|
||
title: category.title,
|
||
isActive: value,
|
||
avatarUrl: category.avatarUrl
|
||
}
|
||
updateCategory(
|
||
{ id, params: categoryData },
|
||
{
|
||
onSuccess: () => {
|
||
toast.success('وضعیت دستهبندی با موفقیت تغییر کرد')
|
||
},
|
||
onError: (error: ErrorType) => {
|
||
toast.error(extractErrorMessage(error))
|
||
}
|
||
}
|
||
)
|
||
}
|
||
}
|
||
|
||
const columns = CategoryTableColumns({
|
||
onStatusChange: handleStatusChange,
|
||
onEdit: handleEdit,
|
||
onDelete: handleDelete,
|
||
isDeleting
|
||
})
|
||
|
||
return (
|
||
<div className="flex gap-6 p-6">
|
||
<div className="flex-1">
|
||
<div className="flex items-center justify-between mb-6">
|
||
<h1 className="text-lg font-light">دسته بندی غذا</h1>
|
||
</div>
|
||
<div>
|
||
<Filters
|
||
fields={[
|
||
{
|
||
type: 'input',
|
||
name: 'search',
|
||
placeholder: 'جستجو'
|
||
}
|
||
]}
|
||
onChange={(filters) => setFilters(filters)}
|
||
/>
|
||
</div>
|
||
|
||
<Table
|
||
columns={columns}
|
||
data={filteredCategories}
|
||
isloading={isLoading}
|
||
selectable
|
||
/>
|
||
</div>
|
||
|
||
<CategoryForm
|
||
formik={formik}
|
||
isActive={isActive}
|
||
onActiveChange={setIsActive}
|
||
onIconChange={handleIconFileChange}
|
||
iconFiles={iconFiles}
|
||
isSubmitting={isCreating || isUpdating || isUploading}
|
||
isEditing={!!editingCategory}
|
||
onIconUrlChange={handleIconUrlChange}
|
||
selectedIconUrl={selectedIconUrl}
|
||
/>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default CategoryFood |