import { useState, useMemo, useEffect } from 'react'; import { api, S } from '../lib.js'; const DEFAULTS = { materialpreis_pro_gramm: 0.01, stromverbrauch_kw: 0.15, strompreis_pro_kwh: 0.38, druckerpreis: 550, gesamtdruckstunden: 5000, verschleiss_pro_stunde: 0.06, }; // Farbfaktor: jede weitere Farbe +0.1 const getFarbFaktor = n => Math.round((1.0 + Math.max(0, n-1) * 0.1) * 100) / 100; const STUFEN = [ { key:'f', emoji:'💖', label:'Freundschaft', mult:1.0, c:'#ff6b9d' }, { key:'n', emoji:'🤝', label:'Normal', mult:1.5, c:'#4ecdc4' }, { key:'a', emoji:'💼', label:'Auftrag', mult:2.5, c:'#ffe66d' }, ]; // Stepper-Eingabe – kein Tippen nötig, gut für Mobile function Stepper({ label, value, onChange, step, unit, min=0, hint }) { const dec = () => onChange(Math.max(min, Math.round((value - step) * 10000) / 10000)); const inc = () => onChange(Math.round((value + step) * 10000) / 10000); return (
{label.toUpperCase()}{unit ? ` (${unit})` : ''}
onChange(parseFloat(e.target.value) || 0)} style={{ flex:1, background:'transparent', border:'none', color:'#fff', fontSize:15, fontFamily:"'Space Mono',monospace", textAlign:'center', outline:'none', minWidth:0, padding:'10px 4px' }} />
{hint &&
{hint}
}
); } function SmallInput({ label, value, onChange, step=0.01 }) { return (
{label}
onChange(parseFloat(e.target.value)||0)} style={{ ...S.inp, padding:'7px 8px', fontSize:13 }} />
); } export default function Kalkulator3D({ toast, editData, onEditDone, mobile }) { const [gramm, setGramm] = useState(editData?.gramm ?? 50); const [stunden, setStunden] = useState(editData?.stunden ?? 3); const [farben, setFarben] = useState(editData?.farben ?? 1); const [vars, setVars] = useState(editData ? { materialpreis_pro_gramm: editData.materialpreis_pro_gramm, stromverbrauch_kw: editData.stromverbrauch_kw, strompreis_pro_kwh: editData.strompreis_pro_kwh, druckerpreis: editData.druckerpreis, gesamtdruckstunden: editData.gesamtdruckstunden, verschleiss_pro_stunde: editData.verschleiss_pro_stunde, } : { ...DEFAULTS }); const [showAdv, setShowAdv] = useState(false); const [name, setName] = useState(editData?.name ?? ''); const [saving, setSaving] = useState(false); const sv = (k,v) => setVars(p => ({ ...p, [k]:v })); const calc = useMemo(() => { const f = getFarbFaktor(farben); const mat = gramm * vars.materialpreis_pro_gramm * f; const str = stunden * vars.stromverbrauch_kw * vars.strompreis_pro_kwh; const ver = stunden * vars.verschleiss_pro_stunde; return { mat, str, ver, grund: mat + str + ver }; }, [gramm, stunden, farben, vars]); const save = async () => { if (!name.trim()) { toast('Bitte einen Namen eingeben','error'); return; } setSaving(true); try { const payload = { name, gramm, stunden, farben, ...vars, preis_freundschaft: calc.grund * 1.0, preis_normal: calc.grund * 1.5, preis_auftrag: calc.grund * 2.5 }; if (editData?.id) { await api(`/tools/kalkulator3d/${editData.id}`, { method:'PUT', body:payload }); toast('Aktualisiert ✓'); onEditDone?.(); } else { await api('/tools/kalkulator3d', { body:payload }); toast('Gespeichert ✓'); setName(''); } } catch(e) { toast(e.message,'error'); } finally { setSaving(false); } }; const pad = mobile ? '14px 14px 90px' : '36px 44px'; return (
{/* Header */}
{editData &&
✎ {editData.name}
}

3D-Kalkulator

{editData && }
{/* Haupteingaben */}
setFarben(Math.max(1, Math.round(v)))} step={1} min={1} hint={`Farbfaktor: ×${getFarbFaktor(farben).toFixed(2)}`} /> {/* Erweiterte Einstellungen */} {showAdv && (
sv('materialpreis_pro_gramm',v)} step={0.001}/> sv('stromverbrauch_kw',v)} step={0.01}/> sv('strompreis_pro_kwh',v)} step={0.01}/> sv('druckerpreis',v)} step={10}/> sv('gesamtdruckstunden',v)} step={100}/> sv('verschleiss_pro_stunde',v)} step={0.01}/>
)}
{/* Ergebnis – kompakt */}
{[['Material',calc.mat,'#a78bfa'],['Strom',calc.str,'#60a5fa'],['Verschleiß',calc.ver,'#f59e0b'],['Grundkosten',calc.grund,'#4ecdc4']].map(([l,v,c],i)=>(
{l.toUpperCase()}
{v.toFixed(2)} €
))}
{/* Preis-Cards */}
{STUFEN.map((s,i) => (
{s.emoji}
{s.label}
{(calc.grund*s.mult).toFixed(2)}€
×{s.mult}
))}
{/* Speichern */}
setName(e.target.value)} onKeyDown={e=>e.key==='Enter'&&save()} placeholder='Name z.B. "Halterung Müller"' style={{ ...S.inp, flex:1, fontSize:15 }} />
); } // ── Gespeicherte Berechnungen ───────────────────────────────────────────────── export function SavedList({ toast, mobile }) { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [delId, setDelId] = useState(null); const [editData, setEditData] = useState(null); useEffect(() => { api('/tools/kalkulator3d').then(setItems).catch(e=>toast(e.message,'error')).finally(()=>setLoading(false)); }, []); const del = async id => { try { await api(`/tools/kalkulator3d/${id}`,{method:'DELETE'}); setItems(p=>p.filter(i=>i.id!==id)); toast('Gelöscht'); } catch(e) { toast(e.message,'error'); } finally { setDelId(null); } }; if (editData) return ( { setEditData(null); api('/tools/kalkulator3d').then(setItems).catch(()=>{}); }} /> ); return (

Gespeichert

{items.length} Einträge

{loading &&
Lädt…
} {!loading && items.length===0 && (
Noch keine Berechnungen.
)} {items.map(item => (
{/* Name + Datum */}
{item.name}
{item.gramm}g · {item.stunden}h · {item.farben} Farbe{item.farben>1?'n':''}
{new Date(item.created_at).toLocaleDateString('de-DE')}
{/* Preise */}
{[['💖',item.preis_freundschaft,'#ff6b9d'],['🤝',item.preis_normal,'#4ecdc4'],['💼',item.preis_auftrag,'#ffe66d']].map(([e,v,c])=>(
{e}
{v.toFixed(2)}€
))}
{/* Aktionen */} {delId===item.id ? (
Wirklich löschen?
) : (
)}
))}
); }