"use client"; import type { DragEndEvent, DragStartEvent } from "@dnd-kit/core"; import { closestCenter, DndContext, DragOverlay, KeyboardSensor, PointerSensor, useSensor, useSensors, } from "@dnd-kit/core"; import { arrayMove, SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { ChevronDown, ChevronRight, GripVertical, Plus } from "lucide-react"; import { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Item, ItemContent, ItemGroup, ItemMedia, ItemTitle, } from "@/components/ui/item"; import { cn } from "@/lib/utils"; interface Subtask { id: string; title: string; completed: boolean; } interface Todo { id: string; title: string; completed: boolean; subtasks: Subtask[]; expanded?: boolean; } interface SubtaskLabels { placeholder: string; addButton: string; submit: string; cancel: string; expand: string; collapse: string; } interface Todo8Props { heading?: string; description?: string; subtaskLabels?: SubtaskLabels; initialTodos?: Todo[]; className?: string; } const defaultSubtaskLabels: SubtaskLabels = { placeholder: "عنوان وظیفه فرعی", addButton: "افزودن وظیفه فرعی", submit: "افزودن", cancel: "انصراف", expand: "نمایش وظایف فرعی", collapse: "پنهان کردن وظایف فرعی", }; const defaultTodos: Todo[] = [ { id: "1", title: "برنامه‌ریزی راه‌اندازی پروژه", completed: false, expanded: true, subtasks: [ { id: "1-1", title: "تدوین جدول زمانی پروژه", completed: true }, { id: "1-2", title: "هماهنگی جلسات تیم", completed: true }, { id: "1-3", title: "آماده‌سازی ارائه", completed: false }, { id: "1-4", title: "بررسی بودجه", completed: false }, ], }, { id: "2", title: "بررسی و تأیید طراحی رابط کاربری", completed: false, expanded: false, subtasks: [ { id: "2-1", title: "طراحی وایرفریم", completed: false }, { id: "2-2", title: "دریافت تأیید ذی‌نفعان", completed: false }, ], }, { id: "3", title: "نوشتن مستندات فنی", completed: false, expanded: false, subtasks: [], }, ]; interface SortableTodoItemProps { todo: Todo; onToggle: (id: string) => void; onToggleSubtask: (todoId: string, subtaskId: string) => void; onToggleExpand: (id: string) => void; onAddSubtask: (todoId: string, title: string) => void; subtaskLabels: SubtaskLabels; } const SortableTodoItem = ({ todo, onToggle, onToggleSubtask, onToggleExpand, onAddSubtask, subtaskLabels, }: SortableTodoItemProps) => { const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id: todo.id }); const style = { transform: CSS.Translate.toString(transform), transition, opacity: isDragging ? 0.5 : 1, }; const completedSubtasks = todo.subtasks.filter((st) => st.completed).length; const totalSubtasks = todo.subtasks.length; const progress = totalSubtasks > 0 ? Math.round((completedSubtasks / totalSubtasks) * 100) : 0; const [newSubtaskTitle, setNewSubtaskTitle] = useState(""); const [isAddingSubtask, setIsAddingSubtask] = useState(false); const handleAddSubtask = () => { if (newSubtaskTitle.trim()) { onAddSubtask(todo.id, newSubtaskTitle.trim()); setNewSubtaskTitle(""); setIsAddingSubtask(false); } }; return (
onToggle(todo.id)} >
onToggle(todo.id)} onClick={(e) => e.stopPropagation()} aria-label={ todo.completed ? `علامت‌گذاری «${todo.title}» به‌عنوان انجام‌نشده` : `علامت‌گذاری «${todo.title}» به‌عنوان انجام‌شده` } />
{todo.title} {totalSubtasks > 0 && (
{completedSubtasks}/{totalSubtasks}
)}
{todo.expanded && (
{todo.subtasks.map((subtask) => (
onToggleSubtask(todo.id, subtask.id)} onClick={(e) => e.stopPropagation()} aria-label={ subtask.completed ? `علامت‌گذاری «${subtask.title}» به‌عنوان انجام‌نشده` : `علامت‌گذاری «${subtask.title}» به‌عنوان انجام‌شده` } /> {subtask.title}
))} {isAddingSubtask ? (
setNewSubtaskTitle(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { handleAddSubtask(); } if (e.key === "Escape") { setIsAddingSubtask(false); setNewSubtaskTitle(""); } }} className="h-8" onClick={(e) => e.stopPropagation()} aria-label={subtaskLabels.placeholder} />
) : ( )}
)}
); }; const Todo8 = ({ heading = "مدیریت وظایف با زیر وظایف", description = "وظایف تاشو با زیروظایف تو در تو، نمایش پیشرفت و مرتب‌سازی با کشیدن. زیروظایف یا وظیفه اصلی را جداگانه تکمیل کنید.", subtaskLabels = defaultSubtaskLabels, initialTodos = defaultTodos, className, }: Todo8Props) => { const [todos, setTodos] = useState(initialTodos); const [activeTodo, setActiveTodo] = useState(null); const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, }), ); const toggleTodo = (id: string) => { setTodos((prevTodos) => prevTodos.map((todo) => todo.id === id ? { ...todo, completed: !todo.completed } : todo, ), ); }; const toggleSubtask = (todoId: string, subtaskId: string) => { setTodos((prevTodos) => prevTodos.map((todo) => todo.id === todoId ? { ...todo, subtasks: todo.subtasks.map((st) => st.id === subtaskId ? { ...st, completed: !st.completed } : st, ), } : todo, ), ); }; const toggleExpand = (id: string) => { setTodos((prevTodos) => prevTodos.map((todo) => todo.id === id ? { ...todo, expanded: !todo.expanded } : todo, ), ); }; const addSubtask = (todoId: string, title: string) => { setTodos((prevTodos) => prevTodos.map((todo) => todo.id === todoId ? { ...todo, subtasks: [ ...todo.subtasks, { id: `${todoId}-${Date.now()}`, title, completed: false }, ], expanded: true, } : todo, ), ); }; const handleDragStart = (event: DragStartEvent) => { const { active } = event; const todo = todos.find((t) => t.id === active.id); setActiveTodo(todo || null); }; const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (over && active.id !== over.id) { const oldIndex = todos.findIndex((item) => item.id === active.id); const newIndex = todos.findIndex((item) => item.id === over.id); if (oldIndex !== -1 && newIndex !== -1) { setTodos((items) => arrayMove(items, oldIndex, newIndex)); } } setActiveTodo(null); }; const allTodoIds = useMemo(() => todos.map((t) => t.id), [todos]); return (

{heading}

{description}

{todos.map((todo) => ( ))}
{activeTodo ? (
{activeTodo.title}
) : null}
); }; export { Todo8 };