Files
danak-admin/src/pages/taskmanager/components/task-detail/TaskDetailModal.tsx
T
hamid zarghami a335f2b66c
deploy to danak / build_and_deploy (push) Has been cancelled
fix save desciption
2026-07-18 12:07:29 +03:30

228 lines
7.1 KiB
TypeScript

import { type FC, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "react-toastify";
import DefaulModal from "../../../../components/DefaulModal";
import PageLoading from "../../../../components/PageLoading";
import { ErrorType } from "../../../../helpers/types";
import { useChangeTaskPhase, useDeleteTask, useGetTaskDetail, useUpdateTask } from "../../task/hooks/useTaskData";
import { useGetTaskTimes } from "../../task/hooks/useTaskTimeData";
import type { TaskDetailType } from "../../task/types/TaskTypes";
import type { Column, Task } from "../../types";
import AttachmentList from "./attachment/List";
import CheckLists from "./checklist/List";
import TimeList from "./time/List";
import TaskDetailCoverColorBar from "./cover/TaskDetailCoverColorBar";
import TaskDetailDescription from "./TaskDetailDescription";
import TaskDetailHeader from "./TaskDetailHeader";
import TaskDetailToolbar from "./TaskDetailToolbar";
import type { TaskDetailTab } from "./types";
type Props = {
open: boolean;
task: Task | null;
columns: Column[];
projectId: string;
onClose: () => void;
onTaskPhaseChanged?: (taskId: string, taskPhaseId: string) => void;
};
const TaskDetailModal: FC<Props> = ({ open, task, columns, projectId, onClose, onTaskPhaseChanged }) => {
const { t } = useTranslation("global");
const [activeTab, setActiveTab] = useState<TaskDetailTab | null>(null);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [color, setColor] = useState<string | null>(null);
const initialTitleRef = useRef("");
const initialDescriptionRef = useRef("");
const getTaskDetail = useGetTaskDetail(task?.id ?? "", open && !!task?.id);
const getTaskTimes = useGetTaskTimes(task?.id ?? "", open && !!task?.id);
const updateTask = useUpdateTask();
const changeTaskPhase = useChangeTaskPhase();
const deleteTask = useDeleteTask();
const rawTaskDetail = getTaskDetail.data?.data;
const taskDetail: TaskDetailType | undefined = rawTaskDetail
? {
...rawTaskDetail,
id: rawTaskDetail.id ?? task?.id ?? "",
labels: rawTaskDetail.labels ?? rawTaskDetail.remarks,
}
: undefined;
useEffect(() => {
if (!open) {
setActiveTab(null);
setTitle("");
setDescription("");
setColor(null);
initialTitleRef.current = "";
initialDescriptionRef.current = "";
}
}, [open]);
useEffect(() => {
if (!rawTaskDetail) return;
setTitle(rawTaskDetail.title);
setDescription(rawTaskDetail.description ?? "");
setColor(rawTaskDetail.color || null);
initialTitleRef.current = rawTaskDetail.title;
initialDescriptionRef.current = rawTaskDetail.description ?? "";
}, [rawTaskDetail]);
if (!task) return null;
const selectedPhaseId = task.columnId || taskDetail?.taskPhaseId || "";
const handleTabChange = (tab: TaskDetailTab) => {
setActiveTab((prev) => (prev === tab ? null : tab));
};
const handleDelete = () => {
if (!task.id) return;
deleteTask.mutate(
{ id: task.id, projectId: taskDetail?.projectId ?? "" },
{
onSuccess: () => {
toast.success(t("taskmanager.task_deleted"));
onClose();
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error.message[0]);
},
},
);
};
const saveField = (field: "title" | "description", value: string) => {
const initialValue = field === "title" ? initialTitleRef.current : initialDescriptionRef.current;
if (value === initialValue) return;
updateTask.mutate(
{ id: task.id, params: { [field]: value } },
{
onSuccess: () => {
if (field === "title") {
initialTitleRef.current = value;
} else {
initialDescriptionRef.current = value;
}
toast.success(t("success"));
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error.message[0]);
if (field === "title") {
setTitle(initialTitleRef.current);
} else {
setDescription(initialDescriptionRef.current);
}
},
},
);
};
const handleColorChange = (newColor: string | null) => {
const previousColor = color;
setColor(newColor);
updateTask.mutate(
{ id: task.id, params: { color: newColor ?? "" } },
{
onSuccess: () => {
toast.success(t("success"));
},
onError: (error: ErrorType) => {
setColor(previousColor);
toast.error(error.response?.data?.error.message[0]);
},
},
);
};
const handlePhaseChange = (taskPhaseId: string) => {
if (taskPhaseId === selectedPhaseId) return;
changeTaskPhase.mutate(
{ id: task.id, params: { taskPhaseId }, projectId: taskDetail?.projectId ?? projectId },
{
onSuccess: () => {
onTaskPhaseChanged?.(task.id, taskPhaseId);
toast.success(t("success"));
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error.message[0]);
},
},
);
};
return (
<DefaulModal open={open} close={onClose} width={680}>
{getTaskDetail.isPending ? (
<PageLoading />
) : (
<>
<TaskDetailHeader
columns={columns}
selectedPhaseId={selectedPhaseId}
onPhaseChange={handlePhaseChange}
isChangingPhase={changeTaskPhase.isPending}
taskColor={color}
onColorSelect={(newColor) => handleColorChange(newColor)}
isUpdatingColor={updateTask.isPending}
onClose={onClose}
onDelete={handleDelete}
isDeleting={deleteTask.isPending}
/>
{color ? (
<TaskDetailCoverColorBar
color={color}
onRemove={() => handleColorChange(null)}
isRemoving={updateTask.isPending}
/>
) : null}
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
onBlur={() => saveField("title", title)}
className="mt-6 w-full text-sm font-bold bg-transparent outline-none"
/>
<div className="mt-4">
<TaskDetailToolbar
activeTab={activeTab}
onTabChange={handleTabChange}
taskDetail={taskDetail}
taskId={task.id}
/>
</div>
<TaskDetailDescription
value={description}
onChange={setDescription}
onSave={() => saveField("description", description)}
isSaving={updateTask.isPending}
/>
<AttachmentList attachments={taskDetail?.attachments} />
<CheckLists
taskId={task.id}
checkListItems={taskDetail?.checkListItems}
checkListItemCount={taskDetail?.checkListItemCount}
completedCheckListItemCount={taskDetail?.completedCheckListItemCount}
/>
<TimeList times={getTaskTimes.data?.data} />
</>
)}
</DefaulModal>
);
};
export default TaskDetailModal;