Postkarten-Maße-Fix, Sammelversand-Staffel, Extern-Format, Mustertexte

- render_formats: Upsert statt nur-anlegen → korrekte A6-Maße werden beim
  Bootstrap erzwungen (behebt „Postkarten passen nicht, weder quer noch hoch")
- Sammelversand-Staffel: bis 5 Schriftstücke 1,80 € (neues Preisitem
  sammelversand_klein), ab 6 der reguläre Satz – Backend + App-Option
- Extern-Modus: Postkarten-Format (hoch/quer) wieder wählbar; optionaler
  ?format=-Parameter; kein Auto-Vorbelegen bei mehreren Formaten
- Unbegrenzte Mustertexte: neue Collection muster_texts (Dropdown im
  „Manueller Druck", Du/Sie als Fallback + Seeds)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Lucas Orth
2026-08-04 07:49:22 +02:00
parent 26040bd68f
commit 0b2f2ba50a
4 changed files with 60 additions and 19 deletions

View File

@@ -499,14 +499,14 @@ export default function Configurator({ vorwahlProdukt }: { vorwahlProdukt?: stri
}
}, [vorwahlProdukt, product, products]);
// Genau ein Format → still setzen (kein Format-Schritt nötig). Im Extern-Modus
// gibt es keinen Format-Schritt → dort immer das erste Format vorbelegen.
// Genau ein Format → still setzen (kein Format-Schritt nötig). Bei mehreren
// Formaten (z. B. Postkarte hoch/quer) NICHT automatisch vorbelegen – der Kunde
// wählt im Format-Schritt (auch im Extern-Modus).
useEffect(() => {
if (!produktFormate.length) return;
if ((produktFormate.length === 1 || (externModus && !format)) && format !== produktFormate[0].key) {
if (produktFormate.length === 1 && format !== produktFormate[0].key) {
setFormat(produktFormate[0].key);
}
}, [produktFormate, format, externModus]);
}, [produktFormate, format]);
const nurUpload = quantity > TABELLEN_GRENZE;
@@ -568,6 +568,7 @@ export default function Configurator({ vorwahlProdukt }: { vorwahlProdukt?: stri
const v = searchParams.get('versand'); if (v === 'einzeln' || v === 'sammel') setShippingType(v);
const k = searchParams.get('kuvert'); if (k === '1' || k === '0') setNeedsEnvelope(k === '1');
const b = searchParams.get('beschriftung'); if (b === 'empfaenger' || b === 'freitext' || b === 'keine') setLabeling(b);
const fmt = searchParams.get('format'); if (fmt) setFormat(fmt);
const m = parseInt(searchParams.get('menge') || '', 10);
if (Number.isFinite(m) && m > 0) { setQuantity(m); setQuantityChosen(true); setMengeText(String(m)); }
// Nur einmal beim Betreten des Extern-Modus anwenden.
@@ -601,6 +602,9 @@ export default function Configurator({ vorwahlProdukt }: { vorwahlProdukt?: stri
// legt der Betreiber über die Direktlink-Parameter fest (s. Extern-Preset).
// Keine Preise, keine Rechnungsdaten, keine Zahlung.
? [
// Format wählbar, wenn es eine echte Wahl gibt (z. B. Postkarte hoch/quer)
// und nicht per ?format=… vorgegeben wurde.
...(brauchtFormatSchritt && !searchParams.get('format') ? ['Format'] : []),
...(empfaengerSchritt ? [empfaengerSchritt] : []),
'Text',
'Rechnungsdaten',
@@ -808,7 +812,7 @@ export default function Configurator({ vorwahlProdukt }: { vorwahlProdukt?: stri
if (!it || !it.price) return null;
// Nur Sammelversand wird für alle identisch (netto) angezeigt. Einzelversand
// (Porto) und alles andere: Privat=brutto, Unternehmen=netto.
const ohneMwStAnzeige = key === 'sammelversand';
const ohneMwStAnzeige = key === 'sammelversand' || key === 'sammelversand_klein';
const betrag = ohneMwStAnzeige || personType === 'unternehmen'
? it.price
: it.price * (1 + mwst / 100);
@@ -1235,7 +1239,7 @@ export default function Configurator({ vorwahlProdukt }: { vorwahlProdukt?: stri
preis={aufpreis('porto_inland')} />
<Option name="ship" selected={shippingType === 'sammel'} onSelect={() => waehleVersand('sammel')}
title="Sammelversand an Sie" desc="Sie erhalten alles zur eigenen Verteilung"
preis={aufpreis('sammelversand')} />
preis={aufpreis(quantity <= 5 ? 'sammelversand_klein' : 'sammelversand')} />
</div>
{shippingType === 'einzeln' && (
<p className="sk-note" style={{ marginTop: 'var(--sk-space-3)' }}>