diff --git a/routes/notes.routes.js b/routes/notes.routes.js index 422373b..e7a40d0 100644 --- a/routes/notes.routes.js +++ b/routes/notes.routes.js @@ -38,6 +38,30 @@ const upload = multer({ // Toutes les routes nécessitent authentification router.use(authenticateToken); +/** + * GET /api/notes/counts + * Récupérer le nombre de notes actives et archivées + */ +router.get('/counts', async (req, res) => { + try { + const activeCount = await getAll(` + SELECT COUNT(*) as count FROM notes WHERE user_id = $1 AND archived = false + `, [req.user.id]); + + const archivedCount = await getAll(` + SELECT COUNT(*) as count FROM notes WHERE user_id = $1 AND archived = true + `, [req.user.id]); + + res.json({ + active: parseInt(activeCount[0]?.count || 0), + archived: parseInt(archivedCount[0]?.count || 0) + }); + } catch (error) { + logger.error('Erreur lors de la récupération des compteurs:', error); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + /** * GET /api/notes?archived=true/false * Liste toutes les notes de l'utilisateur diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index a5bedda..8534bab 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -159,6 +159,7 @@ const Index = () => { const [rssFeeds, setRssFeeds] = useState([]); const [rssArticles, setRssArticles] = useState([]); const [calendarEvents, setCalendarEvents] = useState([]); + const [counts, setCounts] = useState({ active: 0, archived: 0 }); const [settings, setSettings] = useState({}); const [adminTab, setAdminTab] = useState("users"); const [showArchived, setShowArchived] = useState(false); @@ -303,10 +304,15 @@ const Index = () => { const loadNotes = async () => { try { - const fetchedNotes = await NotesService.getNotes(showArchived); + const [fetchedNotes, fetchedCounts] = await Promise.all([ + NotesService.getNotes(showArchived), + NotesService.getCounts() + ]); + if (Array.isArray(fetchedNotes)) { setNotes(fetchedNotes); } + setCounts(fetchedCounts); } catch (error) { console.error("Erreur lors du chargement des notes:", error); } @@ -1352,13 +1358,13 @@ const Index = () => { variant={!showArchived ? "default" : "outline"} onClick={() => setShowArchived(false)} > - Actives ({notes.filter(n => !n.archived).length}) + Actives ({counts.active}) @@ -1407,145 +1413,145 @@ const Index = () => { {/* Notes View - List or Kanban */} {notesViewMode === 'list' ? ( <> -
- {filteredNotes.length > 0 ? ( - paginatedNotes.map(note => ( - handleOpenNote(note)} - > - - {/* Priority icon */} - + + {/* Priority icon */} + - {/* Archive icon */} - + {/* Archive icon */} + -

- {note.title || "Sans titre"} -

-

- {note.content ? cleanHtmlContent(note.content) : "Note vide"} +

+ {note.title || "Sans titre"} +

+

+ {note.content ? cleanHtmlContent(note.content) : "Note vide"} +

+
+ {note.todos && note.todos.length > 0 && ( + + + {note.todos.length} + + )} + {note.images && note.images.length > 0 && ( + + + {note.images.length} + + )} + {note.files && note.files.length > 0 && ( + + + {note.files.length} + + )} + {note.tags && note.tags.length > 0 && ( + note.tags.map((tag) => ( + + + {tag.name} + + )) + )} +
+ {note.updated_at && ( +

+ Modifiée le {new Date(note.updated_at).toLocaleDateString('fr-FR', { + day: '2-digit', + month: '2-digit', + year: 'numeric' + })} +

+ )} +
+
+ )) + ) : ( +
+ +

+ {searchQuery ? "Aucune note trouvée" : "Aucune note"}

-
- {note.todos && note.todos.length > 0 && ( - - - {note.todos.length} - - )} - {note.images && note.images.length > 0 && ( - - - {note.images.length} - - )} - {note.files && note.files.length > 0 && ( - - - {note.files.length} - - )} - {note.tags && note.tags.length > 0 && ( - note.tags.map((tag) => ( - - - {tag.name} - - )) - )} -
- {note.updated_at && ( -

- Modifiée le {new Date(note.updated_at).toLocaleDateString('fr-FR', { - day: '2-digit', - month: '2-digit', - year: 'numeric' - })} -

+ {!searchQuery && ( + )} - - - )) - ) : ( -
- -

- {searchQuery ? "Aucune note trouvée" : "Aucune note"} -

- {!searchQuery && ( - +
)}
- )} -
- {/* Pagination pour les notes */} - {totalNotesPages > 1 && ( -
- - - Page {notesPage + 1} sur {totalNotesPages} - - -
- )} - + {/* Pagination pour les notes */} + {totalNotesPages > 1 && ( +
+ + + Page {notesPage + 1} sur {totalNotesPages} + + +
+ )} + ) : ( /* Kanban View */ {
{/* Todos & RSS Boxes side by side */}
- {/* Todos Box */} - - -
- - - Tâches - - -
-
- - - - Actives ({todos.filter(t => !t.completed).length}) - Complétées ({todos.filter(t => t.completed).length}) - - - -
- {activeTodos.length > 0 ? ( - paginatedActiveTodos.map(todo => ( -
- todo.id && handleToggleTodo(todo.id)} - className="h-4 w-4" - /> - - {todo.text} - - - - -
- )) - ) : ( -

Aucune tâche active

- )} -
- {totalActiveTodosPages > 1 && ( -
- - - {todosActivePage + 1}/{totalActiveTodosPages} - - -
- )} -
- - -
- {completedTodos.length > 0 ? ( - paginatedCompletedTodos.map(todo => ( -
- todo.id && handleToggleTodo(todo.id)} - className="h-4 w-4" - /> - - {todo.text} - - {todo.priority && ( - - )} - -
- )) - ) : ( -

Aucune tâche complétée

- )} -
- {totalCompletedTodosPages > 1 && ( -
- - - {todosCompletedPage + 1}/{totalCompletedTodosPages} - - -
- )} -
-
-
-
- - {/* RSS Box */} - - -
- - - Flux RSS - -
- -
-
-
- -
- {rssArticles.length > 0 ? ( - paginatedRssArticles.map(article => ( -
article.link && window.open(article.link, '_blank')} - > -
-
-

{article.title}

- {article.description && ( -

- {article.description.replace(/<[^>]*>/g, '')} -

- )} -
- -
-
- )) - ) : ( -

- Aucun article -
- -

- )} -
- {totalRssPages > 1 && ( -
- - - {rssPage + 1}/{totalRssPages} - - -
- )} -
-
+ + + + + Actives ({todos.filter(t => !t.completed).length}) + Complétées ({todos.filter(t => t.completed).length}) + -
+ +
+ {activeTodos.length > 0 ? ( + paginatedActiveTodos.map(todo => ( +
+ todo.id && handleToggleTodo(todo.id)} + className="h-4 w-4" + /> + + {todo.text} + + + + +
+ )) + ) : ( +

Aucune tâche active

+ )} +
+ {totalActiveTodosPages > 1 && ( +
+ + + {todosActivePage + 1}/{totalActiveTodosPages} + + +
+ )} +
+ + +
+ {completedTodos.length > 0 ? ( + paginatedCompletedTodos.map(todo => ( +
+ todo.id && handleToggleTodo(todo.id)} + className="h-4 w-4" + /> + + {todo.text} + + {todo.priority && ( + + )} + +
+ )) + ) : ( +

Aucune tâche complétée

+ )} +
+ {totalCompletedTodosPages > 1 && ( +
+ + + {todosCompletedPage + 1}/{totalCompletedTodosPages} + + +
+ )} +
+ + + + + {/* RSS Box */} + + +
+ + + Flux RSS + +
+ + +
+
+
+ +
+ {rssArticles.length > 0 ? ( + paginatedRssArticles.map(article => ( +
article.link && window.open(article.link, '_blank')} + > +
+
+

{article.title}

+ {article.description && ( +

+ {article.description.replace(/<[^>]*>/g, '')} +

+ )} +
+ +
+
+ )) + ) : ( +

+ Aucun article +
+ +

+ )} +
+ {totalRssPages > 1 && ( +
+ + + {rssPage + 1}/{totalRssPages} + + +
+ )} +
+
+ +
diff --git a/src/pages/mobile/NotesPage.tsx b/src/pages/mobile/NotesPage.tsx index a3dbcb4..ec9398d 100644 --- a/src/pages/mobile/NotesPage.tsx +++ b/src/pages/mobile/NotesPage.tsx @@ -31,10 +31,16 @@ export default function NotesPage() { const [viewMode, setViewMode] = useState<'list' | 'kanban'>('list'); const navigate = useNavigate(); + const [counts, setCounts] = useState({ active: 0, archived: 0 }); + const loadNotes = async () => { try { - const data = await NotesService.getNotes(showArchived); - setNotes(data); + const [notesData, countsData] = await Promise.all([ + NotesService.getNotes(showArchived), + NotesService.getCounts() + ]); + setNotes(notesData); + setCounts(countsData); } catch (error) { showError("Erreur lors du chargement des notes"); } @@ -184,7 +190,7 @@ export default function NotesPage() { }} className="flex-1" > - Actives ({notes.filter(n => !n.archived).length}) + Actives ({counts.active}) diff --git a/src/services/NotesService.ts b/src/services/NotesService.ts index 83c16dc..dab034a 100644 --- a/src/services/NotesService.ts +++ b/src/services/NotesService.ts @@ -63,6 +63,23 @@ class NotesService { } } + async getCounts(): Promise<{ active: number; archived: number }> { + try { + const response = await fetch("/api/notes/counts", { + headers: AuthService.getHeaders() + }); + + if (!response.ok) { + throw new Error("Erreur lors de la récupération des compteurs"); + } + + return await response.json(); + } catch (error) { + console.error("Erreur compteur:", error); + return { active: 0, archived: 0 }; + } + } + async createNote(title: string, content: string = ""): Promise { try { const response = await fetch("/api/notes", {