refacto: passing by server actions on taskCard
This commit is contained in:
@@ -6,19 +6,26 @@ import { Button } from '@/components/ui/Button';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { TagInput } from '@/components/ui/TagInput';
|
||||
import { Task, TaskPriority, TaskStatus } from '@/lib/types';
|
||||
import { UpdateTaskData } from '@/clients/tasks-client';
|
||||
// UpdateTaskData removed - using Server Actions directly
|
||||
import { getAllStatuses, getAllPriorities } from '@/lib/status-config';
|
||||
|
||||
interface EditTaskFormProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: (data: UpdateTaskData) => Promise<void>;
|
||||
onSubmit: (data: { taskId: string; title?: string; description?: string; status?: TaskStatus; priority?: TaskPriority; tags?: string[]; dueDate?: Date; }) => Promise<void>;
|
||||
task: Task | null;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
export function EditTaskForm({ isOpen, onClose, onSubmit, task, loading = false }: EditTaskFormProps) {
|
||||
const [formData, setFormData] = useState<Omit<UpdateTaskData, 'taskId'>>({
|
||||
const [formData, setFormData] = useState<{
|
||||
title: string;
|
||||
description: string;
|
||||
status: TaskStatus;
|
||||
priority: TaskPriority;
|
||||
tags: string[];
|
||||
dueDate?: Date;
|
||||
}>({
|
||||
title: '',
|
||||
description: '',
|
||||
status: 'todo' as TaskStatus,
|
||||
|
||||
@@ -18,15 +18,13 @@ import { TaskCard } from './TaskCard';
|
||||
interface KanbanBoardProps {
|
||||
tasks: Task[];
|
||||
onCreateTask?: (data: CreateTaskData) => Promise<void>;
|
||||
onDeleteTask?: (taskId: string) => Promise<void>;
|
||||
onEditTask?: (task: Task) => void;
|
||||
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
|
||||
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
|
||||
compactView?: boolean;
|
||||
visibleStatuses?: TaskStatus[];
|
||||
}
|
||||
|
||||
export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onUpdateTitle, onUpdateStatus, compactView = false, visibleStatuses }: KanbanBoardProps) {
|
||||
export function KanbanBoard({ tasks, onCreateTask, onEditTask, onUpdateStatus, compactView = false, visibleStatuses }: KanbanBoardProps) {
|
||||
const [activeTask, setActiveTask] = useState<Task | null>(null);
|
||||
const { isColumnVisible } = useUserPreferences();
|
||||
const { isMounted, sensors } = useDragAndDrop();
|
||||
@@ -95,9 +93,7 @@ export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onU
|
||||
id={column.id}
|
||||
tasks={column.tasks}
|
||||
onCreateTask={onCreateTask}
|
||||
onDeleteTask={onDeleteTask}
|
||||
onEditTask={onEditTask}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
compactView={compactView}
|
||||
/>
|
||||
))}
|
||||
@@ -124,9 +120,7 @@ export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onU
|
||||
<div className="rotate-3 opacity-90">
|
||||
<TaskCard
|
||||
task={activeTask}
|
||||
onDelete={undefined}
|
||||
onEdit={undefined}
|
||||
onUpdateTitle={undefined}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -9,8 +9,9 @@ import { KanbanFilters } from './KanbanFilters';
|
||||
import { EditTaskForm } from '@/components/forms/EditTaskForm';
|
||||
import { useTasksContext } from '@/contexts/TasksContext';
|
||||
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
|
||||
import { Task, TaskStatus } from '@/lib/types';
|
||||
import { UpdateTaskData, CreateTaskData } from '@/clients/tasks-client';
|
||||
import { Task, TaskStatus, TaskPriority } from '@/lib/types';
|
||||
import { CreateTaskData } from '@/clients/tasks-client';
|
||||
import { updateTask, createTask } from '@/actions/tasks';
|
||||
import { getAllStatuses } from '@/lib/status-config';
|
||||
|
||||
interface KanbanBoardContainerProps {
|
||||
@@ -26,13 +27,11 @@ export function KanbanBoardContainer({
|
||||
filteredTasks,
|
||||
pinnedTasks,
|
||||
loading,
|
||||
createTask,
|
||||
deleteTask,
|
||||
updateTask,
|
||||
updateTaskOptimistic,
|
||||
kanbanFilters,
|
||||
setKanbanFilters,
|
||||
tags
|
||||
tags,
|
||||
refreshTasks
|
||||
} = useTasksContext();
|
||||
|
||||
const { preferences, toggleColumnVisibility, isColumnVisible } = useUserPreferences();
|
||||
@@ -45,24 +44,20 @@ export function KanbanBoardContainer({
|
||||
setEditingTask(task);
|
||||
};
|
||||
|
||||
const handleUpdateTask = async (data: UpdateTaskData) => {
|
||||
await updateTask(data);
|
||||
setEditingTask(null);
|
||||
const handleUpdateTask = async (data: { taskId: string; title?: string; description?: string; status?: TaskStatus; priority?: TaskPriority; tags?: string[]; dueDate?: Date; }) => {
|
||||
const result = await updateTask(data);
|
||||
if (result.success) {
|
||||
await refreshTasks(); // Rafraîchir les données
|
||||
setEditingTask(null);
|
||||
} else {
|
||||
console.error('Error updating task:', result.error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateTitle = async (taskId: string, newTitle: string) => {
|
||||
await updateTask({
|
||||
taskId,
|
||||
title: newTitle
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateStatus = async (taskId: string, newStatus: TaskStatus) => {
|
||||
// Utiliser la mise à jour optimiste pour le drag & drop
|
||||
await updateTaskOptimistic({
|
||||
taskId,
|
||||
status: newStatus
|
||||
});
|
||||
await updateTaskOptimistic(taskId, newStatus);
|
||||
};
|
||||
|
||||
// Obtenir le nom du tag épinglé pour l'affichage
|
||||
@@ -70,7 +65,12 @@ export function KanbanBoardContainer({
|
||||
|
||||
// Wrapper pour adapter le type de createTask
|
||||
const handleCreateTask = async (data: CreateTaskData): Promise<void> => {
|
||||
await createTask(data);
|
||||
const result = await createTask(data);
|
||||
if (result.success) {
|
||||
await refreshTasks(); // Rafraîchir les données
|
||||
} else {
|
||||
console.error('Error creating task:', result.error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -89,9 +89,7 @@ export function KanbanBoardContainer({
|
||||
{showObjectives && pinnedTasks.length > 0 && (
|
||||
<ObjectivesBoard
|
||||
tasks={pinnedTasks}
|
||||
onDeleteTask={deleteTask}
|
||||
onEditTask={handleEditTask}
|
||||
onUpdateTitle={handleUpdateTitle}
|
||||
onUpdateStatus={handleUpdateStatus}
|
||||
compactView={kanbanFilters.compactView}
|
||||
pinnedTagName={pinnedTagName}
|
||||
@@ -103,9 +101,7 @@ export function KanbanBoardContainer({
|
||||
<PrioritySwimlanesBoard
|
||||
tasks={filteredTasks}
|
||||
onCreateTask={handleCreateTask}
|
||||
onDeleteTask={deleteTask}
|
||||
onEditTask={handleEditTask}
|
||||
onUpdateTitle={handleUpdateTitle}
|
||||
onUpdateStatus={handleUpdateStatus}
|
||||
compactView={kanbanFilters.compactView}
|
||||
visibleStatuses={visibleStatuses}
|
||||
@@ -115,9 +111,7 @@ export function KanbanBoardContainer({
|
||||
<SwimlanesBoard
|
||||
tasks={filteredTasks}
|
||||
onCreateTask={handleCreateTask}
|
||||
onDeleteTask={deleteTask}
|
||||
onEditTask={handleEditTask}
|
||||
onUpdateTitle={handleUpdateTitle}
|
||||
onUpdateStatus={handleUpdateStatus}
|
||||
compactView={kanbanFilters.compactView}
|
||||
visibleStatuses={visibleStatuses}
|
||||
@@ -128,9 +122,7 @@ export function KanbanBoardContainer({
|
||||
<KanbanBoard
|
||||
tasks={filteredTasks}
|
||||
onCreateTask={handleCreateTask}
|
||||
onDeleteTask={deleteTask}
|
||||
onEditTask={handleEditTask}
|
||||
onUpdateTitle={handleUpdateTitle}
|
||||
onUpdateStatus={handleUpdateStatus}
|
||||
compactView={kanbanFilters.compactView}
|
||||
visibleStatuses={visibleStatuses}
|
||||
|
||||
@@ -12,13 +12,11 @@ interface KanbanColumnProps {
|
||||
id: TaskStatus;
|
||||
tasks: Task[];
|
||||
onCreateTask?: (data: CreateTaskData) => Promise<void>;
|
||||
onDeleteTask?: (taskId: string) => Promise<void>;
|
||||
onEditTask?: (task: Task) => void;
|
||||
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
|
||||
compactView?: boolean;
|
||||
}
|
||||
|
||||
export function KanbanColumn({ id, tasks, onCreateTask, onDeleteTask, onEditTask, onUpdateTitle, compactView = false }: KanbanColumnProps) {
|
||||
export function KanbanColumn({ id, tasks, onCreateTask, onEditTask, compactView = false }: KanbanColumnProps) {
|
||||
const [showQuickAdd, setShowQuickAdd] = useState(false);
|
||||
|
||||
// Configuration de la zone droppable
|
||||
@@ -91,7 +89,7 @@ export function KanbanColumn({ id, tasks, onCreateTask, onDeleteTask, onEditTask
|
||||
</div>
|
||||
) : (
|
||||
tasks.map((task) => (
|
||||
<TaskCard key={task.id} task={task} onDelete={onDeleteTask} onEdit={onEditTask} onUpdateTitle={onUpdateTitle} compactView={compactView} />
|
||||
<TaskCard key={task.id} task={task} onEdit={onEditTask} compactView={compactView} />
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -21,9 +21,7 @@ import { useDroppable } from '@dnd-kit/core';
|
||||
|
||||
interface ObjectivesBoardProps {
|
||||
tasks: Task[];
|
||||
onDeleteTask?: (taskId: string) => Promise<void>;
|
||||
onEditTask?: (task: Task) => void;
|
||||
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
|
||||
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
|
||||
compactView?: boolean;
|
||||
pinnedTagName?: string;
|
||||
@@ -36,9 +34,7 @@ function DroppableColumn({
|
||||
title,
|
||||
color,
|
||||
icon,
|
||||
onDeleteTask,
|
||||
onEditTask,
|
||||
onUpdateTitle,
|
||||
compactView
|
||||
}: {
|
||||
status: TaskStatus;
|
||||
@@ -46,9 +42,7 @@ function DroppableColumn({
|
||||
title: string;
|
||||
color: string;
|
||||
icon: string;
|
||||
onDeleteTask?: (taskId: string) => Promise<void>;
|
||||
onEditTask?: (task: Task) => void;
|
||||
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
|
||||
compactView: boolean;
|
||||
}) {
|
||||
const { setNodeRef } = useDroppable({
|
||||
@@ -80,9 +74,7 @@ function DroppableColumn({
|
||||
<div key={task.id} className="transform hover:scale-[1.02] transition-transform duration-200">
|
||||
<TaskCard
|
||||
task={task}
|
||||
onDelete={onDeleteTask}
|
||||
onEdit={onEditTask}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
compactView={compactView}
|
||||
/>
|
||||
</div>
|
||||
@@ -96,9 +88,7 @@ function DroppableColumn({
|
||||
|
||||
export function ObjectivesBoard({
|
||||
tasks,
|
||||
onDeleteTask,
|
||||
onEditTask,
|
||||
onUpdateTitle,
|
||||
onUpdateStatus,
|
||||
compactView = false,
|
||||
pinnedTagName = "Objectifs"
|
||||
@@ -209,9 +199,7 @@ export function ObjectivesBoard({
|
||||
title="À faire"
|
||||
color="bg-[var(--primary)]"
|
||||
icon="📋"
|
||||
onDeleteTask={onDeleteTask}
|
||||
onEditTask={onEditTask}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
compactView={compactView}
|
||||
/>
|
||||
|
||||
@@ -221,9 +209,7 @@ export function ObjectivesBoard({
|
||||
title="En cours"
|
||||
color="bg-yellow-400"
|
||||
icon="🔄"
|
||||
onDeleteTask={onDeleteTask}
|
||||
onEditTask={onEditTask}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
compactView={compactView}
|
||||
/>
|
||||
|
||||
@@ -233,9 +219,7 @@ export function ObjectivesBoard({
|
||||
title="Terminé"
|
||||
color="bg-green-400"
|
||||
icon="✅"
|
||||
onDeleteTask={onDeleteTask}
|
||||
onEditTask={onEditTask}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
compactView={compactView}
|
||||
/>
|
||||
</div>
|
||||
@@ -267,9 +251,7 @@ export function ObjectivesBoard({
|
||||
<div className="rotate-3 opacity-90">
|
||||
<TaskCard
|
||||
task={activeTask}
|
||||
onDelete={undefined}
|
||||
onEdit={undefined}
|
||||
onUpdateTitle={undefined}
|
||||
compactView={compactView}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -10,9 +10,7 @@ interface PrioritySwimlanesBoardProps {
|
||||
loading: boolean;
|
||||
tasks: Task[];
|
||||
onCreateTask?: (data: CreateTaskData) => Promise<void>;
|
||||
onDeleteTask?: (taskId: string) => Promise<void>;
|
||||
onEditTask?: (task: Task) => void;
|
||||
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
|
||||
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
|
||||
compactView?: boolean;
|
||||
visibleStatuses?: TaskStatus[];
|
||||
@@ -21,9 +19,7 @@ interface PrioritySwimlanesBoardProps {
|
||||
export function PrioritySwimlanesBoard({
|
||||
tasks,
|
||||
onCreateTask,
|
||||
onDeleteTask,
|
||||
onEditTask,
|
||||
onUpdateTitle,
|
||||
onUpdateStatus,
|
||||
compactView = false,
|
||||
visibleStatuses,
|
||||
@@ -66,9 +62,7 @@ export function PrioritySwimlanesBoard({
|
||||
tasks={tasks}
|
||||
swimlanes={swimlanesData}
|
||||
onCreateTask={onCreateTask}
|
||||
onDeleteTask={onDeleteTask}
|
||||
onEditTask={onEditTask}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
onUpdateStatus={onUpdateStatus}
|
||||
compactView={compactView}
|
||||
visibleStatuses={visibleStatuses}
|
||||
|
||||
@@ -25,9 +25,7 @@ import { useDroppable } from '@dnd-kit/core';
|
||||
function DroppableColumn({
|
||||
status,
|
||||
tasks,
|
||||
onDeleteTask,
|
||||
onEditTask,
|
||||
onUpdateTitle,
|
||||
compactView,
|
||||
onCreateTask,
|
||||
showQuickAdd,
|
||||
@@ -36,9 +34,7 @@ function DroppableColumn({
|
||||
}: {
|
||||
status: TaskStatus;
|
||||
tasks: Task[];
|
||||
onDeleteTask?: (taskId: string) => Promise<void>;
|
||||
onEditTask?: (task: Task) => void;
|
||||
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
|
||||
compactView: boolean;
|
||||
onCreateTask?: (data: CreateTaskData) => Promise<void>;
|
||||
showQuickAdd?: boolean;
|
||||
@@ -60,9 +56,7 @@ function DroppableColumn({
|
||||
<TaskCard
|
||||
key={task.id}
|
||||
task={task}
|
||||
onDelete={onDeleteTask}
|
||||
onEdit={onEditTask}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
compactView={compactView}
|
||||
/>
|
||||
))}
|
||||
@@ -117,9 +111,7 @@ interface SwimlanesBaseProps {
|
||||
tasks: Task[];
|
||||
swimlanes: SwimlaneData[];
|
||||
onCreateTask?: (data: CreateTaskData) => Promise<void>;
|
||||
onDeleteTask?: (taskId: string) => Promise<void>;
|
||||
onEditTask?: (task: Task) => void;
|
||||
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
|
||||
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
|
||||
compactView?: boolean;
|
||||
visibleStatuses?: TaskStatus[];
|
||||
@@ -129,9 +121,7 @@ export function SwimlanesBase({
|
||||
tasks,
|
||||
swimlanes,
|
||||
onCreateTask,
|
||||
onDeleteTask,
|
||||
onEditTask,
|
||||
onUpdateTitle,
|
||||
onUpdateStatus,
|
||||
compactView = false,
|
||||
visibleStatuses
|
||||
@@ -270,9 +260,7 @@ export function SwimlanesBase({
|
||||
key={columnId}
|
||||
status={status}
|
||||
tasks={statusTasks}
|
||||
onDeleteTask={onDeleteTask}
|
||||
onEditTask={onEditTask}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
compactView={compactView}
|
||||
onCreateTask={onCreateTask ? (data) => handleQuickAdd(data, columnId) : undefined}
|
||||
showQuickAdd={showQuickAdd[columnId] || false}
|
||||
|
||||
@@ -9,9 +9,7 @@ import { SwimlanesBase, SwimlaneData } from './SwimlanesBase';
|
||||
interface SwimlanesboardProps {
|
||||
tasks: Task[];
|
||||
onCreateTask?: (data: CreateTaskData) => Promise<void>;
|
||||
onDeleteTask?: (taskId: string) => Promise<void>;
|
||||
onEditTask?: (task: Task) => void;
|
||||
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
|
||||
onUpdateStatus?: (taskId: string, newStatus: TaskStatus) => Promise<void>;
|
||||
compactView?: boolean;
|
||||
visibleStatuses?: TaskStatus[];
|
||||
@@ -21,9 +19,7 @@ interface SwimlanesboardProps {
|
||||
export function SwimlanesBoard({
|
||||
tasks,
|
||||
onCreateTask,
|
||||
onDeleteTask,
|
||||
onEditTask,
|
||||
onUpdateTitle,
|
||||
onUpdateStatus,
|
||||
compactView = false,
|
||||
visibleStatuses,
|
||||
@@ -88,9 +84,7 @@ export function SwimlanesBoard({
|
||||
tasks={tasks}
|
||||
swimlanes={swimlanesData}
|
||||
onCreateTask={onCreateTask}
|
||||
onDeleteTask={onDeleteTask}
|
||||
onEditTask={onEditTask}
|
||||
onUpdateTitle={onUpdateTitle}
|
||||
onUpdateStatus={onUpdateStatus}
|
||||
compactView={compactView}
|
||||
visibleStatuses={visibleStatuses}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useState, useEffect, useRef, useTransition } from 'react';
|
||||
import { Task } from '@/lib/types';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { fr } from 'date-fns/locale';
|
||||
@@ -9,21 +9,21 @@ import { useTasksContext } from '@/contexts/TasksContext';
|
||||
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
|
||||
import { useDraggable } from '@dnd-kit/core';
|
||||
import { getPriorityConfig, getPriorityColorHex } from '@/lib/status-config';
|
||||
import { updateTaskTitle, deleteTask } from '@/actions/tasks';
|
||||
|
||||
interface TaskCardProps {
|
||||
task: Task;
|
||||
onDelete?: (taskId: string) => Promise<void>;
|
||||
onEdit?: (task: Task) => void;
|
||||
onUpdateTitle?: (taskId: string, newTitle: string) => Promise<void>;
|
||||
compactView?: boolean;
|
||||
}
|
||||
|
||||
export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView = false }: TaskCardProps) {
|
||||
export function TaskCard({ task, onEdit, compactView = false }: TaskCardProps) {
|
||||
const [isEditingTitle, setIsEditingTitle] = useState(false);
|
||||
const [editTitle, setEditTitle] = useState(task.title);
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const { tags: availableTags } = useTasksContext();
|
||||
const { tags: availableTags, refreshTasks } = useTasksContext();
|
||||
const { preferences } = useUserPreferences();
|
||||
|
||||
// Helper pour construire l'URL Jira
|
||||
@@ -61,8 +61,18 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
|
||||
const handleDelete = async (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (onDelete) {
|
||||
await onDelete(task.id);
|
||||
|
||||
if (window.confirm('Êtes-vous sûr de vouloir supprimer cette tâche ?')) {
|
||||
startTransition(async () => {
|
||||
const result = await deleteTask(task.id);
|
||||
if (!result.success) {
|
||||
console.error('Error deleting task:', result.error);
|
||||
// TODO: Afficher une notification d'erreur
|
||||
} else {
|
||||
// Rafraîchir les données après suppression réussie
|
||||
await refreshTasks();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -77,7 +87,7 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
|
||||
const handleTitleClick = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (onUpdateTitle && !isDragging) {
|
||||
if (!isDragging && !isPending) {
|
||||
setIsEditingTitle(true);
|
||||
setShowTooltip(false);
|
||||
}
|
||||
@@ -85,8 +95,19 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
|
||||
|
||||
const handleTitleSave = async () => {
|
||||
const trimmedTitle = editTitle.trim();
|
||||
if (trimmedTitle && trimmedTitle !== task.title && onUpdateTitle) {
|
||||
await onUpdateTitle(task.id, trimmedTitle);
|
||||
if (trimmedTitle && trimmedTitle !== task.title) {
|
||||
startTransition(async () => {
|
||||
const result = await updateTaskTitle(task.id, trimmedTitle);
|
||||
if (!result.success) {
|
||||
console.error('Error updating task title:', result.error);
|
||||
// Remettre l'ancien titre en cas d'erreur
|
||||
setEditTitle(task.title);
|
||||
} else {
|
||||
// Mettre à jour optimistiquement le titre local
|
||||
// La Server Action a déjà mis à jour la DB, on synchronise juste l'affichage
|
||||
task.title = trimmedTitle;
|
||||
}
|
||||
});
|
||||
}
|
||||
setIsEditingTitle(false);
|
||||
setShowTooltip(false);
|
||||
@@ -142,7 +163,7 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
|
||||
onClick={handleTitleClick}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
title={onUpdateTitle ? "Cliquer pour éditer" : undefined}
|
||||
title="Cliquer pour éditer"
|
||||
>
|
||||
{titleWithoutEmojis}
|
||||
</h4>
|
||||
@@ -190,6 +211,8 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
|
||||
task.status === 'done' ? 'opacity-60' : ''
|
||||
} ${
|
||||
isJiraTask ? 'jira-task' : ''
|
||||
} ${
|
||||
isPending ? 'opacity-70 pointer-events-none' : ''
|
||||
}`}
|
||||
{...attributes}
|
||||
{...(isEditingTitle ? {} : listeners)}
|
||||
@@ -231,17 +254,19 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
|
||||
{!isEditingTitle && onEdit && (
|
||||
<button
|
||||
onClick={handleEdit}
|
||||
className="opacity-0 group-hover:opacity-100 w-5 h-5 rounded-full bg-[var(--primary)]/20 hover:bg-[var(--primary)]/30 border border-[var(--primary)]/30 hover:border-[var(--primary)]/50 flex items-center justify-center transition-all duration-200 text-[var(--primary)] hover:text-[var(--primary)] text-xs"
|
||||
disabled={isPending}
|
||||
className="opacity-0 group-hover:opacity-100 w-5 h-5 rounded-full bg-[var(--primary)]/20 hover:bg-[var(--primary)]/30 border border-[var(--primary)]/30 hover:border-[var(--primary)]/50 flex items-center justify-center transition-all duration-200 text-[var(--primary)] hover:text-[var(--primary)] text-xs disabled:opacity-50"
|
||||
title="Modifier la tâche"
|
||||
>
|
||||
✎
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!isEditingTitle && onDelete && (
|
||||
{!isEditingTitle && (
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
className="opacity-0 group-hover:opacity-100 w-5 h-5 rounded-full bg-[var(--destructive)]/20 hover:bg-[var(--destructive)]/30 border border-[var(--destructive)]/30 hover:border-[var(--destructive)]/50 flex items-center justify-center transition-all duration-200 text-[var(--destructive)] hover:text-[var(--destructive)] text-xs"
|
||||
disabled={isPending}
|
||||
className="opacity-0 group-hover:opacity-100 w-5 h-5 rounded-full bg-[var(--destructive)]/20 hover:bg-[var(--destructive)]/30 border border-[var(--destructive)]/30 hover:border-[var(--destructive)]/50 flex items-center justify-center transition-all duration-200 text-[var(--destructive)] hover:text-[var(--destructive)] text-xs disabled:opacity-50"
|
||||
title="Supprimer la tâche"
|
||||
>
|
||||
×
|
||||
@@ -270,6 +295,8 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
|
||||
task.status === 'done' ? 'opacity-60' : ''
|
||||
} ${
|
||||
isJiraTask ? 'jira-task' : ''
|
||||
} ${
|
||||
isPending ? 'opacity-70 pointer-events-none' : ''
|
||||
}`}
|
||||
{...attributes}
|
||||
{...(isEditingTitle ? {} : listeners)}
|
||||
@@ -312,7 +339,8 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
|
||||
{!isEditingTitle && onEdit && (
|
||||
<button
|
||||
onClick={handleEdit}
|
||||
className="opacity-0 group-hover:opacity-100 w-4 h-4 rounded-full bg-[var(--primary)]/20 hover:bg-[var(--primary)]/30 border border-[var(--primary)]/30 hover:border-[var(--primary)]/50 flex items-center justify-center transition-all duration-200 text-[var(--primary)] hover:text-[var(--primary)] text-xs"
|
||||
disabled={isPending}
|
||||
className="opacity-0 group-hover:opacity-100 w-4 h-4 rounded-full bg-[var(--primary)]/20 hover:bg-[var(--primary)]/30 border border-[var(--primary)]/30 hover:border-[var(--primary)]/50 flex items-center justify-center transition-all duration-200 text-[var(--primary)] hover:text-[var(--primary)] text-xs disabled:opacity-50"
|
||||
title="Modifier la tâche"
|
||||
>
|
||||
✎
|
||||
@@ -320,10 +348,11 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle, compactView =
|
||||
)}
|
||||
|
||||
{/* Bouton de suppression discret - masqué en mode édition */}
|
||||
{!isEditingTitle && onDelete && (
|
||||
{!isEditingTitle && (
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
className="opacity-0 group-hover:opacity-100 w-4 h-4 rounded-full bg-[var(--destructive)]/20 hover:bg-[var(--destructive)]/30 border border-[var(--destructive)]/30 hover:border-[var(--destructive)]/50 flex items-center justify-center transition-all duration-200 text-[var(--destructive)] hover:text-[var(--destructive)] text-xs"
|
||||
disabled={isPending}
|
||||
className="opacity-0 group-hover:opacity-100 w-4 h-4 rounded-full bg-[var(--destructive)]/20 hover:bg-[var(--destructive)]/30 border border-[var(--destructive)]/30 hover:border-[var(--destructive)]/50 flex items-center justify-center transition-all duration-200 text-[var(--destructive)] hover:text-[var(--destructive)] text-xs disabled:opacity-50"
|
||||
title="Supprimer la tâche"
|
||||
>
|
||||
×
|
||||
|
||||
Reference in New Issue
Block a user