3 Commits

Author SHA1 Message Date
s4luorth
aa5a0d960f Konfigurator: Freitext-Adresse nur bei Adressmodus 'freitext' senden
All checks were successful
Build & Deploy / deploy (push) Successful in 22s
Bei strukturierter Standardadresse wurde der Umschlag trotzdem mit dem
Freitext beschriftet, wenn im Freitextfeld noch Reste eines frueheren
Moduswechsels standen: das Backend (addressText) bevorzugt free_text.
free_text wird jetzt zentral in mitPlatzhaltern() normalisiert und in
Vorschau, Textpruefung und Bestellung identisch mitgeschickt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-18 12:59:56 +02:00
s4luorth
25547df815 Konfigurator: Platzhalter sicher ersetzen + Anrede kein Standard-Platzhalter
All checks were successful
Build & Deploy / deploy (push) Successful in 20s
- Standard-Platzhalter (Vorname/Nachname/Strasse/PLZ/Ort/Land) werden beim Senden
  je Empfänger explizit aufgelöst (standardWert) und mit den eigenen Platzhaltern
  in entries.placeholders geschrieben – so wird zuverlässig ersetzt (Vorschau +
  Bestellung).
- „Anrede" aus STANDARD_STRUKTUR entfernt (kein Empfängerfeld dafür) → [[Anrede]]
  ist jetzt ein frei befüllbarer eigener Platzhalter statt fälschlich als Standard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-18 12:17:21 +02:00
s4luorth
b5669ee8a3 Motiv-Freigabe: Entwurf eingepasst statt beschnitten (objectFit contain)
All checks were successful
Build & Deploy / deploy (push) Successful in 23s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-18 11:47:49 +02:00
2 changed files with 46 additions and 19 deletions

View File

