ToDo-Board: BoardModal, Roadmap, Wünsche, Unread-Polling
This commit is contained in:
@@ -284,6 +284,28 @@ for (const [k, v] of loginDefaults) {
|
||||
db.prepare('INSERT INTO admin_settings (key, value) VALUES (?, ?)').run(k, v);
|
||||
}
|
||||
|
||||
// ── Ideen-Board ───────────────────────────────────────────────────────────────
|
||||
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='board_items'").get()) {
|
||||
db.exec(`
|
||||
CREATE TABLE board_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
type TEXT NOT NULL CHECK(type IN ('roadmap','wish')),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
created_at DATETIME DEFAULT NULL
|
||||
)
|
||||
`);
|
||||
}
|
||||
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='board_reads'").get()) {
|
||||
db.exec(`
|
||||
CREATE TABLE board_reads (
|
||||
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
|
||||
last_read DATETIME DEFAULT NULL
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
// ── Link-Liste ────────────────────────────────────────────────────────────────
|
||||
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='link_list'").get()) {
|
||||
db.exec(`
|
||||
|
||||
@@ -67,7 +67,6 @@ router.put('/note', authenticate, (req, res) => {
|
||||
res.json({ success: true, updated_at: new Date().toISOString() });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
// ── Statistiken ───────────────────────────────────────────────────────────────
|
||||
router.get('/stats', authenticate, (req, res) => {
|
||||
@@ -123,3 +122,71 @@ router.get('/stats', authenticate, (req, res) => {
|
||||
|
||||
res.json({ totalOrders, bezahltOrders, totalRevenue, offeneRevenue, ordersThisMonth, totalCalcs, byStatus });
|
||||
});
|
||||
|
||||
// ── Ideen-Board ───────────────────────────────────────────────────────────────
|
||||
router.get('/board', authenticate, (req, res) => {
|
||||
const u = uid(req);
|
||||
const items = db.prepare(`
|
||||
SELECT b.*, us.username as author
|
||||
FROM board_items b JOIN users us ON us.id=b.user_id
|
||||
ORDER BY b.type ASC, b.created_at DESC
|
||||
`).all();
|
||||
|
||||
const lastRead = db.prepare('SELECT last_read FROM board_reads WHERE user_id=?').get(u)?.last_read;
|
||||
const unread = lastRead
|
||||
? db.prepare("SELECT COUNT(*) c FROM board_items WHERE created_at > ? AND user_id != ?").get(lastRead, u).c
|
||||
: db.prepare('SELECT COUNT(*) c FROM board_items WHERE user_id != ?').get(u).c;
|
||||
|
||||
res.json({ items, unread });
|
||||
});
|
||||
|
||||
router.get('/board/unread', authenticate, (req, res) => {
|
||||
const u = uid(req);
|
||||
const lastRead = db.prepare('SELECT last_read FROM board_reads WHERE user_id=?').get(u)?.last_read;
|
||||
const unread = lastRead
|
||||
? db.prepare("SELECT COUNT(*) c FROM board_items WHERE created_at > ? AND user_id != ?").get(lastRead, u).c
|
||||
: db.prepare('SELECT COUNT(*) c FROM board_items WHERE user_id != ?').get(u).c;
|
||||
res.json({ unread });
|
||||
});
|
||||
|
||||
router.post('/board/read', authenticate, (req, res) => {
|
||||
const u = uid(req);
|
||||
db.prepare(`INSERT OR REPLACE INTO board_reads (user_id, last_read) VALUES (?, datetime('now','localtime'))`).run(u);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
router.post('/board', authenticate, (req, res) => {
|
||||
const u = uid(req);
|
||||
const { type, title, description = '' } = req.body;
|
||||
if (!['roadmap','wish'].includes(type)) return res.status(400).json({ error: 'Ungültiger Typ' });
|
||||
if (type === 'roadmap' && req.user.role !== 'admin') return res.status(403).json({ error: 'Nur Admins' });
|
||||
if (!title?.trim()) return res.status(400).json({ error: 'Titel erforderlich' });
|
||||
const r = db.prepare(`INSERT INTO board_items (type, user_id, title, description, created_at) VALUES (?,?,?,?,datetime('now','localtime'))`)
|
||||
.run(type, u, title.trim(), description.trim());
|
||||
res.json({ ...db.prepare('SELECT * FROM board_items WHERE id=?').get(r.lastInsertRowid),
|
||||
author: req.user.username });
|
||||
});
|
||||
|
||||
router.put('/board/:id', authenticate, (req, res) => {
|
||||
const item = db.prepare('SELECT * FROM board_items WHERE id=?').get(req.params.id);
|
||||
if (!item) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||
const isAdmin = req.user.role === 'admin';
|
||||
const isOwn = item.user_id === uid(req);
|
||||
if (!isAdmin && (!isOwn || item.type === 'roadmap')) return res.status(403).json({ error: 'Kein Zugriff' });
|
||||
const { title, description } = req.body;
|
||||
db.prepare('UPDATE board_items SET title=?, description=? WHERE id=?')
|
||||
.run(title ?? item.title, description ?? item.description, item.id);
|
||||
res.json({ ...db.prepare('SELECT * FROM board_items WHERE id=?').get(item.id), author: req.user.username });
|
||||
});
|
||||
|
||||
router.delete('/board/:id', authenticate, (req, res) => {
|
||||
const item = db.prepare('SELECT * FROM board_items WHERE id=?').get(req.params.id);
|
||||
if (!item) return res.status(404).json({ error: 'Nicht gefunden' });
|
||||
const isAdmin = req.user.role === 'admin';
|
||||
const isOwn = item.user_id === uid(req);
|
||||
if (!isAdmin && !isOwn) return res.status(403).json({ error: 'Kein Zugriff' });
|
||||
db.prepare('DELETE FROM board_items WHERE id=?').run(item.id);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user