fix: revenue aus order_items korrekt berechnet, custom Zeitraum (Monat/Jahr)
This commit is contained in:
@@ -25,19 +25,51 @@ const fmt = v => `${(v||0).toFixed(2)} €`;
|
||||
const fmtMonth = m => { if (!m) return ''; const [y,mo] = m.split('-'); return ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'][parseInt(mo)-1]+' '+y.slice(2); };
|
||||
|
||||
const PERIODS = [
|
||||
{ id:'week', label:'Woche' },
|
||||
{ id:'month', label:'Monat' },
|
||||
{ id:'year', label:'Jahr' },
|
||||
{ id:'all', label:'Gesamt'},
|
||||
{ id:'week', label:'Diese Woche' },
|
||||
{ id:'month', label:'Dieser Monat' },
|
||||
{ id:'year', label:'Dieses Jahr' },
|
||||
{ id:'custom', label:'Eigener Zeitraum' },
|
||||
{ id:'all', label:'Gesamt'},
|
||||
];
|
||||
|
||||
function periodRange(id) {
|
||||
// Generiere Monat-Optionen (letzten 24 Monate)
|
||||
function getMonthOptions() {
|
||||
const opts = [];
|
||||
const now = new Date();
|
||||
for (let i = 0; i < 24; i++) {
|
||||
const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
||||
const val = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;
|
||||
const label = d.toLocaleDateString('de-DE', { month:'long', year:'numeric' });
|
||||
opts.push({ val, label });
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
// Generiere Jahr-Optionen
|
||||
function getYearOptions() {
|
||||
const opts = [];
|
||||
const now = new Date().getFullYear();
|
||||
for (let y = now; y >= now - 5; y--) opts.push(y);
|
||||
return opts;
|
||||
}
|
||||
|
||||
function periodRange(id, custom) {
|
||||
const now = new Date();
|
||||
const pad = n => String(n).padStart(2,'0');
|
||||
const iso = d => `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
|
||||
if (id==='week') { const d=new Date(now); d.setDate(d.getDate()-6); return { from:iso(d), to:iso(now) }; }
|
||||
if (id==='month') { const d=new Date(now.getFullYear(),now.getMonth(),1); return { from:iso(d), to:iso(now) }; }
|
||||
if (id==='year') { return { from:`${now.getFullYear()}-01-01`, to:iso(now) }; }
|
||||
if (id==='custom' && custom) {
|
||||
if (custom.type==='month' && custom.month) {
|
||||
const [y,m] = custom.month.split('-').map(Number);
|
||||
const last = new Date(y, m, 0);
|
||||
return { from:`${y}-${pad(m)}-01`, to:iso(last) };
|
||||
}
|
||||
if (custom.type==='year' && custom.year) {
|
||||
return { from:`${custom.year}-01-01`, to:`${custom.year}-12-31` };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -53,21 +85,22 @@ function KPI({ label, value, color, sub }) {
|
||||
|
||||
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'); // overview | expenses | orders
|
||||
const [tab, setTab] = useState('overview');
|
||||
const [expForm,setExpForm] = useState({ date: new Date().toISOString().slice(0,10), category:'Filament', description:'', amount:'' });
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const load = useCallback(() => {
|
||||
setLoading(true);
|
||||
const { from, to } = periodRange(period);
|
||||
const { from, to } = periodRange(period, custom);
|
||||
const q = from ? `?from=${from}&to=${to}` : '';
|
||||
api('/tools/statistik/overview'+q)
|
||||
.then(d => setData(d))
|
||||
.catch(()=>{})
|
||||
.finally(()=>setLoading(false));
|
||||
}, [period]);
|
||||
}, [period, custom]);
|
||||
|
||||
useEffect(() => { load(); }, [load]);
|
||||
|
||||
@@ -97,8 +130,8 @@ export default function Statistik({ mobile }) {
|
||||
📊 3D-Druck Statistik
|
||||
</h2>
|
||||
{/* Zeitraum */}
|
||||
<div style={{display:'flex',gap:4}}>
|
||||
{PERIODS.map(p => (
|
||||
<div style={{display:'flex',gap:4,flexWrap:'wrap',alignItems:'center'}}>
|
||||
{PERIODS.filter(p=>p.id!=='custom').map(p => (
|
||||
<button key={p.id} onClick={()=>setPeriod(p.id)} style={{
|
||||
background: period===p.id ? 'rgba(78,205,196,0.15)' : 'rgba(255,255,255,0.04)',
|
||||
border: `1px solid ${period===p.id ? 'rgba(78,205,196,0.5)' : 'rgba(255,255,255,0.1)'}`,
|
||||
@@ -107,6 +140,36 @@ export default function Statistik({ mobile }) {
|
||||
fontFamily:'monospace', fontSize:11, cursor:'pointer',
|
||||
}}>{p.label}</button>
|
||||
))}
|
||||
{/* Eigener Zeitraum */}
|
||||
<div style={{display:'flex',gap:4,alignItems:'center'}}>
|
||||
<select value={custom.type} onChange={e=>setCustom(p=>({...p,type:e.target.value}))}
|
||||
onClick={()=>setPeriod('custom')}
|
||||
style={{background:'rgba(255,255,255,0.06)',border:'1px solid rgba(255,255,255,0.12)',
|
||||
borderRadius:8,padding:'5px 8px',color:'rgba(255,255,255,0.6)',fontFamily:'monospace',fontSize:11,cursor:'pointer'}}>
|
||||
<option value="month">Monat</option>
|
||||
<option value="year">Jahr</option>
|
||||
</select>
|
||||
{custom.type==='month' && (
|
||||
<select value={custom.month}
|
||||
onChange={e=>{setCustom(p=>({...p,month:e.target.value}));setPeriod('custom');}}
|
||||
style={{background: period==='custom'?'rgba(78,205,196,0.1)':'rgba(255,255,255,0.06)',
|
||||
border:`1px solid ${period==='custom'?'rgba(78,205,196,0.4)':'rgba(255,255,255,0.12)'}`,
|
||||
borderRadius:8,padding:'5px 8px',color:period==='custom'?'#4ecdc4':'rgba(255,255,255,0.6)',
|
||||
fontFamily:'monospace',fontSize:11,cursor:'pointer'}}>
|
||||
{getMonthOptions().map(o=><option key={o.val} value={o.val}>{o.label}</option>)}
|
||||
</select>
|
||||
)}
|
||||
{custom.type==='year' && (
|
||||
<select value={custom.year}
|
||||
onChange={e=>{setCustom(p=>({...p,year:Number(e.target.value)}));setPeriod('custom');}}
|
||||
style={{background: period==='custom'?'rgba(78,205,196,0.1)':'rgba(255,255,255,0.06)',
|
||||
border:`1px solid ${period==='custom'?'rgba(78,205,196,0.4)':'rgba(255,255,255,0.12)'}`,
|
||||
borderRadius:8,padding:'5px 8px',color:period==='custom'?'#4ecdc4':'rgba(255,255,255,0.6)',
|
||||
fontFamily:'monospace',fontSize:11,cursor:'pointer'}}>
|
||||
{getYearOptions().map(y=><option key={y} value={y}>{y}</option>)}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user