Initial commit
Dieser Commit ist enthalten in:
279
backend/routes/subtasks.js
Normale Datei
279
backend/routes/subtasks.js
Normale Datei
@ -0,0 +1,279 @@
|
||||
/**
|
||||
* 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;
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren