const express = require('express'); const db = require('../../db'); const { authenticate } = require('../../middleware/auth'); const router = express.Router(); // ── DB-Migration ────────────────────────────────────────────────────────────── (function migrate() { db.exec(` CREATE TABLE IF NOT EXISTS kanban_columns ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, title TEXT NOT NULL, color TEXT NOT NULL DEFAULT '#4ecdc4', position INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime('now','localtime')) ); CREATE TABLE IF NOT EXISTS kanban_cards ( id INTEGER PRIMARY KEY AUTOINCREMENT, column_id INTEGER NOT NULL REFERENCES kanban_columns(id) ON DELETE CASCADE, user_id INTEGER NOT NULL, title TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', priority TEXT NOT NULL DEFAULT 'none', position INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime('now','localtime')), updated_at TEXT NOT NULL DEFAULT (datetime('now','localtime')) ); `); // Nachrüsten falls Tabelle schon existiert aber color fehlt const cols = db.pragma('table_info(kanban_columns)').map(c => c.name); if (!cols.includes('color')) { db.exec("ALTER TABLE kanban_columns ADD COLUMN color TEXT NOT NULL DEFAULT '#4ecdc4'"); } })(); const uid = req => req.user.id; // ── Alle Spalten + Karten laden ─────────────────────────────────────────────── router.get('/board', authenticate, (req, res) => { const me = uid(req); const columns = db.prepare( 'SELECT * FROM kanban_columns WHERE user_id=? ORDER BY position ASC, id ASC' ).all(me); const cards = db.prepare( 'SELECT * FROM kanban_cards WHERE user_id=? ORDER BY position ASC, id ASC' ).all(me); const colMap = {}; for (const c of columns) { c.cards = []; colMap[c.id] = c; } for (const card of cards) { if (colMap[card.column_id]) colMap[card.column_id].cards.push(card); } res.json({ columns }); }); // ── Spalte erstellen ────────────────────────────────────────────────────────── router.post('/columns', authenticate, (req, res) => { const { title, color = '#4ecdc4' } = req.body; if (!title?.trim()) return res.status(400).json({ error: 'Titel fehlt' }); const me = uid(req); const maxPos = db.prepare('SELECT MAX(position) as m FROM kanban_columns WHERE user_id=?').get(me); const pos = (maxPos?.m ?? -1) + 1; const r = db.prepare( "INSERT INTO kanban_columns (user_id,title,color,position) VALUES (?,?,?,?)" ).run(me, title.trim(), color, pos); res.json(db.prepare('SELECT * FROM kanban_columns WHERE id=?').get(r.lastInsertRowid)); }); // ── Spalte aktualisieren (Titel + Farbe) ───────────────────────────────────── // Feste Route vor :param (Lesson Learned) router.post('/columns/reorder', authenticate, (req, res) => { const { order } = req.body; if (!Array.isArray(order)) return res.status(400).json({ error: 'order fehlt' }); const me = uid(req); const upd = db.prepare('UPDATE kanban_columns SET position=? WHERE id=? AND user_id=?'); db.transaction(() => { for (let i = 0; i < order.length; i++) upd.run(i, order[i], me); })(); res.json({ ok: true }); }); router.patch('/columns/:id', authenticate, (req, res) => { const me = uid(req); const col = db.prepare('SELECT * FROM kanban_columns WHERE id=? AND user_id=?').get(req.params.id, me); if (!col) return res.status(404).json({ error: 'Spalte nicht gefunden' }); const title = req.body.title !== undefined ? req.body.title.trim() : col.title; const color = req.body.color !== undefined ? req.body.color : col.color; if (!title) return res.status(400).json({ error: 'Titel fehlt' }); db.prepare('UPDATE kanban_columns SET title=?, color=? WHERE id=?').run(title, color, col.id); res.json({ ok: true }); }); router.delete('/columns/:id', authenticate, (req, res) => { const me = uid(req); const col = db.prepare('SELECT * FROM kanban_columns WHERE id=? AND user_id=?').get(req.params.id, me); if (!col) return res.status(404).json({ error: 'Spalte nicht gefunden' }); db.prepare('DELETE FROM kanban_columns WHERE id=?').run(col.id); res.json({ ok: true }); }); // ── Karten ──────────────────────────────────────────────────────────────────── router.post('/cards', authenticate, (req, res) => { const { column_id, title, description = '', priority = 'none' } = req.body; if (!column_id || !title?.trim()) return res.status(400).json({ error: 'column_id und title erforderlich' }); const me = uid(req); const col = db.prepare('SELECT * FROM kanban_columns WHERE id=? AND user_id=?').get(column_id, me); if (!col) return res.status(404).json({ error: 'Spalte nicht gefunden' }); const maxPos = db.prepare('SELECT MAX(position) as m FROM kanban_cards WHERE column_id=?').get(column_id); const pos = (maxPos?.m ?? -1) + 1; const r = db.prepare( "INSERT INTO kanban_cards (column_id,user_id,title,description,priority,position) VALUES (?,?,?,?,?,?)" ).run(column_id, me, title.trim(), description.trim(), priority, pos); res.json(db.prepare('SELECT * FROM kanban_cards WHERE id=?').get(r.lastInsertRowid)); }); router.patch('/cards/:id', authenticate, (req, res) => { const me = uid(req); const card = db.prepare('SELECT * FROM kanban_cards WHERE id=? AND user_id=?').get(req.params.id, me); if (!card) return res.status(404).json({ error: 'Karte nicht gefunden' }); const title = req.body.title !== undefined ? req.body.title.trim() : card.title; const description = req.body.description !== undefined ? req.body.description.trim() : card.description; const priority = req.body.priority !== undefined ? req.body.priority : card.priority; if (!title) return res.status(400).json({ error: 'Titel darf nicht leer sein' }); db.prepare( "UPDATE kanban_cards SET title=?,description=?,priority=?,updated_at=datetime('now','localtime') WHERE id=?" ).run(title, description, priority, card.id); res.json(db.prepare('SELECT * FROM kanban_cards WHERE id=?').get(card.id)); }); router.delete('/cards/:id', authenticate, (req, res) => { const me = uid(req); const card = db.prepare('SELECT * FROM kanban_cards WHERE id=? AND user_id=?').get(req.params.id, me); if (!card) return res.status(404).json({ error: 'Karte nicht gefunden' }); db.prepare('DELETE FROM kanban_cards WHERE id=?').run(card.id); res.json({ ok: true }); }); router.post('/cards/:id/move', authenticate, (req, res) => { const { column_id, position } = req.body; const me = uid(req); const card = db.prepare('SELECT * FROM kanban_cards WHERE id=? AND user_id=?').get(req.params.id, me); if (!card) return res.status(404).json({ error: 'Karte nicht gefunden' }); const targetColId = column_id !== undefined ? column_id : card.column_id; const col = db.prepare('SELECT * FROM kanban_columns WHERE id=? AND user_id=?').get(targetColId, me); if (!col) return res.status(404).json({ error: 'Zielspalte nicht gefunden' }); const siblings = db.prepare( 'SELECT id FROM kanban_cards WHERE column_id=? AND id!=? ORDER BY position ASC, id ASC' ).all(targetColId, card.id).map(r => r.id); const targetPos = position !== undefined ? Math.max(0, Math.min(position, siblings.length)) : siblings.length; siblings.splice(targetPos, 0, card.id); const upd = db.prepare('UPDATE kanban_cards SET column_id=?, position=? WHERE id=?'); db.transaction(() => { for (let i = 0; i < siblings.length; i++) upd.run(targetColId, i, siblings[i]); })(); res.json({ ok: true }); }); module.exports = router;