diff --git a/src/components/DefaulModal.tsx b/src/components/DefaulModal.tsx index 0f2b2d5..0c00d88 100644 --- a/src/components/DefaulModal.tsx +++ b/src/components/DefaulModal.tsx @@ -1,4 +1,5 @@ import { type FC, Fragment, type ReactNode, useEffect } from 'react' +import { createPortal } from 'react-dom' import HeaderModal from './HeaderModal' interface Props { @@ -21,37 +22,41 @@ const DefaulModal: FC = (props: Props) => { }, [props.open]) - return ( + const modalContent = props.open ? ( - { - props.open && ( - -
-
-
+
+
+
e.stopPropagation()} + > +
- { - props.isHeader && props.title_header && -
-
- -
- } - - {props.children} - - -
+ { + props.isHeader && props.title_header && +
+
+
-
+ } -
- - ) - } + {props.children} + + +
+
+
- ) + ) : null + + return createPortal(modalContent, document.body) } export default DefaulModal \ No newline at end of file diff --git a/src/pages/food/Category.tsx b/src/pages/food/Category.tsx index 19982be..32e64aa 100644 --- a/src/pages/food/Category.tsx +++ b/src/pages/food/Category.tsx @@ -25,6 +25,7 @@ type CategoryFormValues = { const CategoryFood: FC = () => { const [isActive, setIsActive] = useState(true) const [iconFiles, setIconFiles] = useState([]) + const [selectedIconUrl, setSelectedIconUrl] = useState('') const [editingCategory, setEditingCategory] = useState(null) const [filters, setFilters] = useState<{ search?: string | null }>({}) @@ -56,7 +57,7 @@ const CategoryFood: FC = () => { const categoryData: CreateCategoryType = { title: values.title, isActive, - avatarUrl: avatarUrl || editingCategory?.avatarUrl || '' + avatarUrl: avatarUrl || selectedIconUrl || editingCategory?.avatarUrl || '' } if (editingCategory) { @@ -85,7 +86,9 @@ const CategoryFood: FC = () => { } } - if (iconFiles.length > 0) { + if (selectedIconUrl) { + submitCategory(selectedIconUrl) + } else if (iconFiles.length > 0) { singleUpload(iconFiles[0], { onSuccess: (response) => { const avatarUrl = response?.data?.url || '' @@ -105,6 +108,7 @@ const CategoryFood: FC = () => { formik.resetForm() setIsActive(true) setIconFiles([]) + setSelectedIconUrl('') setEditingCategory(null) } @@ -115,9 +119,21 @@ const CategoryFood: FC = () => { }) 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: () => { @@ -189,10 +205,12 @@ const CategoryFood: FC = () => { formik={formik} isActive={isActive} onActiveChange={setIsActive} - onIconChange={setIconFiles} + onIconChange={handleIconFileChange} iconFiles={iconFiles} isSubmitting={isCreating || isUpdating || isUploading} isEditing={!!editingCategory} + onIconUrlChange={handleIconUrlChange} + selectedIconUrl={selectedIconUrl} />
) diff --git a/src/pages/food/components/CategoryForm.tsx b/src/pages/food/components/CategoryForm.tsx index f9858aa..4645e7a 100644 --- a/src/pages/food/components/CategoryForm.tsx +++ b/src/pages/food/components/CategoryForm.tsx @@ -1,10 +1,11 @@ -import { type FC } from 'react' -import { Add } from 'iconsax-react' +import { type FC, useState } from 'react' +import { Add, Image } 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' +import { useGetIcons } from '../hooks/useFoodData' +import IconSelectModal from './IconSelectModal' type CategoryFormValues = { title: string @@ -15,21 +16,34 @@ type Props = { formik: FormikProps isActive: boolean onActiveChange: (value: boolean) => void - onIconChange: (files: File[]) => void - iconFiles: File[] + onIconChange?: (files: File[]) => void + iconFiles?: File[] isSubmitting: boolean isEditing?: boolean + onIconUrlChange?: (url: string) => void + selectedIconUrl?: string } const CategoryForm: FC = ({ formik, isActive, onActiveChange, - onIconChange, - iconFiles, isSubmitting, - isEditing = false + isEditing = false, + onIconUrlChange, + selectedIconUrl }) => { + const { data: iconsData } = useGetIcons() + const icons = iconsData?.data || [] + const [isIconModalOpen, setIsIconModalOpen] = useState(false) + + const handleIconSelect = (url: string) => { + if (onIconUrlChange) { + onIconUrlChange(url) + } + setIsIconModalOpen(false) + } + return (

{isEditing ? 'ویرایش دسته بندی' : 'اضافه کردن دسته بندی'}

@@ -53,16 +67,61 @@ const CategoryForm: FC = ({
- { - onIconChange(files) - formik.setFieldValue('icon', files) - }} - isReset={iconFiles.length === 0 && formik.values.icon.length === 0} - /> +
آیکون دسته بندی
+ + {selectedIconUrl && ( +
+
+ selected icon { + e.currentTarget.style.display = 'none' + }} + /> +
+
+
آیکون انتخاب شده
+
+ {selectedIconUrl} +
+
+ +
+ )}
+ setIsIconModalOpen(false)} + icons={icons} + onSelect={handleIconSelect} + selectedUrl={selectedIconUrl} + /> + + ))} +
+ )} + + ))} + + )} + + + ) +} + +export default IconSelectModal + diff --git a/src/pages/food/hooks/useFoodData.ts b/src/pages/food/hooks/useFoodData.ts index 781d548..6601f0b 100644 --- a/src/pages/food/hooks/useFoodData.ts +++ b/src/pages/food/hooks/useFoodData.ts @@ -80,3 +80,10 @@ export const useUpdateCategory = () => { }, }); }; + +export const useGetIcons = () => { + return useQuery({ + queryKey: ["icons"], + queryFn: api.getIcons, + }); +}; diff --git a/src/pages/food/service/FoodService.ts b/src/pages/food/service/FoodService.ts index 59e655f..c1036e7 100644 --- a/src/pages/food/service/FoodService.ts +++ b/src/pages/food/service/FoodService.ts @@ -6,6 +6,7 @@ import type { GetFoodDetailsResponseType, GetFoodsParams, GetFoodsResponseType, + GetIconsResponseType, } from "../types/Types"; export const createFood = async (params: CreateFoodType) => { @@ -65,3 +66,8 @@ export const updateCategory = async ( const { data } = await axios.patch(`/admin/categories/${id}`, params); return data; }; + +export const getIcons = async (): Promise => { + const { data } = await axios.get(`/admin/groups/icons`); + return data; +}; diff --git a/src/pages/food/types/Types.ts b/src/pages/food/types/Types.ts index cacc253..5cdaa38 100644 --- a/src/pages/food/types/Types.ts +++ b/src/pages/food/types/Types.ts @@ -129,3 +129,23 @@ export type CreateCategoryType = { isActive: boolean; avatarUrl: string; }; + +export type Icon = { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + url: string; + group: string; +}; + +export type IconGroup = { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string | null; + name: string; + icons: Icon[]; +}; + +export type GetIconsResponseType = IResponse;