@@ -44,8 +44,10 @@ const PRODUKT_TEXT: Record<string, string> = {
follow_ups: 'Automatisierte handschriftliche Nachfass-Mailings für mehr Rücklauf.', follow_ups: 'Automatisierte handschriftliche Nachfass-Mailings für mehr Rücklauf.',
}; };
/** Standard-Platzhalter der strukturierten Adresse (werden automatisch befüllt). */ /** Standard-Platzhalter der strukturierten Adresse (werden automatisch befüllt).
const STANDARD_STRUKTUR = ['Anrede', 'Vorname', 'Nachname', 'Strasse', 'PLZ', 'Ort', 'Land']; * Ohne „Anrede" – dafür gibt es kein Empfängerfeld; als [[Anrede]] ist es ein
* frei befüllbarer eigener Platzhalter. */
const STANDARD_STRUKTUR = ['Vorname', 'Nachname', 'Strasse', 'PLZ', 'Ort', 'Land'];
/** Standard-Platzhalter der Freitext-Adresse. */ /** Standard-Platzhalter der Freitext-Adresse. */
const STANDARD_FREITEXT = ['Zeile1', 'Zeile2', 'Zeile3', 'Zeile4', 'Zeile5']; const STANDARD_FREITEXT = ['Zeile1', 'Zeile2', 'Zeile3', 'Zeile4', 'Zeile5'];
@@ -792,6 +794,28 @@ export default function Configurator({ vorwahlProdukt }: { vorwahlProdukt?: stri
const eigeneImText = genutztePlatzhalter.filter((p) => !standardSet.has(p.toLowerCase())); const eigeneImText = genutztePlatzhalter.filter((p) => !standardSet.has(p.toLowerCase()));
/** Platzhalter im individuellen Umschlagtext – analog zur Text-Tabelle. */ /** Platzhalter im individuellen Umschlagtext – analog zur Text-Tabelle. */
const genutztePlatzhalterUmschlag = useMemo(() => textPlatzhalter(umschlagText), [umschlagText]); const genutztePlatzhalterUmschlag = useMemo(() => textPlatzhalter(umschlagText), [umschlagText]);
/**
* Entry für den Versand ans Backend: löst ALLE im Text/Umschlag genutzten
* Platzhalter explizit auf (Standard-Platzhalter aus der Adresse via standardWert,
* eigene aus e.placeholders). So wird je Empfänger sicher ersetzt – unabhängig
* davon, wie das Backend die Adressfelder interpretiert.
*
* Zusätzlich wird der Freitext nur bei Adressmodus „freitext" mitgeschickt.
* Bei strukturierter Adresse muss free_text leer sein, sonst bevorzugt das
* Backend (addressText) den Freitext und beschriftet den Umschlag falsch –
* auch wenn im Freitextfeld nur Reste von einem früheren Moduswechsel stehen.
*/
const mitPlatzhaltern = (e: Entry): Entry => {
const used = [...new Set([...genutztePlatzhalter, ...genutztePlatzhalterUmschlag])];
return {
...e,
free_text: adressModus === 'freitext' ? e.free_text : '',
placeholders: {
...Object.fromEntries(used.map((p) => [p, standardWert(e, p)])),
...e.placeholders,
},
};
};
/** /**
* Werden Platzhalterwerte NICHT bereits in einem Empfänger-Schritt erfasst * Werden Platzhalterwerte NICHT bereits in einem Empfänger-Schritt erfasst
* (kein Adress-Schritt, kein Umschlag-Freitext)? Dann muss der Text-Schritt * (kein Adress-Schritt, kein Umschlag-Freitext)? Dann muss der Text-Schritt
@@ -932,7 +956,7 @@ export default function Configurator({ vorwahlProdukt }: { vorwahlProdukt?: stri
realistic: realistisch, realistic: realistisch,
envelope_labeling: needsEnvelope ? labeling : 'keine', envelope_labeling: needsEnvelope ? labeling : 'keine',
envelope_text: umschlagText, envelope_text: umschlagText,
entries: entries.slice(0, 3), entries: entries.slice(0, 3).map(mitPlatzhaltern),
}); });
if (r.has_overflow) { if (r.has_overflow) {
setTextUeberlauf(true); setTextUeberlauf(true);
@@ -981,7 +1005,7 @@ export default function Configurator({ vorwahlProdukt }: { vorwahlProdukt?: stri
realistic: realistisch, realistic: realistisch,
envelope_labeling: needsEnvelope ? labeling : 'keine', envelope_labeling: needsEnvelope ? labeling : 'keine',
envelope_text: umschlagText, envelope_text: umschlagText,
entries: entries.slice(0, 3), entries: entries.slice(0, 3).map(mitPlatzhaltern),
}); });
setPreviewDocs(ergebnis.docs); setPreviewDocs(ergebnis.docs);
setPreviewVerbleibend(ergebnis.remaining); setPreviewVerbleibend(ergebnis.remaining);
@@ -1080,20 +1104,23 @@ export default function Configurator({ vorwahlProdukt }: { vorwahlProdukt?: stri
street: lieferung.street, zip: lieferung.zip, city: lieferung.city, country: lieferung.country, street: lieferung.street, zip: lieferung.zip, city: lieferung.city, country: lieferung.country,
} }
: null, : null,
entries: entries.map((e) => ({ entries: entries.map((e) => {
salutation: e.salutation, const n = mitPlatzhaltern(e); // free_text-Normalisierung + Platzhalter
first_name: e.first_name, return {
last_name: e.last_name, salutation: e.salutation,
street: e.street, first_name: e.first_name,
house_no: e.house_no, last_name: e.last_name,
zip: e.zip, street: e.street,
city: e.city, house_no: e.house_no,
country: e.country, zip: e.zip,
free_text: adressModus === 'freitext' ? e.free_text : '', city: e.city,
placeholders: e.placeholders, country: e.country,
// Abweichender Text nur, wenn wirklich gesetzt (sonst greift der Standardtext). free_text: n.free_text,
text: (e.text && e.text.trim()) ? e.text : undefined, placeholders: n.placeholders,
})), // Abweichender Text nur, wenn wirklich gesetzt (sonst greift der Standardtext).
text: (e.text && e.text.trim()) ? e.text : undefined,
};
}),
}); });
// Auftrag ist abgesendet – der zwischengespeicherte Entwurf wird verworfen. // Auftrag ist abgesendet – der zwischengespeicherte Entwurf wird verworfen.
try { localStorage.removeItem(ENTWURF_KEY); } catch { /* egal */ } try { localStorage.removeItem(ENTWURF_KEY); } catch { /* egal */ }

View File

@@ -50,7 +50,7 @@ export default function FreigabeSeite() {
background: '#f4f6fa', boxShadow: '0 2px 10px rgba(0,0,0,.08)', background: '#f4f6fa', boxShadow: '0 2px 10px rgba(0,0,0,.08)',
}}> }}>
{d.has_layout {d.has_layout
? <img src={freigabeLayoutUrl(token)} alt="A6-Entwurf" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} /> ? <img src={freigabeLayoutUrl(token)} alt="A6-Entwurf" style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block' }} />
: <div style={{ display: 'grid', placeItems: 'center', height: '100%' }}><span className="sk-note">Entwurf wird noch erstellt …</span></div>} : <div style={{ display: 'grid', placeItems: 'center', height: '100%' }}><span className="sk-note">Entwurf wird noch erstellt …</span></div>}
</div> </div>
); );