From 1618b5fb5749c6f008634b38240e472e19f95817 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sat, 11 Jul 2026 11:01:01 +0330 Subject: [PATCH] update project --- src/config/Pages.ts | 1 + src/pages/taskmanager/project/Update.tsx | 242 ++++++++++++++++++ .../project/components/ProjectCard.tsx | 6 +- .../project/hooks/useProjectData.ts | 30 ++- .../project/service/ProjectService.ts | 16 +- .../taskmanager/project/types/ProjectTypes.ts | 13 + src/router/Main.tsx | 5 + 7 files changed, 306 insertions(+), 7 deletions(-) create mode 100644 src/pages/taskmanager/project/Update.tsx diff --git a/src/config/Pages.ts b/src/config/Pages.ts index b8473de..8cf7feb 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -173,6 +173,7 @@ export const Pages = { createWorkspace: "/workspace/create", updateWorkspace: "/workspace/update/", createProject: "/project/create", + updateProject: "/project/update/", projectList: "/project/list/", }, }; diff --git a/src/pages/taskmanager/project/Update.tsx b/src/pages/taskmanager/project/Update.tsx new file mode 100644 index 0000000..dd3df31 --- /dev/null +++ b/src/pages/taskmanager/project/Update.tsx @@ -0,0 +1,242 @@ +import { FC, useEffect, useState } from "react"; +import { TickCircle } from "iconsax-react"; +import { useFormik } from "formik"; +import { useTranslation } from "react-i18next"; +import { useNavigate, useParams } from "react-router-dom"; +import { toast } from "react-toastify"; +import * as Yup from "yup"; +import moment from "moment-jalaali"; +import Button from "../../../components/Button"; +import DatePickerComponent from "../../../components/DatePicker"; +import Input from "../../../components/Input"; +import PageLoading from "../../../components/PageLoading"; +import Textarea from "../../../components/Textarea"; +import { Pages } from "../../../config/Pages"; +import { ErrorType } from "../../../helpers/types"; +import { useSingleUpload } from "../../service/hooks/useServiceData"; +import CreateProjectSidebar, { + BACKGROUND_PRESETS, + SelectedUser, +} from "./components/CreateProjectSidebar"; +import { useGetProjectDetail, useUpdateProject } from "./hooks/useProjectData"; +import { ProjectDetailType, UpdateProjectType } from "./types/ProjectTypes"; + +const UpdateProject: FC = () => { + const { t } = useTranslation("global"); + const { id = "" } = useParams(); + const navigate = useNavigate(); + const getProjectDetail = useGetProjectDetail(id); + const updateProject = useUpdateProject(); + const singleUpload = useSingleUpload(); + + const [isActive, setIsActive] = useState(true); + const [selectedColor, setSelectedColor] = useState("#A8E6CF"); + const [selectedUsers, setSelectedUsers] = useState([]); + const [selectedBackground, setSelectedBackground] = useState(BACKGROUND_PRESETS[0]); + const [backgroundImage, setBackgroundImage] = useState(null); + const [workspaceId, setWorkspaceId] = useState(""); + const [existingBackgroundPicture, setExistingBackgroundPicture] = useState(""); + const [startDateDefault, setStartDateDefault] = useState(""); + + const project: ProjectDetailType | undefined = + getProjectDetail.data?.data?.project ?? getProjectDetail.data?.data; + + const handleUpdateProject = (values: UpdateProjectType) => { + if (!id) return; + + updateProject.mutate( + { id, params: values }, + { + onSuccess: () => { + toast.success(t("success")); + navigate(`${Pages.taskmanager.projectList}${values.workspaceId}`); + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error.message[0]); + }, + }, + ); + }; + + const formik = useFormik< + Pick + >({ + initialValues: { + name: "", + ownerName: "", + description: "", + startDate: "", + }, + validationSchema: Yup.object({ + name: Yup.string().required(t("errors.required")), + ownerName: Yup.string().required(t("errors.required")), + description: Yup.string(), + startDate: Yup.string().required(t("errors.required")), + }), + onSubmit: async (values) => { + if (!workspaceId) { + toast.error(t("taskmanager.select_workspace")); + return; + } + + let backgroundPicture = existingBackgroundPicture; + + if (backgroundImage) { + const formData = new FormData(); + formData.append("file", backgroundImage); + + try { + const uploadResult = await singleUpload.mutateAsync(formData); + backgroundPicture = uploadResult?.data?.url ?? ""; + } catch (error) { + const uploadError = error as ErrorType; + toast.error(uploadError.response?.data?.error.message[0]); + return; + } + } + + handleUpdateProject({ + ...values, + startDate: moment(values.startDate, "jYYYY/jMM/jDD").format("YYYY-MM-DD"), + backgroundPicture, + backgroundColor: selectedColor, + isActive, + workspaceId, + userIds: selectedUsers.map((user) => user.id), + }); + }, + }); + + useEffect(() => { + if (!project) return; + + formik.setValues({ + name: project.name, + ownerName: project.ownerName, + description: project.description ?? "", + startDate: project.startDate + ? moment(project.startDate, "YYYY-MM-DD").format("jYYYY/jMM/jDD") + : "", + }); + setIsActive(project.isActive); + setSelectedColor(project.backgroundColor ?? "#A8E6CF"); + setWorkspaceId(project.workspaceId); + setExistingBackgroundPicture(project.backgroundPicture ?? ""); + setStartDateDefault( + project.startDate + ? moment(project.startDate, "YYYY-MM-DD").format("jYYYY/jMM/jDD") + : "", + ); + + if (project.users?.length) { + setSelectedUsers( + project.users.map((user) => ({ + id: user.id, + name: `${user.firstName} ${user.lastName}`, + })), + ); + } else if (project.userIds?.length) { + setSelectedUsers( + project.userIds.map((userId) => ({ + id: userId, + name: userId, + })), + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [project]); + + if (getProjectDetail.isPending) { + return ; + } + + return ( +
+
+
{t("edit")}
+
+ +
+
+ +
+
+
+
{t("taskmanager.project_info")}
+ +
+ +
+ +
+ formik.setFieldValue("startDate", value)} + error_text={ + formik.touched.startDate && formik.errors.startDate + ? formik.errors.startDate + : "" + } + /> + +
+ +
+