280 Zeilen
7.8 KiB
JavaScript
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' });
|
|
}
|
|
|
|
// Höchste Position ermitteln
|
|
const maxPos = db.prepare(
|
|
'SELECT COALESCE(MAX(position), -1) as max FROM subtasks WHERE task_id = ?'
|
|
).get(taskId).max;
|
|
|
|
// Subtask erstellen
|
|
const result = db.prepare(`
|
|
INSERT INTO subtasks (task_id, title, position)
|
|
VALUES (?, ?, ?)
|
|
`).run(taskId, title, maxPos + 1);
|
|
|
|
// 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;
|