From d4fb2d7527981b310ba2370e89a989e588f44f54 Mon Sep 17 00:00:00 2001 From: Dicken Date: Wed, 17 Jun 2026 17:32:34 +0200 Subject: [PATCH] feat: Tab Einnahmen (bezahlt_am Zeitraum) + Bestellungen (created_at diesen Monat) --- backend/src/tools/statistik/routes.js | 28 ++++++ frontend/src/tools/statistik.jsx | 124 ++++++++++++++++++++------ 2 files changed, 125 insertions(+), 27 deletions(-) diff --git a/backend/src/tools/statistik/routes.js b/backend/src/tools/statistik/routes.js index adb5de4..3fd1220 100644 --- a/backend/src/tools/statistik/routes.js +++ b/backend/src/tools/statistik/routes.js @@ -190,6 +190,14 @@ router.get('/overview', authenticate, (req, res) => { base_cost: round2(getOrderBaseCost(o.id)), })); + const paidOrdersOut = paidOrders.slice(0,50).map(o => ({ + id: o.id, name: o.name, status: o.status, + bezahlt: o.bezahlt, abgeholt: o.abgeholt, + created_at: o.created_at, bezahlt_am: o.bezahlt_am, + revenue: round2(getOrderRevenue(o)), + base_cost: round2(getOrderBaseCost(o.id)), + })); + res.json({ totalRevenue: round2(totalRevenue), openRevenue: round2(openRevenue), @@ -202,9 +210,29 @@ router.get('/overview', authenticate, (req, res) => { byStatus, byCategory, monthlyData, cumulativeData, expenses: expenses.slice(0, 100), recentOrders, + paidOrders: paidOrdersOut, }); }); +// Bestellungen nach created_at (für "Bestellungen diesen Monat" Tab) +router.get('/month-orders', authenticate, (req, res) => { + const { from, to } = req.query; + const u = uid(req); + let q = 'SELECT * FROM orders WHERE user_id=?'; + const p = [u]; + if (from) { q += ' AND DATE(created_at)>=?'; p.push(from); } + if (to) { q += ' AND DATE(created_at)<=?'; p.push(to); } + q += ' ORDER BY created_at DESC'; + const orders = db.prepare(q).all(...p); + res.json(orders.map(o => ({ + id: o.id, name: o.name, status: o.status, + bezahlt: o.bezahlt, abgeholt: o.abgeholt, + created_at: o.created_at, bezahlt_am: o.bezahlt_am, + revenue: round2(getOrderRevenue(o)), + base_cost: round2(getOrderBaseCost(o.id)), + }))); +}); + // Debug: rohe Auftragsdaten mit Revenue router.get('/debug', authenticate, (req, res) => { const u = uid(req); diff --git a/frontend/src/tools/statistik.jsx b/frontend/src/tools/statistik.jsx index 693c172..2e80fb6 100644 --- a/frontend/src/tools/statistik.jsx +++ b/frontend/src/tools/statistik.jsx @@ -96,12 +96,64 @@ function KPI({ label, value, color, sub }) { ); } +// Bestellungen diesen Monat – eigene Komponente mit eigenem Fetch +function MonthOrders({ currentMonth }) { + const [orders, setOrders] = useState(null); + + useEffect(() => { + const from = currentMonth + '-01'; + const last = new Date(currentMonth.split('-')[0], currentMonth.split('-')[1], 0).getDate(); + const to = currentMonth + '-' + String(last).padStart(2,'0'); + api(`/tools/statistik/month-orders?from=${from}&to=${to}`).then(setOrders).catch(()=>setOrders([])); + }, [currentMonth]); + + const statusColor = { warteliste:'#ffe66d', in_arbeit:'#4ecdc4', fertig:'#6bcb77' }; + + return ( +
+
+ BESTELLUNGEN DIESEN MONAT {orders ? `(${orders.length})` : ''} +
+ {!orders + ?
Lädt…
+ : !orders.length + ?
Keine Bestellungen diesen Monat.
+ : orders.map(o => { + const st = !!o.bezahlt && !!o.abgeholt ? 'abgeschlossen' : !!o.bezahlt ? 'bezahlt' : o.status; + const col = st==='abgeschlossen'?'#4ade80': st==='bezahlt'?'#c084fc': statusColor[st]||'#888'; + return ( +
+ + {new Date(o.created_at).toLocaleDateString('de-DE')} + + + {o.name} + + + {st} + + 0?'#4ade80':'rgba(255,255,255,0.3)', + fontFamily:"'Space Mono',monospace",fontSize:12,fontWeight:700,flexShrink:0}}> + {fmt(o.revenue||0)} + +
+ ); + }) + } +
+ ); +} + export default function Statistik({ mobile }) { const [period, setPeriod] = useState('month'); const [custom, setCustom] = useState({ type:'month', month: new Date().toISOString().slice(0,7), year: new Date().getFullYear() }); const [data, setData] = useState(null); const [loading,setLoading] = useState(false); const [tab, setTab] = useState('overview'); + // Bestellungen-Tab: immer aktueller Monat + const currentMonth = new Date().toISOString().slice(0,7); const [expForm,setExpForm] = useState({ date: new Date().toISOString().slice(0,10), category:'Filament', description:'', amount:'' }); const [saving, setSaving] = useState(false); @@ -188,7 +240,7 @@ export default function Statistik({ mobile }) { {/* ── Tabs ── */}
- {[['overview','📈 Übersicht'],['expenses','💸 Ausgaben'],['orders','📦 Bestellungen']].map(([id,label])=>( + {[['overview','📈 Übersicht'],['revenue','💰 Einnahmen'],['expenses','💸 Ausgaben'],['orders','📦 Bestellungen']].map(([id,label])=>(