diff --git a/src/langs/fa.json b/src/langs/fa.json index 3beec05..0bd6589 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -1024,6 +1024,7 @@ "new_label": "برچسب جدید", "label_title": "عنوان", "choose_custom_color": "انتخاب رنگ دلخواه", + "select_cover_color": "انتخاب رنگ کاور", "members": "اعضا", "copy_from": "کپی از", "none": "هیچ کدام", diff --git a/src/pages/taskmanager/components/Task.tsx b/src/pages/taskmanager/components/Task.tsx index d4dc60e..69f060c 100644 --- a/src/pages/taskmanager/components/Task.tsx +++ b/src/pages/taskmanager/components/Task.tsx @@ -36,7 +36,7 @@ const Task: FC = ({ task, isDragging, onDragStart, onDragEnd, onClick }) isDragging ? "opacity-40" : "" }`} > -
+ {task.color ?
: null}
{task.tag}
diff --git a/src/pages/taskmanager/components/task-detail/TaskDetailHeader.tsx b/src/pages/taskmanager/components/task-detail/TaskDetailHeader.tsx index 174bc38..ff7ac3b 100644 --- a/src/pages/taskmanager/components/task-detail/TaskDetailHeader.tsx +++ b/src/pages/taskmanager/components/task-detail/TaskDetailHeader.tsx @@ -1,15 +1,19 @@ import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; -import { ArrowDown2, CloseCircle } from "iconsax-react"; +import { ArrowDown2, CloseCircle, Paintbucket } from "iconsax-react"; import { type FC } from "react"; import TrashWithConfrim from "../../../../components/TrashWithConfrim"; import { clx } from "../../../../helpers/utils"; import type { Column } from "../../types"; +import TaskDetailCoverColorPopover from "./cover/TaskDetailCoverColorPopover"; type Props = { columns: Column[]; selectedPhaseId: string; onPhaseChange: (phaseId: string) => void; isChangingPhase?: boolean; + taskColor: string | null; + onColorSelect: (color: string) => void; + isUpdatingColor?: boolean; onClose: () => void; onDelete?: () => void; isDeleting?: boolean; @@ -20,6 +24,9 @@ const TaskDetailHeader: FC = ({ selectedPhaseId, onPhaseChange, isChangingPhase, + taskColor, + onColorSelect, + isUpdatingColor, onClose, onDelete, isDeleting, @@ -65,6 +72,33 @@ const TaskDetailHeader: FC = ({
+ + {({ close }) => ( + <> + + + + + + { + onColorSelect(color); + close(); + }} + /> + + + )} + + {onDelete ? (
diff --git a/src/pages/taskmanager/components/task-detail/TaskDetailModal.tsx b/src/pages/taskmanager/components/task-detail/TaskDetailModal.tsx index 7fb4328..45ac920 100644 --- a/src/pages/taskmanager/components/task-detail/TaskDetailModal.tsx +++ b/src/pages/taskmanager/components/task-detail/TaskDetailModal.tsx @@ -9,6 +9,7 @@ import type { TaskDetailType } from "../../task/types/TaskTypes"; import type { Column, Task } from "../../types"; import AttachmentList from "./attachment/List"; import CheckLists from "./checklist/List"; +import TaskDetailCoverColorBar from "./cover/TaskDetailCoverColorBar"; import TaskDetailDescription from "./TaskDetailDescription"; import TaskDetailHeader from "./TaskDetailHeader"; import TaskDetailToolbar from "./TaskDetailToolbar"; @@ -28,6 +29,7 @@ const TaskDetailModal: FC = ({ open, task, columns, projectId, onClose, o const [activeTab, setActiveTab] = useState(null); const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); + const [color, setColor] = useState(null); const initialTitleRef = useRef(""); const initialDescriptionRef = useRef(""); @@ -50,6 +52,7 @@ const TaskDetailModal: FC = ({ open, task, columns, projectId, onClose, o setActiveTab(null); setTitle(""); setDescription(""); + setColor(null); initialTitleRef.current = ""; initialDescriptionRef.current = ""; } @@ -60,6 +63,7 @@ const TaskDetailModal: FC = ({ open, task, columns, projectId, onClose, o setTitle(taskDetail.title); setDescription(taskDetail.description ?? ""); + setColor(taskDetail.color || null); initialTitleRef.current = taskDetail.title; initialDescriptionRef.current = taskDetail.description ?? ""; }, [taskDetail]); @@ -116,6 +120,24 @@ const TaskDetailModal: FC = ({ open, task, columns, projectId, onClose, o ); }; + 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; @@ -144,11 +166,22 @@ const TaskDetailModal: FC = ({ open, task, columns, projectId, onClose, o selectedPhaseId={selectedPhaseId} onPhaseChange={handlePhaseChange} isChangingPhase={changeTaskPhase.isPending} + taskColor={color} + onColorSelect={(newColor) => handleColorChange(newColor)} + isUpdatingColor={updateTask.isPending} onClose={onClose} onDelete={handleDelete} isDeleting={deleteTask.isPending} /> + {color ? ( + handleColorChange(null)} + isRemoving={updateTask.isPending} + /> + ) : null} + setTitle(e.target.value)} diff --git a/src/pages/taskmanager/components/task-detail/cover/TaskDetailCoverColorBar.tsx b/src/pages/taskmanager/components/task-detail/cover/TaskDetailCoverColorBar.tsx new file mode 100644 index 0000000..5206383 --- /dev/null +++ b/src/pages/taskmanager/components/task-detail/cover/TaskDetailCoverColorBar.tsx @@ -0,0 +1,27 @@ +import { Trash } from "iconsax-react"; +import { type FC } from "react"; + +type Props = { + color: string; + onRemove: () => void; + isRemoving?: boolean; +}; + +const TaskDetailCoverColorBar: FC = ({ color, onRemove, isRemoving }) => { + return ( +
+
+ +
+ ); +}; + +export default TaskDetailCoverColorBar; diff --git a/src/pages/taskmanager/components/task-detail/cover/TaskDetailCoverColorPopover.tsx b/src/pages/taskmanager/components/task-detail/cover/TaskDetailCoverColorPopover.tsx new file mode 100644 index 0000000..386eab3 --- /dev/null +++ b/src/pages/taskmanager/components/task-detail/cover/TaskDetailCoverColorPopover.tsx @@ -0,0 +1,37 @@ +import { type FC } from "react"; +import { useTranslation } from "react-i18next"; +import { clx } from "../../../../../helpers/utils"; +import { TASK_COVER_COLORS } from "./constants"; + +type Props = { + selectedColor: string | null; + onSelect: (color: string) => void; +}; + +const TaskDetailCoverColorPopover: FC = ({ selectedColor, onSelect }) => { + const { t } = useTranslation("global"); + + return ( +
+
{t("taskmanager.task_detail.select_cover_color")}
+
+ {TASK_COVER_COLORS.map((color) => ( +
+
+ ); +}; + +export default TaskDetailCoverColorPopover; diff --git a/src/pages/taskmanager/components/task-detail/cover/constants.ts b/src/pages/taskmanager/components/task-detail/cover/constants.ts new file mode 100644 index 0000000..53c3d49 --- /dev/null +++ b/src/pages/taskmanager/components/task-detail/cover/constants.ts @@ -0,0 +1,10 @@ +export const TASK_COVER_COLORS = [ + "#FF6B9D", + "#FF9F43", + "#6EE7B7", + "#60A5FA", + "#A78BFA", + "#EF4444", + "#D1D5DB", + "#FDE047", +] as const; diff --git a/src/pages/taskmanager/task/types/TaskTypes.ts b/src/pages/taskmanager/task/types/TaskTypes.ts index 486942b..20ea861 100644 --- a/src/pages/taskmanager/task/types/TaskTypes.ts +++ b/src/pages/taskmanager/task/types/TaskTypes.ts @@ -14,6 +14,7 @@ export type TaskItemType = { order: number; tag?: string; dateRange?: string; + color?: string | null; attachments?: number; checklistDone?: number; checklistTotal?: number; @@ -114,7 +115,7 @@ export type ChangeTaskPhaseType = { export type UpdateTaskType = Partial< Pick > & { - color?: string; + color?: string | null; userIds?: string[]; }; diff --git a/src/pages/taskmanager/task/utils/mapTaskItemToTask.ts b/src/pages/taskmanager/task/utils/mapTaskItemToTask.ts index c492813..2bc043f 100644 --- a/src/pages/taskmanager/task/utils/mapTaskItemToTask.ts +++ b/src/pages/taskmanager/task/utils/mapTaskItemToTask.ts @@ -8,6 +8,7 @@ export const mapTaskItemToTask = (task: TaskItemType, taskPhaseId: string, fallb title: task.title, tag: task.tag ?? "", dateRange: task.dateRange ?? "", + color: task.color || null, attachments: task.attachments ?? 0, checklistDone: task.checklistDone ?? 0, checklistTotal: task.checklistTotal ?? 0, diff --git a/src/pages/taskmanager/types.ts b/src/pages/taskmanager/types.ts index c528d95..50955cc 100644 --- a/src/pages/taskmanager/types.ts +++ b/src/pages/taskmanager/types.ts @@ -12,6 +12,7 @@ export type Task = { title: string; tag: string; dateRange: string; + color?: string | null; attachments: number; checklistDone: number; checklistTotal: number;