Board: Suche entlaggt, Order-Sprung, Versandadresse, Motiv-Hinweis; Mail: tägliche Statusübersicht
produktion: - Empfängersuche entlaggt (uncontrolled Input + 250ms-Debounce + vorab kleingeschriebenes Haystack) - kein Full-Rerender je Tastendruck mehr. - "Auftrag öffnen"-Link je Karte (-> Directus-Order, neuer Tab). - Sammelversand: Lieferadresse direkt auf der Karte (abweichende Order- Lieferadresse -> Kunden-Liefer-/Rechnungsadresse als Fallback). - Postkarten-Motiv wird als Badge hervorgehoben. mailer: - API-Einzelzusammenfassung entfernt; stattdessen taegliche Gesamtuebersicht um 03:00 ueber ALLE Auftraege inkl. Mengen je Produktionsstatus. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -260,44 +260,59 @@ export default (register, { services, getSchema, logger }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Tagesübersicht der API-Aufträge (statt Einzelmail je Auftrag) ───────────
|
// ── Tägliche Gesamtübersicht (03:00) – alle Aufträge nach Produktionsstatus ──
|
||||||
// Einmal täglich (Default 18:00): pro API-Kunde eine Zusammenfassung der heute
|
// Ersetzt die frühere API-Einzelzusammenfassung: jeden Morgen eine Übersicht
|
||||||
// eingegangenen Aufträge, aufgeschlüsselt nach Versandtag.
|
// ALLER Bestellungen inkl. Mengen (Schriftstücke) je Status. Keine separate
|
||||||
const API_DIGEST_CRON = process.env.SKRIFT_API_DIGEST_CRON || '0 18 * * *';
|
// API-Zusammenfassung mehr.
|
||||||
register.schedule(API_DIGEST_CRON, async () => {
|
const UEBERSICHT_CRON = process.env.SKRIFT_UEBERSICHT_CRON || '0 3 * * *';
|
||||||
|
const PSTATUS_ORDER = [
|
||||||
|
['eingegangen', 'Eingegangen / Neu'],
|
||||||
|
['im_druck', 'Im Druck'],
|
||||||
|
['kuvertieren', 'Kuvertierung'],
|
||||||
|
['versandfertig', 'Versandfertig'],
|
||||||
|
['versendet', 'Versendet'],
|
||||||
|
['storniert', 'Storniert'],
|
||||||
|
];
|
||||||
|
register.schedule(UEBERSICHT_CRON, async () => {
|
||||||
try {
|
try {
|
||||||
const schema = await getSchema();
|
const schema = await getSchema();
|
||||||
const to = await teamEmpfaenger(schema);
|
const to = await teamEmpfaenger(schema);
|
||||||
if (!to) return;
|
if (!to) return;
|
||||||
const settings = await loadSettings(schema);
|
const settings = await loadSettings(schema);
|
||||||
const start = new Date(); start.setHours(0, 0, 0, 0);
|
|
||||||
const orders = await new ItemsService('orders', { schema, accountability: null }).readByQuery({
|
const orders = await new ItemsService('orders', { schema, accountability: null }).readByQuery({
|
||||||
filter: { _and: [{ source: { _eq: 'api' } }, { date_created: { _gte: start.toISOString() } }] },
|
fields: ['production_status', 'entries_count'], limit: -1,
|
||||||
fields: ['order_number', 'shipping_day', 'api_client.id', 'api_client.name', 'product.name'],
|
|
||||||
limit: -1, sort: ['order_number'],
|
|
||||||
});
|
});
|
||||||
if (!orders || !orders.length) return;
|
const agg = {};
|
||||||
|
let gesamtA = 0, gesamtM = 0;
|
||||||
const byClient = new Map();
|
for (const o of (orders || [])) {
|
||||||
for (const o of orders) {
|
const st = o.production_status || 'eingegangen';
|
||||||
const kid = o.api_client?.id ?? 'x';
|
if (!agg[st]) agg[st] = { auftraege: 0, menge: 0 };
|
||||||
let g = byClient.get(kid);
|
agg[st].auftraege += 1; agg[st].menge += Number(o.entries_count) || 0;
|
||||||
if (!g) { g = { name: o.api_client?.name || 'API-Kunde', orders: [] }; byClient.set(kid, g); }
|
gesamtA += 1; gesamtM += Number(o.entries_count) || 0;
|
||||||
g.orders.push(o);
|
|
||||||
}
|
}
|
||||||
for (const g of byClient.values()) {
|
// Bekannte Status in sinnvoller Reihenfolge, unbekannte hinten anhängen.
|
||||||
const byDay = {};
|
const reihen = [...PSTATUS_ORDER];
|
||||||
for (const o of g.orders) { const d = o.shipping_day || 'ohne Versandtag'; byDay[d] = (byDay[d] || 0) + 1; }
|
for (const st of Object.keys(agg)) if (!reihen.some(([k]) => k === st)) reihen.push([st, st]);
|
||||||
const zeilen = Object.entries(byDay).map(([d, n]) => `<li>${esc(d)}: <strong>${n}</strong></li>`).join('');
|
const td = 'padding:4px 10px;';
|
||||||
const html = `<p>Heute über die API eingegangen von <strong>${esc(g.name)}</strong>: <strong>${g.orders.length}</strong> Auftrag/Aufträge.</p>`
|
const zeilen = reihen.filter(([k]) => agg[k]).map(([k, label]) =>
|
||||||
+ `<p>Nach Versandtag:</p><ul>${zeilen}</ul>`
|
`<tr><td style="${td}">${esc(label)}</td>`
|
||||||
+ '<p>Im Produktions-Board unter „Neu" als Charge(n) gruppiert.</p>';
|
+ `<td style="${td}text-align:right;"><strong>${agg[k].auftraege}</strong></td>`
|
||||||
const nachricht = { to, subject: `API-Tagesübersicht ${g.name} – ${g.orders.length} neue`, html, text: htmlZuText(html), replyTo: REPLY_TO };
|
+ `<td style="${td}text-align:right;">${agg[k].menge}</td></tr>`).join('');
|
||||||
if (settings.sender_email) nachricht.from = settings.sender_name ? `${settings.sender_name} <${settings.sender_email}>` : settings.sender_email;
|
const heute = new Date().toLocaleDateString('de-DE');
|
||||||
try { await new MailService({ schema, accountability: null }).send(nachricht); }
|
const html = `<p>Gesamtübersicht aller Aufträge (Stand ${esc(heute)}):</p>`
|
||||||
catch (e) { logger.warn(`[skrift-mailer] API-Tagesmail ${g.name}: ${e.message}`); }
|
+ '<table style="border-collapse:collapse;font-size:14px;">'
|
||||||
}
|
+ `<thead><tr><th style="text-align:left;${td}border-bottom:1px solid #ccc;">Status</th>`
|
||||||
logger.info(`[skrift-mailer] API-Tagesübersicht verschickt: ${byClient.size} Kunde(n)`);
|
+ `<th style="text-align:right;${td}border-bottom:1px solid #ccc;">Aufträge</th>`
|
||||||
} catch (e) { logger.warn(`[skrift-mailer] API-Tageslauf: ${e.message}`); }
|
+ `<th style="text-align:right;${td}border-bottom:1px solid #ccc;">Schriftstücke</th></tr></thead>`
|
||||||
|
+ `<tbody>${zeilen || `<tr><td style="${td}" colspan="3">Keine Aufträge.</td></tr>`}</tbody>`
|
||||||
|
+ `<tfoot><tr><td style="${td}border-top:2px solid #333;"><strong>Gesamt</strong></td>`
|
||||||
|
+ `<td style="${td}text-align:right;border-top:2px solid #333;"><strong>${gesamtA}</strong></td>`
|
||||||
|
+ `<td style="${td}text-align:right;border-top:2px solid #333;"><strong>${gesamtM}</strong></td></tr></tfoot>`
|
||||||
|
+ '</table>';
|
||||||
|
const nachricht = { to, subject: `Auftragsübersicht ${heute} – ${gesamtA} Aufträge`, html, text: htmlZuText(html), replyTo: REPLY_TO };
|
||||||
|
if (settings.sender_email) nachricht.from = settings.sender_name ? `${settings.sender_name} <${settings.sender_email}>` : settings.sender_email;
|
||||||
|
await new MailService({ schema, accountability: null }).send(nachricht);
|
||||||
|
logger.info(`[skrift-mailer] Auftragsübersicht verschickt: ${gesamtA} Aufträge in ${Object.keys(agg).length} Status`);
|
||||||
|
} catch (e) { logger.warn(`[skrift-mailer] Übersicht-Tageslauf: ${e.message}`); }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -181,6 +181,11 @@ const Board = defineComponent({
|
|||||||
// mitgenommen wird. Wird beim Verlassen des Reiters geleert.
|
// mitgenommen wird. Wird beim Verlassen des Reiters geleert.
|
||||||
const sucheText = ref('');
|
const sucheText = ref('');
|
||||||
const sucheEintraege = ref([]);
|
const sucheEintraege = ref([]);
|
||||||
|
// Suche entlaggen: die Eingabe ist uncontrolled, der eigentliche Filterbegriff
|
||||||
|
// (sucheText) wird nur nach einer kurzen Tipp-Pause gesetzt. So rendert das Board
|
||||||
|
// nicht bei jedem Tastendruck die (bis zu 200) Trefferkarten neu.
|
||||||
|
let sucheTimer = null;
|
||||||
|
const sucheSetzen = (v) => { clearTimeout(sucheTimer); sucheTimer = setTimeout(() => { sucheText.value = v; }, 250); };
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const fehler = ref('');
|
const fehler = ref('');
|
||||||
const meldung = ref('');
|
const meldung = ref('');
|
||||||
@@ -222,10 +227,10 @@ const Board = defineComponent({
|
|||||||
// werden separat versucht, damit ein fehlendes Feld nicht das ganze Board sperrt.
|
// werden separat versucht, damit ein fehlendes Feld nicht das ganze Board sperrt.
|
||||||
const ORDER_FELDER = 'id,order_number,status,payment_status,production_status,needs_envelope,envelope_format,'
|
const ORDER_FELDER = 'id,order_number,status,payment_status,production_status,needs_envelope,envelope_format,'
|
||||||
+ 'envelope_labeling,shipping_type,shipping_day,keep_contacts,entries_count,count_inland,count_ausland,brief_gedruckt,kuvert_gedruckt,'
|
+ 'envelope_labeling,shipping_type,shipping_day,keep_contacts,entries_count,count_inland,count_ausland,brief_gedruckt,kuvert_gedruckt,'
|
||||||
+ 'date_updated,date_created,format.key,format.name,product.name,product.input_mode,'
|
+ 'date_updated,date_created,format.key,format.name,product.name,product.type,product.input_mode,'
|
||||||
+ 'source,api_client.id,api_client.name,'
|
+ 'source,api_client.id,api_client.name,motif_mode,motif.name,shipping_address,'
|
||||||
+ 'source_file,internal_note,approval_status,approval_note,linked_order.id,linked_order.order_number,'
|
+ 'source_file,internal_note,approval_status,approval_note,linked_order.id,linked_order.order_number,'
|
||||||
+ 'customer.first_name,customer.last_name,customer.email,customer.phone';
|
+ 'customer.first_name,customer.last_name,customer.company,customer.email,customer.phone,customer.billing_address,customer.shipping_address';
|
||||||
async function ladeOrders() {
|
async function ladeOrders() {
|
||||||
try {
|
try {
|
||||||
// source_files/versandt_am sind ggf. noch nicht gebootstrapt → separater Versuch.
|
// source_files/versandt_am sind ggf. noch nicht gebootstrapt → separater Versuch.
|
||||||
@@ -292,7 +297,7 @@ const Board = defineComponent({
|
|||||||
if (e.country && !/^(de|d|deu|deutschland|germany)$/i.test(String(e.country).trim())) adr.push(e.country);
|
if (e.country && !/^(de|d|deu|deutschland|germany)$/i.test(String(e.country).trim())) adr.push(e.country);
|
||||||
if (!adr.length && e.free_text) adr.push(...String(e.free_text).split('\n').map((z) => z.trim()).filter(Boolean));
|
if (!adr.length && e.free_text) adr.push(...String(e.free_text).split('\n').map((z) => z.trim()).filter(Boolean));
|
||||||
// Eine durchsuchbare Zeile je Empfänger (mit Briefnummer) für den Suche-Reiter.
|
// Eine durchsuchbare Zeile je Empfänger (mit Briefnummer) für den Suche-Reiter.
|
||||||
flach.push({ orderId: oid, nr: Number(e.letter_number) || flach.filter((x) => x.orderId === oid).length + 1, name: nm, adr, hay: txt });
|
flach.push({ orderId: oid, nr: Number(e.letter_number) || flach.filter((x) => x.orderId === oid).length + 1, name: nm, adr, hay: String(txt || '').toLowerCase() });
|
||||||
}
|
}
|
||||||
empfIndex.value = idx;
|
empfIndex.value = idx;
|
||||||
empfNamen.value = namen;
|
empfNamen.value = namen;
|
||||||
@@ -493,8 +498,10 @@ const Board = defineComponent({
|
|||||||
for (const e of sucheEintraege.value) {
|
for (const e of sucheEintraege.value) {
|
||||||
const o = orderById.value[e.orderId];
|
const o = orderById.value[e.orderId];
|
||||||
if (!o || !SUCHE_AKTIV.includes(o.production_status || 'eingegangen')) continue;
|
if (!o || !SUCHE_AKTIV.includes(o.production_status || 'eingegangen')) continue;
|
||||||
const hay = [e.hay, o.order_number, o.product?.name].filter(Boolean).join(' ').toLowerCase();
|
// e.hay ist bereits kleingeschrieben; Auftragsnr./Produkt separat prüfen (kein Rebuild je Eintrag).
|
||||||
if (!hay.includes(q)) continue;
|
if (!e.hay.includes(q)
|
||||||
|
&& !String(o.order_number || '').toLowerCase().includes(q)
|
||||||
|
&& !String(o.product?.name || '').toLowerCase().includes(q)) continue;
|
||||||
res.push({ e, o });
|
res.push({ e, o });
|
||||||
if (res.length >= 200) break;
|
if (res.length >= 200) break;
|
||||||
}
|
}
|
||||||
@@ -1359,6 +1366,17 @@ const Board = defineComponent({
|
|||||||
onChange: (e) => setStatus(order, e.target.value),
|
onChange: (e) => setStatus(order, e.target.value),
|
||||||
}, Object.entries(STATUS_LABEL).map(([k, v]) => h('option', { value: k, selected: (order.production_status || 'eingegangen') === k }, v)));
|
}, Object.entries(STATUS_LABEL).map(([k, v]) => h('option', { value: k, selected: (order.production_status || 'eingegangen') === k }, v)));
|
||||||
}
|
}
|
||||||
|
// Lieferadresse für den (Sammel-)Versand: abweichende Order-Lieferadresse →
|
||||||
|
// Liefer-/Rechnungsadresse des Kunden. So muss der Versand nicht in die Order.
|
||||||
|
function lieferAdresse(o) {
|
||||||
|
const a = o.shipping_address || o.customer?.shipping_address || o.customer?.billing_address || null;
|
||||||
|
if (!a || typeof a !== 'object') return null;
|
||||||
|
const name = [o.customer?.company, [o.customer?.first_name, o.customer?.last_name].filter(Boolean).join(' ')].filter(Boolean).join(' · ');
|
||||||
|
const zeilen = [name, a.street, [a.zip, a.city].filter(Boolean).join(' '), a.country].map((z) => String(z || '').trim()).filter(Boolean);
|
||||||
|
if (!zeilen.length) return null;
|
||||||
|
const quelle = o.shipping_address ? 'Lieferadresse' : (o.customer?.shipping_address ? 'Lieferadresse (Kunde)' : 'Rechnungsadresse (Kunde)');
|
||||||
|
return { zeilen, quelle };
|
||||||
|
}
|
||||||
function orderCard(order, opts) {
|
function orderCard(order, opts) {
|
||||||
const o = order;
|
const o = order;
|
||||||
const kopf = h('div', { style: 'display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;' }, [
|
const kopf = h('div', { style: 'display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;' }, [
|
||||||
@@ -1368,6 +1386,9 @@ const Board = defineComponent({
|
|||||||
h('div', {}, [
|
h('div', {}, [
|
||||||
h('div', { style: 'font-weight:700;font-size:15px;' }, `#${o.order_number}`),
|
h('div', { style: 'font-weight:700;font-size:15px;' }, `#${o.order_number}`),
|
||||||
subdued(`${kundeName(o)}${o.customer?.email ? ' · ' + o.customer.email : ''}${o.customer?.phone ? ' · ' + o.customer.phone : ''}`, 'margin-top:2px;'),
|
subdued(`${kundeName(o)}${o.customer?.email ? ' · ' + o.customer.email : ''}${o.customer?.phone ? ' · ' + o.customer.phone : ''}`, 'margin-top:2px;'),
|
||||||
|
h('a', { href: `/admin/content/orders/${o.id}`, target: '_blank', rel: 'noopener',
|
||||||
|
style: 'display:inline-block;margin-top:3px;font-size:12px;color:var(--primary,#1e50a5);text-decoration:none;font-weight:600;' },
|
||||||
|
'↗ Auftrag öffnen'),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
statusSelect(o),
|
statusSelect(o),
|
||||||
@@ -1383,6 +1404,10 @@ const Board = defineComponent({
|
|||||||
o.shipping_day ? chip('📅 Versandtag: ' + o.shipping_day.charAt(0).toUpperCase() + o.shipping_day.slice(1), 'var(--primary-25,rgba(30,80,165,.14))') : null,
|
o.shipping_day ? chip('📅 Versandtag: ' + o.shipping_day.charAt(0).toUpperCase() + o.shipping_day.slice(1), 'var(--primary-25,rgba(30,80,165,.14))') : null,
|
||||||
chip(o.product?.name || 'Produkt?'),
|
chip(o.product?.name || 'Produkt?'),
|
||||||
chip(o.format?.name || o.format?.key || 'Format?'),
|
chip(o.format?.name || o.format?.key || 'Format?'),
|
||||||
|
// Postkarten-Motiv: deutlich hervorheben, damit das Motiv beim Druck berücksichtigt wird.
|
||||||
|
(o.motif?.name || (o.motif_mode && !['ohne', ''].includes(o.motif_mode)))
|
||||||
|
? chip('🖼 Motiv' + (o.motif?.name ? ': ' + o.motif.name : (o.motif_mode === 'eigene_karten' ? ': eigene Karten' : (o.motif_mode === 'upload' ? ': Upload' : ''))), 'var(--warning-25,rgba(230,160,0,.22))')
|
||||||
|
: null,
|
||||||
chip(o.payment_status === 'bezahlt' ? '✓ bezahlt' : (o.payment_status === 'storniert' ? 'storniert' : 'offen'),
|
chip(o.payment_status === 'bezahlt' ? '✓ bezahlt' : (o.payment_status === 'storniert' ? 'storniert' : 'offen'),
|
||||||
o.payment_status === 'bezahlt' ? 'var(--success-25,rgba(46,125,50,.15))' : 'var(--warning-25,rgba(230,160,0,.15))'),
|
o.payment_status === 'bezahlt' ? 'var(--success-25,rgba(46,125,50,.15))' : 'var(--warning-25,rgba(230,160,0,.15))'),
|
||||||
o.linked_order?.order_number ? chip('🔗 Kuverts: #' + o.linked_order.order_number, 'var(--primary-25,rgba(30,80,165,.14))') : null,
|
o.linked_order?.order_number ? chip('🔗 Kuverts: #' + o.linked_order.order_number, 'var(--primary-25,rgba(30,80,165,.14))') : null,
|
||||||
@@ -1398,8 +1423,14 @@ const Board = defineComponent({
|
|||||||
send.brief.length ? chip('🖨 Brief gesendet: ' + send.brief.join(' · '), 'var(--primary-25,rgba(30,80,165,.14))') : null,
|
send.brief.length ? chip('🖨 Brief gesendet: ' + send.brief.join(' · '), 'var(--primary-25,rgba(30,80,165,.14))') : null,
|
||||||
send.umschlag.length ? chip('✉ Kuvert gesendet: ' + send.umschlag.join(' · '), 'var(--primary-25,rgba(30,80,165,.14))') : null,
|
send.umschlag.length ? chip('✉ Kuvert gesendet: ' + send.umschlag.join(' · '), 'var(--primary-25,rgba(30,80,165,.14))') : null,
|
||||||
]) : null;
|
]) : null;
|
||||||
|
// Sammelversand: Lieferadresse direkt auf der Karte (kein Sprung in die Order nötig).
|
||||||
|
const liefer = (o.shipping_type !== 'einzeln') ? lieferAdresse(o) : null;
|
||||||
|
const lieferBox = liefer ? h('div', { style: 'margin:0 0 10px;padding:8px 10px;background:var(--background-subdued,#f1f4f9);border-radius:8px;font-size:13px;line-height:1.5;' }, [
|
||||||
|
h('div', { style: 'font-weight:600;color:var(--foreground-subdued);font-size:11px;text-transform:uppercase;letter-spacing:.03em;margin-bottom:2px;' }, '📦 Versand an · ' + liefer.quelle),
|
||||||
|
...liefer.zeilen.map((z) => h('div', {}, z)),
|
||||||
|
]) : null;
|
||||||
return h('div', { key: 'o' + o.id, style: CARD + `margin-bottom:14px;border-left:4px solid ${ACCENT};` }, [
|
return h('div', { key: 'o' + o.id, style: CARD + `margin-bottom:14px;border-left:4px solid ${ACCENT};` }, [
|
||||||
kopf, meta, badges,
|
kopf, meta, badges, lieferBox,
|
||||||
notizFeld(o.internal_note, (v) => setOrderNotiz(o, v), 'Notiz zum Auftrag …'),
|
notizFeld(o.internal_note, (v) => setOrderNotiz(o, v), 'Notiz zum Auftrag …'),
|
||||||
actionButtons(o),
|
actionButtons(o),
|
||||||
weiteresOffen.value[o.id] ? weiteresPanel(o) : null,
|
weiteresOffen.value[o.id] ? weiteresPanel(o) : null,
|
||||||
@@ -1491,10 +1522,10 @@ const Board = defineComponent({
|
|||||||
// Großes, unverkennbar weißes Suchfeld.
|
// Großes, unverkennbar weißes Suchfeld.
|
||||||
const feld = h('div', { style: 'margin-top:18px;' }, [
|
const feld = h('div', { style: 'margin-top:18px;' }, [
|
||||||
h('div', { style: 'font-size:13px;font-weight:700;color:var(--foreground-normal);margin-bottom:8px;' }, '2. Empfänger suchen'),
|
h('div', { style: 'font-size:13px;font-weight:700;color:var(--foreground-normal);margin-bottom:8px;' }, '2. Empfänger suchen'),
|
||||||
h('input', { type: 'search', value: sucheText.value, placeholder: 'Name, Straße, Ort oder Auftragsnummer eingeben …',
|
h('input', { type: 'search', placeholder: 'Name, Straße, Ort oder Auftragsnummer eingeben …',
|
||||||
style: 'width:100%;box-sizing:border-box;background:#fff;color:#111;border:2px solid #cdd6e2;border-radius:10px;'
|
style: 'width:100%;box-sizing:border-box;background:#fff;color:#111;border:2px solid #cdd6e2;border-radius:10px;'
|
||||||
+ 'font:inherit;font-size:22px;padding:16px 20px;outline:none;box-shadow:0 1px 3px rgba(0,0,0,.08);',
|
+ 'font:inherit;font-size:22px;padding:16px 20px;outline:none;box-shadow:0 1px 3px rgba(0,0,0,.08);',
|
||||||
onInput: (e) => { sucheText.value = e.target.value; } }),
|
onInput: (e) => sucheSetzen(e.target.value) }),
|
||||||
]);
|
]);
|
||||||
const rahmen = (inhalt) => h('div', { style: 'display:flex;flex-direction:column;gap:0;' }, [
|
const rahmen = (inhalt) => h('div', { style: 'display:flex;flex-direction:column;gap:0;' }, [
|
||||||
h('div', { style: 'background:#fff;border:1px solid #cdd6e2;border-radius:12px;padding:20px 22px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.06);' }, [typWahl, feld]),
|
h('div', { style: 'background:#fff;border:1px solid #cdd6e2;border-radius:12px;padding:20px 22px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.06);' }, [typWahl, feld]),
|
||||||
@@ -1957,7 +1988,7 @@ const Board = defineComponent({
|
|||||||
return () => {
|
return () => {
|
||||||
const navi = h('div', { style: 'padding:12px 10px;' }, TABS.map((t) =>
|
const navi = h('div', { style: 'padding:12px 10px;' }, TABS.map((t) =>
|
||||||
h('button', {
|
h('button', {
|
||||||
type: 'button', onClick: () => { if (tab.value === 'suche' && t.key !== 'suche') sucheText.value = ''; tab.value = t.key; },
|
type: 'button', onClick: () => { if (tab.value === 'suche' && t.key !== 'suche') { clearTimeout(sucheTimer); sucheText.value = ''; } tab.value = t.key; },
|
||||||
style: 'display:flex;justify-content:space-between;align-items:center;gap:8px;width:100%;text-align:left;'
|
style: 'display:flex;justify-content:space-between;align-items:center;gap:8px;width:100%;text-align:left;'
|
||||||
+ 'cursor:pointer;border:none;border-radius:6px;padding:10px 12px;margin-bottom:3px;font:inherit;font-size:14px;'
|
+ 'cursor:pointer;border:none;border-radius:6px;padding:10px 12px;margin-bottom:3px;font:inherit;font-size:14px;'
|
||||||
+ (tab.value === t.key ? `background:${ACCENT};color:#fff;font-weight:600;` : 'background:transparent;color:var(--foreground-normal);'),
|
+ (tab.value === t.key ? `background:${ACCENT};color:#fff;font-weight:600;` : 'background:transparent;color:var(--foreground-normal);'),
|
||||||
|
|||||||
Reference in New Issue
Block a user