diff --git a/.env b/.env index 33603eb..808e83d 100644 --- a/.env +++ b/.env @@ -1,8 +1,8 @@ VITE_TOKEN_NAME = 'dkala_a_t' VITE_REFRESH_TOKEN_NAME = 'dkala-a-rt' -VITE_BASE_URL = 'https://dkala-api.danakcorp.com' -# VITE_BASE_URL = 'http://192.168.99.131:4000' +# VITE_BASE_URL = 'https://dkala-api.danakcorp.com' +VITE_BASE_URL = 'http://192.168.99.131:4000' VITE_SOCKET_URL = 'https://dkala-api.danakcorp.com' \ No newline at end of file diff --git a/src/pages/product/Create.tsx b/src/pages/product/Create.tsx index 2a53822..2c4cad0 100644 --- a/src/pages/product/Create.tsx +++ b/src/pages/product/Create.tsx @@ -1,164 +1,147 @@ -import { type FC, useState } from 'react' -import { useFormik } from 'formik' -import * as Yup from 'yup' -import { TickCircle } from 'iconsax-react' -import { toast } from 'react-toastify' -import Button from '@/components/Button' -import CreateProductSidebar from './components/CreateProductSidebar' -import BasicInfoSection from './components/BasicInfoSection' -import type { CreateProductType } from './types/Types' -import { useCreateProduct, useGetCategories } from './hooks/useProductData' -import { useMultipleUpload } from '../uploader/hooks/useUploaderData' -import { Pages } from '@/config/Pages' -import type { ErrorType } from '@/helpers/types' -import { PaymentType, PricingType, PurchaseUnit } from './enum/Enum' - - +import Button from "@/components/Button"; +import type { ErrorType } from "@/helpers/types"; +import { useFormik } from "formik"; +import { TickCircle } from "iconsax-react"; +import { type FC, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { toast } from "react-toastify"; +import * as Yup from "yup"; +import { useMultipleUpload } from "../uploader/hooks/useUploaderData"; +import BasicInfoSection from "./components/BasicInfoSection"; +import CreateProductSidebar from "./components/CreateProductSidebar"; +import { PaymentType, PricingType, PurchaseUnit } from "./enum/Enum"; +import { useCreateProduct, useGetCategories } from "./hooks/useProductData"; +import type { CreateProductType } from "./types/Types"; const CreateProduct: FC = () => { + const navigate = useNavigate(); + const { data } = useGetCategories(); + const [isActive, setIsActive] = useState(true); + const [isSpecial, setIsSpecial] = useState(false); + const [imageFiles, setImageFiles] = useState([]); + const { mutate: createProduct, isPending } = useCreateProduct(); + const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload(); - const { data } = useGetCategories(); - const [isActive, setIsActive] = useState(true) - const [isSpecial, setIsSpecial] = useState(false) - const [imageFiles, setImageFiles] = useState([]) - const { mutate: createProduct, isPending } = useCreateProduct() - const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload() + const categories = data?.data ?? []; - const categories = data?.data ?? [] - - const formik = useFormik({ - initialValues: { - categoryId: '', - title: '', - attribute: '', - variants: [], - desc: '', - price: 0, - isActive: true, - images: [], - discount: 0, - isSpecialOffer: false, - order: 0, - stock: undefined, - maxPurchaseQuantity: undefined, - minPurchaseQuantity: undefined, - purchasePitch: undefined, - pricingType: PricingType.FIXED, - paymentType: PaymentType.INSTANT, - purchaseUnit: PurchaseUnit.NUMBER, - needAdminAcceptance: false, - }, - validationSchema: Yup.object().shape({ - title: Yup.string().required('نام کالا الزامی است'), - categoryId: Yup.string().required('دسته‌بندی الزامی است'), - price: Yup.number().required('قیمت الزامی است').min(0, 'قیمت باید مثبت باشد'), - discount: Yup.number().min(0, 'تخفیف باید مثبت باشد'), - attribute: Yup.string(), - pricingType: Yup.string().required(), - paymentType: Yup.string().required(), - minPurchaseQuantity: Yup.number().when('pricingType', { - is: PricingType.VARIABLE, - then: (schema) => schema.min(0, 'حداقل مقدار باید مثبت باشد'), - }), - maxPurchaseQuantity: Yup.number().min(0, 'حداکثر مقدار باید مثبت باشد'), - purchasePitch: Yup.number().when('pricingType', { - is: PricingType.VARIABLE, - then: (schema) => schema.min(0, 'گام تغییر باید مثبت باشد'), - }), - variants: Yup.array().of( - Yup.object().shape({ - id: Yup.string(), - value: Yup.string(), - price: Yup.number().min(0) - }) - ) + const formik = useFormik({ + initialValues: { + categoryId: "", + title: "", + attribute: "", + variants: [], + desc: "", + price: 0, + isActive: true, + images: [], + discount: 0, + isSpecialOffer: false, + order: 0, + stock: undefined, + maxPurchaseQuantity: undefined, + minPurchaseQuantity: undefined, + purchasePitch: undefined, + pricingType: PricingType.FIXED, + paymentType: PaymentType.INSTANT, + purchaseUnit: PurchaseUnit.NUMBER, + needAdminAcceptance: false, + }, + validationSchema: Yup.object().shape({ + title: Yup.string().required("نام کالا الزامی است"), + categoryId: Yup.string().required("دسته‌بندی الزامی است"), + price: Yup.number().required("قیمت الزامی است").min(0, "قیمت باید مثبت باشد"), + discount: Yup.number().min(0, "تخفیف باید مثبت باشد"), + attribute: Yup.string(), + pricingType: Yup.string().required(), + paymentType: Yup.string().required(), + minPurchaseQuantity: Yup.number().when("pricingType", { + is: PricingType.VARIABLE, + then: (schema) => schema.min(0, "حداقل مقدار باید مثبت باشد"), + }), + maxPurchaseQuantity: Yup.number().min(0, "حداکثر مقدار باید مثبت باشد"), + purchasePitch: Yup.number().when("pricingType", { + is: PricingType.VARIABLE, + then: (schema) => schema.min(0, "گام تغییر باید مثبت باشد"), + }), + variants: Yup.array().of( + Yup.object().shape({ + id: Yup.string(), + value: Yup.string(), + price: Yup.number().min(0), }), - onSubmit: (values) => { - const submitProduct = (imageUrls: string[] = []) => { - const variants = - values.variants.length > 0 - ? values.variants - : [{ value: '', price: values.price, ...(values.stock !== undefined && values.stock !== null && { stock: values.stock }) }] - const productData: CreateProductType = { - ...values, - variants, - isActive, - isSpecialOffer: isSpecial, - images: imageUrls, - needAdminAcceptance: values.paymentType === PaymentType.AFTER_PREPARATION, - ...(values.pricingType === PricingType.FIXED && { - purchaseUnit: values.purchaseUnit ?? PurchaseUnit.NUMBER, - minPurchaseQuantity: values.minPurchaseQuantity ?? 1, - purchasePitch: undefined, - }), - } + ), + }), + onSubmit: (values) => { + const submitProduct = (imageUrls: string[] = []) => { + const variants = values.variants.length > 0 ? values.variants : [{ value: "", price: values.price, ...(values.stock !== undefined && values.stock !== null && { stock: values.stock }) }]; + const productData: CreateProductType = { + ...values, + variants, + isActive, + isSpecialOffer: isSpecial, + images: imageUrls, + needAdminAcceptance: values.paymentType === PaymentType.AFTER_PREPARATION, + ...(values.pricingType === PricingType.FIXED && { + purchaseUnit: values.purchaseUnit ?? PurchaseUnit.NUMBER, + minPurchaseQuantity: values.minPurchaseQuantity ?? 1, + purchasePitch: undefined, + }), + }; - createProduct(productData, { - onSuccess: () => { - toast.success('کالا با موفقیت ایجاد شد') - window.location.href = Pages.products.list - }, - onError: (error: ErrorType) => { - toast.error(error?.response?.data?.error?.message[0] || 'خطا در ایجاد کالا') - } - }) - } + createProduct(productData, { + onSuccess: () => { + toast.success("کالا با موفقیت ایجاد شد"); + navigate(-1); + }, + onError: (error: ErrorType) => { + toast.error(error?.response?.data?.error?.message[0] || "خطا در ایجاد کالا"); + }, + }); + }; - if (imageFiles.length > 0) { - multipleUpload(imageFiles, { - onSuccess: (response) => { - const imageUrls = response?.data?.map(item => item.url) || [] - submitProduct(imageUrls) - }, - onError: (error: ErrorType) => { - toast.error(error?.response?.data?.error?.message[0] || 'خطا در آپلود تصاویر') - } - }) - } else { - submitProduct() - } - } - }) + if (imageFiles.length > 0) { + multipleUpload(imageFiles, { + onSuccess: (response) => { + const imageUrls = response?.data?.map((item) => item.url) || []; + submitProduct(imageUrls); + }, + onError: (error: ErrorType) => { + toast.error(error?.response?.data?.error?.message[0] || "خطا در آپلود تصاویر"); + }, + }); + } else { + submitProduct(); + } + }, + }); - return ( -
-
-
- کالای جدید -
-
- -
-
- -
-
-
- -
-
- -
- -
+ return ( +
+
+
کالای جدید
+
+
- ) -} +
-export default CreateProduct +
+
+
+ +
+
+ +
+ +
+
+
+ ); +}; + +export default CreateProduct; diff --git a/src/pages/product/Update.tsx b/src/pages/product/Update.tsx index d5cd9ec..9f120a3 100644 --- a/src/pages/product/Update.tsx +++ b/src/pages/product/Update.tsx @@ -1,44 +1,43 @@ -import { type FC, useState, useEffect } from 'react' -import { useFormik } from 'formik' -import * as Yup from 'yup' -import { TickCircle } from 'iconsax-react' -import { toast } from 'react-toastify' -import { useNavigate, useParams } from 'react-router-dom' -import Button from '@/components/Button' -import CreateProductSidebar from './components/CreateProductSidebar' -import BasicInfoSection from './components/BasicInfoSection' -import type { CreateProductType } from './types/Types' -import { useQueryClient } from '@tanstack/react-query' -import { useUpdateProduct, useGetProductDetails, useGetCategories } from './hooks/useProductData' -import { useMultipleUpload } from '../uploader/hooks/useUploaderData' -import { Pages } from '@/config/Pages' -import type { ErrorType } from '@/helpers/types' -import { extractErrorMessage } from '@/config/func' -import { PaymentType, PricingType, PurchaseUnit } from './enum/Enum' +import Button from "@/components/Button"; +import { extractErrorMessage } from "@/config/func"; +import type { ErrorType } from "@/helpers/types"; +import { useQueryClient } from "@tanstack/react-query"; +import { useFormik } from "formik"; +import { TickCircle } from "iconsax-react"; +import { type FC, useEffect, useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { toast } from "react-toastify"; +import * as Yup from "yup"; +import { useMultipleUpload } from "../uploader/hooks/useUploaderData"; +import BasicInfoSection from "./components/BasicInfoSection"; +import CreateProductSidebar from "./components/CreateProductSidebar"; +import { PaymentType, PricingType, PurchaseUnit } from "./enum/Enum"; +import { useGetCategories, useGetProductDetails, useUpdateProduct } from "./hooks/useProductData"; +import type { CreateProductType } from "./types/Types"; const UpdateProduct: FC = () => { - const { id } = useParams<{ id: string }>() - const queryClient = useQueryClient() - const { data: productData, isLoading } = useGetProductDetails(id || '') - const { data: categoriesData } = useGetCategories() - const [isActive, setIsActive] = useState(true) - const [isSpecial, setIsSpecial] = useState(false) - const [imageFiles, setImageFiles] = useState([]) - const [existingImages, setExistingImages] = useState([]) - const { mutate: updateProduct, isPending } = useUpdateProduct() - const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload() - const navigate = useNavigate() - const categories = categoriesData?.data ?? [] + const { id } = useParams<{ id: string }>(); + const queryClient = useQueryClient(); + const { data: productData, isLoading } = useGetProductDetails(id || ""); + const { data: categoriesData } = useGetCategories(); + const [isActive, setIsActive] = useState(true); + const [isSpecial, setIsSpecial] = useState(false); + const [imageFiles, setImageFiles] = useState([]); + const [existingImages, setExistingImages] = useState([]); + const { mutate: updateProduct, isPending } = useUpdateProduct(); + const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload(); + const navigate = useNavigate(); + const categories = categoriesData?.data ?? []; - const product = productData?.data + const product = productData?.data; const formik = useFormik({ initialValues: { - categoryId: '', - title: '', - attribute: '', + categoryId: "", + title: "", + attribute: "", variants: [], - desc: '', + desc: "", price: 0, isActive: true, images: [], @@ -55,47 +54,47 @@ const UpdateProduct: FC = () => { needAdminAcceptance: false, }, validationSchema: Yup.object().shape({ - title: Yup.string().required('نام کالا الزامی است'), - categoryId: Yup.string().required('دسته‌بندی الزامی است'), - price: Yup.number().required('قیمت الزامی است').min(0, 'قیمت باید مثبت باشد'), - discount: Yup.number().min(0, 'تخفیف باید مثبت باشد'), + title: Yup.string().required("نام کالا الزامی است"), + categoryId: Yup.string().required("دسته‌بندی الزامی است"), + price: Yup.number().required("قیمت الزامی است").min(0, "قیمت باید مثبت باشد"), + discount: Yup.number().min(0, "تخفیف باید مثبت باشد"), attribute: Yup.string(), pricingType: Yup.string().required(), paymentType: Yup.string().required(), - minPurchaseQuantity: Yup.number().when('pricingType', { + minPurchaseQuantity: Yup.number().when("pricingType", { is: PricingType.VARIABLE, - then: (schema) => schema.min(0, 'حداقل مقدار باید مثبت باشد'), + then: (schema) => schema.min(0, "حداقل مقدار باید مثبت باشد"), }), - maxPurchaseQuantity: Yup.number().min(0, 'حداکثر مقدار باید مثبت باشد'), - purchasePitch: Yup.number().when('pricingType', { + maxPurchaseQuantity: Yup.number().min(0, "حداکثر مقدار باید مثبت باشد"), + purchasePitch: Yup.number().when("pricingType", { is: PricingType.VARIABLE, - then: (schema) => schema.min(0, 'گام تغییر باید مثبت باشد'), + then: (schema) => schema.min(0, "گام تغییر باید مثبت باشد"), }), variants: Yup.array().of( Yup.object().shape({ id: Yup.string(), value: Yup.string(), - price: Yup.number().min(0) - }) - ) + price: Yup.number().min(0), + }), + ), }), onSubmit: (values) => { if (!id) { - toast.error('شناسه کالا یافت نشد') - return + toast.error("شناسه کالا یافت نشد"); + return; } const submitProduct = (newImageUrls: string[] = []) => { - const allImages = [...values.images, ...newImageUrls] + const allImages = [...values.images, ...newImageUrls]; const variants = values.variants.length > 0 ? values.variants.map((v) => ({ - ...(v.id && { id: v.id }), - value: v.value ?? '', - price: v.price ?? 0, - ...(v.stock !== undefined && v.stock !== null && { stock: v.stock }) - })) - : [{ value: '', price: values.price, ...(values.stock !== undefined && values.stock !== null && { stock: values.stock }) }] + ...(v.id && { id: v.id }), + value: v.value ?? "", + price: v.price ?? 0, + ...(v.stock !== undefined && v.stock !== null && { stock: v.stock }), + })) + : [{ value: "", price: values.price, ...(values.stock !== undefined && values.stock !== null && { stock: values.stock }) }]; const productData: CreateProductType = { ...values, variants, @@ -108,47 +107,50 @@ const UpdateProduct: FC = () => { minPurchaseQuantity: values.minPurchaseQuantity ?? 1, purchasePitch: undefined, }), - } + }; - updateProduct({ id, params: productData }, { - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['product', id] }) - queryClient.invalidateQueries({ queryKey: ['products'] }) - toast.success('کالا با موفقیت به‌روزرسانی شد') - navigate(Pages.products.list) + updateProduct( + { id, params: productData }, + { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["product", id] }); + queryClient.invalidateQueries({ queryKey: ["products"] }); + toast.success("کالا با موفقیت به‌روزرسانی شد"); + navigate(-1); + }, + onError: (error: ErrorType) => { + toast.error(extractErrorMessage(error)); + }, }, - onError: (error: ErrorType) => { - toast.error(extractErrorMessage(error)) - } - }) - } + ); + }; if (imageFiles.length > 0) { multipleUpload(imageFiles, { onSuccess: (response) => { - const imageUrls = response?.data?.map(item => item.url) || [] - submitProduct(imageUrls) + const imageUrls = response?.data?.map((item) => item.url) || []; + submitProduct(imageUrls); }, onError: (error: ErrorType) => { - toast.error(extractErrorMessage(error, 'خطا در آپلود تصاویر')) - } - }) + toast.error(extractErrorMessage(error, "خطا در آپلود تصاویر")); + }, + }); } else { - submitProduct() + submitProduct(); } - } - }) + }, + }); useEffect(() => { if (product) { - const variants = product.variants || [] - const priceFromVariant = variants.length > 0 ? variants[0].price : product.price + const variants = product.variants || []; + const priceFromVariant = variants.length > 0 ? variants[0].price : product.price; formik.setValues({ - categoryId: product.category?.id || '', - title: product.title || '', - attribute: product.attribute || '', + categoryId: product.category?.id || "", + title: product.title || "", + attribute: product.attribute || "", variants, - desc: product.desc || '', + desc: product.desc || "", price: priceFromVariant ?? product.price ?? 0, isActive: product.isActive || false, images: product.images || [], @@ -163,70 +165,61 @@ const UpdateProduct: FC = () => { paymentType: product.paymentType ?? (product.needAdminAcceptance ? PaymentType.AFTER_PREPARATION : PaymentType.INSTANT), purchaseUnit: product.purchaseUnit ?? PurchaseUnit.NUMBER, needAdminAcceptance: product.needAdminAcceptance ?? false, - }) - setIsActive(product.isActive || false) - setIsSpecial(product.isSpecialOffer || false) - setExistingImages(product.images || []) + }); + setIsActive(product.isActive || false); + setIsSpecial(product.isSpecialOffer || false); + setExistingImages(product.images || []); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [product]) + }, [product]); useEffect(() => { - formik.setFieldValue('images', existingImages) + formik.setFieldValue("images", existingImages); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [existingImages]) + }, [existingImages]); if (isLoading) { return ( -
+
در حال بارگذاری...
- ) + ); } if (!product) { return ( -
+
کالا یافت نشد
- ) + ); } return ( -
-
-
- ویرایش کالا -
+
+
+
ویرایش کالا
-
-
-
-
+
+
+
1 || - ((product?.variants?.length === 1) && (product.variants[0].value ?? '') !== '') - } + initialHasVariants={(product?.variants?.length ?? 0) > 1 || (product?.variants?.length === 1 && (product.variants[0].value ?? "") !== "")} />
-
+
{
- ) -} + ); +}; -export default UpdateProduct +export default UpdateProduct;