/** * TASKMATE - Subtask Routes * ========================= * CRUD für Unteraufgaben/Checkliste */ const express = require('express'); const router = express.Router(); const { getDb } = require('../database'); const logger = require('../utils/logger'); const { validators } = require('../middleware/validation'); /** * GET /api/subtasks/:taskId * Alle Unteraufgaben einer Aufgabe */ router.get('/:taskId', (req, res) => { try { const db = getDb(); const subtasks = db.prepare(` SELECT * FROM subtasks WHERE task_id = ? ORDER BY position `).all(req.params.taskId); res.json(subtasks.map(s => ({ id: s.id, taskId: s.task_id, title: s.title, completed: !!s.completed, position: s.position }))); } catch (error) { logger.error('Fehler beim Abrufen der Subtasks:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * POST /api/subtasks * Neue Unteraufgabe erstellen */ router.post('/', (req, res) => { try { const { taskId, title } = req.body; // Validierung const titleError = validators.required(title, 'Titel') || validators.maxLength(title, 200, 'Titel'); if (titleError) { return res.status(400).json({ error: titleError }); } const db = getDb(); // Task prüfen const task = db.prepare('SELECT * FROM tasks WHERE id = ?').get(taskId); if (!task) { return res.status(404).json({ error: 'Aufgabe nicht gefunden' }); } // Alle bestehenden Subtasks um eine Position nach unten verschieben db.prepare(` UPDATE subtasks SET position = position + 1 WHERE task_id = ? `).run(taskId); // Neue Subtask an Position 0 erstellen (immer an erster Stelle) const result = db.prepare(` INSERT INTO subtasks (task_id, title, position) VALUES (?, ?, 0) `).run(taskId, title); // Task updated_at aktualisieren db.prepare('UPDATE tasks SET updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(taskId); const subtask = db.prepare('SELECT * FROM subtasks WHERE id = ?').get(result.lastInsertRowid); logger.info(`Subtask erstellt: ${title} in Task ${taskId}`); // WebSocket const io = req.app.get('io'); io.to(`project:${task.project_id}`).emit('subtask:created', { taskId, subtask: { id: subtask.id, taskId: subtask.task_id, title: subtask.title, completed: false, position: subtask.position } }); res.status(201).json({ id: subtask.id, taskId: subtask.task_id, title: subtask.title, completed: false, position: subtask.position }); } catch (error) { logger.error('Fehler beim Erstellen des Subtasks:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * PUT /api/subtasks/:id * Unteraufgabe aktualisieren */ router.put('/:id', (req, res) => { try { const subtaskId = req.params.id; const { title, completed } = req.body; const db = getDb(); const subtask = db.prepare('SELECT * FROM subtasks WHERE id = ?').get(subtaskId); if (!subtask) { return res.status(404).json({ error: 'Unteraufgabe nicht gefunden' }); } // Validierung if (title) { const titleError = validators.maxLength(title, 200, 'Titel'); if (titleError) { return res.status(400).json({ error: titleError }); } } db.prepare(` UPDATE subtasks SET title = COALESCE(?, title), completed = COALESCE(?, completed) WHERE id = ? `).run(title || null, completed !== undefined ? (completed ? 1 : 0) : null, subtaskId); // Task updated_at aktualisieren db.prepare('UPDATE tasks SET updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(subtask.task_id); const updated = db.prepare('SELECT * FROM subtasks WHERE id = ?').get(subtaskId); const task = db.prepare('SELECT project_id FROM tasks WHERE id = ?').get(subtask.task_id); // WebSocket const io = req.app.get('io'); io.to(`project:${task.project_id}`).emit('subtask:updated', { taskId: subtask.task_id, subtask: { id: updated.id, taskId: updated.task_id, title: updated.title, completed: !!updated.completed, position: updated.position } }); res.json({ id: updated.id, taskId: updated.task_id, title: updated.title, completed: !!updated.completed, position: updated.position }); } catch (error) { logger.error('Fehler beim Aktualisieren des Subtasks:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * PUT /api/subtasks/:id/position * Unteraufgabe-Position ändern */ router.put('/:id/position', (req, res) => { try { const subtaskId = req.params.id; const { newPosition } = req.body; const db = getDb(); const subtask = db.prepare('SELECT * FROM subtasks WHERE id = ?').get(subtaskId); if (!subtask) { return res.status(404).json({ error: 'Unteraufgabe nicht gefunden' }); } const oldPosition = subtask.position; const taskId = subtask.task_id; if (newPosition > oldPosition) { db.prepare(` UPDATE subtasks SET position = position - 1 WHERE task_id = ? AND position > ? AND position <= ? `).run(taskId, oldPosition, newPosition); } else if (newPosition < oldPosition) { db.prepare(` UPDATE subtasks SET position = position + 1 WHERE task_id = ? AND position >= ? AND position < ? `).run(taskId, newPosition, oldPosition); } db.prepare('UPDATE subtasks SET position = ? WHERE id = ?').run(newPosition, subtaskId); const subtasks = db.prepare( 'SELECT * FROM subtasks WHERE task_id = ? ORDER BY position' ).all(taskId); const task = db.prepare('SELECT project_id FROM tasks WHERE id = ?').get(taskId); // WebSocket const io = req.app.get('io'); io.to(`project:${task.project_id}`).emit('subtasks:reordered', { taskId, subtasks: subtasks.map(s => ({ id: s.id, title: s.title, completed: !!s.completed, position: s.position })) }); res.json({ subtasks: subtasks.map(s => ({ id: s.id, taskId: s.task_id, title: s.title, completed: !!s.completed, position: s.position })) }); } catch (error) { logger.error('Fehler beim Verschieben des Subtasks:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); /** * DELETE /api/subtasks/:id * Unteraufgabe löschen */ router.delete('/:id', (req, res) => { try { const subtaskId = req.params.id; const db = getDb(); const subtask = db.prepare('SELECT * FROM subtasks WHERE id = ?').get(subtaskId); if (!subtask) { return res.status(404).json({ error: 'Unteraufgabe nicht gefunden' }); } const taskId = subtask.task_id; db.prepare('DELETE FROM subtasks WHERE id = ?').run(subtaskId); // Positionen neu nummerieren const remaining = db.prepare( 'SELECT id FROM subtasks WHERE task_id = ? ORDER BY position' ).all(taskId); remaining.forEach((s, idx) => { db.prepare('UPDATE subtasks SET position = ? WHERE id = ?').run(idx, s.id); }); // Task updated_at aktualisieren db.prepare('UPDATE tasks SET updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(taskId); const task = db.prepare('SELECT project_id FROM tasks WHERE id = ?').get(taskId); // WebSocket const io = req.app.get('io'); io.to(`project:${task.project_id}`).emit('subtask:deleted', { taskId, subtaskId }); res.json({ message: 'Unteraufgabe gelöscht' }); } catch (error) { logger.error('Fehler beim Löschen des Subtasks:', { error: error.message }); res.status(500).json({ error: 'Interner Serverfehler' }); } }); module.exports = router;