diff --git a/config/database-postgres.js b/config/database-postgres.js
index 2708c25..584f60c 100644
--- a/config/database-postgres.js
+++ b/config/database-postgres.js
@@ -74,6 +74,7 @@ async function initDatabase() {
text TEXT NOT NULL,
completed BOOLEAN DEFAULT FALSE,
completed_at TIMESTAMP,
+ priority BOOLEAN DEFAULT FALSE,
position INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
@@ -87,6 +88,7 @@ async function initDatabase() {
text TEXT NOT NULL,
completed BOOLEAN DEFAULT FALSE,
completed_at TIMESTAMP,
+ priority BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
diff --git a/package.json b/package.json
index bd2bc94..636d780 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"docker:logs": "docker-compose logs -f",
"docker:restart": "docker-compose restart",
"db:migrate": "node scripts/add-cleanup-tracking-fields.js",
+ "db:migrate:priority": "node scripts/add-priority-field.js",
"db:cleanup": "node scripts/database-cleanup.js",
"db:cleanup:dry-run": "DRY_RUN=true node scripts/database-cleanup.js"
},
diff --git a/routes/todos.routes.js b/routes/todos.routes.js
index 82a77ac..c89d892 100644
--- a/routes/todos.routes.js
+++ b/routes/todos.routes.js
@@ -17,10 +17,10 @@ router.use(authenticateToken);
router.get('/', async (req, res) => {
try {
const todos = await getAll(`
- SELECT id, text, completed, created_at
+ SELECT id, text, completed, priority, created_at
FROM global_todos
WHERE user_id = $1
- ORDER BY created_at DESC
+ ORDER BY priority DESC, created_at DESC
`, [req.user.id]);
res.json(todos);
@@ -59,6 +59,7 @@ router.post('/',
id: result.id,
text,
completed: false,
+ priority: false,
created_at: new Date().toISOString()
});
} catch (error) {
@@ -75,7 +76,8 @@ router.post('/',
router.put('/:id',
[
body('text').optional().trim().notEmpty(),
- body('completed').optional().isBoolean()
+ body('completed').optional().isBoolean(),
+ body('priority').optional().isBoolean()
],
async (req, res) => {
try {
@@ -84,7 +86,7 @@ router.put('/:id',
return res.status(400).json({ error: 'Données invalides', details: errors.array() });
}
- const { text, completed } = req.body;
+ const { text, completed, priority } = req.body;
// Vérifier que le todo appartient à l'utilisateur
const todo = await getOne('SELECT id FROM global_todos WHERE id = $1 AND user_id = $2', [req.params.id, req.user.id]);
@@ -106,6 +108,11 @@ router.put('/:id',
updates.push(`completed = $${paramCount}`);
params.push(completed ? 1 : 0);
}
+ if (priority !== undefined) {
+ paramCount++;
+ updates.push(`priority = $${paramCount}`);
+ params.push(priority ? 1 : 0);
+ }
if (updates.length > 0) {
paramCount++;
@@ -145,6 +152,30 @@ router.patch('/:id/toggle', async (req, res) => {
}
});
+/**
+ * PATCH /api/todos/:id/priority
+ * Basculer l'état priority d'un todo
+ */
+router.patch('/:id/priority', async (req, res) => {
+ try {
+ // Vérifier que le todo appartient à l'utilisateur
+ const todo = await getOne('SELECT id, priority FROM global_todos WHERE id = $1 AND user_id = $2', [req.params.id, req.user.id]);
+ if (!todo) {
+ return res.status(404).json({ error: 'Todo non trouvé' });
+ }
+
+ const newPriority = todo.priority ? 0 : 1;
+ await runQuery('UPDATE global_todos SET priority = $1 WHERE id = $2', [newPriority, req.params.id]);
+
+ logger.info(`Todo global ${newPriority ? 'marqué prioritaire' : 'démarqué prioritaire'} (ID: ${req.params.id}) par ${req.user.username}`);
+
+ res.json({ message: 'Priorité modifiée avec succès', priority: newPriority === 1 });
+ } catch (error) {
+ logger.error('Erreur lors du toggle de la priorité:', error);
+ res.status(500).json({ error: 'Erreur serveur' });
+ }
+});
+
/**
* DELETE /api/todos/:id
* Supprimer un todo global
diff --git a/scripts/add-priority-field.js b/scripts/add-priority-field.js
new file mode 100644
index 0000000..37f2888
--- /dev/null
+++ b/scripts/add-priority-field.js
@@ -0,0 +1,87 @@
+#!/usr/bin/env node
+
+/**
+ * Script de migration pour ajouter le champ priority aux tâches
+ * - priority (BOOLEAN) pour global_todos
+ * - priority (BOOLEAN) pour note_todos
+ */
+
+const { Pool } = require('pg');
+
+const DATABASE_URL = process.env.DATABASE_URL ||
+ `postgresql://${process.env.PGUSER || 'noteflow'}:${process.env.PGPASSWORD || 'noteflow_secure_password_change_me'}@${process.env.PGHOST || 'postgres'}:${process.env.PGPORT || '5499'}/${process.env.PGDATABASE || 'noteflow'}`;
+
+async function migrate() {
+ const pool = new Pool({ connectionString: DATABASE_URL });
+
+ try {
+ console.log('\n╔════════════════════════════════════════════════════════════╗');
+ console.log('║ MIGRATION: Ajout du champ priority aux tâches ║');
+ console.log('╚════════════════════════════════════════════════════════════╝\n');
+
+ const client = await pool.connect();
+
+ try {
+ await client.query('BEGIN');
+
+ // 1. Ajouter priority à la table global_todos
+ console.log('📝 Ajout du champ priority à la table global_todos...');
+ await client.query(`
+ ALTER TABLE global_todos
+ ADD COLUMN IF NOT EXISTS priority BOOLEAN DEFAULT FALSE
+ `);
+ console.log('✅ Champ priority ajouté à global_todos\n');
+
+ // 2. Ajouter priority à la table note_todos
+ console.log('📝 Ajout du champ priority à la table note_todos...');
+ await client.query(`
+ ALTER TABLE note_todos
+ ADD COLUMN IF NOT EXISTS priority BOOLEAN DEFAULT FALSE
+ `);
+ console.log('✅ Champ priority ajouté à note_todos\n');
+
+ // 3. Créer un index pour améliorer les performances de tri par priorité
+ console.log('🔧 Création des index pour les tâches prioritaires...');
+ await client.query(`
+ CREATE INDEX IF NOT EXISTS idx_global_todos_priority
+ ON global_todos(priority DESC, created_at DESC)
+ `);
+ await client.query(`
+ CREATE INDEX IF NOT EXISTS idx_note_todos_priority
+ ON note_todos(priority DESC, position)
+ `);
+ console.log('✅ Index créés avec succès\n');
+
+ await client.query('COMMIT');
+
+ console.log('╔════════════════════════════════════════════════════════════╗');
+ console.log('║ ✅ MIGRATION TERMINÉE AVEC SUCCÈS ║');
+ console.log('╚════════════════════════════════════════════════════════════╝\n');
+
+ console.log('📝 RÉSUMÉ DES CHANGEMENTS:\n');
+ console.log('• Champ priority (BOOLEAN) ajouté à global_todos');
+ console.log('• Champ priority (BOOLEAN) ajouté à note_todos');
+ console.log('• Index créés pour optimiser le tri par priorité');
+ console.log('• Valeur par défaut: FALSE (non prioritaire)\n');
+
+ console.log('💡 UTILISATION:\n');
+ console.log('Les tâches peuvent maintenant être marquées comme prioritaires');
+ console.log('avec une étoile dans l\'interface.\n');
+
+ } catch (error) {
+ await client.query('ROLLBACK');
+ throw error;
+ } finally {
+ client.release();
+ }
+
+ await pool.end();
+
+ } catch (error) {
+ console.error('\n❌ Erreur:', error.message);
+ console.error(error.stack);
+ process.exit(1);
+ }
+}
+
+migrate();
diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx
index aacbecd..a23c1b2 100644
--- a/src/pages/Index.tsx
+++ b/src/pages/Index.tsx
@@ -15,7 +15,7 @@ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, Command
import { RichTextEditor } from "@/components/RichTextEditor";
import {
PlusCircle, Search, User, LogOut, Settings, ChevronDown, Plus, Archive, Trash2,
- Image as ImageIcon, CheckSquare, FileText, Rss, ExternalLink, RefreshCw, Key, Zap, Paperclip, X, Edit, Calendar as CalendarIcon, Tag as TagIcon, MessageSquare, Send, Check, ChevronsUpDown
+ Image as ImageIcon, CheckSquare, FileText, Rss, ExternalLink, RefreshCw, Key, Zap, Paperclip, X, Edit, Calendar as CalendarIcon, Tag as TagIcon, MessageSquare, Send, Check, ChevronsUpDown, Star
} from "lucide-react";
import AuthService from "@/services/AuthService";
import AdminService from "@/services/AdminService";
@@ -553,6 +553,15 @@ const Index = () => {
}
};
+ const handleToggleTodoPriority = async (todoId: number) => {
+ try {
+ await TodosService.togglePriority(todoId);
+ setTodos(todos.map(t => t.id === todoId ? { ...t, priority: !t.priority } : t));
+ } catch (error) {
+ showError("Erreur lors de la mise à jour de la priorité");
+ }
+ };
+
const handleDeleteTodo = async (todoId: number) => {
try {
await TodosService.deleteTodo(todoId);
@@ -1579,6 +1588,15 @@ const Index = () => {
{todo.text}
+