From 9c8b15a2cf1a3f99679862f4c01325c893d4f3c2 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Wed, 29 Jan 2025 11:51:09 +0330 Subject: [PATCH] uploader and create service --- .env | 4 +- src/components/UploadBoxDraggble.tsx | 80 +++++++++++++----- src/langs/fa.json | 7 +- src/pages/service/AddService.tsx | 82 +++++++++++++++++-- src/pages/service/List.tsx | 24 +++--- .../service/components/AddServiceSidebar.tsx | 9 +- src/pages/service/hooks/useServiceData.ts | 18 ++++ src/pages/service/service/ServiceService.ts | 16 ++++ 8 files changed, 196 insertions(+), 44 deletions(-) diff --git a/.env b/.env index 3fbd3a5..74f350e 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ VITE_TOKEN_NAME = 'admin_token' VITE_REFRESH_TOKEN_NAME = 'admin_refresh_token' -# VITE_BASE_URL = 'https://danak-dsc-api.run.danakcorp.com' -VITE_BASE_URL = 'http://192.168.0.207:3500' \ No newline at end of file +VITE_BASE_URL = 'https://danak-dsc-api.run.danakcorp.com' +# VITE_BASE_URL = 'http://192.168.0.207:3500' \ No newline at end of file diff --git a/src/components/UploadBoxDraggble.tsx b/src/components/UploadBoxDraggble.tsx index aaff9b6..3659fc7 100644 --- a/src/components/UploadBoxDraggble.tsx +++ b/src/components/UploadBoxDraggble.tsx @@ -1,40 +1,80 @@ -import { DocumentUpload, Gallery } from 'iconsax-react' -import { FC, useCallback } from 'react' +import { CloseCircle, DocumentUpload, Gallery } from 'iconsax-react' +import { FC, useCallback, useState } from 'react' import { useDropzone } from 'react-dropzone'; import { useTranslation } from 'react-i18next'; type Props = { label: string; - onChange: (file: File) => void; + onChange: (file: File[]) => void; + isMultiple?: boolean; } const UploadBoxDraggble: FC = (props: Props) => { + + const [files, setFiles] = useState([]) const { t } = useTranslation('global') const onDrop = useCallback((acceptedFiles: File[]) => { - props.onChange(acceptedFiles[0]) + if (props.isMultiple) { + const array = [...files] + array.push(acceptedFiles[0]) + setFiles(array) + props.onChange(array) + } else { + setFiles([acceptedFiles[0]]) + props.onChange([acceptedFiles[0]]) + } // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + }, [files]) const { getRootProps, getInputProps } = useDropzone({ onDrop }) + const handleDelete = (index: number) => { + const array = [...files] + array.splice(index, 1) + setFiles(array) + props.onChange(array) + } + return ( -
- - -
- {props.label} +
+
+ + +
+ {props.label} +
+ +
+ +
{t('upload')}
+
-
- -
{t('upload')}
-
+ { + files.length > 0 && ( +
+ { + files.map((file, index) => ( +
+
+ {file.name} +
+ handleDelete(index)} className='size-4 ' color='red' /> +
+
+
+ )) + } +
+ ) + } +
) } diff --git a/src/langs/fa.json b/src/langs/fa.json index 04f4839..b7f3de1 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -38,7 +38,8 @@ "login_with_otp": "ورود با رمز عبور یکبار مصرف" }, "errors": { - "required": "این فیلد اجباری می باشد" + "required": "این فیلد اجباری می باشد", + "upload_image": "تصاویر مربوطه باید انتخاب شوند" }, "description": "توضیحات", "sidebar": { @@ -153,7 +154,9 @@ "category_parent_2": "دسته بندی مادر", "icon_serice_category": "آیکون سرویس مورد نظر را آپلود کنید", "active": "فعال", - "deactive": "غیرفعال" + "deactive": "غیرفعال", + "software_langauge": "زبان نرم افزار", + "link": "لینک" }, "save": "ذخیره", "search": "جستجو", diff --git a/src/pages/service/AddService.tsx b/src/pages/service/AddService.tsx index 7c61952..5281da8 100644 --- a/src/pages/service/AddService.tsx +++ b/src/pages/service/AddService.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useRef } from 'react' +import { FC, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '../../components/Input' import Select from '../../components/Select' @@ -10,11 +10,23 @@ import { CreateServiceType } from './types/ServiceTypes'; import DatePickerComponent from '../../components/DatePicker'; import AddServiceSidebar from './components/AddServiceSidebar'; import * as Yup from 'yup' +import { useCreateService, useMultiUpload, useSingleUpload } from './hooks/useServiceData'; +import moment from 'moment-jalaali'; +import { ErrorType } from '../../helpers/types'; +import { toast } from 'react-toastify'; +import { useNavigate } from 'react-router-dom'; +import { Pages } from '../../config/Pages'; const AddService: FC = () => { + const navigate = useNavigate() const { t } = useTranslation('global') + const singleUpload = useSingleUpload() + const multiUpload = useMultiUpload() const editorRef = useRef(null); + const createService = useCreateService() + const [iconFile, setIconFile] = useState() + const [imagesFile, setImagesFile] = useState() useEffect(() => { const quill = new Quill('#editor', { @@ -63,12 +75,55 @@ const AddService: FC = () => { metaDescription: Yup.string().required(t('errors.required')), link: Yup.string().required(t('errors.required')), }), - onSubmit(values) { - console.log(values); + onSubmit: async (values) => { + if (iconFile && imagesFile) { + const formData = new FormData() + formData.append('file', iconFile) + await singleUpload.mutateAsync(formData, { + onSuccess: (data) => { + values.icon = data?.data?.url + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + + const images = new FormData() + imagesFile.forEach(file => { + images.append('files', file) + }) + await multiUpload.mutateAsync(images, { + onSuccess: async (data) => { + values.images = await data.data.map((item: { url: string }) => item?.url) + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + + const params = { + ...values, + userCount: +values.userCount, + createDate: moment(values.createDate).format('YYYY-MM-DD'), + } + createService.mutate(params, { + onSuccess: () => { + formik.resetForm() + toast.success(t('success')) + navigate(Pages.services.list) + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + } else { + toast.error(t('errors.upload_image')) + } }, }) + return (
@@ -79,6 +134,7 @@ const AddService: FC = () => { + + +
diff --git a/src/pages/service/components/AddServiceSidebar.tsx b/src/pages/service/components/AddServiceSidebar.tsx index 4213cad..dae0e64 100644 --- a/src/pages/service/components/AddServiceSidebar.tsx +++ b/src/pages/service/components/AddServiceSidebar.tsx @@ -9,7 +9,9 @@ import { CreateServiceType, ServiceCategoryType } from '../types/ServiceTypes' import { useGetCategoryParents } from '../hooks/useServiceData' type Props = { - formik: FormikProps + formik: FormikProps, + onChangeIconFile: (file: File) => void, + onChangeImagesFile: (file: File[]) => void } const AddServiceSidebar: FC = (props: Props) => { @@ -76,7 +78,7 @@ const AddServiceSidebar: FC = (props: Props) => {
null} + onChange={(file: File[]) => props.onChangeIconFile(file[0])} />
@@ -85,7 +87,8 @@ const AddServiceSidebar: FC = (props: Props) => {
null} + onChange={props.onChangeImagesFile} + isMultiple />
diff --git a/src/pages/service/hooks/useServiceData.ts b/src/pages/service/hooks/useServiceData.ts index 0765ad5..f779416 100644 --- a/src/pages/service/hooks/useServiceData.ts +++ b/src/pages/service/hooks/useServiceData.ts @@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import * as api from "../service/ServiceService"; import { CreateServiceCategoryType, + CreateServiceType, ToggleStatusCategoryType, } from "../types/ServiceTypes"; @@ -42,3 +43,20 @@ export const useChangeStatusCategory = () => { api.changeStatusCategory(variables), }); }; + +export const useCreateService = () => { + return useMutation({ + mutationFn: (variables: CreateServiceType) => api.createService(variables), + }); +}; + +export const useSingleUpload = () => { + return useMutation({ + mutationFn: (variables: FormData) => api.uploadSingle(variables), + }); +}; +export const useMultiUpload = () => { + return useMutation({ + mutationFn: (variables: FormData) => api.uploadMultiple(variables), + }); +}; diff --git a/src/pages/service/service/ServiceService.ts b/src/pages/service/service/ServiceService.ts index cf19646..497ecb5 100644 --- a/src/pages/service/service/ServiceService.ts +++ b/src/pages/service/service/ServiceService.ts @@ -1,6 +1,7 @@ import axios from "../../../config/axios"; import { CreateServiceCategoryType, + CreateServiceType, ToggleStatusCategoryType, } from "../types/ServiceTypes"; @@ -37,3 +38,18 @@ export const changeStatusCategory = async ( ); return data; }; + +export const createService = async (params: CreateServiceType) => { + const { data } = await axios.post(`/services`, params); + return data; +}; + +export const uploadSingle = async (params: FormData) => { + const { data } = await axios.post(`/uploader/single-file`, params); + return data; +}; + +export const uploadMultiple = async (params: FormData) => { + const { data } = await axios.post(`/uploader/multi-file`, params); + return data; +};