diff --git a/components/kanban/TaskCard.tsx b/components/kanban/TaskCard.tsx
index 79e3156..bb13824 100644
--- a/components/kanban/TaskCard.tsx
+++ b/components/kanban/TaskCard.tsx
@@ -186,32 +186,36 @@ export function TaskCard({ task, onDelete, onEdit, onUpdateTitle }: TaskCardProp
)}
- {/* Footer tech avec séparateur néon */}
-
-
- {task.dueDate ? (
-
- ⏰
- {formatDistanceToNow(new Date(task.dueDate), {
- addSuffix: true,
- locale: fr
- })}
-
- ) : (
-
--:--
- )}
-
- {task.source !== 'manual' && task.source && (
-
- {task.source}
-
- )}
-
- {task.completedAt && (
-
✓ DONE
- )}
+ {/* Footer tech avec séparateur néon - seulement si des données à afficher */}
+ {(task.dueDate || (task.source && task.source !== 'manual') || task.completedAt) && (
+
+
+ {task.dueDate ? (
+
+ ⏰
+ {formatDistanceToNow(new Date(task.dueDate), {
+ addSuffix: true,
+ locale: fr
+ })}
+
+ ) : (
+
+ )}
+
+
+ {task.source !== 'manual' && task.source && (
+
+ {task.source}
+
+ )}
+
+ {task.completedAt && (
+ ✓ DONE
+ )}
+
+
-
+ )}
);
}
diff --git a/components/ui/TagList.tsx b/components/ui/TagList.tsx
index 48edffe..f2e2fb5 100644
--- a/components/ui/TagList.tsx
+++ b/components/ui/TagList.tsx
@@ -1,5 +1,4 @@
import { Tag } from '@/lib/types';
-import { Button } from '@/components/ui/Button';
interface TagListProps {
tags: (Tag & { usage?: number })[];
@@ -7,13 +6,15 @@ interface TagListProps {
onTagDelete?: (tag: Tag) => void;
showActions?: boolean;
showUsage?: boolean;
+ deletingTagId?: string | null;
}
export function TagList({
tags,
onTagEdit,
onTagDelete,
- showActions = true
+ showActions = true,
+ deletingTagId
}: TagListProps) {
if (tags.length === 0) {
return (
@@ -27,10 +28,15 @@ export function TagList({
return (
- {tags.map((tag) => (
+ {tags.map((tag) => {
+ const isDeleting = deletingTagId === tag.id;
+
+ return (
{/* Contenu principal */}
@@ -57,24 +63,21 @@ export function TagList({
{showActions && (onTagEdit || onTagDelete) && (
{onTagEdit && (
-
+
)}
{onTagDelete && (
-
+ {isDeleting ? '⏳' : '🗑️'}
+
)}
)}
@@ -85,7 +88,8 @@ export function TagList({
style={{ backgroundColor: tag.color }}
/>
- ))}
+ );
+ })}
);
}
diff --git a/hooks/useTasks.ts b/hooks/useTasks.ts
index cefc23c..671736b 100644
--- a/hooks/useTasks.ts
+++ b/hooks/useTasks.ts
@@ -222,10 +222,12 @@ export function useTasks(
}
}, [refreshTasks]);
- // Charger les tâches au montage et quand les filtres changent
+ // Charger les tâches au montage seulement si pas de données initiales
useEffect(() => {
- refreshTasks();
- }, [refreshTasks]);
+ if (!initialData?.tasks?.length) {
+ refreshTasks();
+ }
+ }, [refreshTasks, initialData?.tasks?.length]);
return {
...state,
diff --git a/services/tags.ts b/services/tags.ts
index 658494e..21d3b59 100644
--- a/services/tags.ts
+++ b/services/tags.ts
@@ -133,7 +133,7 @@ export const tagsService = {
},
/**
- * Supprime un tag
+ * Supprime un tag et toutes ses relations
*/
async deleteTag(id: string): Promise
{
// Vérifier que le tag existe
@@ -142,15 +142,12 @@ export const tagsService = {
throw new Error(`Tag avec l'ID "${id}" non trouvé`);
}
- // Vérifier si le tag est utilisé par des tâches via la relation TaskTag
- const taskTagCount = await prisma.taskTag.count({
+ // Supprimer d'abord toutes les relations TaskTag
+ await prisma.taskTag.deleteMany({
where: { tagId: id }
});
- if (taskTagCount > 0) {
- throw new Error(`Impossible de supprimer le tag "${existing.name}" car il est utilisé par ${taskTagCount} tâche(s)`);
- }
-
+ // Puis supprimer le tag lui-même
await prisma.tag.delete({
where: { id }
});
diff --git a/services/tasks.ts b/services/tasks.ts
index e8c975c..4943ddd 100644
--- a/services/tasks.ts
+++ b/services/tasks.ts
@@ -38,7 +38,7 @@ export class TasksService {
}
}
},
- take: filters?.limit || 100,
+ take: filters?.limit, // Pas de limite par défaut - récupère toutes les tâches
skip: filters?.offset || 0,
orderBy: [
{ completedAt: 'desc' },
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 14ecebd..8f23f1f 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -4,7 +4,7 @@ import { HomePageClient } from '@/components/HomePageClient';
export default async function HomePage() {
// SSR - Récupération des données côté serveur
const [initialTasks, initialStats] = await Promise.all([
- tasksService.getTasks({ limit: 20 }),
+ tasksService.getTasks(),
tasksService.getTaskStats()
]);
diff --git a/src/app/tags/TagsPageClient.tsx b/src/app/tags/TagsPageClient.tsx
index 86d2e2d..14b4b9f 100644
--- a/src/app/tags/TagsPageClient.tsx
+++ b/src/app/tags/TagsPageClient.tsx
@@ -1,6 +1,7 @@
'use client';
import { useState } from 'react';
+import React from 'react';
import { Tag } from '@/lib/types';
import { useTags } from '@/hooks/useTags';
import { CreateTagData, UpdateTagData } from '@/clients/tags-client';
@@ -32,11 +33,20 @@ export function TagsPageClient({ initialTags }: TagsPageClientProps) {
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
const [editingTag, setEditingTag] = useState(null);
const [showPopular, setShowPopular] = useState(false);
+ const [deletingTagId, setDeletingTagId] = useState(null);
+ const [localTags, setLocalTags] = useState(initialTags);
- // Utiliser les tags initiaux si pas encore chargés
- const displayTags = tags.length > 0 ? tags : initialTags;
+ // Utiliser les tags du hook s'ils sont chargés, sinon les tags locaux
+ const displayTags = tags.length > 0 ? tags : localTags;
const filteredTags = searchQuery ? searchResults : displayTags;
+ // Synchroniser les tags locaux avec les tags du hook une seule fois
+ React.useEffect(() => {
+ if (tags.length > 0 && localTags === initialTags) {
+ setLocalTags(tags);
+ }
+ }, [tags, localTags, initialTags]);
+
const handleSearch = async (query: string) => {
setSearchQuery(query);
if (query.trim()) {
@@ -67,13 +77,22 @@ export function TagsPageClient({ initialTags }: TagsPageClientProps) {
};
const handleDeleteTag = async (tag: Tag) => {
- if (confirm(`Êtes-vous sûr de vouloir supprimer le tag "${tag.name}" ?`)) {
- try {
- await deleteTag(tag.id);
- } catch (error) {
- // L'erreur est déjà gérée dans le hook
- console.error('Erreur lors de la suppression:', error);
- }
+ // Suppression optimiste : retirer immédiatement de l'affichage
+ setDeletingTagId(tag.id);
+
+ // Mettre à jour les tags locaux pour suppression immédiate
+ const updatedTags = displayTags.filter(t => t.id !== tag.id);
+ setLocalTags(updatedTags);
+
+ try {
+ await deleteTag(tag.id);
+ // Succès : les tags seront mis à jour par le hook
+ } catch (error) {
+ // En cas d'erreur, restaurer le tag dans l'affichage
+ setLocalTags(prev => [...prev, tag].sort((a, b) => a.name.localeCompare(b.name)));
+ console.error('Erreur lors de la suppression:', error);
+ } finally {
+ setDeletingTagId(null);
}
};
@@ -122,14 +141,14 @@ export function TagsPageClient({ initialTags }: TagsPageClientProps) {
@@ -194,7 +213,7 @@ export function TagsPageClient({ initialTags }: TagsPageClientProps) {
)}
- {loading && (
+ {loading && displayTags.length === 0 && (
@@ -215,6 +234,7 @@ export function TagsPageClient({ initialTags }: TagsPageClientProps) {
onTagEdit={handleEditTag}
onTagDelete={handleDeleteTag}
showUsage={true}
+ deletingTagId={deletingTagId}
/>
@@ -225,7 +245,7 @@ export function TagsPageClient({ initialTags }: TagsPageClientProps) {
isOpen={isCreateModalOpen}
onClose={() => setIsCreateModalOpen(false)}
onSubmit={handleCreateTag}
- loading={loading}
+ loading={false}
/>