3D-Modelle Ordner + Dateianhang im Kalkulator

This commit is contained in:
2026-05-28 12:45:19 +02:00
parent ab752d7c93
commit da1a1e9641
3 changed files with 121 additions and 2 deletions

View File

@@ -2,6 +2,8 @@ import { useConfirm } from '../confirm.jsx';
import { useState, useMemo, useEffect, useRef } from 'react';
import { api, S } from '../lib.js';
const MODEL_EXTENSIONS = ['.stl','.3mf','.f3d','.step','.stp','.obj','.iges','.igs','.fcstd','.amf','.scad'];
const DEFAULTS = {
materialpreis_pro_rolle: 10, // €/Rolle (1 kg = 1000 g → 0.01 €/g)
stromverbrauch_kw: 0.15,
@@ -138,7 +140,9 @@ export default function Kalkulator3D({ toast, editData, onEditDone, mobile, setA
const [bemerkung, setBemerkung] = useState(editData?.bemerkung ?? '');
const [image, setImage] = useState(editData?.image ?? null);
const [saving, setSaving] = useState(false);
const fileRef = useRef(null);
const [modelFiles, setModelFiles] = useState([]); // Array von File-Objekten
const fileRef = useRef(null);
const modelFileRef = useRef(null);
const sv = (k, v) => setVars(p => ({ ...p, [k]: v }));
const calc = useMemo(() => {
@@ -177,12 +181,36 @@ export default function Kalkulator3D({ toast, editData, onEditDone, mobile, setA
preis_normal: calc.grund * 1.5,
preis_auftrag: calc.grund * 2.5,
image, bemerkung };
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(''); setBemerkung(''); setImage(null);
// 3D-Dateien hochladen wenn vorhanden
if (modelFiles.length > 0) {
try {
// 1. "3D-Modelle" Basisordner sicherstellen
const base = await api('/tools/dateien/ensure-folder', { body: { name: '3D-Modelle' } });
// 2. Unterordner mit Modellnamen anlegen
const sub = await api('/tools/dateien/ensure-folder', { body: { name: name.trim(), parent_id: base.id } });
// 3. Dateien hochladen
const form = new FormData();
form.append('folder_id', sub.id);
for (const f of modelFiles) form.append('files', f);
await fetch('/api/tools/dateien/upload-3d', {
method: 'POST',
headers: { Authorization: `Bearer ${localStorage.getItem('sk_token')}` },
body: form,
});
toast(`Gespeichert ✓ · ${modelFiles.length} Datei(en) in 3D-Modelle/${name.trim()} abgelegt`);
} catch {
toast('Gespeichert ✓ (Datei-Upload fehlgeschlagen)', 'error');
}
} else {
toast('Gespeichert ✓');
}
setName(''); setBemerkung(''); setImage(null); setModelFiles([]);
setActive?.('kalkulator3d-saved');
}
} catch(e) { toast(e.message, 'error'); } finally { setSaving(false); }
@@ -340,6 +368,50 @@ export default function Kalkulator3D({ toast, editData, onEditDone, mobile, setA
)}
</div>
{/* 3D-Modelldateien nur beim Neuanlegen */}
{!editData?.id && (
<div style={{ marginBottom:14 }}>
<label style={{ ...S.head, display:'block', marginBottom:6 }}>
3D-DATEIEN (optional) werden in 3D-Modelle/{name.trim()||'…'} abgelegt
</label>
<input ref={modelFileRef} type="file" multiple
accept={MODEL_EXTENSIONS.join(',')}
onChange={e => setModelFiles(Array.from(e.target.files||[]))}
style={{ display:'none' }} />
{modelFiles.length > 0 ? (
<div style={{ background:'rgba(255,255,255,0.03)', border:'1px solid rgba(255,255,255,0.1)',
borderRadius:8, padding:10 }}>
{modelFiles.map((f,i) => (
<div key={i} style={{ display:'flex', alignItems:'center', justifyContent:'space-between',
padding:'4px 0', borderBottom: i<modelFiles.length-1 ? '1px solid rgba(255,255,255,0.05)' : 'none' }}>
<span style={{ color:'rgba(255,255,255,0.7)', fontFamily:'monospace', fontSize:11,
overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', maxWidth:'80%' }}>
🖨 {f.name}
</span>
<span style={{ color:'rgba(255,255,255,0.3)', fontFamily:'monospace', fontSize:9, flexShrink:0 }}>
{(f.size/1024/1024).toFixed(1)} MB
</span>
</div>
))}
<div style={{ display:'flex', gap:6, marginTop:8 }}>
<button onClick={() => modelFileRef.current?.click()}
style={{ ...S.btn('#4ecdc4', true), flex:1, textAlign:'center', fontSize:11 }}>
+ Weitere hinzufügen
</button>
<button onClick={() => setModelFiles([])}
style={{ ...S.btn('#ff6b9d', true), fontSize:11 }}></button>
</div>
</div>
) : (
<button onClick={() => modelFileRef.current?.click()} style={{
width:'100%', padding:'16px', background:'rgba(255,255,255,0.03)',
border:'1px dashed rgba(255,255,255,0.15)', borderRadius:8,
color:'rgba(255,255,255,0.6)', cursor:'pointer', fontFamily:'monospace', fontSize:12,
}}>🖨 3D-Dateien anhängen (STL, 3MF, F3D, STEP )</button>
)}
</div>
)}
<button onClick={save} disabled={saving} style={{
width:'100%', background:'linear-gradient(135deg,#4ecdc4,#45b7d1)', border:'none',
borderRadius:8, padding:'12px 0', color:'#0d0d0f',