diff --git a/config/database.js b/config/database.js index 5b13b8f..4649db5 100644 --- a/config/database.js +++ b/config/database.js @@ -242,6 +242,7 @@ function initDatabase() { const hasImageFilename = columns.some(col => col.name === 'image_filename'); const hasArchived = columns.some(col => col.name === 'archived'); + const hasPriority = columns.some(col => col.name === 'priority'); if (!hasImageFilename) { logger.info('Migration: Ajout de la colonne image_filename...'); @@ -264,6 +265,25 @@ function initDatabase() { } }); } + + if (!hasPriority) { + logger.info('Migration: Ajout de la colonne priority...'); + db.run('ALTER TABLE notes ADD COLUMN priority INTEGER DEFAULT 0', (err) => { + if (err) { + logger.error('Erreur lors de l\'ajout de la colonne priority:', err); + } else { + logger.info('✓ Colonne priority ajoutée avec succès'); + // Créer l'index pour la performance + db.run('CREATE INDEX IF NOT EXISTS idx_notes_priority ON notes(priority DESC, updated_at DESC)', (indexErr) => { + if (indexErr) { + logger.error('Erreur lors de la création de l\'index priority:', indexErr); + } else { + logger.info('✓ Index priority créé avec succès'); + } + }); + } + }); + } }); // Créer l'utilisateur admin par défaut si la table est vide diff --git a/migrations/002_add_priority.sql b/migrations/002_add_priority.sql new file mode 100644 index 0000000..f400bfa --- /dev/null +++ b/migrations/002_add_priority.sql @@ -0,0 +1,7 @@ +-- Migration: Add priority column to notes table +-- Date: 2025-01-12 + +ALTER TABLE notes ADD COLUMN priority INTEGER DEFAULT 0; + +-- Create index for better performance when sorting by priority +CREATE INDEX idx_notes_priority ON notes(priority DESC, updated_at DESC); diff --git a/routes/notes.routes.js b/routes/notes.routes.js index e3a7d96..1a2e1b6 100644 --- a/routes/notes.routes.js +++ b/routes/notes.routes.js @@ -49,16 +49,16 @@ router.get('/', async (req, res) => { const notes = await getAll(` SELECT - n.id, n.title, n.content, n.image_filename, n.archived, + n.id, n.title, n.content, n.image_filename, n.archived, n.priority, n.created_at, n.updated_at, (SELECT COUNT(*) FROM note_todos WHERE note_id = n.id) as todos_count, (SELECT COUNT(*) FROM note_todos WHERE note_id = n.id AND completed = 1) as todos_completed FROM notes n WHERE n.user_id = ? AND n.archived = ? - ORDER BY n.updated_at DESC + ORDER BY n.priority DESC, n.updated_at DESC `, [req.user.id, archivedFilter]); - // Charger les todos, images et fichiers pour chaque note + // Charger les todos, images, fichiers et tags pour chaque note for (const note of notes) { const todos = await getAll(` SELECT id, text, completed, position @@ -84,6 +84,15 @@ router.get('/', async (req, res) => { `, [note.id]); note.files = files || []; note.files_count = files?.length || 0; + + const tags = await getAll(` + SELECT t.id, t.name + FROM tags t + INNER JOIN note_tags nt ON t.id = nt.tag_id + WHERE nt.note_id = ? + ORDER BY t.name ASC + `, [note.id]); + note.tags = tags || []; } res.json(notes || []); @@ -895,5 +904,29 @@ router.delete('/:noteId/tags/:tagId', async (req, res) => { } }); +/** + * PATCH /api/notes/:id/priority + * Modifier la priorité d'une note + */ +router.patch('/:id/priority', async (req, res) => { + try { + const { priority } = req.body; + + // Vérifier que la note appartient à l'utilisateur + const note = await getOne('SELECT id FROM notes WHERE id = ? AND user_id = ?', [req.params.id, req.user.id]); + if (!note) { + return res.status(404).json({ error: 'Note non trouvée' }); + } + + await runQuery('UPDATE notes SET priority = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', [priority ? 1 : 0, req.params.id]); + + logger.info(`Priorité de la note ${req.params.id} modifiée: ${priority}`); + res.json({ message: 'Priorité modifiée avec succès', priority }); + } catch (error) { + logger.error('Erreur lors de la modification de la priorité:', error); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + module.exports = router; module.exports.searchNotes = searchNotes; diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 67714b3..fee8952 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -944,11 +944,34 @@ const Index = () => { paginatedNotes.map(note => ( handleOpenNote(note)} > -

+ {/* Priority icon */} + + +

{note.title || "Sans titre"}

@@ -973,6 +996,14 @@ const Index = () => { {note.files.length} )} + {note.tags && note.tags.length > 0 && ( + note.tags.map((tag) => ( + + + {tag.name} + + )) + )} {note.updated_at && (

diff --git a/src/services/NotesService.ts b/src/services/NotesService.ts index 03db3d9..0bb04a3 100644 --- a/src/services/NotesService.ts +++ b/src/services/NotesService.ts @@ -31,6 +31,8 @@ export interface Note { todos: Todo[]; images: Image[]; files?: NoteFile[]; + tags?: Array<{ id: number; name: string }>; + priority?: boolean; } class NotesService { @@ -158,11 +160,30 @@ class NotesService { method: "DELETE", headers: AuthService.getHeaders() }); - + if (!response.ok) { throw new Error("Erreur lors de la suppression de l'image"); } - + + return true; + } catch (error) { + showError(error instanceof Error ? error.message : "Erreur serveur"); + return false; + } + } + + async togglePriority(noteId: number, priority: boolean): Promise { + try { + const response = await fetch(`/api/notes/${noteId}/priority`, { + method: "PATCH", + headers: AuthService.getHeaders(), + body: JSON.stringify({ priority }) + }); + + if (!response.ok) { + throw new Error("Erreur lors de la mise à jour de la priorité"); + } + return true; } catch (error) { showError(error instanceof Error ? error.message : "Erreur serveur");