From f4aace299c89dfbcbb9b708c4333c9397d00a15d Mon Sep 17 00:00:00 2001 From: Dicken Date: Mon, 25 May 2026 23:54:30 +0200 Subject: [PATCH] Dateien nach "frontend/src/tools" hochladen --- frontend/src/tools/bestellungen.jsx | 418 ++++++++++++++++++++++++++++ 1 file changed, 418 insertions(+) create mode 100644 frontend/src/tools/bestellungen.jsx diff --git a/frontend/src/tools/bestellungen.jsx b/frontend/src/tools/bestellungen.jsx new file mode 100644 index 0000000..1e6ec87 --- /dev/null +++ b/frontend/src/tools/bestellungen.jsx @@ -0,0 +1,418 @@ +import { useState, useEffect, useMemo } from 'react'; +import { api, S } from '../lib.js'; +import { SearchIcon, PlusIcon, XIcon, EditIcon, TrashIcon, CheckIcon, ChevronIcon } from '../icons.jsx'; + +const STATUS = { + warteliste: { label:'Warteliste', color:'#ffe66d', bg:'rgba(255,230,109,0.12)' }, + in_arbeit: { label:'In Arbeit', color:'#4ecdc4', bg:'rgba(78,205,196,0.12)' }, + fertig: { label:'Fertig', color:'#6bcb77', bg:'rgba(107,203,119,0.12)' }, +}; + +const PREISE = [ + { key:'freundschaft', emoji:'💖', label:'Freundschaft', c:'#ff6b9d' }, + { key:'normal', emoji:'🤝', label:'Normal', c:'#4ecdc4' }, + { key:'auftrag', emoji:'💼', label:'Auftrag', c:'#ffe66d' }, +]; + +function StatusBadge({ status, onClick, small }) { + const s = STATUS[status] || STATUS.warteliste; + return ( + + ); +} + +// ── Bestellung Detail / Bearbeiten ──────────────────────────────────────────── +function BestellungDetail({ id, toast, onBack, onSaved }) { + const [order, setOrder] = useState(null); + const [loading, setLoading] = useState(true); + const [archiv, setArchiv] = useState([]); + const [showPicker, setShowPicker] = useState(false); + const [pickerSearch, setPickerSearch] = useState(''); + const [editMode, setEditMode] = useState(!id); // neue Bestellung direkt im Edit-Mode + const [form, setForm] = useState({ name:'', bemerkung:'', custom_price:'', status:'warteliste' }); + + useEffect(() => { + api('/tools/kalkulator3d').then(setArchiv).catch(() => {}); + if (id) { + api(`/tools/bestellungen/${id}`) + .then(o => { setOrder(o); setForm({ name:o.name, bemerkung:o.bemerkung, custom_price:o.custom_price??'', status:o.status }); }) + .catch(e => toast(e.message,'error')) + .finally(() => setLoading(false)); + } else { + setLoading(false); + } + }, [id]); + + const totals = useMemo(() => { + const items = order?.items || []; + return { + freundschaft: items.reduce((s,i) => s + i.preis_freundschaft * i.stueckzahl, 0), + normal: items.reduce((s,i) => s + i.preis_normal * i.stueckzahl, 0), + auftrag: items.reduce((s,i) => s + i.preis_auftrag * i.stueckzahl, 0), + }; + }, [order]); + + const saveOrder = async () => { + if (!form.name.trim()) { toast('Name erforderlich','error'); return; } + try { + const payload = { ...form, custom_price: form.custom_price !== '' ? parseFloat(form.custom_price) : null }; + if (id) { + const updated = await api(`/tools/bestellungen/${id}`, { method:'PUT', body:payload }); + setOrder(updated); setEditMode(false); toast('Gespeichert'); + } else { + const created = await api('/tools/bestellungen', { body:payload }); + toast('Bestellung angelegt'); + onSaved(created.id); + } + } catch(e) { toast(e.message,'error'); } + }; + + const addItem = async calcId => { + try { + const updated = await api(`/tools/bestellungen/${id}/items`, { body:{ calculation_id:calcId, stueckzahl:1 } }); + setOrder(updated); setShowPicker(false); toast('Hinzugefügt'); + } catch(e) { toast(e.message,'error'); } + }; + + const updateQty = async (itemId, qty) => { + if (qty < 1) return; + try { const u = await api(`/tools/bestellungen/${id}/items/${itemId}`, { method:'PUT', body:{ stueckzahl:qty } }); setOrder(u); } + catch(e) { toast(e.message,'error'); } + }; + + const removeItem = async itemId => { + try { const u = await api(`/tools/bestellungen/${id}/items/${itemId}`, { method:'DELETE' }); setOrder(u); toast('Entfernt'); } + catch(e) { toast(e.message,'error'); } + }; + + const cycleStatus = async () => { + const states = ['warteliste','in_arbeit','fertig']; + const next = states[(states.indexOf(order.status)+1) % states.length]; + try { const u = await api(`/tools/bestellungen/${id}`, { method:'PUT', body:{...order, status:next} }); setOrder(u); } + catch(e) { toast(e.message,'error'); } + }; + + const filteredArchiv = pickerSearch + ? archiv.filter(a => a.name.toLowerCase().includes(pickerSearch.toLowerCase())) + : archiv; + + if (loading) return
Lädt…
; + + return ( +
+ {/* Header */} +
+ +

+ {id ? (editMode ? 'Bearbeiten' : order?.name) : 'Neue Bestellung'} +

+ {order && !editMode && } + {order && !editMode && ( + + )} +
+ + {/* Formular */} + {(editMode || !id) && ( +
+
+ + setForm(f=>({...f,name:e.target.value}))} + placeholder="z.B. Max Mustermann" + style={{ ...S.inp, fontSize:15 }} /> +
+
+ +