Dateien nach "frontend/src" hochladen
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { api, S } from './lib.js';
|
||||
import { TOOLS, getGroupedTools } from './toolRegistry.js';
|
||||
import { useConfirm } from './confirm.jsx';
|
||||
import { HomeIcon, AppsIcon, AdminIcon, MoreIcon, UserIcon, LogOutIcon, ChevronIcon, UpdateIcon } from './icons.jsx';
|
||||
|
||||
const ICONS = ['🔗','🌐','📊','📁','🛠','📧','🗓','💾','🖥','📡','🔒','📖','🎯','⚡','🏠','🔧','📱','🎮','🚀','💡'];
|
||||
@@ -330,6 +331,7 @@ function BottomNav({ active, setActive, user, onLogout, updateInfo, onUpdateClic
|
||||
|
||||
// ── Dashboard Widgets ─────────────────────────────────────────────────────────
|
||||
function QuickLinks({ toast, mobile }) {
|
||||
const { confirm, ConfirmDialog } = useConfirm();
|
||||
const [links, setLinks] = useState([]);
|
||||
const [editMode, setEdit] = useState(false);
|
||||
const [showForm, setForm] = useState(false);
|
||||
@@ -356,12 +358,14 @@ function QuickLinks({ toast, mobile }) {
|
||||
};
|
||||
|
||||
const del = async id => {
|
||||
if (!await confirm('Link wirklich löschen?')) return;
|
||||
try { await api(`/dashboard/links/${id}`, { method:'DELETE' }); setLinks(p => p.filter(l => l.id!==id)); toast('Gelöscht'); }
|
||||
catch(e) { toast(e.message, 'error'); }
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ ...S.card, marginBottom:10 }}>
|
||||
<ConfirmDialog/>
|
||||
<div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:10 }}>
|
||||
<div style={S.head}>SCHNELLZUGRIFF</div>
|
||||
<div style={{ display:'flex', gap:6 }}>
|
||||
@@ -539,6 +543,64 @@ function Notepad({ toast }) {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ── Bestell-Statistik Widget ─────────────────────────────────────────────────
|
||||
function BestellStats() {
|
||||
const [stats, setStats] = useState(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
const load = () => {
|
||||
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'}) => (
|
||||
<div style={{display:'flex',justifyContent:'space-between',alignItems:'center',padding:'7px 0',borderBottom:'1px solid rgba(255,255,255,0.05)'}}>
|
||||
<span style={{color:'rgba(255,255,255,0.45)',fontFamily:'monospace',fontSize:12}}>{label}</span>
|
||||
<span style={{color,fontFamily:"'Space Mono',monospace",fontSize:13,fontWeight:700}}>{val}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{...S.card,marginBottom:10}}>
|
||||
<button onClick={toggle} style={{width:'100%',background:'transparent',border:'none',
|
||||
display:'flex',justifyContent:'space-between',alignItems:'center',cursor:'pointer',padding:0}}>
|
||||
<div style={S.head}>STATISTIK</div>
|
||||
<span style={{color:'rgba(255,255,255,0.3)',fontSize:11,display:'inline-block',
|
||||
transform:open?'rotate(90deg)':'rotate(0)',transition:'transform 0.2s'}}>▶</span>
|
||||
</button>
|
||||
{open && (
|
||||
<div style={{marginTop:12}}>
|
||||
{!stats ? (
|
||||
<div style={{color:'rgba(255,255,255,0.25)',fontFamily:'monospace',fontSize:12}}>Lädt…</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{marginBottom:12}}>
|
||||
<div style={{...S.head,marginBottom:6}}>BESTELLUNGEN</div>
|
||||
<Row label="Gesamt" val={stats.totalOrders}/>
|
||||
<Row label="Diesen Monat" val={stats.ordersThisMonth} color="#4ecdc4"/>
|
||||
<Row label="Warteliste" val={stats.byStatus.warteliste||0} color="#ffe66d"/>
|
||||
<Row label="In Arbeit" val={stats.byStatus.in_arbeit||0} color="#4ecdc4"/>
|
||||
<Row label="Fertig (unbezahlt)" val={stats.byStatus.fertig||0} color="#6bcb77"/>
|
||||
<Row label="Bezahlt" val={stats.bezahltOrders} color="#c084fc"/>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{...S.head,marginBottom:6}}>EINNAHMEN</div>
|
||||
<Row label="Eingenommen (bezahlt)" val={`${stats.totalRevenue.toFixed(2)} €`} color="#6bcb77"/>
|
||||
<Row label="Offen (in Arbeit/Fertig)" val={`${stats.offeneRevenue.toFixed(2)} €`} color="#ffe66d"/>
|
||||
<Row label="Archiv-Einträge" val={stats.totalCalcs} color="rgba(255,255,255,0.5)"/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Dashboard({ toast, mobile }) {
|
||||
return (
|
||||
<div style={{ padding: mobile ? '14px 12px 90px' : '36px 44px', maxWidth:1100 }}>
|
||||
@@ -549,6 +611,7 @@ function Dashboard({ toast, mobile }) {
|
||||
</p>
|
||||
</div>
|
||||
<QuickLinks toast={toast} mobile={mobile} />
|
||||
<BestellStats/>
|
||||
<TodoList toast={toast} />
|
||||
<Notepad toast={toast} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user