category compelete
This commit is contained in:
@@ -46,7 +46,7 @@ const DefaulModal: FC<Props> = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div onClick={props.close} className='fixed size-full top-0 bottom-0 right-0 modalGlass inset-0 z-50 '></div>
|
<div onClick={props.close} className='fixed size-full top-0 bottom-0 right-0 bg-black/40 inset-0 z-50 '></div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ body,
|
|||||||
}
|
}
|
||||||
* {
|
* {
|
||||||
outline: none;
|
outline: none;
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
display: none; /* Chrome, Safari, Opera */
|
||||||
}
|
}
|
||||||
input::placeholder,
|
input::placeholder,
|
||||||
textarea::placeholder {
|
textarea::placeholder {
|
||||||
|
|||||||
+154
-165
@@ -1,126 +1,165 @@
|
|||||||
import { type FC, useState } from 'react'
|
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 Table from '@/components/Table'
|
||||||
import Button from '@/components/Button'
|
|
||||||
import Input from '@/components/Input'
|
|
||||||
import Select from '@/components/Select'
|
|
||||||
import SwitchComponent from '@/components/Switch'
|
|
||||||
import UploadBox from '@/components/UploadBox'
|
|
||||||
import { type RowActionItem } from '@/components/RowActionsDropdown'
|
|
||||||
import { Add } from 'iconsax-react'
|
|
||||||
import Filters from '@/components/Filters'
|
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'
|
||||||
|
|
||||||
interface CategoryData {
|
type CategoryFormValues = {
|
||||||
id: number
|
title: string
|
||||||
name: string
|
icon: File[]
|
||||||
icon: string
|
|
||||||
iconColor: string
|
|
||||||
order: number
|
|
||||||
foodCount: number
|
|
||||||
status: boolean
|
|
||||||
[key: string]: string | number | boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const CategoryFood: FC = () => {
|
const CategoryFood: FC = () => {
|
||||||
const [selectedRow, setSelectedRow] = useState<number | null>(null)
|
const [isActive, setIsActive] = useState<boolean>(true)
|
||||||
const [currentPage, setCurrentPage] = useState(1)
|
const [iconFiles, setIconFiles] = useState<File[]>([])
|
||||||
|
const [editingCategory, setEditingCategory] = useState<Category | null>(null)
|
||||||
|
const [filters, setFilters] = useState<{ search?: string | null }>({})
|
||||||
|
|
||||||
// فرم دستهبندی جدید
|
const { data: categoriesData, isLoading } = useGetCategories()
|
||||||
const [formData, setFormData] = useState({
|
const { mutate: createCategory, isPending: isCreating } = useCreateCategory()
|
||||||
isActive: true,
|
const { mutate: updateCategory, isPending: isUpdating } = useUpdateCategory()
|
||||||
categoryStatus: '',
|
const { mutate: deleteCategory, isPending: isDeleting } = useDeleteCategory()
|
||||||
title: '',
|
const { mutate: singleUpload, isPending: isUploading } = useSingleUpload()
|
||||||
order: '',
|
|
||||||
icon: [] as File[]
|
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>({
|
||||||
const [categories] = useState<CategoryData[]>([
|
initialValues: {
|
||||||
{ id: 1, name: 'ایرانی', icon: '🍕', iconColor: '#FF6B6B', order: 1, foodCount: 14, status: true },
|
|
||||||
{ id: 2, name: 'پیتزا آمریکایی', icon: '🍕', iconColor: '#FF6B6B', order: 2, foodCount: 14, status: true },
|
|
||||||
{ id: 3, name: 'پیتزا آمریکایی', icon: '🍕', iconColor: '#FF6B6B', order: 3, foodCount: 14, status: true },
|
|
||||||
{ id: 4, name: 'پیتزا آمریکایی', icon: '🍕', iconColor: '#FF6B6B', order: 4, foodCount: 14, status: true },
|
|
||||||
{ id: 5, name: 'پیتزا آمریکایی', icon: '🍕', iconColor: '#FF6B6B', order: 5, foodCount: 14, status: true },
|
|
||||||
{ id: 6, name: 'پیتزا آمریکایی', icon: '🍕', iconColor: '#FF6B6B', order: 6, foodCount: 14, status: true },
|
|
||||||
])
|
|
||||||
|
|
||||||
const orderOptions = Array.from({ length: 20 }, (_, i) => ({
|
|
||||||
value: String(i + 1),
|
|
||||||
label: String(i + 1)
|
|
||||||
}))
|
|
||||||
|
|
||||||
const handleSave = () => {
|
|
||||||
console.log('ذخیره:', formData)
|
|
||||||
// پاک کردن فرم بعد از ذخیره
|
|
||||||
setFormData({
|
|
||||||
isActive: true,
|
|
||||||
categoryStatus: '',
|
|
||||||
title: '',
|
title: '',
|
||||||
order: '',
|
|
||||||
icon: []
|
icon: []
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object().shape({
|
||||||
|
title: Yup.string().required('عنوان دستهبندی الزامی است')
|
||||||
|
}),
|
||||||
|
onSubmit: (values) => {
|
||||||
|
const submitCategory = (avatarUrl?: string) => {
|
||||||
|
const categoryData: CreateCategoryType = {
|
||||||
|
title: values.title,
|
||||||
|
isActive,
|
||||||
|
avatarUrl: avatarUrl || 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 (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([])
|
||||||
|
setEditingCategory(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEdit = (category: Category) => {
|
||||||
|
formik.setValues({
|
||||||
|
title: category.title,
|
||||||
|
icon: []
|
||||||
|
})
|
||||||
|
setIsActive(category.isActive)
|
||||||
|
setIconFiles([])
|
||||||
|
setEditingCategory(category)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = (id: string) => {
|
||||||
|
deleteCategory(id, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success('دستهبندی با موفقیت حذف شد')
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(extractErrorMessage(error))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const rowActions = (item: CategoryData): RowActionItem[] => [
|
const handleStatusChange = (id: string, value: boolean) => {
|
||||||
{
|
const category = categories.find((cat) => cat.id === id)
|
||||||
label: 'ویرایش',
|
if (category) {
|
||||||
onClick: () => {
|
const categoryData: CreateCategoryType = {
|
||||||
console.log('ویرایش', item)
|
title: category.title,
|
||||||
// بارگذاری دادهها در فرم
|
isActive: value,
|
||||||
setFormData({
|
avatarUrl: category.avatarUrl
|
||||||
isActive: item.status,
|
|
||||||
categoryStatus: '',
|
|
||||||
title: item.name,
|
|
||||||
order: String(item.order),
|
|
||||||
icon: []
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
updateCategory(
|
||||||
{
|
{ id, params: categoryData },
|
||||||
label: 'حذف',
|
{
|
||||||
onClick: () => console.log('حذف', item)
|
onSuccess: () => {
|
||||||
|
toast.success('وضعیت دستهبندی با موفقیت تغییر کرد')
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast.error(extractErrorMessage(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
|
|
||||||
const columns = [
|
const columns = CategoryTableColumns({
|
||||||
{
|
onStatusChange: handleStatusChange,
|
||||||
title: 'آیکون',
|
onEdit: handleEdit,
|
||||||
key: 'icon',
|
onDelete: handleDelete,
|
||||||
render: (item: CategoryData) => (
|
isDeleting
|
||||||
<div
|
})
|
||||||
className="w-8 h-8 rounded-lg flex items-center justify-center text-lg"
|
|
||||||
style={{ backgroundColor: item.iconColor }}
|
|
||||||
>
|
|
||||||
{item.icon}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'عنوان',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'ترتیب نمایش',
|
|
||||||
key: 'order',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'تعداد غذا',
|
|
||||||
key: 'foodCount',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'وضعیت',
|
|
||||||
key: 'status',
|
|
||||||
render: (item: CategoryData) => (
|
|
||||||
<SwitchComponent
|
|
||||||
active={item.status}
|
|
||||||
onChange={(value) => console.log('تغییر وضعیت:', item.id, value)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-6 p-6">
|
<div className="flex gap-6 p-6">
|
||||||
{/* بخش اصلی - جدول */}
|
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<h1 className="text-lg font-light">دسته بندی غذا</h1>
|
<h1 className="text-lg font-light">دسته بندی غذا</h1>
|
||||||
@@ -131,80 +170,30 @@ const CategoryFood: FC = () => {
|
|||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'search',
|
name: 'search',
|
||||||
placeholder: 'جستجو',
|
placeholder: 'جستجو'
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
onChange={() => { }}
|
onChange={(filters) => setFilters(filters)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={categories}
|
data={filteredCategories}
|
||||||
|
isLoading={isLoading}
|
||||||
selectable
|
selectable
|
||||||
rowActions={rowActions}
|
|
||||||
onRowClick={(id) => setSelectedRow(id as number)}
|
|
||||||
rowClassName={(item) =>
|
|
||||||
selectedRow === item.id ? 'bg-blue-50 border-2 border-blue-400 border-dashed' : ''
|
|
||||||
}
|
|
||||||
pagination={{
|
|
||||||
currentPage,
|
|
||||||
totalPages: 2,
|
|
||||||
onPageChange: setCurrentPage
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* سایدبار - فرم اضافه کردن */}
|
<CategoryForm
|
||||||
<div className="w-[300px] bg-white rounded-3xl p-6 h-fit sticky top-6">
|
formik={formik}
|
||||||
<h2 className="font-light mb-6">اضافه کردن دسته بندی</h2>
|
isActive={isActive}
|
||||||
|
onActiveChange={setIsActive}
|
||||||
<div className="flex items-center justify-between mb-6">
|
onIconChange={setIconFiles}
|
||||||
<div className="text-sm">فعال</div>
|
iconFiles={iconFiles}
|
||||||
<SwitchComponent
|
isSubmitting={isCreating || isUpdating || isUploading}
|
||||||
active={formData.isActive}
|
isEditing={!!editingCategory}
|
||||||
onChange={(value) => setFormData({ ...formData, isActive: value })}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<Input
|
|
||||||
label="وضعیت دسته بندی"
|
|
||||||
value={formData.categoryStatus}
|
|
||||||
onChange={(e) => setFormData({ ...formData, categoryStatus: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<Input
|
|
||||||
label="عنوان دسته بندی"
|
|
||||||
value={formData.title}
|
|
||||||
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<Select
|
|
||||||
label="ترتیب نمایش"
|
|
||||||
items={orderOptions}
|
|
||||||
value={formData.order}
|
|
||||||
onChange={(e) => setFormData({ ...formData, order: e.target.value })}
|
|
||||||
placeholder="انتخاب کنید"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-6">
|
|
||||||
<UploadBox
|
|
||||||
label="آیکون دسته بندی"
|
|
||||||
onChange={(files) => setFormData({ ...formData, icon: files })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button onClick={handleSave} className="w-full">
|
|
||||||
<Add size={20} className="ml-2" />
|
|
||||||
ذخیره
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { type FC } from 'react'
|
||||||
|
import { Add } from 'iconsax-react'
|
||||||
|
import Input from '@/components/Input'
|
||||||
|
import UploadBox from '@/components/UploadBox'
|
||||||
|
import SwitchComponent from '@/components/Switch'
|
||||||
|
import Button from '@/components/Button'
|
||||||
|
import type { FormikProps } from 'formik'
|
||||||
|
|
||||||
|
type CategoryFormValues = {
|
||||||
|
title: string
|
||||||
|
icon: File[]
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
formik: FormikProps<CategoryFormValues>
|
||||||
|
isActive: boolean
|
||||||
|
onActiveChange: (value: boolean) => void
|
||||||
|
onIconChange: (files: File[]) => void
|
||||||
|
iconFiles: File[]
|
||||||
|
isSubmitting: boolean
|
||||||
|
isEditing?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const CategoryForm: FC<Props> = ({
|
||||||
|
formik,
|
||||||
|
isActive,
|
||||||
|
onActiveChange,
|
||||||
|
onIconChange,
|
||||||
|
iconFiles,
|
||||||
|
isSubmitting,
|
||||||
|
isEditing = false
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="w-[300px] bg-white rounded-3xl p-6 h-fit sticky top-6">
|
||||||
|
<h2 className="font-light mb-6">{isEditing ? 'ویرایش دسته بندی' : 'اضافه کردن دسته بندی'}</h2>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between mb-6">
|
||||||
|
<div className="text-sm">فعال</div>
|
||||||
|
<SwitchComponent
|
||||||
|
active={isActive}
|
||||||
|
onChange={onActiveChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-4">
|
||||||
|
<Input
|
||||||
|
label="عنوان دسته بندی"
|
||||||
|
name="title"
|
||||||
|
value={formik.values.title}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
<UploadBox
|
||||||
|
label="آیکون دسته بندی"
|
||||||
|
onChange={(files) => {
|
||||||
|
onIconChange(files)
|
||||||
|
formik.setFieldValue('icon', files)
|
||||||
|
}}
|
||||||
|
isReset={iconFiles.length === 0 && formik.values.icon.length === 0}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
className="w-full"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
>
|
||||||
|
<Add color='#fff' size={20} className="ml-2" />
|
||||||
|
ذخیره
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CategoryForm
|
||||||
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { Edit } from 'iconsax-react'
|
||||||
|
import SwitchComponent from '@/components/Switch'
|
||||||
|
import TrashWithConfrim from '@/components/TrashWithConfrim'
|
||||||
|
import type { Category } from '../types/Types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onStatusChange: (id: string, value: boolean) => void
|
||||||
|
onEdit: (category: Category) => void
|
||||||
|
onDelete: (id: string) => void
|
||||||
|
isDeleting?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const CategoryTableColumns = ({ onStatusChange, onEdit, onDelete, isDeleting = false }: Props) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'آیکون',
|
||||||
|
key: 'avatarUrl',
|
||||||
|
render: (item: Category) => (
|
||||||
|
<div className="w-8 h-8 rounded-lg flex items-center justify-center overflow-hidden">
|
||||||
|
{item.avatarUrl ? (
|
||||||
|
<img
|
||||||
|
src={item.avatarUrl}
|
||||||
|
alt={item.title}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="w-full h-full bg-gray-200 flex items-center justify-center text-xs">
|
||||||
|
بدون آیکون
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'عنوان',
|
||||||
|
key: 'title'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'وضعیت',
|
||||||
|
key: 'isActive',
|
||||||
|
render: (item: Category) => (
|
||||||
|
<SwitchComponent
|
||||||
|
active={item.isActive}
|
||||||
|
onChange={(value) => onStatusChange(item.id, value)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'actions',
|
||||||
|
title: '',
|
||||||
|
render: (item: Category) => {
|
||||||
|
return (
|
||||||
|
<div className='flex gap-2 items-center'>
|
||||||
|
<Edit
|
||||||
|
onClick={() => onEdit(item)}
|
||||||
|
size={20}
|
||||||
|
color='#8C90A3'
|
||||||
|
className='cursor-pointer'
|
||||||
|
/>
|
||||||
|
<TrashWithConfrim
|
||||||
|
onDelete={() => onDelete(item.id)}
|
||||||
|
isLoading={isDeleting}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CategoryTableColumns
|
||||||
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import * as api from "../service/FoodService";
|
import * as api from "../service/FoodService";
|
||||||
import type { CreateFoodType, GetFoodsParams } from "../types/Types";
|
import type {
|
||||||
|
CreateCategoryType,
|
||||||
|
CreateFoodType,
|
||||||
|
GetFoodsParams,
|
||||||
|
} from "../types/Types";
|
||||||
|
|
||||||
export const useCreateFood = () => {
|
export const useCreateFood = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
@@ -45,3 +49,34 @@ export const useDeleteFood = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useCreateCategory = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.createCategory,
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["categories"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteCategory = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.deleteCategory,
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["categories"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateCategory = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({ id, params }: { id: string; params: CreateCategoryType }) =>
|
||||||
|
api.updateCategory(id, params),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["categories"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import axios from "@/config/axios";
|
import axios from "@/config/axios";
|
||||||
import type {
|
import type {
|
||||||
|
CreateCategoryType,
|
||||||
CreateFoodType,
|
CreateFoodType,
|
||||||
GetCategoriesResponseType,
|
GetCategoriesResponseType,
|
||||||
GetFoodDetailsResponseType,
|
GetFoodDetailsResponseType,
|
||||||
@@ -40,3 +41,21 @@ export const deleteFood = async (id: string) => {
|
|||||||
const { data } = await axios.delete(`/foods/${id}`);
|
const { data } = await axios.delete(`/foods/${id}`);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createCategory = async (params: CreateCategoryType) => {
|
||||||
|
const { data } = await axios.post(`/categories`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteCategory = async (id: string) => {
|
||||||
|
const { data } = await axios.delete(`/categories/${id}`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateCategory = async (
|
||||||
|
id: string,
|
||||||
|
params: CreateCategoryType
|
||||||
|
) => {
|
||||||
|
const { data } = await axios.patch(`/categories/${id}`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export type Category = {
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
restId: string;
|
restId: string;
|
||||||
avatarUrl: string;
|
avatarUrl: string;
|
||||||
|
order: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Food = {
|
export type Food = {
|
||||||
@@ -84,3 +85,9 @@ export type GetFoodsParams = {
|
|||||||
export type GetCategoriesResponseType = IResponse<Category[]>;
|
export type GetCategoriesResponseType = IResponse<Category[]>;
|
||||||
export type GetFoodsResponseType = IResponse<Food[]>;
|
export type GetFoodsResponseType = IResponse<Food[]>;
|
||||||
export type GetFoodDetailsResponseType = IResponse<Food>;
|
export type GetFoodDetailsResponseType = IResponse<Food>;
|
||||||
|
|
||||||
|
export type CreateCategoryType = {
|
||||||
|
title: string;
|
||||||
|
isActive: boolean;
|
||||||
|
avatarUrl: string;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user