From 30ecc89ba6fab1a209a8e3e76883aeb98a980dc3 Mon Sep 17 00:00:00 2001 From: Dicken Date: Tue, 16 Jun 2026 14:48:09 +0200 Subject: [PATCH] feat: 3D-Statistik Gewinn-Sektion + Status-Badges im zugeklappten Zustand --- backend/src/routes/dashboard.js | 33 +++++++++++++- frontend/src/App.jsx | 81 +++++++++++++++++++++++++++------ 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/backend/src/routes/dashboard.js b/backend/src/routes/dashboard.js index cce2794..33e246c 100644 --- a/backend/src/routes/dashboard.js +++ b/backend/src/routes/dashboard.js @@ -133,7 +133,38 @@ router.get('/stats', authenticate, (req, res) => { byStatus[r.status] = r.c; } - res.json({ totalOrders, bezahltOrders, totalRevenue, offeneRevenue, ordersThisMonth, totalCalcs, byStatus }); + // Grundkosten berechnen (preis_freundschaft = Selbstkosten pro Stück aus Kalkulation) + function getBaseCost(orderId) { + const items = db.prepare(` + SELECT oi.stueckzahl, oi.custom_price, + COALESCE(c.preis_freundschaft, oi.preis_freundschaft) as basis + FROM order_items oi + LEFT JOIN calculations c ON c.id = oi.calculation_id + WHERE oi.order_id = ? + `).all(orderId); + return items.reduce((sum, i) => sum + (i.basis * i.stueckzahl), 0); + } + + function getRevenue(order) { + if (order.custom_price != null) return parseFloat(order.custom_price); + return db.prepare('SELECT COALESCE(SUM(custom_price*stueckzahl),0) s FROM order_items WHERE order_id=? AND custom_price IS NOT NULL').get(order.id).s; + } + + let totalProfit = 0; + for (const o of paidOrders) { + const rev = getRevenue(o); + const cost = getBaseCost(o.id); + totalProfit += rev - cost; + } + + let offeneProfit = 0; + for (const o of openOrders) { + const rev = getRevenue(o); + const cost = getBaseCost(o.id); + offeneProfit += rev - cost; + } + + res.json({ totalOrders, bezahltOrders, totalRevenue, offeneRevenue, ordersThisMonth, totalCalcs, byStatus, totalProfit, offeneProfit }); }); // ── Ideen-Board ─────────────────────────────────────────────────────────────── diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 3a42a7b..4a46d54 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -726,44 +726,95 @@ function BestellStats() { if (loaded) return; api('/dashboard/stats').then(s => { setStats(s); setLoaded(true); }).catch(() => {}); }; - const toggle = () => { setOpen(v => !v); if (!open) load(); }; - const Row = ({label, val, color='#fff'}) => ( -
- {label} + const Row = ({ label, val, color='rgba(255,255,255,0.85)', sub }) => ( +
+
+ {label} + {sub && {sub}} +
{val}
); + // Status-Badge für zugeklappte Ansicht + const Badge = ({ val, color, label }) => val > 0 ? ( + + {val} {label} + + ) : null; + return (
+ {open && (
{!stats ? (
Lädt…
) : ( <> + {/* Bestellungen */}
BESTELLUNGEN
- - - - + + + +
-
+ + {/* Einnahmen + Gewinn */} +
EINNAHMEN
- - - + + +
+ + {/* Gewinn */} +
+
GEWINN nach Materialkosten
+ = 0 ? '#6bcb77' : '#f87171'} + sub="bezahlt" + /> + = 0 ? '#ffe66d' : '#f87171'} + sub="in Arbeit/Fertig" + /> + +
+ +
+
)}