From 0bd046b19258f39075af70c5e7fd730a6d8f1d64 Mon Sep 17 00:00:00 2001 From: s4luorth Date: Thu, 1 Oct 2026 12:01:50 +0200 Subject: [PATCH] Mitarbeiter-Uebersicht & Board: Blau statt Lila, Rahmen, Chargen-Zusammenfassung - Uebersicht: nur Status im_druck/kuvertieren/versandfertig (eingegangen/versendet raus) - Uebersicht: Versandart + Versanddatum nur lesbar (nicht vom Mitarbeiter aenderbar), Status bleibt editierbar - Uebersicht: Chargen-Zusammenfassung mit grossen Kacheln (Schriftstuecke/Kuverts/Format/Versand) + aggregiertem Ablauf; Ablauf nicht mehr pro Mitglied - Board: Farbe zurueck auf Blau #1E50A5, Lupen-Emoji entfernt, Rahmen auf Kacheln/Druckstrip/Lieferbox Co-Authored-By: Claude Opus 4.8 --- .../dist/index.js | 4 +- .../dist/index.js | 20 ++--- .../dist/index.js | 88 +++++++++++++------ 3 files changed, 75 insertions(+), 37 deletions(-) diff --git a/extensions/directus-extension-skrift-neuauftrag/dist/index.js b/extensions/directus-extension-skrift-neuauftrag/dist/index.js index ac97118..d34cc9e 100644 --- a/extensions/directus-extension-skrift-neuauftrag/dist/index.js +++ b/extensions/directus-extension-skrift-neuauftrag/dist/index.js @@ -12,7 +12,7 @@ import { defineComponent, ref, reactive, onMounted, h, resolveComponent, Teleport } from 'vue'; import { useApi } from '@directus/extensions-sdk'; -const ACCENT = 'var(--primary,#1E50A5)'; +const ACCENT = '#1E50A5'; // Klar umrandetes, theme-sicheres Eingabefeld (sieht aus wie ein Feld). const WFELD = 'background:var(--background-page);border:1px solid var(--border-normal);border-radius:8px;padding:9px 12px;font:inherit;font-size:14px;color:var(--foreground-normal);outline:none;'; const WSELECT = WFELD + 'cursor:pointer;'; @@ -728,7 +728,7 @@ const NeuAuftrag = defineComponent({ label('Kunde verknüpfen (E-Mail, optional)'), h('input', { type: 'email', value: neu.customer_email, placeholder: 'kunde@example.de', style: WFELD + 'width:100%;', onInput: (e) => { neu.customer_email = e.target.value; pruefeKunde(); } }), neu.kundeStatus === 'vorhanden' ? h('div', { style: 'font-size:12px;color:var(--success,#2e7d32);margin-top:5px;font-weight:600;' }, '✓ Bestehender Kunde wird verknüpft') : null, - neu.kundeStatus === 'neu' ? h('div', { style: 'font-size:12px;color:var(--primary,#1e50a5);margin-top:5px;font-weight:600;' }, 'Neuer Kunde – bitte unten Stammdaten erfassen.') : null, + neu.kundeStatus === 'neu' ? h('div', { style: 'font-size:12px;color:#1E50A5;margin-top:5px;font-weight:600;' }, 'Neuer Kunde – bitte unten Stammdaten erfassen.') : null, ]), ]), // Neukundenanlage erzwingen, sobald eine unbekannte E-Mail eingetragen ist. diff --git a/extensions/directus-extension-skrift-produktion/dist/index.js b/extensions/directus-extension-skrift-produktion/dist/index.js index 13fc96f..1727c4a 100644 --- a/extensions/directus-extension-skrift-produktion/dist/index.js +++ b/extensions/directus-extension-skrift-produktion/dist/index.js @@ -70,7 +70,7 @@ const STATUS_LABEL = { // Farbe + Material-Icon je Produktionsstatus (für den klaren Status-Pill). const STATUS_META = { eingegangen: { bg: 'var(--background-subdued,#eef1f6)', fg: 'var(--foreground-subdued)', icon: 'inbox' }, - im_druck: { bg: 'rgba(30,80,165,.12)', fg: 'var(--primary,#1e50a5)', icon: 'print' }, + im_druck: { bg: 'rgba(30,80,165,.12)', fg: '#1E50A5', icon: 'print' }, kuvertieren: { bg: 'rgba(106,63,208,.12)', fg: '#6a3fd0', icon: 'mail' }, versandfertig: { bg: 'rgba(230,160,0,.16)', fg: '#8a6d00', icon: 'inventory_2' }, versendet: { bg: 'rgba(46,125,50,.14)', fg: 'var(--success,#2e7d32)', icon: 'local_shipping' }, @@ -94,7 +94,7 @@ const S_INPUT = 'padding:8px 11px;border:1px solid var(--border-normal);border-r const S_SELECT = S_INPUT + 'cursor:pointer;'; // Directus-Primärfarbe (Marke) + Vorgangs-Akzente. -const ACCENT = 'var(--primary,#1E50A5)'; // Aufträge / Druck → blau (Theme-Primär) +const ACCENT = '#1E50A5'; // Aufträge / Druck → blau (Theme-Primär) const FARBE_MUSTER = '#d13438'; // Musteranfrage → rot const FARBE_ANGEBOT = '#e07b00'; // Angebotsanfrage → orange @@ -129,7 +129,7 @@ function metaItem(text) { } // Beschriftete Eckdaten-Kachel (Label oben, Wert darunter) – klare Struktur. function tile(labelTxt, value, gross) { - return h('div', { style: 'background:var(--background-subdued,#f1f4f9);border-radius:8px;padding:9px 12px;min-width:0;' }, [ + return h('div', { style: 'background:var(--background-subdued,#f1f4f9);border:1px solid var(--border-normal);border-radius:8px;padding:9px 12px;min-width:0;' }, [ h('div', { style: 'font-size:11.5px;color:var(--foreground-subdued);' }, labelTxt), h('div', { style: `font-weight:500;overflow:hidden;text-overflow:ellipsis;${gross ? 'font-size:20px;line-height:1.1;margin-top:2px;' : 'font-size:15px;margin-top:3px;'}` }, value), ]); @@ -1440,7 +1440,7 @@ const Board = defineComponent({ : h('input', { type: 'checkbox', checked: istGewaehlt(o.id), onClick: (e) => toggleAuswahl(o.id, e), title: 'Für Sammel-Druck auswählen', style: 'width:19px;height:19px;margin-top:3px;cursor:pointer;flex-shrink:0;' }), h('div', { style: 'min-width:0;' }, [ - o.order_name ? h('div', { style: 'font-size:12px;font-weight:500;color:var(--primary,#1e50a5);text-transform:uppercase;letter-spacing:.03em;margin-bottom:1px;' }, o.order_name) : null, + o.order_name ? h('div', { style: 'font-size:12px;font-weight:500;color:#1E50A5;text-transform:uppercase;letter-spacing:.03em;margin-bottom:1px;' }, o.order_name) : null, h('div', { style: 'font-size:20px;font-weight:500;letter-spacing:-.01em;line-height:1.2;' }, identName || `#${o.order_number}`), h('div', { style: 'font-size:14px;color:var(--foreground-subdued);margin-top:2px;' }, (identName ? `#${o.order_number} · ` : '') + `${o.entries_count || 0} Empfänger`), ]), @@ -1460,7 +1460,7 @@ const Board = defineComponent({ const kuvOk = !!o.kuvert_gedruckt || !!(send && send.umschlag.length); const statusWort = (ok, ranges) => h('span', { style: `font-weight:500;color:${ok ? 'var(--success,#2e7d32)' : 'var(--danger,#c62828)'};` }, ok ? ('gedruckt' + (ranges && ranges.length ? ' · ' + ranges.join(', ') : '')) : 'nicht gedruckt'); - const druckStrip = h('div', { style: 'display:flex;gap:20px;flex-wrap:wrap;padding:10px 12px;background:var(--background-subdued,#f6f8fb);border-radius:8px;font-size:14px;' }, [ + const druckStrip = h('div', { style: 'display:flex;gap:20px;flex-wrap:wrap;padding:10px 12px;background:var(--background-subdued,#f6f8fb);border:1px solid var(--border-normal);border-radius:8px;font-size:14px;' }, [ h('span', { style: 'display:inline-flex;align-items:center;gap:6px;' }, [icon('description'), 'Brief: ', statusWort(briefOk, send && send.brief)]), o.needs_envelope ? h('span', { style: 'display:inline-flex;align-items:center;gap:6px;' }, [icon('mail'), 'Kuvert: ', statusWort(kuvOk, send && send.umschlag)]) : null, ].filter(Boolean)); @@ -1477,7 +1477,7 @@ const Board = defineComponent({ const pillRow = pills.length ? h('div', { style: 'display:flex;gap:7px;flex-wrap:wrap;' }, pills) : null; // ── Sammelversand: Lieferadresse ─────────────────────────────────────── const liefer = (o.shipping_type !== 'einzeln') ? lieferAdresse(o) : null; - const lieferBox = liefer ? h('div', { style: 'padding:11px 13px;background:var(--background-subdued,#f6f8fb);border-radius:8px;font-size:14px;line-height:1.55;' }, [ + const lieferBox = liefer ? h('div', { style: 'padding:11px 13px;background:var(--background-subdued,#f6f8fb);border:1px solid var(--border-normal);border-radius:8px;font-size:14px;line-height:1.55;' }, [ h('div', { style: 'display:flex;align-items:center;gap:5px;font-weight:500;color:var(--foreground-subdued);font-size:11.5px;text-transform:uppercase;letter-spacing:.04em;margin-bottom:3px;' }, [icon('local_shipping'), 'Versand an · ' + liefer.quelle]), ...liefer.zeilen.map((z) => h('div', {}, z)), ]) : null; @@ -1487,7 +1487,7 @@ const Board = defineComponent({ h('span', { style: `color:${bezahlt ? 'var(--success,#2e7d32)' : '#8a6d00'};font-weight:500;` }, bezahlt ? 'bezahlt' : (o.payment_status === 'storniert' ? 'storniert' : 'offen')), (Number(o.count_ausland) > 0) ? h('span', {}, `${o.count_inland ?? 0} DE · ${o.count_ausland} Ausland`) : null, o.shipping_day ? h('span', {}, 'Versandtag ' + o.shipping_day.charAt(0).toUpperCase() + o.shipping_day.slice(1)) : null, - h('a', { href: `/admin/content/orders/${o.id}`, target: '_blank', rel: 'noopener', style: 'display:inline-flex;align-items:center;gap:3px;color:var(--primary,#1e50a5);text-decoration:none;font-weight:500;' }, [icon('open_in_new'), 'Auftrag öffnen']), + h('a', { href: `/admin/content/orders/${o.id}`, target: '_blank', rel: 'noopener', style: 'display:inline-flex;align-items:center;gap:3px;color:#1E50A5;text-decoration:none;font-weight:500;' }, [icon('open_in_new'), 'Auftrag öffnen']), ].filter(Boolean); const subline = h('div', { style: 'display:flex;align-items:center;gap:12px;flex-wrap:wrap;font-size:13px;color:var(--foreground-subdued);' }, sub.reduce((acc, el, i) => (i === 0 ? [el] : acc.concat(h('span', { style: 'color:var(--border-normal);', 'aria-hidden': 'true' }, '·'), el)), [])); @@ -1544,7 +1544,7 @@ const Board = defineComponent({ h('div', { style: 'display:flex;align-items:center;gap:7px;font-weight:500;font-size:16px;' }, [icon('inbox'), `${c.kundeName} · ${c.produkt}`]), chip(`${c.orders.length} Aufträge`), c.versandtag ? chip('Versandtag ' + c.versandtag.charAt(0).toUpperCase() + c.versandtag.slice(1)) : null, - c.druckZeit ? chip('gedruckt ' + dmYhm(c.druckZeit), 'rgba(30,80,165,.1)', 'var(--primary,#1e50a5)') : null, + c.druckZeit ? chip('gedruckt ' + dmYhm(c.druckZeit), 'rgba(30,80,165,.1)', '#1E50A5') : null, c.faellig ? chip('fällig bis ' + dmY(c.faellig), faelligFarbe) : null, ]); // Summen der Charge (Schriftstücke · Kuverts · Format · Versand). @@ -1763,7 +1763,7 @@ const Board = defineComponent({ ]); }); return [ - bestandMsg.value ? h('div', { style: 'margin-bottom:10px;color:var(--primary,#1e50a5);font-size:13px;font-weight:600;' }, bestandMsg.value) : null, + bestandMsg.value ? h('div', { style: 'margin-bottom:10px;color:#1E50A5;font-size:13px;font-weight:600;' }, bestandMsg.value) : null, subdued('Zugänge/Auffüllungen hier eintragen. Abgänge werden beim Druck automatisch gebucht (+10 % Puffer).', 'margin-bottom:10px;'), ...bloecke, ]; @@ -2144,7 +2144,7 @@ const Board = defineComponent({ const kopfzeile = h('div', { style: 'display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:18px;' }, [ // Globales Filter-Feld – im Suche-Reiter ausgeblendet (dort gilt das eigene // große Suchfeld, das nicht in die anderen Reiter übernommen wird). - tab.value === 'suche' ? null : h('input', { type: 'search', value: suche.value, placeholder: '🔎 Auftrag, Kunde, Empfänger …', + tab.value === 'suche' ? null : h('input', { type: 'text', value: suche.value, placeholder: 'Auftrag, Kunde, Empfänger …', style: S_INPUT + 'flex:1;min-width:240px;', onInput: (e) => { suche.value = e.target.value; } }), sichtbareOrders().length ? btn(alleSichtbarGewaehlt() ? 'Auswahl aufheben' : 'Alle auswählen', alleAuswaehlen, 'default', alleSichtbarGewaehlt() ? 'deselect' : 'select_all') : null, btn('Aktualisieren', laden, 'default', 'refresh'), diff --git a/extensions/directus-extension-skrift-uebersicht/dist/index.js b/extensions/directus-extension-skrift-uebersicht/dist/index.js index f59beae..c2c6ece 100644 --- a/extensions/directus-extension-skrift-uebersicht/dist/index.js +++ b/extensions/directus-extension-skrift-uebersicht/dist/index.js @@ -20,13 +20,13 @@ const STATUS_LABEL = { }; const STATUS_META = { eingegangen: { bg: 'var(--background-subdued,#eef1f6)', fg: 'var(--foreground-subdued)', icon: 'inbox' }, - im_druck: { bg: 'rgba(30,80,165,.12)', fg: 'var(--primary,#1e50a5)', icon: 'print' }, + im_druck: { bg: 'rgba(30,80,165,.12)', fg: '#1E50A5', icon: 'print' }, kuvertieren: { bg: 'rgba(106,63,208,.12)', fg: '#6a3fd0', icon: 'mail' }, versandfertig: { bg: 'rgba(230,160,0,.16)', fg: '#8a6d00', icon: 'inventory_2' }, versendet: { bg: 'rgba(46,125,50,.14)', fg: 'var(--success,#2e7d32)', icon: 'local_shipping' }, storniert: { bg: 'rgba(198,40,40,.12)', fg: 'var(--danger,#c62828)', icon: 'cancel' }, }; -const AKTIV_STATUS = ['eingegangen', 'im_druck', 'kuvertieren', 'versandfertig']; +const AKTIV_STATUS = ['im_druck', 'kuvertieren', 'versandfertig']; const FORMAT_LABEL = { a4: 'A4', a5p: 'A5 hoch', a5l: 'A5 quer', dinlang_karte: 'DIN lang', a6_hoch: 'A6 hoch', a6_quer: 'A6 quer', dinlang: 'DIN Lang', c6: 'C6', c4: 'C4' }; // Format → Standard-Template-Schlüssel (Fallback, wie im Board). const LETTER_TPL = { a4: 'a4_single', a5p: 'a5_v1', a5l: 'a5l_v1', dinlang_karte: 'dl_v1', a6_hoch: 'a6_v1', a6_quer: 'a6l_v1' }; @@ -101,9 +101,10 @@ const Board = { const idx = {}; const flach = []; const ids = orders.value.map((x) => x.id); try { + // Relationsfilter (kurze URL) statt _in mit hunderten IDs (sonst 414). const ent = ids.length ? await api.get('/items/order_entries', { params: { - limit: -1, sort: 'letter_number', - filter: { order: { _in: ids } }, + limit: -1, sort: 'order', + filter: { order: { production_status: { _in: AKTIV_STATUS } } }, fields: 'order,letter_number,salutation,first_name,last_name,company,street,house_no,zip,city,country,free_text', } }) : { data: { data: [] } }; for (const e of (ent.data?.data || [])) { @@ -217,23 +218,26 @@ const Board = { const einzeln = o.shipping_type === 'einzeln'; switch (o.production_status) { case 'eingegangen': return { t: 'Wartet auf Druck', c: 'var(--foreground-subdued)', i: 'hourglass_empty' }; - case 'im_druck': return { t: 'Im Druck', c: 'var(--primary,#1e50a5)', i: 'print' }; + case 'im_druck': return { t: 'Im Druck', c: '#1E50A5', i: 'print' }; case 'kuvertieren': return { t: (o.needs_envelope ? 'Kuvertieren' : 'Vorbereiten') + ' + frankieren', c: '#6a3fd0', i: 'mail' }; case 'versandfertig': return { t: einzeln ? 'Einzelversand' : 'Sammelversand', c: '#8a6d00', i: 'local_shipping' }; default: return { t: STATUS_LABEL[o.production_status] || '', c: 'var(--foreground-subdued)', i: 'info' }; } } + // Nur-Lese-Anzeige für Mitarbeiter: Versandart darf hier nicht geändert werden. function versandToggle(o) { const einzeln = o.shipping_type === 'einzeln'; - return h('button', { type: 'button', title: 'Umschalten Sammel/Einzel', onClick: () => setVersand(o, einzeln ? 'sammel' : 'einzeln'), - style: 'cursor:pointer;border:1px solid var(--border-normal);background:var(--background-page,#fff);border-radius:999px;padding:4px 11px;font:inherit;font-size:12.5px;font-weight:500;display:inline-flex;align-items:center;gap:5px;' }, + return h('span', { title: 'Versandart (nur Ansicht)', + style: 'border:1px solid var(--border-subdued);background:var(--background-subdued,#f2f4f8);border-radius:999px;padding:4px 11px;font-size:12.5px;font-weight:500;display:inline-flex;align-items:center;gap:5px;color:var(--foreground-normal);' }, [icon(einzeln ? 'person' : 'groups'), einzeln ? 'Einzeln' : 'Sammel']); } + // Nur-Lese-Anzeige für Mitarbeiter: Versanddatum darf hier nicht geändert werden. function datumFeld(o) { - return h('input', { type: 'date', value: o.versanddatum || '', title: 'Versanddatum (leer = schnellstmöglich)', - style: 'border:1px solid var(--border-normal);border-radius:8px;padding:6px 8px;font:inherit;font-size:13px;background:var(--background-page);color:var(--foreground-normal);' - + (o.versanddatum && o.versanddatum <= new Date().toISOString().slice(0, 10) ? 'border-color:var(--danger,#c62828);color:var(--danger,#c62828);' : ''), - onChange: (e) => setVersanddatum(o, e.target.value) }); + const faellig = o.versanddatum && o.versanddatum <= new Date().toISOString().slice(0, 10); + const txt = o.versanddatum ? o.versanddatum : 'schnellstmöglich'; + return h('span', { title: 'Versanddatum (nur Ansicht)', + style: 'display:inline-flex;align-items:center;gap:5px;font-size:13px;color:' + (faellig ? 'var(--danger,#c62828)' : 'var(--foreground-normal)') + ';font-weight:' + (faellig ? '600' : '400') + ';' }, + [icon('event'), txt]); } // Ein Ablauf-Schritt: nicht nötig (✗, grau) · erledigt (✓, grün) · offen (amber). function stepPill(labelTxt, benoetigt, erledigt) { @@ -264,29 +268,63 @@ const Board = { (e && e.anzahl > 1) ? h('div', { style: 'font-size:12.5px;color:var(--foreground-subdued);' }, '… ' + empfKurz(e.letzter)) : null, ])), td(h('div', { style: 'line-height:1.4;font-size:13px;' }, [ - o.order_name ? h('div', { style: 'font-weight:500;color:var(--primary,#1e50a5);' }, o.order_name) : null, + o.order_name ? h('div', { style: 'font-weight:500;color:#1E50A5;' }, o.order_name) : null, h('div', {}, kundeName(o) || `#${o.order_number}`), h('div', { style: 'color:var(--foreground-subdued);' }, `#${o.order_number} · ${o.entries_count || 0} Empf.`), ])), td(h('span', {}, o.format?.name || FORMAT_LABEL[o.format?.key] || o.format?.key || '—')), - td(ablaufStrip(o)), + td(indent ? h('span', { style: 'color:var(--foreground-subdued);font-size:12px;' }, '— (s. Zusammenfassung)') : ablaufStrip(o)), td(versandToggle(o)), td(datumFeld(o)), td(statusPill(o)), ]); } + // Große Kachel für die Chargen-Zusammenfassung. + function kachel(label, wert, ic) { + return h('div', { style: 'flex:1;min-width:104px;border:1px solid var(--border-normal);border-radius:10px;padding:9px 12px;background:var(--background-page,#fff);' }, [ + h('div', { style: 'display:flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--foreground-subdued);text-transform:uppercase;letter-spacing:.04em;' }, [icon(ic), label]), + h('div', { style: 'font-size:19px;font-weight:600;color:var(--foreground-normal);margin-top:3px;line-height:1.2;' }, String(wert)), + ]); + } + // Aggregierter Ablauf über alle Aufträge einer Charge. + function ablaufStripAgg(orders) { + const needEnv = orders.filter((o) => o.needs_envelope); + const needFrank = orders.filter((o) => (typeof o.frankiert === 'boolean') ? o.frankiert : (o.shipping_type !== 'sammel')); + const alle = (arr, fn) => arr.length > 0 && arr.every(fn); + const fertigOrder = (o) => ['versandfertig', 'versendet'].includes(o.production_status); + const steps = [ + ['Druck Schriftstück', true, alle(orders, (o) => !!o.brief_gedruckt)], + ['Druck Kuvert', needEnv.length > 0, alle(needEnv, (o) => !!o.kuvert_gedruckt)], + ['Kuvertieren', needEnv.length > 0, alle(needEnv, fertigOrder)], + ['Frankieren', needFrank.length > 0, alle(needFrank, fertigOrder)], + ['Versand', true, alle(orders, (o) => o.production_status === 'versendet')], + ]; + return h('div', { style: 'display:flex;gap:5px;flex-wrap:wrap;' }, steps.map(([l, b, e]) => stepPill(l, b, e))); + } function chargeHeaderRow(c) { const auf = !!expandedCharges[c.key]; const sumS = c.orders.reduce((a, o) => a + (Number(o.entries_count) || 0), 0); const sumK = c.orders.reduce((a, o) => a + (o.needs_envelope ? (Number(o.entries_count) || 0) : 0), 0); const fmts = [...new Set(c.orders.map((o) => o.format?.name || o.format?.key).filter(Boolean))]; const versands = [...new Set(c.orders.map((o) => o.shipping_type === 'einzeln' ? 'Einzel' : 'Sammel'))]; - return h('tr', { key: 'c' + c.key, style: 'background:var(--background-subdued,#f6f8fb);cursor:pointer;', onClick: () => { expandedCharges[c.key] = !auf; } }, [ - h('td', { colspan: 7, style: 'padding:10px 12px;border-bottom:1px solid var(--border-normal);' }, [ - h('div', { style: 'display:flex;align-items:center;gap:10px;flex-wrap:wrap;' }, [ - icon(auf ? 'expand_more' : 'chevron_right'), - h('span', { style: 'font-weight:500;font-size:15px;display:inline-flex;align-items:center;gap:6px;' }, [icon('inbox'), `${c.kundeName} · ${c.produkt}`]), - h('span', { style: 'font-size:13px;color:var(--foreground-subdued);' }, `${c.orders.length} Aufträge · ${sumS} Schriftstücke · ${sumK} Kuverts · ${fmts.join(', ') || '—'} · ${versands.length === 1 ? versands[0] : versands.join('/')}`), + return h('tr', { key: 'c' + c.key, style: 'background:var(--background-subdued,#f6f8fb);' }, [ + h('td', { colspan: 7, style: 'padding:0;border-bottom:1px solid var(--border-normal);' }, [ + h('div', { style: 'padding:12px 14px;display:flex;flex-direction:column;gap:11px;' }, [ + // Kopfzeile: aufklappbar + h('div', { style: 'display:flex;align-items:center;gap:10px;flex-wrap:wrap;cursor:pointer;', onClick: () => { expandedCharges[c.key] = !auf; } }, [ + icon(auf ? 'expand_more' : 'chevron_right'), + h('span', { style: 'font-weight:600;font-size:15px;display:inline-flex;align-items:center;gap:6px;' }, [icon('inbox'), `${c.kundeName} · ${c.produkt}`]), + h('span', { style: 'font-size:13px;color:var(--foreground-subdued);' }, `${c.orders.length} Aufträge`), + ]), + // Große Kacheln + h('div', { style: 'display:flex;gap:9px;flex-wrap:wrap;' }, [ + kachel('Schriftstücke', sumS, 'description'), + kachel('Kuverts', sumK, 'mail'), + kachel('Format', fmts.join(', ') || '—', 'crop_free'), + kachel('Versand', versands.length === 1 ? versands[0] : versands.join(' / '), 'local_shipping'), + ]), + // Aggregierter Ablauf + ablaufStripAgg(c.orders), ]), ]), ]); @@ -316,7 +354,7 @@ const Board = { aktiveMaschinen.value.length ? aktiveMaschinen.value.map((m) => h('div', { key: 'm' + m.id, style: 'display:flex;align-items:center;gap:12px;flex-wrap:wrap;' }, [ h('div', { style: 'font-weight:500;min-width:150px;' }, [m.name, h('span', { style: 'color:var(--foreground-subdued);font-weight:400;font-size:13px;' }, m.current_format ? ` · eingelegt: ${FORMAT_LABEL[m.current_format] || m.current_format}` : ' · kein Format eingelegt')]), ...(formate.length ? formate.map((f) => h('button', { type: 'button', onClick: () => formatStarten(m, f), - style: `cursor:pointer;border-radius:8px;padding:8px 14px;font:inherit;font-size:14px;font-weight:500;display:inline-flex;align-items:center;gap:6px;border:1px solid;${m.current_format === f ? 'border-color:var(--primary,#1e50a5);background:var(--primary,#1e50a5);color:#fff;' : 'border-color:var(--border-normal);background:var(--background-page,#fff);color:var(--foreground-normal);'}`, + style: `cursor:pointer;border-radius:8px;padding:8px 14px;font:inherit;font-size:14px;font-weight:500;display:inline-flex;align-items:center;gap:6px;border:1px solid;${m.current_format === f ? 'border-color:#1E50A5;background:#1E50A5;color:#fff;' : 'border-color:var(--border-normal);background:var(--background-page,#fff);color:var(--foreground-normal);'}`, }, [icon('play_arrow'), `${qf[f].label} (${qf[f].n})`])) : [h('span', { style: 'color:var(--foreground-subdued);font-size:13px;' }, 'Keine Jobs in der Warteschlange.')]), ])) : [h('span', { style: 'color:var(--foreground-subdued);font-size:13px;' }, 'Keine aktive Maschine.')]), @@ -325,7 +363,7 @@ const Board = { function uebersichtInhalt() { if (loading.value) return h('div', { style: 'color:var(--foreground-subdued);' }, 'Lädt …'); - const GRUPPEN = [['im_druck', 'Im Druck'], ['kuvertieren', 'Kuvertierung'], ['eingegangen', 'Eingegangen']]; + const GRUPPEN = [['im_druck', 'Im Druck'], ['kuvertieren', 'Kuvertierung']]; const sek = []; for (const [st, label] of GRUPPEN) { const list = gefiltert.value.filter((o) => (o.production_status || 'eingegangen') === st); @@ -353,7 +391,7 @@ const Board = { h('div', { style: 'display:flex;gap:10px;flex-wrap:wrap;' }, TYPEN.map(([k, l, ic]) => { const aktiv = sucheTyp.value === k; return h('button', { type: 'button', onClick: () => { sucheTyp.value = k; }, - style: `cursor:pointer;border-radius:10px;padding:13px 20px;font:inherit;font-size:16px;font-weight:500;display:inline-flex;align-items:center;gap:8px;border:2px solid;${aktiv ? 'background:var(--primary,#1e50a5);color:#fff;border-color:var(--primary,#1e50a5);' : 'background:var(--background-page,#fff);color:var(--foreground-normal);border-color:var(--border-normal);'}` }, [icon(ic), l]); + style: `cursor:pointer;border-radius:10px;padding:13px 20px;font:inherit;font-size:16px;font-weight:500;display:inline-flex;align-items:center;gap:8px;border:2px solid;${aktiv ? 'background:#1E50A5;color:#fff;border-color:#1E50A5;' : 'background:var(--background-page,#fff);color:var(--foreground-normal);border-color:var(--border-normal);'}` }, [icon(ic), l]); })), ]); const feld = h('div', { style: 'margin-top:18px;' }, [ @@ -374,11 +412,11 @@ const Board = { const adrLines = (e.adr && e.adr.length) ? e.adr : [e.name || '—']; const kannKuvert = !!(o.needs_envelope && o.envelope_labeling && o.envelope_labeling !== 'keine'); const aktion = sucheTyp.value === 'objekt' - ? h('a', { href: `/admin/content/orders/${o.id}`, target: '_blank', rel: 'noopener', style: 'display:inline-flex;align-items:center;gap:6px;background:var(--primary,#1e50a5);color:#fff;border-radius:8px;padding:9px 16px;font-size:14px;font-weight:500;text-decoration:none;' }, [icon('open_in_new'), 'Auftrag öffnen']) + ? h('a', { href: `/admin/content/orders/${o.id}`, target: '_blank', rel: 'noopener', style: 'display:inline-flex;align-items:center;gap:6px;background:#1E50A5;color:#fff;border-radius:8px;padding:9px 16px;font-size:14px;font-weight:500;text-decoration:none;' }, [icon('open_in_new'), 'Auftrag öffnen']) : (sucheTyp.value === 'umschlag' && !kannKuvert) ? h('span', { style: 'color:var(--foreground-subdued);font-size:14px;' }, 'Kein Kuvert') : h('button', { type: 'button', onClick: () => schnelldruck(o, e.nr, sucheTyp.value), - style: 'cursor:pointer;display:inline-flex;align-items:center;gap:6px;background:var(--primary,#1e50a5);color:#fff;border:none;border-radius:8px;padding:10px 18px;font:inherit;font-size:15px;font-weight:500;' }, [icon('print'), 'In Warteschlange']); + style: 'cursor:pointer;display:inline-flex;align-items:center;gap:6px;background:#1E50A5;color:#fff;border:none;border-radius:8px;padding:10px 18px;font:inherit;font-size:15px;font-weight:500;' }, [icon('print'), 'In Warteschlange']); return h('div', { key: `s${o.id}-${e.nr}`, style: 'border:1px solid var(--border-normal);border-radius:12px;background:var(--background-page,#fff);padding:14px 16px;margin-bottom:8px;display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;' }, [ h('div', { style: 'min-width:0;' }, [ ...adrLines.map((z, i) => h('div', { style: i === 0 ? 'font-weight:500;font-size:16px;' : 'font-size:14px;color:var(--foreground-normal);' }, z)), @@ -396,7 +434,7 @@ const Board = { const navItem = (key, label, ic) => h('button', { type: 'button', onClick: () => { clearTimeout(sucheTimer); sucheQuery.value = ''; view.value = key; }, style: 'display:flex;align-items:center;gap:10px;width:100%;text-align:left;cursor:pointer;border:none;border-radius:8px;padding:11px 13px;margin-bottom:3px;font:inherit;font-size:14px;' - + (view.value === key ? 'background:var(--primary,#1e50a5);color:#fff;font-weight:500;' : 'background:transparent;color:var(--foreground-normal);'), + + (view.value === key ? 'background:#1E50A5;color:#fff;font-weight:500;' : 'background:transparent;color:var(--foreground-normal);'), }, [icon(ic), label]); return () => {