From 02c7e9b5fd42b779a9e2fcff5f3c62c44fd9b79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?m=C3=BCde?= Date: Tue, 19 May 2026 18:18:10 +0200 Subject: [PATCH] admin: add and rename bars --- client/src/admin/Admin.tsx | 36 +++++++++++++++++++++-- server/src/routes/admin.ts | 60 +++++++++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/client/src/admin/Admin.tsx b/client/src/admin/Admin.tsx index 8ff5127..aa441b8 100644 --- a/client/src/admin/Admin.tsx +++ b/client/src/admin/Admin.tsx @@ -194,6 +194,7 @@ function Drinks() { function Bars() { const [bars, setBars] = useState([]); const [drinks, setDrinks] = useState([]); + const [newName, setNewName] = useState(''); function reload() { fetch('/admin/api/bars').then(j).then(setBars); @@ -202,11 +203,28 @@ function Bars() { useEffect(reload, []); async function patch(id: number, body: Partial) { - await fetch(`/admin/api/bars/${id}`, { + const res = await fetch(`/admin/api/bars/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); + if (!res.ok) alert(`Fehler: ${await res.text()}`); + reload(); + } + + async function addBar() { + const name = newName.trim(); + if (!name) return; + const res = await fetch('/admin/api/bars', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name, pfand_cents: 200 }), + }); + if (!res.ok) { + alert(`Fehler: ${await res.text()}`); + return; + } + setNewName(''); reload(); } @@ -216,7 +234,13 @@ function Bars() { {bars.map(b => (
- {b.name} +
))} +
+ setNewName(e.currentTarget.value)} + /> + +
); } diff --git a/server/src/routes/admin.ts b/server/src/routes/admin.ts index 35ae220..0f56474 100644 --- a/server/src/routes/admin.ts +++ b/server/src/routes/admin.ts @@ -89,24 +89,58 @@ export function registerAdminRoutes(app: FastifyInstance, db: DB) { })); }); - app.patch<{ Params: { id: string }; Body: { pfand_cents?: number; drink_ids?: number[] } }>( + app.post<{ Body: { name: string; pfand_cents?: number } }>( + '/admin/api/bars', + async (req, reply) => { + if (!requireAuth(req, reply)) return; + const name = req.body?.name?.trim(); + const pfand_cents = req.body?.pfand_cents ?? 0; + if (!name) return reply.code(400).send({ error: 'name required' }); + try { + const info = db + .prepare('INSERT INTO bars (name, pfand_cents) VALUES (?, ?)') + .run(name, pfand_cents); + return { id: Number(info.lastInsertRowid) }; + } catch (e: any) { + if (String(e.message).includes('UNIQUE')) { + return reply.code(409).send({ error: 'name already exists' }); + } + throw e; + } + } + ); + + app.patch<{ Params: { id: string }; Body: { name?: string; pfand_cents?: number; drink_ids?: number[] } }>( '/admin/api/bars/:id', async (req, reply) => { if (!requireAuth(req, reply)) return; const id = Number(req.params.id); - const { pfand_cents, drink_ids } = req.body ?? {}; - db.transaction(() => { - if (pfand_cents !== undefined) { - db.prepare('UPDATE bars SET pfand_cents = ? WHERE id = ?').run(pfand_cents, id); + const { name, pfand_cents, drink_ids } = req.body ?? {}; + try { + db.transaction(() => { + if (name !== undefined) { + const trimmed = name.trim(); + if (!trimmed) throw new Error('name empty'); + db.prepare('UPDATE bars SET name = ? WHERE id = ?').run(trimmed, id); + } + if (pfand_cents !== undefined) { + db.prepare('UPDATE bars SET pfand_cents = ? WHERE id = ?').run(pfand_cents, id); + } + if (Array.isArray(drink_ids)) { + db.prepare('DELETE FROM bar_drinks WHERE bar_id = ?').run(id); + const ins = db.prepare( + 'INSERT INTO bar_drinks (bar_id, drink_id, sort_order) VALUES (?, ?, ?)' + ); + drink_ids.forEach((did, idx) => ins.run(id, did, idx)); + } + })(); + } catch (e: any) { + if (String(e.message).includes('UNIQUE')) { + return reply.code(409).send({ error: 'name already exists' }); } - if (Array.isArray(drink_ids)) { - db.prepare('DELETE FROM bar_drinks WHERE bar_id = ?').run(id); - const ins = db.prepare( - 'INSERT INTO bar_drinks (bar_id, drink_id, sort_order) VALUES (?, ?, ?)' - ); - drink_ids.forEach((did, idx) => ins.run(id, did, idx)); - } - })(); + if (e.message === 'name empty') return reply.code(400).send({ error: 'name empty' }); + throw e; + } return { ok: true }; } );