From 64cc665f78cfa42296d19ec003a6353c3f771d0f Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Sun, 14 Sep 2025 08:56:41 +0200 Subject: [PATCH] feat: add task editing and title updating features - Introduced `onEditTask` and `onUpdateTitle` props in `KanbanBoard`, `KanbanColumn`, and `TaskCard` components for enhanced task management. - Implemented editing functionality in `TaskCard` to allow users to update task titles directly. - Added `EditTaskForm` in `KanbanBoardContainer` to handle task editing state and submission. - Updated `TasksService` to ensure all task properties can be modified during updates. --- components/forms/EditTaskForm.tsx | 259 +++++++++++++++++++++++++++ components/kanban/Board.tsx | 6 +- components/kanban/BoardContainer.tsx | 47 ++++- components/kanban/Column.tsx | 6 +- components/kanban/TaskCard.tsx | 86 ++++++++- services/tasks.ts | 6 +- 6 files changed, 394 insertions(+), 16 deletions(-) create mode 100644 components/forms/EditTaskForm.tsx diff --git a/components/forms/EditTaskForm.tsx b/components/forms/EditTaskForm.tsx new file mode 100644 index 0000000..5dc2634 --- /dev/null +++ b/components/forms/EditTaskForm.tsx @@ -0,0 +1,259 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { Modal } from '@/components/ui/Modal'; +import { Button } from '@/components/ui/Button'; +import { Input } from '@/components/ui/Input'; +import { Badge } from '@/components/ui/Badge'; +import { Task, TaskPriority, TaskStatus } from '@/lib/types'; +import { UpdateTaskData } from '@/clients/tasks-client'; + +interface EditTaskFormProps { + isOpen: boolean; + onClose: () => void; + onSubmit: (data: UpdateTaskData) => Promise; + task: Task | null; + loading?: boolean; +} + +export function EditTaskForm({ isOpen, onClose, onSubmit, task, loading = false }: EditTaskFormProps) { + const [formData, setFormData] = useState>({ + title: '', + description: '', + status: 'todo' as TaskStatus, + priority: 'medium' as TaskPriority, + tags: [], + dueDate: undefined + }); + + const [tagInput, setTagInput] = useState(''); + const [errors, setErrors] = useState>({}); + + // Pré-remplir le formulaire quand la tâche change + useEffect(() => { + if (task) { + setFormData({ + title: task.title, + description: task.description || '', + status: task.status, + priority: task.priority, + tags: task.tags || [], + dueDate: task.dueDate ? new Date(task.dueDate) : undefined + }); + } + }, [task]); + + const validateForm = (): boolean => { + const newErrors: Record = {}; + + if (!formData.title?.trim()) { + newErrors.title = 'Le titre est requis'; + } + + if (formData.title && formData.title.length > 200) { + newErrors.title = 'Le titre ne peut pas dépasser 200 caractères'; + } + + if (formData.description && formData.description.length > 1000) { + newErrors.description = 'La description ne peut pas dépasser 1000 caractères'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validateForm() || !task) return; + + try { + await onSubmit({ + taskId: task.id, + ...formData + }); + handleClose(); + } catch (error) { + console.error('Erreur lors de la mise à jour:', error); + } + }; + + const handleClose = () => { + setTagInput(''); + setErrors({}); + onClose(); + }; + + const addTag = () => { + const tag = tagInput.trim(); + if (tag && !formData.tags?.includes(tag)) { + setFormData(prev => ({ + ...prev, + tags: [...(prev.tags || []), tag] + })); + setTagInput(''); + } + }; + + const removeTag = (tagToRemove: string) => { + setFormData(prev => ({ + ...prev, + tags: prev.tags?.filter((tag: string) => tag !== tagToRemove) || [] + })); + }; + + const handleTagKeyPress = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + addTag(); + } + }; + + if (!task) return null; + + return ( + +
+ {/* Titre */} + setFormData(prev => ({ ...prev, title: e.target.value }))} + placeholder="Titre de la tâche..." + error={errors.title} + disabled={loading} + /> + + {/* Description */} +
+ +