Files
dkala-admin/src/pages/product/Update.tsx
T
morteza 3658e6ffc0
deploy to danak / build_and_deploy (push) Has been cancelled
update price bug fixed
2026-07-09 15:45:03 +03:30

225 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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";
import { buildProductPayload } from "./utils/productPayload";
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<boolean>(true);
const [isSpecial, setIsSpecial] = useState<boolean>(false);
const [imageFiles, setImageFiles] = useState<File[]>([]);
const [existingImages, setExistingImages] = useState<string[]>([]);
const { mutate: updateProduct, isPending } = useUpdateProduct();
const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload();
const navigate = useNavigate();
const categories = categoriesData?.data ?? [];
const product = productData?.data;
const isSubmitting = isPending || isUploading;
const formik = useFormik<CreateProductType>({
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.required("حداقل مقدار الزامی است").positive("حداقل مقدار باید مثبت باشد"),
}),
maxPurchaseQuantity: Yup.number().required("حداکثر مقدار الزامی است").positive("حداکثر مقدار باید مثبت باشد"),
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) => {
if (!id) {
toast.error("شناسه کالا یافت نشد");
return;
}
const submitProduct = (newImageUrls: string[] = []) => {
const allImages = [...values.images, ...newImageUrls];
const productData = buildProductPayload({
values,
isActive,
isSpecialOffer: isSpecial,
images: allImages,
});
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));
},
},
);
};
if (imageFiles.length > 0) {
multipleUpload(imageFiles, {
onSuccess: (response) => {
const imageUrls = response?.data?.map((item) => item.url) || [];
submitProduct(imageUrls);
},
onError: (error: ErrorType) => {
toast.error(extractErrorMessage(error, "خطا در آپلود تصاویر"));
},
});
} else {
submitProduct();
}
},
});
useEffect(() => {
if (product) {
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 || "",
variants,
desc: product.desc || "",
price: priceFromVariant ?? product.price ?? 0,
isActive: product.isActive || false,
images: product.images || [],
discount: product.discount || 0,
isSpecialOffer: product.isSpecialOffer || false,
order: product.order ?? 0,
stock: product.variants?.[0]?.stock,
maxPurchaseQuantity: product.maxPurchaseQuantity,
minPurchaseQuantity: product.minPurchaseQuantity,
purchasePitch: product.purchasePitch,
pricingType: product.pricingType ?? PricingType.FIXED,
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 || []);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [product]);
useEffect(() => {
formik.setFieldValue("images", existingImages);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [existingImages]);
if (isLoading) {
return (
<div className="w-full mt-4 flex justify-center items-center">
<div>در حال بارگذاری...</div>
</div>
);
}
if (!product) {
return (
<div className="w-full mt-4 flex justify-center items-center">
<div>کالا یافت نشد</div>
</div>
);
}
return (
<div className="w-full mt-4">
<div className="flex flex-col sm:flex-row w-full justify-between items-start sm:items-center gap-4">
<div className="text-lg font-light">ویرایش کالا</div>
<div>
<Button className="px-5 w-full sm:w-auto" onClick={() => formik.handleSubmit()} isloading={isSubmitting}>
<div className="flex gap-2 items-center">
<TickCircle className="size-5" color="white" />
<div>ذخیره تغییرات</div>
</div>
</Button>
</div>
</div>
<div className="flex flex-col lg:flex-row gap-6 mt-6">
<div className="flex-1">
<div className="bg-white py-6 sm:py-8 xl:px-10 px-4 rounded-3xl">
<BasicInfoSection
formik={formik}
categories={categories}
initialHasVariants={(product?.variants?.length ?? 0) > 1 || (product?.variants?.length === 1 && (product.variants[0].value ?? "") !== "")}
/>
</div>
</div>
<div className="w-full lg:w-auto">
<CreateProductSidebar
isActive={isActive}
isSpecial={isSpecial}
onChangeActive={setIsActive}
onChangeSpecial={setIsSpecial}
onChangeImages={setImageFiles}
existingImages={existingImages}
onChangeExistingImages={setExistingImages}
/>
</div>
</div>
</div>
);
};
export default UpdateProduct;