3D-Dateien im Archiv bearbeitbar - hinzufügen und löschen
This commit is contained in:
@@ -140,11 +140,27 @@ export default function Kalkulator3D({ toast, editData, onEditDone, mobile, setA
|
|||||||
const [bemerkung, setBemerkung] = useState(editData?.bemerkung ?? '');
|
const [bemerkung, setBemerkung] = useState(editData?.bemerkung ?? '');
|
||||||
const [image, setImage] = useState(editData?.image ?? null);
|
const [image, setImage] = useState(editData?.image ?? null);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [modelFiles, setModelFiles] = useState([]); // Array von File-Objekten
|
const [modelFiles, setModelFiles] = useState([]); // neue Dateien zum Hochladen
|
||||||
const fileRef = useRef(null);
|
const [existingFiles, setExistingFiles] = useState([]); // bereits gespeicherte Dateien
|
||||||
|
const [modelFolderId, setModelFolderId] = useState(null);
|
||||||
|
const fileRef = useRef(null);
|
||||||
const modelFileRef = useRef(null);
|
const modelFileRef = useRef(null);
|
||||||
const sv = (k, v) => setVars(p => ({ ...p, [k]: v }));
|
const sv = (k, v) => setVars(p => ({ ...p, [k]: v }));
|
||||||
|
|
||||||
|
// Im Bearbeitungsmodus: existierende Dateien laden
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editData?.id || !editData?.name) return;
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const base = await api('/tools/dateien/ensure-folder', { body: { name: '3D-Modelle' } });
|
||||||
|
const sub = await api('/tools/dateien/ensure-folder', { body: { name: editData.name.trim(), parent_id: base.id } });
|
||||||
|
setModelFolderId(sub.id);
|
||||||
|
const data = await api(`/tools/dateien?folder_id=${sub.id}`);
|
||||||
|
setExistingFiles(data.own || []);
|
||||||
|
} catch {}
|
||||||
|
})();
|
||||||
|
}, [editData?.id]);
|
||||||
|
|
||||||
const calc = useMemo(() => {
|
const calc = useMemo(() => {
|
||||||
const f = getFarbFaktor(farben);
|
const f = getFarbFaktor(farben);
|
||||||
const matProGramm = vars.materialpreis_pro_rolle / 1000;
|
const matProGramm = vars.materialpreis_pro_rolle / 1000;
|
||||||
@@ -184,6 +200,21 @@ export default function Kalkulator3D({ toast, editData, onEditDone, mobile, setA
|
|||||||
|
|
||||||
if (editData?.id) {
|
if (editData?.id) {
|
||||||
await api(`/tools/kalkulator3d/${editData.id}`, { method:'PUT', body:payload });
|
await api(`/tools/kalkulator3d/${editData.id}`, { method:'PUT', body:payload });
|
||||||
|
// Neue Dateien hochladen falls vorhanden
|
||||||
|
if (modelFiles.length > 0 && modelFolderId) {
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('folder_id', modelFolderId);
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
setModelFiles([]);
|
||||||
|
// Dateien neu laden
|
||||||
|
const data = await api(`/tools/dateien?folder_id=${modelFolderId}`);
|
||||||
|
setExistingFiles(data.own || []);
|
||||||
|
}
|
||||||
toast('Aktualisiert ✓'); onEditDone?.();
|
toast('Aktualisiert ✓'); onEditDone?.();
|
||||||
} else {
|
} else {
|
||||||
await api('/tools/kalkulator3d', { body:payload });
|
await api('/tools/kalkulator3d', { body:payload });
|
||||||
@@ -368,7 +399,7 @@ export default function Kalkulator3D({ toast, editData, onEditDone, mobile, setA
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 3D-Modelldateien – nur beim Neuanlegen */}
|
{/* 3D-Modelldateien – beim Neuanlegen */}
|
||||||
{!editData?.id && (
|
{!editData?.id && (
|
||||||
<div style={{ marginBottom:14 }}>
|
<div style={{ marginBottom:14 }}>
|
||||||
<label style={{ ...S.head, display:'block', marginBottom:6 }}>
|
<label style={{ ...S.head, display:'block', marginBottom:6 }}>
|
||||||
@@ -412,6 +443,77 @@ export default function Kalkulator3D({ toast, editData, onEditDone, mobile, setA
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 3D-Modelldateien – beim Bearbeiten */}
|
||||||
|
{editData?.id && (
|
||||||
|
<div style={{ marginBottom:14 }}>
|
||||||
|
<label style={{ ...S.head, display:'block', marginBottom:6 }}>
|
||||||
|
3D-DATEIEN – 3D-Modelle/{editData.name}
|
||||||
|
</label>
|
||||||
|
<input ref={modelFileRef} type="file" multiple
|
||||||
|
accept={MODEL_EXTENSIONS.join(',')}
|
||||||
|
onChange={e => setModelFiles(Array.from(e.target.files||[]))}
|
||||||
|
style={{ display:'none' }} />
|
||||||
|
|
||||||
|
{/* Existierende Dateien */}
|
||||||
|
{existingFiles.length > 0 && (
|
||||||
|
<div style={{ background:'rgba(255,255,255,0.03)', border:'1px solid rgba(255,255,255,0.08)',
|
||||||
|
borderRadius:8, padding:10, marginBottom:8 }}>
|
||||||
|
{existingFiles.map((f,i) => (
|
||||||
|
<div key={f.id} style={{ display:'flex', alignItems:'center', gap:8,
|
||||||
|
padding:'5px 0', borderBottom: i<existingFiles.length-1 ? '1px solid rgba(255,255,255,0.05)' : 'none' }}>
|
||||||
|
<span style={{ fontSize:16, flexShrink:0 }}>🖨</span>
|
||||||
|
<span style={{ color:'rgba(255,255,255,0.7)', fontFamily:'monospace', fontSize:11,
|
||||||
|
flex:1, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
|
||||||
|
{f.originalname}
|
||||||
|
</span>
|
||||||
|
<span style={{ color:'rgba(255,255,255,0.25)', fontFamily:'monospace', fontSize:9, flexShrink:0 }}>
|
||||||
|
{(f.size/1024/1024).toFixed(1)} MB
|
||||||
|
</span>
|
||||||
|
<button onClick={async () => {
|
||||||
|
try {
|
||||||
|
await api(`/tools/dateien/${f.id}`, { method:'DELETE' });
|
||||||
|
setExistingFiles(p => p.filter(x => x.id !== f.id));
|
||||||
|
} catch(e) { toast(e.message, 'error'); }
|
||||||
|
}} style={{ background:'transparent', border:'none', cursor:'pointer',
|
||||||
|
color:'rgba(255,107,157,0.5)', fontSize:14, padding:'0 2px', flexShrink:0, lineHeight:1 }}>✕</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Neue Dateien zum Hochladen */}
|
||||||
|
{modelFiles.length > 0 && (
|
||||||
|
<div style={{ background:'rgba(78,205,196,0.04)', border:'1px solid rgba(78,205,196,0.15)',
|
||||||
|
borderRadius:8, padding:10, marginBottom:8 }}>
|
||||||
|
<div style={{ ...S.head, marginBottom:6 }}>NEU HINZUFÜGEN</div>
|
||||||
|
{modelFiles.map((f,i) => (
|
||||||
|
<div key={i} style={{ display:'flex', justifyContent:'space-between',
|
||||||
|
padding:'3px 0', borderBottom: i<modelFiles.length-1 ? '1px solid rgba(255,255,255,0.05)' : 'none' }}>
|
||||||
|
<span style={{ color:'rgba(255,255,255,0.6)', 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>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div style={{ display:'flex', gap:6 }}>
|
||||||
|
<button onClick={() => modelFileRef.current?.click()}
|
||||||
|
style={{ ...S.btn('#4ecdc4', true), flex:1, textAlign:'center', fontSize:11, padding:'9px 0' }}>
|
||||||
|
+ Dateien hinzufügen
|
||||||
|
</button>
|
||||||
|
{modelFiles.length > 0 && (
|
||||||
|
<button onClick={() => setModelFiles([])}
|
||||||
|
style={{ ...S.btn('#ff6b9d', true), fontSize:11 }}>✕ Abbrechen</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<button onClick={save} disabled={saving} style={{
|
<button onClick={save} disabled={saving} style={{
|
||||||
width:'100%', background:'linear-gradient(135deg,#4ecdc4,#45b7d1)', border:'none',
|
width:'100%', background:'linear-gradient(135deg,#4ecdc4,#45b7d1)', border:'none',
|
||||||
borderRadius:8, padding:'12px 0', color:'#0d0d0f',
|
borderRadius:8, padding:'12px 0', color:'#0d0d0f',
|
||||||
|
|||||||
Reference in New Issue
Block a user