Files
dmenu-admin/src/pages/food/Category.tsx
T
hamid zarghami 2ab830bd48 icon selected
2025-12-16 15:26:19 +03:30

219 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
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 { 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