Kuvert-Absender (Feld direkt sichtbar), Textmengen-/Session-Aenderungen

- neuauftrag: Absenderfeld fuers Kuvert IMMER sichtbar (nicht mehr an 'Kuverts
  generieren' gebunden), Payloads (Vorschau + Anlage) reichen envelope_sender durch.
- orders: manual-order speichert envelope_sender, Preview-Proxy reicht sender durch.
- bootstrap: Feld envelope_sender.
- (enthaelt weiterhin die Session-Aenderungen: signature-order, mehrseitige Vorschau,
  'In manuellen Druck uebernehmen', gesendet-Nummern, Generierungs-Fehleranzeige,
  seitenumbruch-Hinweis.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
s4luorth
2026-09-01 15:23:06 +02:00
parent 46adf2a2f5
commit 46b5155c27
4 changed files with 179 additions and 33 deletions

View File

@@ -277,6 +277,7 @@ const collections = [
f('needs_envelope', 'boolean', { interface: 'boolean', default: false }),
dropdown('envelope_labeling', ['empfaenger', 'freitext', 'keine']),
f('envelope_text', 'text', { interface: 'input-multiline', note: 'Individueller Kuverttext (bei envelope_labeling = freitext).' }),
f('envelope_sender', 'text', { interface: 'input-multiline', note: 'Absender fürs Kuvert (oben links, 5 Zeilen); manuelle Aufträge. Empfänger rutscht dann unten rechts.' }),
dropdown('envelope_format', ['c6', 'dinlang'], { note: 'Automatisch aus Format.' }),
dropdown('motif_mode', ['katalog', 'upload', 'ohne', 'eigene_karten']),
m2o('motif', 'motifs'),

View File

@@ -78,14 +78,14 @@ const NeuAuftrag = defineComponent({
// Mehrseitiges Schriftstück (Auto-Fluss über mehrere A4-Seiten).
mehrseitig: false, seitenInfo: '',
// Kuvert-Adresse je Empfänger: '5zeilen' (5 Zeilen) oder 'individuell' (einzeilig).
adressModus: '5zeilen', kuverts: false, realistic: true, customer_email: '', text: '', rows: [leereZeile()],
adressModus: '5zeilen', kuverts: false, envelopeSender: '', realistic: true, customer_email: '', text: '', rows: [leereZeile()],
importOpen: false, importText: '',
docs: [], previewSession: null, pLaedt: false, msg: '', err: '', hinweis: '', lb: '', lbDoc: null,
});
// Kuverts nur generieren, wenn angehakt (Beschriftung aus der Empfängeradresse).
const kuvertPayload = () => (neu.kuverts
? { needs_envelope: true, envelope_labeling: 'empfaenger', envelope_text: '' }
: { needs_envelope: false, envelope_labeling: 'keine', envelope_text: '' });
? { needs_envelope: true, envelope_labeling: 'empfaenger', envelope_text: '', envelope_sender: (neu.envelopeSender || '').trim() }
: { needs_envelope: false, envelope_labeling: 'keine', envelope_text: '', envelope_sender: '' });
async function laden() {
try {
@@ -109,6 +109,35 @@ const NeuAuftrag = defineComponent({
try { verbotenRe.value = wl ? new RegExp(`[^${wl}\\n\\r\\t]`, 'g') : null; } catch { verbotenRe.value = null; }
neu.product = produkte.value[0]?.key || 'briefe';
neu.format = neuFormate()[0]?.key || '';
// Übernahme aus dem Produktions-Board (localStorage): Text/Format/Schrift
// und alle Empfänger vorbefüllen, danach den Schlüssel wieder entfernen.
try {
const raw = localStorage.getItem('skrift_neuauftrag_uebernahme');
if (raw) {
localStorage.removeItem('skrift_neuauftrag_uebernahme');
const d = JSON.parse(raw);
if (d && typeof d === 'object') {
if (d.product) neu.product = d.product;
if (d.format) neu.format = d.format;
if (d.font) neu.font = d.font;
if (typeof d.text === 'string') neu.text = d.text;
if (typeof d.multi_page === 'boolean') neu.mehrseitig = d.multi_page;
if (typeof d.realistic === 'boolean') neu.realistic = d.realistic;
if (d.fontSizeLetter) neu.fontSizeLetter = d.fontSizeLetter;
if (d.fontSizeEnvelope) neu.fontSizeEnvelope = d.fontSizeEnvelope;
if (typeof d.kuverts === 'boolean') neu.kuverts = d.kuverts;
if (d.adressModus) neu.adressModus = d.adressModus;
if (Array.isArray(d.rows) && d.rows.length) {
neu.rows = d.rows.map((r) => ({
free_text: String(r.free_text || ''),
placeholders: (r.placeholders && typeof r.placeholders === 'object') ? { ...r.placeholders } : {},
}));
}
neu.previewSession = null;
neu.msg = 'Aus Auftrag übernommen – anpassen, dann Vorschau/Anlegen.';
}
}
} catch { /* localStorage nicht verfügbar → ignorieren */ }
} catch (e) {
neu.err = e?.response?.data?.errors?.[0]?.message || e?.message || 'Laden fehlgeschlagen.';
} finally { geladen.value = true; }
@@ -237,7 +266,7 @@ const NeuAuftrag = defineComponent({
const kv = kuvertPayload();
const res = await api.post('/skrift-orders/preview', {
text: neu.text, font: neu.font, format: neu.format, realistic: neu.realistic,
envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text,
envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text, envelope_sender: kv.envelope_sender,
font_scale_letter: neu.fontSizeLetter / 100, font_scale_envelope: neu.fontSizeEnvelope / 100,
multi_page: neu.mehrseitig,
...briefSigPayload(),
@@ -278,6 +307,33 @@ const NeuAuftrag = defineComponent({
neu.err = e?.response?.data?.error || e?.message || 'Seitenzählung fehlgeschlagen.';
}
}
// Das Backend generiert die Dokumente asynchron (fire-and-forget). Fehler
// landen als status_history-Eintrag 'fehler' am Auftrag, Erfolg als 'in_queue'.
// Wir pollen kurz und zeigen ein Problem DIREKT hier im Dashboard – statt dass
// es still verschluckt wird und „nie Dokumente" entstehen.
async function ueberwacheGenerierung(orderId, nr) {
for (let i = 0; i < 20; i += 1) {
await new Promise((r) => setTimeout(r, 1500));
let sh = [];
try {
sh = (await api.get('/items/status_history', { params: {
'filter[order][_eq]': orderId, sort: '-date_created', limit: 4, fields: 'status,note',
} })).data?.data || [];
} catch { continue; }
const fehler = sh.find((s) => s.status === 'fehler');
if (fehler) {
neu.err = `Generierung fehlgeschlagen: ${fehler.note || 'unbekannter Grund'}`;
neu.msg = '';
return;
}
if (sh.some((s) => s.status === 'in_queue')) {
neu.msg = `✓ Auftrag ${nr} angelegt & generiert – liegt im Produktions-Board unter „Neu". Felder bleiben stehen.`;
return;
}
}
// Kein eindeutiges Signal (langsame Generierung oder Datei-Produkt ohne Kuvert).
neu.msg = `✓ Auftrag ${nr} angelegt – Dokumente werden noch generiert. Status im Produktions-Board prüfen.`;
}
async function absenden() {
// Empfänger sind optional – ohne Empfänger genau ein Schriftstück (Blankozeile).
const ent = entries();
@@ -291,16 +347,19 @@ const NeuAuftrag = defineComponent({
font_scale_letter: neu.fontSizeLetter / 100, font_scale_envelope: neu.fontSizeEnvelope / 100,
multi_page: neu.mehrseitig,
...briefSigPayload(),
needs_envelope: kv.needs_envelope, envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text,
needs_envelope: kv.needs_envelope, envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text, envelope_sender: kv.envelope_sender,
shipping_type: 'sammel',
customer_email: neu.customer_email.trim() || undefined,
text_template: neu.text, preview_session: neu.previewSession || undefined,
entries: payloadEntries,
});
const nr = res.data?.order_number || '';
const oid = res.data?.id;
// Felder bewusst NICHT zurücksetzen – der letzte Auftrag bleibt stehen,
// damit er sich als Vorlage für den nächsten leicht anpassen lässt.
neu.msg = `✓ Auftrag ${nr} angelegt – liegt im Produktions-Board unter „Neu". Felder bleiben stehen; mit „Formular leeren" zurücksetzen.`;
neu.msg = `✓ Auftrag ${nr} angelegt – Dokumente werden generiert …`;
// Generierung im Hintergrund überwachen; ein Backend-Fehler erscheint dann hier.
if (oid) ueberwacheGenerierung(oid, nr);
} catch (e) { neu.err = e?.response?.data?.error || e?.message || 'Anlegen fehlgeschlagen.'; neu.msg = ''; }
}
function zuruecksetzen() {
@@ -308,7 +367,7 @@ const NeuAuftrag = defineComponent({
product: produkte.value[0]?.key || 'briefe', format: neuFormate()[0]?.key || '', font: 'tilda',
fontSizeLetter: 100, fontSizeEnvelope: 100,
sigSvg: '', sigLabel: '', sigX: 0, sigY: 0, sigScale: 100, mehrseitig: false, seitenInfo: '',
adressModus: '5zeilen', kuverts: false, realistic: true, customer_email: '', text: '', rows: [leereZeile()],
adressModus: '5zeilen', kuverts: false, envelopeSender: '', realistic: true, customer_email: '', text: '', rows: [leereZeile()],
importOpen: false, importText: '',
docs: [], previewSession: null, pLaedt: false, msg: '', err: '', hinweis: '', lb: '',
});
@@ -449,6 +508,17 @@ const NeuAuftrag = defineComponent({
h('input', { type: 'checkbox', checked: neu.kuverts, onChange: (e) => { neu.kuverts = e.target.checked; neu.previewSession = null; }, style: 'width:16px;height:16px;' }),
'Kuverts generieren',
]),
h('div', { style: 'margin-top:14px;' }, [
label('Absender aufs Kuvert (oben links, max. 5 Zeilen, optional)'),
h('textarea', {
value: neu.envelopeSender, rows: 5,
placeholder: 'Max Mustermann\nMusterstraße 1\n12345 Musterstadt',
style: WFELD + 'width:100%;font-family:var(--family-monospace,monospace);',
onInput: (e) => { neu.envelopeSender = saeubere(String(e.target.value)).split('\n').slice(0, 5).join('\n'); neu.previewSession = null; },
}),
h('div', { style: 'font-size:11px;color:var(--foreground-subdued,#888);margin-top:4px;line-height:1.5;' },
'Ist ein Absender gesetzt, rutscht der Empfänger auf dem Kuvert automatisch nach unten rechts. Wirkt nur, wenn „Kuverts generieren" aktiv ist.'),
]),
neu.importOpen ? h('div', { style: 'margin-top:12px;' }, [
subdued('Aus Excel kopieren und einfügen – eine Zeile pro Empfänger, Spalten mit Tab/Semikolon/Komma. '
+ 'Reihenfolge: erst individuelle Platzhalter, dann ' + (neu.adressModus === 'individuell' ? 'die eine Adresszeile.' : 'Zeile 1–5.'), 'margin-bottom:6px;'),
@@ -478,6 +548,8 @@ const NeuAuftrag = defineComponent({
neu.previewSession = null;
requestAnimationFrame(() => { try { ta.selectionStart = ta.selectionEnd = s + 1; } catch {} });
} }),
h('div', { style: 'font-size:11px;color:var(--foreground-subdued,#888);margin-top:4px;line-height:1.5;' },
'Tipp: [[seitenumbruch]] erzwingt an dieser Stelle eine neue Seite (mehrseitiger Brief). [[signatur1]] setzt die Unterschrift.'),
// ── Unterschrift auf den Brief ─────────────────────────────────────
h('div', { style: 'margin-top:16px;border-top:1px solid rgba(30,80,165,.15);padding-top:14px;' }, [
label('Unterschrift auf den Brief (optional) · Platzhalter [[signatur1]]'),

View File

@@ -677,6 +677,8 @@ const handler = (router, { services, getSchema, logger }) => {
text: istFreitext ? clean(String(body.envelope_text || '')) : adressBlock(e),
format: umschlagFormat, font, realisticHandwriting: realistisch,
placeholders: platzhalterVon(e, i), fontScale: fsEnv,
// Absender aufs Kuvert (oben links); Empfänger rutscht dann unten rechts.
sender: clean(String(body.envelope_sender || '')),
});
}
});
@@ -1566,6 +1568,7 @@ const handler = (router, { services, getSchema, logger }) => {
envelope_labeling: body.envelope_labeling ?? null,
envelope_text: clean(body.envelope_text) ?? null,
envelope_format: envelopeFormat,
envelope_sender: clean(body.envelope_sender) ?? null,
text_template: clean(body.text_template) ?? null,
preview_session: typeof body.preview_session === 'string' ? body.preview_session : null,
entries_count: entries.length,

View File

@@ -612,14 +612,33 @@ const Board = defineComponent({
}
// ── Empfänger-Drawer (mit Dokument-Vorschau je Zeile) ──
const dateiNummer = (name) => {
const z = String(name || '').replace(/\.[^.]*$/, '').replace(/\D/g, '');
return z ? parseInt(z, 10) : null;
// Zerlegt einen Dateinamen in Brief-Nummer und Seite:
// letter_001_p2.svg → { nummer:1, seite:2 }, letter_003.svg → { nummer:3, seite:1 }.
const briefTeile = (name) => {
const base = String(name || '').replace(/\.[^.]*$/, '');
const m = base.match(/(\d+)(?:_p(\d+))?$/i);
return { nummer: m ? parseInt(m[1], 10) : null, seite: (m && m[2]) ? parseInt(m[2], 10) : 1 };
};
// Distinkte Brief-Nummern eines Typs, aufsteigend – für positionsbasierte
// Zuordnung Empfänger↔Brief (robust gegen 0-/1-basierte Namen). Je Nummer die
// nach Seite sortierten Dateien (mehrseitige Briefe = mehrere Einträge).
const briefeNachRang = (kind) => {
const map = new Map();
for (const f of empf.files.filter((x) => x.kind === kind)) {
const { nummer, seite } = briefTeile(f.name);
const key = nummer ?? f.name;
if (!map.has(key)) map.set(key, []);
map.get(key).push({ f, seite });
}
const keys = [...map.keys()].sort((a, b) => ((typeof a === 'number' && typeof b === 'number') ? a - b : String(a).localeCompare(String(b))));
return keys.map((k) => map.get(k).sort((a, b) => a.seite - b.seite).map((x) => x.f));
};
// Vorschau-Blob-URLs des Schriftstücks (Array) sauber freigeben.
const revokePl = () => { (Array.isArray(empf.pl) ? empf.pl : []).forEach((p) => { try { URL.revokeObjectURL(p.url); } catch { /* egal */ } }); };
async function openEmpf(order) {
empf.open = true; empf.mode = 'order'; empf.titel = `Empfänger · ${order.order_number}`;
empf.order = order.order_number; empf.suche = ''; empf.rows = []; empf.files = []; empf.info = '';
empf.selected = null; empf.pl = ''; empf.pe = ''; empf.perr = ''; empf.lb = ''; empf.laden = true;
empf.order = order.order_number; empf.orderObj = order; empf.suche = ''; empf.rows = []; empf.files = []; empf.info = '';
empf.selected = null; revokePl(); empf.pl = []; empf.pe = ''; empf.perr = ''; empf.lb = ''; empf.laden = true;
try {
const [ent, fl] = await Promise.all([
api.get('/items/order_entries', { params: { 'filter[order][_eq]': order.id, sort: 'letter_number', limit: -1, fields: '*' } }),
@@ -631,7 +650,7 @@ const Board = defineComponent({
}
function openReqInfo(req) {
empf.open = true; empf.mode = 'request'; empf.titel = `Anfrage · ${req.name}`; empf.suche = ''; empf.info = '';
empf.files = []; empf.selected = null; empf.pl = ''; empf.pe = ''; empf.lb = '';
empf.files = []; empf.selected = null; revokePl(); empf.pl = []; empf.pe = ''; empf.lb = '';
const a = req.address && typeof req.address === 'object' ? Object.entries(req.address).map(([k, v]) => `${k}: ${v}`).join(' · ') : (req.address || '');
empf.rows = [
{ nr: '✉', zeile: `${req.name} · ${req.email}${req.phone ? ' · ' + req.phone : ''}`, extra: '' },
@@ -644,27 +663,67 @@ const Board = defineComponent({
const res = await api.get('/skrift-orders/files/' + encodeURIComponent(empf.order) + '/download', { params: { rel: f.rel }, responseType: 'blob' });
return URL.createObjectURL(res.data);
}
// Positionsbasierter Abgleich: der i-te Empfänger → die i-te Datei. Robust
// gegen alte (0-basierte) und neue (1-basierte) Dateinamen.
const nachNummer = (kind) => empf.files
.filter((f) => f.kind === kind)
.sort((a, b) => (dateiNummer(a.name) ?? 0) - (dateiNummer(b.name) ?? 0));
async function previewEntry(nr, rang) {
empf.selected = nr; empf.perr = ''; empf.pLoad = true;
if (empf.pl) URL.revokeObjectURL(empf.pl); if (empf.pe) URL.revokeObjectURL(empf.pe);
empf.pl = ''; empf.pe = '';
revokePl(); if (empf.pe) URL.revokeObjectURL(empf.pe);
empf.pl = []; empf.pe = '';
try {
const brief = nachNummer('schriftstueck')[rang] || empf.files.find((f) => f.kind === 'schriftstueck' && dateiNummer(f.name) === nr);
const kuvert = nachNummer('umschlag')[rang] || empf.files.find((f) => f.kind === 'umschlag' && dateiNummer(f.name) === nr);
if (brief) empf.pl = await empfBlob(brief);
// Positionsbasiert: der rang-te Empfänger → die rang-te Brief-Nummer.
// Mehrseitige Briefe liefern hier mehrere Seiten-Dateien.
const seiten = briefeNachRang('schriftstueck')[rang] || [];
const kuvert = (briefeNachRang('umschlag')[rang] || [])[0] || null;
const geladen = [];
for (let i = 0; i < seiten.length; i += 1) {
geladen.push({ url: await empfBlob(seiten[i]), label: seiten.length > 1 ? `Seite ${i + 1}` : '' });
}
empf.pl = geladen;
if (kuvert) empf.pe = await empfBlob(kuvert);
if (!brief && !kuvert) {
if (!seiten.length && !kuvert) {
empf.perr = empf.files.length
? 'Für diese Nummer sind noch keine Dateien generiert.'
: 'Für diesen Auftrag wurden noch keine Dateien generiert (erst nach Abschluss/Generierung).';
}
} catch (e) { empf.perr = 'Vorschau fehlgeschlagen.'; } finally { empf.pLoad = false; }
}
// Auftrag in den manuellen Druck (neuauftrag-Modul) übernehmen: Text, Schrift,
// Format, Kuvert-Option und ALLE Empfänger (free_text + Platzhalter) per
// localStorage übergeben und dorthin navigieren – dort editierbar/neu druckbar.
async function uebernehmenManuell() {
const o = empf.orderObj;
if (!o) return;
try {
const full = (await api.get('/items/orders/' + o.id, { params: {
fields: 'text_template,font,multi_page,realistic,font_scale_letter,font_scale_envelope,needs_envelope,product.key,format.key',
} })).data?.data || {};
const ent = (await api.get('/items/order_entries', { params: {
'filter[order][_eq]': o.id, sort: 'letter_number', limit: -1, fields: 'free_text,placeholders,letter_number',
} })).data?.data || [];
const rows = ent.map((e) => ({
free_text: String(e.free_text || ''),
placeholders: (e.placeholders && typeof e.placeholders === 'object') ? e.placeholders : {},
}));
const daten = {
product: full.product?.key || '',
format: full.format?.key || o.format?.key || '',
font: full.font || 'tilda',
text: full.text_template || '',
multi_page: !!full.multi_page,
realistic: full.realistic !== false,
fontSizeLetter: Math.round((Number(full.font_scale_letter) || 1) * 100),
fontSizeEnvelope: Math.round((Number(full.font_scale_envelope) || 1) * 100),
kuverts: !!full.needs_envelope,
adressModus: rows.some((r) => r.free_text.includes('\n')) ? '5zeilen' : 'individuell',
rows,
};
localStorage.setItem('skrift_neuauftrag_uebernahme', JSON.stringify(daten));
// Zum manuellen Druck-Modul wechseln. Die Admin-App liegt unter /admin/ –
// Basis aus dem aktuellen Pfad ableiten (robust gegen Unterpfad-Prefixe).
const base = window.location.pathname.split('/admin/')[0] + '/admin/';
window.location.href = base + 'skrift-neuauftrag';
} catch (e) {
flash('Übernahme fehlgeschlagen: ' + (e?.response?.data?.errors?.[0]?.message || e?.message || 'Fehler'));
}
}
const empfTreffer = computed(() => {
const q = empf.suche.trim().toLowerCase();
if (!q) return empf.rows;
@@ -1074,14 +1133,21 @@ const Board = defineComponent({
: subdued('Keine Treffer.')),
]);
// Rechte Spalte (~30 %): Vorschau; Klick aufs Bild → große Lightbox.
const vorschauBox = (titel, url) => h('div', { style: 'display:flex;flex-direction:column;gap:6px;' }, [
h('div', { style: 'font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--foreground-subdued);' }, titel),
h('div', {
onClick: url ? () => { empf.lb = url; empf.lbTitel = titel; } : undefined,
style: 'border:1px solid var(--border-subdued);border-radius:8px;background:#fff;display:flex;align-items:center;justify-content:center;min-height:150px;padding:8px;'
+ (url ? 'cursor:zoom-in;' : ''),
}, url ? h('img', { src: url, style: 'max-width:100%;max-height:30vh;object-fit:contain;' }) : subdued('—')),
]);
const vorschauBox = (titel, quelle) => {
// quelle: Array [{url,label}] (Schriftstück, evtl. mehrseitig) oder einzelne URL (Kuvert).
const bilder = Array.isArray(quelle) ? quelle : (quelle ? [{ url: quelle, label: '' }] : []);
return h('div', { style: 'display:flex;flex-direction:column;gap:6px;' }, [
h('div', { style: 'font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--foreground-subdued);' },
titel + (bilder.length > 1 ? ` · ${bilder.length} Seiten` : '')),
...(bilder.length ? bilder.map((b) => h('div', {
onClick: () => { empf.lb = b.url; empf.lbTitel = titel + (b.label ? ' · ' + b.label : ''); },
style: 'position:relative;border:1px solid var(--border-subdued);border-radius:8px;background:#fff;display:flex;align-items:center;justify-content:center;min-height:150px;padding:8px;cursor:zoom-in;',
}, [
b.label ? h('div', { style: 'position:absolute;top:4px;left:6px;font-size:10px;color:var(--foreground-subdued);background:var(--background-page);padding:0 4px;border-radius:4px;' }, b.label) : null,
h('img', { src: b.url, style: 'max-width:100%;max-height:30vh;object-fit:contain;' }),
])) : [h('div', { style: 'border:1px solid var(--border-subdued);border-radius:8px;background:#fff;display:flex;align-items:center;justify-content:center;min-height:150px;padding:8px;' }, subdued('—'))]),
]);
};
const vorschau = istOrder ? h('div', { style: 'flex:0 0 30%;min-width:220px;display:flex;flex-direction:column;gap:14px;' },
empf.selected == null ? [subdued('Empfänger anklicken → Vorschau (Klick aufs Bild = groß).')]
: empf.pLoad ? [subdued('Vorschau wird geladen …')]
@@ -1090,8 +1156,12 @@ const Board = defineComponent({
vorschauBox('📄 Schriftstück', empf.pl),
vorschauBox('✉ Kuvert', empf.pe),
]) : null;
const uebernahme = istOrder ? h('div', { style: 'padding:12px 20px;border-bottom:1px solid var(--border-subdued);display:flex;justify-content:flex-end;align-items:center;gap:10px;flex-wrap:wrap;' }, [
subdued('Text, Format & alle Empfänger in den manuellen Druck laden – dort anpassen und neu anlegen.'),
btn('✍ In manuellen Druck übernehmen', uebernehmenManuell, 'primary'),
]) : null;
const body = h('div', { style: 'padding:16px 20px;display:flex;gap:20px;flex-wrap:wrap;' }, [liste, vorschau]);
return overlay([modalKopf(empf.titel, () => { empf.open = false; }), body], () => { empf.open = false; }, istOrder ? '980px' : '620px');
return overlay([modalKopf(empf.titel, () => { empf.open = false; }), uebernahme, body].filter(Boolean), () => { empf.open = false; }, istOrder ? '980px' : '620px');
}
// Große Einzelvorschau (Lightbox) über dem Empfänger-Dialog.
function lightboxModal() {