diff --git a/package-lock.json b/package-lock.json index cf2afb3..97b5c75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,7 @@ }, "devDependencies": { "@eslint/js": "^9.33.0", + "@types/node": "^24.5.2", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", "@vitejs/plugin-react": "^5.0.0", @@ -2387,6 +2388,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "24.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", + "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.12.0" + } + }, "node_modules/@types/react": { "version": "19.1.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", @@ -5468,6 +5479,13 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/undici-types": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", + "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", diff --git a/package.json b/package.json index 7dd4730..0974b69 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ }, "devDependencies": { "@eslint/js": "^9.33.0", + "@types/node": "^24.5.2", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", "@vitejs/plugin-react": "^5.0.0", diff --git a/src/App.tsx b/src/App.tsx index 91a0495..33f1980 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -56,7 +56,8 @@ const queryClient = new QueryClient({ try { const refreshTokenValue = await getRefreshToken(); - const { data } = await refreshToken({ refreshToken: refreshTokenValue || '' }); + const response = await refreshToken({ token: refreshTokenValue || '' }); + const data = response.results.data; if (data?.accessToken?.token) { // Save the new token diff --git a/src/config/Pages.ts b/src/config/Pages.ts index e5ab3be..bce9189 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -129,5 +129,10 @@ export const Pages = { list: "/products/list", variants: "/products/variants/", update: "/products/update/", + brands: { + list: "/products/brands", + update: "/products/brands/update/", + create: "/products/brands/create", + }, }, }; diff --git a/src/pages/brand/Create.tsx b/src/pages/brand/Create.tsx new file mode 100644 index 0000000..17231c6 --- /dev/null +++ b/src/pages/brand/Create.tsx @@ -0,0 +1,168 @@ +import { type FC, useState } from 'react' +import { useGetCategories, type CategoryTreeNode } from '../category' +import { useFormik } from 'formik' +import { type CreateBrandType } from './types/Types' +import * as Yup from 'yup' +import { useCreateBrand } from './hooks/useBrandData' +import { useNavigate } from 'react-router-dom' +import Input from '../../components/Input' +import Select from '../../components/Select' +import Textarea from '../../components/Textarea' +import Button from '../../components/Button' +import { TickCircle } from 'iconsax-react' +import { useUploadMultiple, useUploadSingle } from '../uploader/hooks/useUploaderData' +import UploadBoxDraggble from '@/components/UploadBoxDraggble' +import { toast } from 'react-toastify' +import { extractErrorMessage } from '@/helpers/utils' + +const Create: FC = () => { + const navigate = useNavigate() + const { data: categoriesData } = useGetCategories() + const uploadSingle = useUploadSingle() + const uploadMultiple = useUploadMultiple() + const createBrandMutation = useCreateBrand() + + const [logoFile, setLogoFile] = useState([]) + const [imageFiles, setImageFiles] = useState([]) + + const formik = useFormik({ + initialValues: { + title_fa: '', + title_en: '', + category: '', + description: '', + images: [], + logoUrl: '', + }, + validationSchema: Yup.object({ + title_fa: Yup.string().required('عنوان فارسی برند الزامی است'), + title_en: Yup.string().required('عنوان انگلیسی برند الزامی است'), + category: Yup.string().required('دسته بندی برند الزامی است'), + description: Yup.string().required('توضیحات برند الزامی است'), + }), + onSubmit: async (values) => { + let logoUrl = values.logoUrl + let images = values.images + + if (logoFile.length > 0) { + const logoResponse = await uploadSingle.mutateAsync(logoFile[0]) + logoUrl = logoResponse.results?.url?.url || '' + } + + if (imageFiles.length > 0) { + const imagesResponse = await uploadMultiple.mutateAsync(imageFiles) + images = imagesResponse.results?.urls?.map(file => file.url) || [] + } + + const submitData = { + ...values, + logoUrl, + images + } + + createBrandMutation.mutate(submitData, { + onSuccess: () => { + toast.success('برند با موفقیت ایجاد شد') + navigate('/brands') + }, + onError: (error) => { + toast.error(extractErrorMessage(error)) + } + }) + }, + }) + + const categoryOptions = categoriesData?.results?.data?.map((category: CategoryTreeNode) => ({ + value: category._id, + label: category.title_fa + })) || [] + + return ( +
+
+
+

ساخت برند جدید

+
+
+ +
+
+ +
+ {/* اطلاعات اصلی */} +
+

اطلاعات اصلی

+ +
+
+ + +
+ +