Files
TaskMate/backend/routes/subtasks.js
2025-12-30 22:49:56 +00:00

280 Zeilen
7.8 KiB
JavaScript

/**
* 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;