This commit is contained in:
@@ -12,6 +12,34 @@ type Props = {
|
||||
accept?: string;
|
||||
}
|
||||
|
||||
const MIME_TO_EXTENSIONS: Record<string, string[]> = {
|
||||
'image/png': ['.png'],
|
||||
'image/svg+xml': ['.svg'],
|
||||
'image/jpeg': ['.jpg', '.jpeg'],
|
||||
'image/gif': ['.gif'],
|
||||
'image/webp': ['.webp'],
|
||||
}
|
||||
|
||||
const buildAcceptTypes = (accept?: string) => {
|
||||
if (!accept) return undefined
|
||||
|
||||
return accept.split(',').reduce<Record<string, string[]>>((acc, type) => {
|
||||
const trimmed = type.trim()
|
||||
if (trimmed.startsWith('.')) {
|
||||
const ext = trimmed.toLowerCase()
|
||||
const mimeEntry = Object.entries(MIME_TO_EXTENSIONS).find(([, exts]) => exts.includes(ext))
|
||||
if (mimeEntry) {
|
||||
acc[mimeEntry[0]] = mimeEntry[1]
|
||||
}
|
||||
} else if (MIME_TO_EXTENSIONS[trimmed]) {
|
||||
acc[trimmed] = MIME_TO_EXTENSIONS[trimmed]
|
||||
} else {
|
||||
acc[trimmed] = []
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
const UploadBox: FC<Props> = (props: Props) => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
@@ -19,8 +47,7 @@ const UploadBox: FC<Props> = (props: Props) => {
|
||||
|
||||
const onDrop = useCallback((acceptedFiles: File[]) => {
|
||||
if (props.isMultiple) {
|
||||
const array = [...files]
|
||||
array.push(acceptedFiles[0])
|
||||
const array = [...files, ...acceptedFiles]
|
||||
setFiles(array)
|
||||
if (props.onChange) {
|
||||
props.onChange(array);
|
||||
@@ -33,9 +60,11 @@ const UploadBox: FC<Props> = (props: Props) => {
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [files])
|
||||
const acceptTypes = buildAcceptTypes(props.accept)
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop,
|
||||
accept: props.accept ? { [props.accept]: [] } : undefined
|
||||
accept: acceptTypes,
|
||||
multiple: props.isMultiple,
|
||||
})
|
||||
|
||||
const handleRemove = (index: number) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as Yup from 'yup'
|
||||
import { toast } from 'react-toastify'
|
||||
import { TickCircle } from 'iconsax-react'
|
||||
import { useCreateIcon, useGetGroupIcons } from '../../hooks/useIconData'
|
||||
import { useSingleUpload } from '../../../service/hooks/useServiceData'
|
||||
import { useMultiUpload } from '../../../service/hooks/useServiceData'
|
||||
import { CreateIconType, GroupIconType } from '../../types/Types'
|
||||
import { ErrorType } from '../../../../helpers/types'
|
||||
|
||||
@@ -21,9 +21,10 @@ interface CreateIconProps {
|
||||
|
||||
const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
const { t } = useTranslation('global')
|
||||
const [file, setFile] = useState<File>()
|
||||
const [files, setFiles] = useState<File[]>([])
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const createIcon = useCreateIcon()
|
||||
const singleUpload = useSingleUpload()
|
||||
const multiUpload = useMultiUpload()
|
||||
const { data: groupIcons } = useGetGroupIcons()
|
||||
|
||||
const groups = (groupIcons as unknown as { data?: GroupIconType[] })?.data || []
|
||||
@@ -32,21 +33,6 @@ const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
label: group.name
|
||||
}))
|
||||
|
||||
const handleCreateIcon = (values: CreateIconType) => {
|
||||
createIcon.mutate(values, {
|
||||
onSuccess: () => {
|
||||
formik.resetForm()
|
||||
setFile(undefined)
|
||||
close()
|
||||
toast.success(t('success'))
|
||||
onSuccess?.()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const formik = useFormik<CreateIconType>({
|
||||
initialValues: {
|
||||
url: '',
|
||||
@@ -57,27 +43,44 @@ const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
.required(t('errors.required'))
|
||||
}),
|
||||
onSubmit: async (values) => {
|
||||
if (!file) {
|
||||
if (files.length === 0) {
|
||||
toast.error(t('errors.required'))
|
||||
return
|
||||
}
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
await singleUpload.mutateAsync(formData, {
|
||||
onSuccess: (data) => {
|
||||
values.url = data?.data?.url
|
||||
handleCreateIcon(values)
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
const formData = new FormData()
|
||||
files.forEach((file) => {
|
||||
formData.append('files', file)
|
||||
})
|
||||
const uploadResult = await multiUpload.mutateAsync(formData)
|
||||
const uploadedUrls: string[] = uploadResult.data.map((item: { url: string }) => item.url)
|
||||
|
||||
for (const url of uploadedUrls) {
|
||||
await createIcon.mutateAsync({
|
||||
url,
|
||||
groupId: values.groupId
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
formik.resetForm()
|
||||
setFiles([])
|
||||
close()
|
||||
toast.success(t('success'))
|
||||
onSuccess?.()
|
||||
} catch (error) {
|
||||
const err = error as ErrorType
|
||||
toast.error(err.response?.data?.error?.message?.[0])
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
formik.resetForm()
|
||||
setFile(undefined)
|
||||
setFiles([])
|
||||
close()
|
||||
}
|
||||
|
||||
@@ -91,9 +94,10 @@ const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
<div className='mt-6'>
|
||||
<UploadBox
|
||||
label={t('icon.url')}
|
||||
onChange={(files) => setFile(files[0])}
|
||||
onChange={(selectedFiles) => setFiles(selectedFiles)}
|
||||
isReset={!open}
|
||||
accept="image/svg+xml"
|
||||
isMultiple
|
||||
accept="image/svg+xml,.svg,image/png,.png"
|
||||
/>
|
||||
|
||||
<div className='mt-4'>
|
||||
@@ -117,7 +121,7 @@ const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
|
||||
<Button
|
||||
onClick={() => formik.handleSubmit()}
|
||||
isLoading={createIcon.isPending || singleUpload.isPending}
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
<div className='flex gap-2'>
|
||||
<TickCircle
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as Yup from 'yup'
|
||||
import { toast } from 'react-toastify'
|
||||
import { TickCircle } from 'iconsax-react'
|
||||
import { useCreateIcon, useGetGroupIcons } from '../../hooks/useIconData'
|
||||
import { useSingleUpload } from '../../../service/hooks/useServiceData'
|
||||
import { useMultiUpload } from '../../../service/hooks/useServiceData'
|
||||
import { CreateIconType, GroupIconType } from '../../types/Types'
|
||||
import { ErrorType } from '../../../../helpers/types'
|
||||
|
||||
@@ -21,9 +21,10 @@ interface CreateIconProps {
|
||||
|
||||
const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
const { t } = useTranslation('global')
|
||||
const [file, setFile] = useState<File>()
|
||||
const [files, setFiles] = useState<File[]>([])
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
const createIcon = useCreateIcon()
|
||||
const singleUpload = useSingleUpload()
|
||||
const multiUpload = useMultiUpload()
|
||||
const { data: groupIcons } = useGetGroupIcons()
|
||||
|
||||
const groups = (groupIcons as unknown as { data?: GroupIconType[] })?.data || []
|
||||
@@ -32,21 +33,6 @@ const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
label: group.name
|
||||
}))
|
||||
|
||||
const handleCreateIcon = (values: CreateIconType) => {
|
||||
createIcon.mutate(values, {
|
||||
onSuccess: () => {
|
||||
formik.resetForm()
|
||||
setFile(undefined)
|
||||
close()
|
||||
toast.success(t('success'))
|
||||
onSuccess?.()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const formik = useFormik<CreateIconType>({
|
||||
initialValues: {
|
||||
url: '',
|
||||
@@ -57,27 +43,44 @@ const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
.required(t('errors.required'))
|
||||
}),
|
||||
onSubmit: async (values) => {
|
||||
if (!file) {
|
||||
if (files.length === 0) {
|
||||
toast.error(t('errors.required'))
|
||||
return
|
||||
}
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
await singleUpload.mutateAsync(formData, {
|
||||
onSuccess: (data) => {
|
||||
values.url = data?.data?.url
|
||||
handleCreateIcon(values)
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error?.message[0])
|
||||
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
const formData = new FormData()
|
||||
files.forEach((file) => {
|
||||
formData.append('files', file)
|
||||
})
|
||||
const uploadResult = await multiUpload.mutateAsync(formData)
|
||||
const uploadedUrls: string[] = uploadResult.data.map((item: { url: string }) => item.url)
|
||||
|
||||
for (const url of uploadedUrls) {
|
||||
await createIcon.mutateAsync({
|
||||
url,
|
||||
groupId: values.groupId
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
formik.resetForm()
|
||||
setFiles([])
|
||||
close()
|
||||
toast.success(t('success'))
|
||||
onSuccess?.()
|
||||
} catch (error) {
|
||||
const err = error as ErrorType
|
||||
toast.error(err.response?.data?.error?.message?.[0])
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
formik.resetForm()
|
||||
setFile(undefined)
|
||||
setFiles([])
|
||||
close()
|
||||
}
|
||||
|
||||
@@ -91,9 +94,10 @@ const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
<div className='mt-6'>
|
||||
<UploadBox
|
||||
label={t('icon.url')}
|
||||
onChange={(files) => setFile(files[0])}
|
||||
onChange={(selectedFiles) => setFiles(selectedFiles)}
|
||||
isReset={!open}
|
||||
accept="image/svg+xml"
|
||||
isMultiple
|
||||
accept="image/svg+xml,.svg,image/png,.png"
|
||||
/>
|
||||
|
||||
<div className='mt-4'>
|
||||
@@ -117,7 +121,7 @@ const CreateIcon: FC<CreateIconProps> = ({ open, close, onSuccess }) => {
|
||||
|
||||
<Button
|
||||
onClick={() => formik.handleSubmit()}
|
||||
isLoading={createIcon.isPending || singleUpload.isPending}
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
<div className='flex gap-2'>
|
||||
<TickCircle
|
||||
|
||||
Reference in New Issue
Block a user