Files
TaskMate/backend/routes/columns.js
Claude Project Manager ab1e5be9a9 Initial commit
2025-12-28 21:36:45 +00:00

303 Zeilen
8.8 KiB
JavaScript

/**
* TASKMATE - Column Routes
* ========================
* CRUD für Board-Spalten
*/
const express = require('express');
const router = express.Router();
const { getDb } = require('../database');
const logger = require('../utils/logger');
const { validators } = require('../middleware/validation');
/**
* GET /api/columns/:projectId
* Alle Spalten eines Projekts
*/
router.get('/:projectId', (req, res) => {
try {
const db = getDb();
const columns = db.prepare(`
SELECT * FROM columns WHERE project_id = ? ORDER BY position
`).all(req.params.projectId);
res.json(columns.map(c => ({
id: c.id,
projectId: c.project_id,
name: c.name,
position: c.position,
color: c.color,
filterCategory: c.filter_category || 'in_progress'
})));
} catch (error) {
logger.error('Fehler beim Abrufen der Spalten:', { error: error.message });
res.status(500).json({ error: 'Interner Serverfehler' });
}
});
/**
* POST /api/columns
* Neue Spalte erstellen
*/
router.post('/', (req, res) => {
try {
const { projectId, name, color, filterCategory } = req.body;
// Validierung
const errors = [];
errors.push(validators.required(projectId, 'Projekt-ID'));
errors.push(validators.required(name, 'Name'));
errors.push(validators.maxLength(name, 50, 'Name'));
if (color) errors.push(validators.hexColor(color, 'Farbe'));
const firstError = errors.find(e => e !== null);
if (firstError) {
return res.status(400).json({ error: firstError });
}
const db = getDb();
// Höchste Position ermitteln
const maxPos = db.prepare(
'SELECT COALESCE(MAX(position), -1) as max FROM columns WHERE project_id = ?'
).get(projectId).max;
// Spalte erstellen mit filter_category
const result = db.prepare(`
INSERT INTO columns (project_id, name, position, color, filter_category)
VALUES (?, ?, ?, ?, ?)
`).run(projectId, name, maxPos + 1, color || null, filterCategory || 'in_progress');
const column = db.prepare('SELECT * FROM columns WHERE id = ?').get(result.lastInsertRowid);
logger.info(`Spalte erstellt: ${name} in Projekt ${projectId} (Filter: ${filterCategory || 'in_progress'})`);
// WebSocket
const io = req.app.get('io');
io.to(`project:${projectId}`).emit('column:created', {
id: column.id,
projectId: column.project_id,
name: column.name,
position: column.position,
color: column.color,
filterCategory: column.filter_category
});
res.status(201).json({
id: column.id,
projectId: column.project_id,
name: column.name,
position: column.position,
color: column.color,
filterCategory: column.filter_category
});
} catch (error) {
logger.error('Fehler beim Erstellen der Spalte:', { error: error.message });
res.status(500).json({ error: 'Interner Serverfehler' });
}
});
/**
* PUT /api/columns/:id
* Spalte aktualisieren
*/
router.put('/:id', (req, res) => {
try {
const columnId = req.params.id;
const { name, color, filterCategory } = req.body;
// Validierung
if (name) {
const nameError = validators.maxLength(name, 50, 'Name');
if (nameError) {
return res.status(400).json({ error: nameError });
}
}
if (color) {
const colorError = validators.hexColor(color, 'Farbe');
if (colorError) {
return res.status(400).json({ error: colorError });
}
}
const db = getDb();
const existing = db.prepare('SELECT * FROM columns WHERE id = ?').get(columnId);
if (!existing) {
return res.status(404).json({ error: 'Spalte nicht gefunden' });
}
db.prepare(`
UPDATE columns
SET name = COALESCE(?, name), color = ?, filter_category = COALESCE(?, filter_category)
WHERE id = ?
`).run(name || null, color !== undefined ? color : existing.color, filterCategory || null, columnId);
const column = db.prepare('SELECT * FROM columns WHERE id = ?').get(columnId);
logger.info(`Spalte aktualisiert: ${column.name} (ID: ${columnId})`);
// WebSocket
const io = req.app.get('io');
io.to(`project:${column.project_id}`).emit('column:updated', {
id: column.id,
name: column.name,
color: column.color,
filterCategory: column.filter_category
});
res.json({
id: column.id,
projectId: column.project_id,
name: column.name,
position: column.position,
color: column.color,
filterCategory: column.filter_category
});
} catch (error) {
logger.error('Fehler beim Aktualisieren der Spalte:', { error: error.message });
res.status(500).json({ error: 'Interner Serverfehler' });
}
});
/**
* PUT /api/columns/:id/position
* Spalten-Position ändern (Reihenfolge)
*/
router.put('/:id/position', (req, res) => {
try {
const columnId = req.params.id;
const { newPosition } = req.body;
if (typeof newPosition !== 'number' || newPosition < 0) {
return res.status(400).json({ error: 'Ungültige Position' });
}
const db = getDb();
const column = db.prepare('SELECT * FROM columns WHERE id = ?').get(columnId);
if (!column) {
return res.status(404).json({ error: 'Spalte nicht gefunden' });
}
const oldPosition = column.position;
const projectId = column.project_id;
// Positionen der anderen Spalten anpassen
if (newPosition > oldPosition) {
// Nach rechts verschoben: Spalten dazwischen nach links
db.prepare(`
UPDATE columns
SET position = position - 1
WHERE project_id = ? AND position > ? AND position <= ?
`).run(projectId, oldPosition, newPosition);
} else if (newPosition < oldPosition) {
// Nach links verschoben: Spalten dazwischen nach rechts
db.prepare(`
UPDATE columns
SET position = position + 1
WHERE project_id = ? AND position >= ? AND position < ?
`).run(projectId, newPosition, oldPosition);
}
// Neue Position setzen
db.prepare('UPDATE columns SET position = ? WHERE id = ?').run(newPosition, columnId);
// Alle Spalten des Projekts zurückgeben
const columns = db.prepare(
'SELECT * FROM columns WHERE project_id = ? ORDER BY position'
).all(projectId);
logger.info(`Spalte ${column.name} von Position ${oldPosition} zu ${newPosition} verschoben`);
// WebSocket
const io = req.app.get('io');
io.to(`project:${projectId}`).emit('columns:reordered', {
columns: columns.map(c => ({
id: c.id,
name: c.name,
position: c.position,
color: c.color,
filterCategory: c.filter_category
}))
});
res.json({
columns: columns.map(c => ({
id: c.id,
projectId: c.project_id,
name: c.name,
position: c.position,
color: c.color,
filterCategory: c.filter_category
}))
});
} catch (error) {
logger.error('Fehler beim Verschieben der Spalte:', { error: error.message });
res.status(500).json({ error: 'Interner Serverfehler' });
}
});
/**
* DELETE /api/columns/:id
* Spalte löschen
*/
router.delete('/:id', (req, res) => {
try {
const columnId = req.params.id;
const db = getDb();
const column = db.prepare('SELECT * FROM columns WHERE id = ?').get(columnId);
if (!column) {
return res.status(404).json({ error: 'Spalte nicht gefunden' });
}
// Prüfen ob Aufgaben in der Spalte sind
const taskCount = db.prepare(
'SELECT COUNT(*) as count FROM tasks WHERE column_id = ?'
).get(columnId).count;
if (taskCount > 0) {
return res.status(400).json({
error: 'Spalte enthält noch Aufgaben. Verschiebe oder lösche diese zuerst.'
});
}
// Mindestens eine Spalte muss bleiben
const columnCount = db.prepare(
'SELECT COUNT(*) as count FROM columns WHERE project_id = ?'
).get(column.project_id).count;
if (columnCount <= 1) {
return res.status(400).json({
error: 'Mindestens eine Spalte muss vorhanden sein.'
});
}
// Spalte löschen
db.prepare('DELETE FROM columns WHERE id = ?').run(columnId);
// Positionen neu nummerieren
const remainingColumns = db.prepare(
'SELECT id FROM columns WHERE project_id = ? ORDER BY position'
).all(column.project_id);
remainingColumns.forEach((col, index) => {
db.prepare('UPDATE columns SET position = ? WHERE id = ?').run(index, col.id);
});
logger.info(`Spalte gelöscht: ${column.name} (ID: ${columnId})`);
// WebSocket
const io = req.app.get('io');
io.to(`project:${column.project_id}`).emit('column:deleted', { id: columnId });
res.json({ message: 'Spalte gelöscht' });
} catch (error) {
logger.error('Fehler beim Löschen der Spalte:', { error: error.message });
res.status(500).json({ error: 'Interner Serverfehler' });
}
});
module.exports = router;