From 2f71b4c96c34b1709f0e60ca3a504a6dcd84146f Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sun, 26 Jul 2026 14:38:46 +0330 Subject: [PATCH] edit check list item --- .../task-detail/checklist/ChecklistItem.tsx | 104 ++++++++++++++++-- .../components/task-detail/checklist/List.tsx | 26 +++-- .../taskmanager/task/hooks/useTaskData.ts | 8 +- src/pages/taskmanager/task/types/TaskTypes.ts | 4 +- 4 files changed, 123 insertions(+), 19 deletions(-) diff --git a/src/pages/taskmanager/components/task-detail/checklist/ChecklistItem.tsx b/src/pages/taskmanager/components/task-detail/checklist/ChecklistItem.tsx index 1a04e45..0ef0bef 100644 --- a/src/pages/taskmanager/components/task-detail/checklist/ChecklistItem.tsx +++ b/src/pages/taskmanager/components/task-detail/checklist/ChecklistItem.tsx @@ -1,17 +1,83 @@ import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; import { More } from "iconsax-react"; -import { type FC } from "react"; +import { type FC, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import TaskDetailLabelCheckbox from "../labels/TaskDetailLabelCheckbox"; type Props = { title: string; checked: boolean; onToggle: () => void; - onEdit: () => void; + onSave: (title: string) => void; onDelete: () => void; + isSaving?: boolean; }; -const ChecklistItem: FC = ({ title, checked, onToggle, onEdit, onDelete }) => { +const ChecklistItem: FC = ({ title, checked, onToggle, onSave, onDelete, isSaving = false }) => { + const { t } = useTranslation("global"); + const [isEditing, setIsEditing] = useState(false); + const [editTitle, setEditTitle] = useState(title); + + useEffect(() => { + if (!isEditing) { + setEditTitle(title); + } + }, [title, isEditing]); + + const handleSave = () => { + const trimmed = editTitle.trim(); + if (!trimmed || trimmed === title) { + setIsEditing(false); + setEditTitle(title); + return; + } + onSave(trimmed); + setIsEditing(false); + }; + + const handleCancel = () => { + setIsEditing(false); + setEditTitle(title); + }; + + if (isEditing) { + return ( +
+ + +
+ setEditTitle(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") handleSave(); + if (e.key === "Escape") handleCancel(); + }} + autoFocus + disabled={isSaving} + className="flex-1 h-[27px] px-2 text-xs bg-white/50 rounded outline-none" + /> + + +
+
+ ); + } + return (
@@ -24,14 +90,32 @@ const ChecklistItem: FC = ({ title, checked, onToggle, onEdit, onDelete } - - + + {({ close }) => ( + <> + - + + + )}
diff --git a/src/pages/taskmanager/components/task-detail/checklist/List.tsx b/src/pages/taskmanager/components/task-detail/checklist/List.tsx index 45000d2..4166552 100644 --- a/src/pages/taskmanager/components/task-detail/checklist/List.tsx +++ b/src/pages/taskmanager/components/task-detail/checklist/List.tsx @@ -2,7 +2,7 @@ import { Edit } from "iconsax-react"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import ModalConfrim from "../../../../../components/ModalConfrim"; -import { toast } from '../../../../../components/Toast'; +import { toast } from "../../../../../components/Toast"; import ProgressBar from "../../../../../components/ProgressBar"; import { ErrorType } from "../../../../../helpers/types"; import { useDeleteCheckListItem, useUpdateCheckListItem } from "../../../task/hooks/useTaskData"; @@ -37,12 +37,23 @@ const CheckLists = ({ const doneCount = completedCheckListItemCount ?? items.filter((item) => item.checked).length; const progress = totalCount > 0 ? Math.round((doneCount / totalCount) * 100) : 0; - const handleToggle = (id: string, checked: boolean) => { + const handleToggle = (id: string, title: string, checked: boolean) => { updateCheckListItem.mutate( - { id, params: { isDone: !checked }, taskId }, + { id, params: { title, isDone: !checked, taskId }, taskId }, { onError: (error: ErrorType) => { - toast(error.response?.data?.error.message[0], 'error'); + toast(error.response?.data?.error.message[0], "error"); + }, + }, + ); + }; + + const handleSave = (id: string, title: string, checked: boolean) => { + updateCheckListItem.mutate( + { id, params: { title, isDone: checked, taskId }, taskId }, + { + onError: (error: ErrorType) => { + toast(error.response?.data?.error.message[0], "error"); }, }, ); @@ -58,7 +69,7 @@ const CheckLists = ({ setDeleteItemId(null); }, onError: (error: ErrorType) => { - toast(error.response?.data?.error.message[0], 'error'); + toast(error.response?.data?.error.message[0], "error"); }, }, ); @@ -84,9 +95,10 @@ const CheckLists = ({ key={item.id} title={item.title} checked={item.checked} - onToggle={() => handleToggle(item.id, item.checked)} - onEdit={() => {}} + onToggle={() => handleToggle(item.id, item.title, item.checked)} + onSave={(title) => handleSave(item.id, title, item.checked)} onDelete={() => setDeleteItemId(item.id)} + isSaving={updateCheckListItem.isPending} /> ))} diff --git a/src/pages/taskmanager/task/hooks/useTaskData.ts b/src/pages/taskmanager/task/hooks/useTaskData.ts index 717992e..f5dc637 100644 --- a/src/pages/taskmanager/task/hooks/useTaskData.ts +++ b/src/pages/taskmanager/task/hooks/useTaskData.ts @@ -159,7 +159,13 @@ export const useUpdateCheckListItem = () => { if (!current?.data?.checkListItems) return current; const checkListItems = current.data.checkListItems.map((item) => - item.id === id ? { ...item, isDone: params.isDone } : item, + item.id === id + ? { + ...item, + ...(params.isDone !== undefined ? { isDone: params.isDone } : {}), + ...(params.title !== undefined ? { title: params.title } : {}), + } + : item, ); const completedCheckListItemCount = checkListItems.filter((item) => item.isDone).length; diff --git a/src/pages/taskmanager/task/types/TaskTypes.ts b/src/pages/taskmanager/task/types/TaskTypes.ts index 5614a20..08ee8aa 100644 --- a/src/pages/taskmanager/task/types/TaskTypes.ts +++ b/src/pages/taskmanager/task/types/TaskTypes.ts @@ -83,7 +83,9 @@ export interface CreateCheckListItemResponse extends IResponse<{ checkListItem: } export type UpdateCheckListItemType = { - isDone: boolean; + title?: string; + isDone?: boolean; + taskId?: string; }; export interface UpdateCheckListItemResponse extends IResponse<{ checkListItem: TaskChecklistItemType }> {