208 lines
7.4 KiB
TypeScript
208 lines
7.4 KiB
TypeScript
import Button from "@/components/Button";
|
||
import { extractErrorMessage } from "@/config/func";
|
||
import type { ErrorType } from "@/helpers/types";
|
||
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 CreateFoodSidebar from "./components/CreateFoodSidebar";
|
||
import FoodContentSection from "./components/FoodContentSection";
|
||
import MealTimesSection from "./components/MealTimesSection";
|
||
import ServiceOptionsSection from "./components/ServiceOptionsSection";
|
||
import WeekDaysSection from "./components/WeekDaysSection";
|
||
import { useGetCategories, useGetFoodDetails, useUpdateFood } from "./hooks/useFoodData";
|
||
import type { CreateFoodType } from "./types/Types";
|
||
|
||
const UpdateFood: FC = () => {
|
||
const { id } = useParams<{ id: string }>();
|
||
const { data: foodData, isLoading } = useGetFoodDetails(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: updateFood, isPending } = useUpdateFood();
|
||
const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload();
|
||
const navigate = useNavigate();
|
||
const categories =
|
||
categoriesData?.data?.map((category) => ({
|
||
label: category.title,
|
||
value: category.id,
|
||
})) || [];
|
||
|
||
const food = foodData?.data;
|
||
|
||
const formik = useFormik<CreateFoodType>({
|
||
initialValues: {
|
||
title: "",
|
||
desc: "",
|
||
content: [],
|
||
categoryId: "",
|
||
price: 0,
|
||
discount: 0,
|
||
prepareTime: 0,
|
||
weekDays: [],
|
||
mealTypes: [],
|
||
pickupServe: false,
|
||
inPlaceServe: false,
|
||
isActive: true,
|
||
images: [],
|
||
isSpecialOffer: false,
|
||
dailyStock: 0,
|
||
availableStock: 0,
|
||
order: 0,
|
||
},
|
||
validationSchema: Yup.object().shape({
|
||
title: Yup.string().required("نام غذا الزامی است"),
|
||
categoryId: Yup.string().required("دستهبندی الزامی است"),
|
||
price: Yup.number().required("قیمت الزامی است").min(0, "قیمت باید مثبت باشد"),
|
||
prepareTime: Yup.number().required("زمان آمادهسازی الزامی است").min(0, "زمان آمادهسازی باید مثبت باشد"),
|
||
discount: Yup.number().min(0, "تخفیف باید مثبت باشد"),
|
||
dailyStock: Yup.number().required("موجودی روزانه الزامی است").min(0, "موجودی روزانه باید مثبت باشد"),
|
||
availableStock: Yup.number().required("موجودی فعلی الزامی است").min(0, "موجودی فعلی باید مثبت باشد"),
|
||
content: Yup.array().of(Yup.string()),
|
||
weekDays: Yup.array().of(Yup.number()),
|
||
mealTypes: Yup.array().of(Yup.string()),
|
||
}),
|
||
onSubmit: (values) => {
|
||
if (!id) {
|
||
toast.error("شناسه غذا یافت نشد");
|
||
return;
|
||
}
|
||
|
||
const submitFood = (newImageUrls: string[] = []) => {
|
||
const allImages = [...values.images, ...newImageUrls];
|
||
const foodData: CreateFoodType = {
|
||
...values,
|
||
isActive,
|
||
isSpecialOffer: isSpecial,
|
||
images: allImages,
|
||
content: values.content.filter((item) => item && item.trim().length > 0),
|
||
};
|
||
|
||
updateFood(
|
||
{ id, params: foodData },
|
||
{
|
||
onSuccess: () => {
|
||
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) || [];
|
||
submitFood(imageUrls);
|
||
},
|
||
onError: (error: ErrorType) => {
|
||
toast.error(error?.response?.data?.error?.message[0] || "خطا در آپلود تصاویر");
|
||
},
|
||
});
|
||
} else {
|
||
submitFood();
|
||
}
|
||
},
|
||
});
|
||
|
||
useEffect(() => {
|
||
if (food) {
|
||
formik.setValues({
|
||
title: food.title || "",
|
||
desc: food.desc || "",
|
||
content: food.content || [],
|
||
categoryId: food.category?.id || "",
|
||
price: food.price || 0,
|
||
discount: food.discount || 0,
|
||
prepareTime: food.prepareTime || 0,
|
||
weekDays: food.weekDays || [],
|
||
mealTypes: food.mealTypes || [],
|
||
pickupServe: food.pickupServe || false,
|
||
inPlaceServe: food.inPlaceServe || false,
|
||
isActive: food.isActive || false,
|
||
images: food.images || [],
|
||
isSpecialOffer: food.isSpecialOffer || false,
|
||
dailyStock: food.inventory?.totalStock || 0,
|
||
availableStock: food.inventory?.availableStock || 0,
|
||
order: food.order || 0,
|
||
});
|
||
setIsActive(food.isActive || false);
|
||
setIsSpecial(food.isSpecialOffer || false);
|
||
setExistingImages(food.images || []);
|
||
}
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [food]);
|
||
|
||
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 (!food) {
|
||
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={isPending || isUploading}>
|
||
<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} />
|
||
<FoodContentSection formik={formik} />
|
||
<MealTimesSection formik={formik} />
|
||
<WeekDaysSection formik={formik} />
|
||
<ServiceOptionsSection formik={formik} />
|
||
</div>
|
||
</div>
|
||
|
||
<div className="w-full lg:w-auto">
|
||
<CreateFoodSidebar
|
||
isActive={isActive}
|
||
isSpecial={isSpecial}
|
||
onChangeActive={setIsActive}
|
||
onChangeSpecial={setIsSpecial}
|
||
onChangeImages={setImageFiles}
|
||
existingImages={existingImages}
|
||
onChangeExistingImages={setExistingImages}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default UpdateFood;
|