feat: add backend support for hierarchical subtasks

- Create migration 005 to add parent_id and level columns to todos tables
- Fix migration 003 to use correct table name (note_todos instead of todos)
- Update POST /api/notes/:id/todos to accept parent_id parameter
- Update POST /api/todos to accept parent_id for global todos
- Add automatic level calculation based on parent depth
- Update GET endpoints to return parent_id and level fields
- Add validation to ensure parent todo exists before creating subtask
- Support CASCADE DELETE for subtasks when parent is deleted
This commit is contained in:
Claude committed 2025-11-27 13:51:42 +00:00
1 parent a1addbacba
commit bde406bc3e
4 files changed
+71 -25

No files matched your search

+25 -10
View File
@@ -61,7 +61,7 @@ router.get('/', async (req, res) => {
// Charger les todos, images, fichiers et tags pour chaque note
for (const note of notes) {
const todos = await getAll(`
SELECT id, text, completed, position
SELECT id, text, completed, position, parent_id, level
FROM note_todos
WHERE note_id = $1
ORDER BY position ASC, id ASC
@@ -391,10 +391,13 @@ router.delete('/:id/image', async (req, res) => {
/**
* POST /api/notes/:id/todos
* Ajouter un todo à une note
* Ajouter un todo à une note (supports subtasks via parent_id)
*/
router.post('/:id/todos',
[body('text').trim().notEmpty().withMessage('Le texte est requis')],
[
body('text').trim().notEmpty().withMessage('Le texte est requis'),
body('parent_id').optional().isInt()
],
async (req, res) => {
try {
const errors = validationResult(req);
@@ -408,17 +411,27 @@ router.post('/:id/todos',
return res.status(404).json({ error: 'Note non trouvée' });
}
const { text } = req.body;
const { text, parent_id } = req.body;
let level = 0;
logger.info(`[CREATE TODO] Ajout todo à note ${req.params.id}: "${text}"`);
// Si c'est un subtask, vérifier que le parent existe et calculer le niveau
if (parent_id) {
const parent = await getOne('SELECT id, level FROM note_todos WHERE id = $1 AND note_id = $2', [parent_id, req.params.id]);
if (!parent) {
return res.status(404).json({ error: 'Todo parent non trouvé' });
}
level = (parent.level || 0) + 1;
}
logger.info(`[CREATE TODO] Ajout todo à note ${req.params.id}: "${text}"${parent_id ? ` (subtask of ${parent_id})` : ''}`);
const result = await runQuery(`
INSERT INTO note_todos (note_id, text, position)
VALUES ($1, $2, (SELECT COALESCE(MAX(position), 0) + 1 FROM note_todos WHERE note_id = $3))
INSERT INTO note_todos (note_id, text, parent_id, level, position)
VALUES ($1, $2, $3, $4, (SELECT COALESCE(MAX(position), 0) + 1 FROM note_todos WHERE note_id = $5 AND COALESCE(parent_id, 0) = COALESCE($6, 0)))
RETURNING *
`, [req.params.id, text, req.params.id]);
`, [req.params.id, text, parent_id || null, level, req.params.id, parent_id || null]);
logger.info(`[CREATE TODO] Todo créé - ID: ${result.id}, note_id: ${req.params.id}`);
logger.info(`[CREATE TODO] Todo créé - ID: ${result.id}, note_id: ${req.params.id}, level: ${level}`);
await runQuery('UPDATE notes SET updated_at = CURRENT_TIMESTAMP WHERE id = $1', [req.params.id]);
@@ -426,7 +439,9 @@ router.post('/:id/todos',
id: result.id,
text,
completed: false,
position: 0
position: result.position || 0,
parent_id: parent_id || null,
level: level
});
} catch (error) {
logger.error('Erreur lors de l\'ajout du todo:', error);