diff --git a/frontend/src/tools/Kalkulator3D.jsx b/frontend/src/tools/Kalkulator3D.jsx
new file mode 100644
index 0000000..c990896
--- /dev/null
+++ b/frontend/src/tools/Kalkulator3D.jsx
@@ -0,0 +1,237 @@
+import { useState, useMemo, useEffect, useRef } from 'react';
+import { api, S } from '../lib.js';
+
+// ── Konstanten ────────────────────────────────────────────────────────────────
+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,
+};
+const FARB = { 1: 1.0, 2: 1.1, 3: 1.2, 4: 1.3 };
+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' },
+];
+
+// ── Hilfselemente ─────────────────────────────────────────────────────────────
+function NInput({ label, value, onChange, step = 0.01, unit }) {
+ return (
+
+
+ onChange(parseFloat(e.target.value) || 0)} style={S.inp}
+ onFocus={e => e.target.style.borderColor = 'rgba(78,205,196,0.5)'}
+ onBlur={e => e.target.style.borderColor = 'rgba(255,255,255,0.1)'}
+ />
+
+ );
+}
+
+// ── Kalkulator ────────────────────────────────────────────────────────────────
+export default function Kalkulator3D({ toast, editData, onEditDone, onNavigate }) {
+ 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 = FARB[farben] || 1;
+ 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); }
+ };
+
+ return (
+
+ {/* Header */}
+
+
+ {editData &&
✎ Bearbeite: {editData.name}
}
+
3D-Druck Kalkulator
+
Echtzeit-Kalkulation · 3 Preisstufen
+
+ {editData &&
}
+
+
+ {/* Eingaben */}
+
+
EINGABEN
+
+
+
+
+
+
+
+
+
+ {/* 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} />
+
+
+
+ )}
+
+
+ {/* Kostenaufschlüsselung */}
+
+ {[['Material',calc.mat,'#a78bfa'],['Strom',calc.str,'#60a5fa'],['Verschleiß',calc.ver,'#f59e0b'],['Grundkosten',calc.grund,'#4ecdc4',true]].map(([l,v,c,bold],i)=>(
+
0?'1px solid rgba(255,255,255,0.05)':'none' }}>
+
{l}
+
{v.toFixed(4)} €
+
+ ))}
+
+
+ {/* Preis-Cards */}
+
+ {STUFEN.map((s,i) => (
+
+
{s.emoji}
+
{s.label}
+
{(calc.grund*s.mult).toFixed(2)} €
+
×{s.mult}
+
+ ))}
+
+
+ {/* Speichern */}
+
+
BERECHNUNG SPEICHERN
+
+
+ setName(e.target.value)}
+ onKeyDown={e=>e.key==='Enter'&&save()}
+ placeholder='z.B. "Halterung Kunde Müller"' style={S.inp} />
+
+
+
+
+
+ );
+}
+
+// ── Gespeicherte Berechnungen ─────────────────────────────────────────────────
+export function SavedList({ toast, onNavigate }) {
+ 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 (
+
+
Gespeicherte Berechnungen
+
{items.length} Einträge
+ {loading &&
Lädt…
}
+ {!loading && items.length===0 && (
+
+
◉
+
Noch keine Berechnungen gespeichert.
+
+ )}
+ {items.map(item => (
+
+
+
{item.name}
+
+ {item.gramm}g · {item.stunden}h · {item.farben} Farbe{item.farben>1?'n':''} · {new Date(item.created_at).toLocaleDateString('de-DE')}
+
+
+
+ {[['💖',item.preis_freundschaft,'#ff6b9d'],['🤝',item.preis_normal,'#4ecdc4'],['💼',item.preis_auftrag,'#ffe66d']].map(([e,v,c])=>(
+
+
{e}
+
{v.toFixed(2)}€
+
+ ))}
+
+
+
+ {delId===item.id ? (
+
+ Sicher?
+
+
+
+ ) : (
+
+ )}
+
+
+ ))}
+
+ );
+}