From 4ff125bece912b5d465057981ae0b91a291ed26d Mon Sep 17 00:00:00 2001 From: s4luorth Date: Thu, 1 Oct 2026 11:30:18 +0200 Subject: [PATCH] Neuauftrag: Design angeglichen (Felder, Icons, Theme) Umrandete, theme-sichere Eingabefelder (sieht aus wie ein Feld, Dark-Mode ok), Section-Koepfe mit Material-Icons statt Emoji, konsistente Buttons (Primaer gefuellt / Sekundaer Umriss), getoente Bereiche mit Haarlinie. Primaerfarbe ueber Theme-Variable. Co-Authored-By: Claude Opus 4.8 --- .../dist/index.js | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/extensions/directus-extension-skrift-neuauftrag/dist/index.js b/extensions/directus-extension-skrift-neuauftrag/dist/index.js index 523bd29..ac97118 100644 --- a/extensions/directus-extension-skrift-neuauftrag/dist/index.js +++ b/extensions/directus-extension-skrift-neuauftrag/dist/index.js @@ -12,31 +12,32 @@ import { defineComponent, ref, reactive, onMounted, h, resolveComponent, Teleport } from 'vue'; import { useApi } from '@directus/extensions-sdk'; -const ACCENT = '#1E50A5'; -// Weißes, randloses Eingabefeld – hebt sich durch Kontrast + leichten Schatten -// vom hellblauen Bereich ab (kein Rahmen). -const WFELD = 'background:#fff;border:none;border-radius:8px;padding:9px 12px;font:inherit;font-size:14px;color:#1A1A1A;outline:none;box-shadow:0 1px 2px rgba(0,0,0,.08);'; +const ACCENT = 'var(--primary,#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;'; -// Hellblauer Bereich (kein Rahmen). -const BEREICH = 'background:rgba(30,80,165,.06);border-radius:12px;padding:18px 20px;'; +// Getönter Bereich mit Haarlinie. +const BEREICH = 'background:var(--background-subdued,#f6f8fb);border:1px solid var(--border-subdued);border-radius:12px;padding:18px 20px;'; const STANDARD_PLATZHALTER = ['Anrede', 'Vorname', 'Nachname', 'Strasse', 'PLZ', 'Ort', 'Land', 'Briefnummer']; -function btn(label, onClick, kind) { - const base = 'cursor:pointer;border-radius:8px;padding:8px 16px;font:inherit;font-size:14px;border:none;'; - const style = kind === 'primary' ? base + `background:${ACCENT};color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.15);` - : base + `background:#fff;color:${ACCENT};box-shadow:0 1px 2px rgba(0,0,0,.1);`; - return h('button', { type: 'button', onClick, style }, label); +// Echtes Directus-Icon (Material Symbols) statt Emoji. +const icon = (name, extra = {}) => h(resolveComponent('v-icon'), { name, small: true, ...extra }); +function btn(label, onClick, kind, ic) { + const base = 'cursor:pointer;border-radius:8px;padding:9px 16px;font:inherit;font-size:14px;font-weight:500;display:inline-flex;align-items:center;justify-content:center;gap:6px;border:1px solid;'; + const style = kind === 'primary' ? base + `border-color:${ACCENT};background:${ACCENT};color:#fff;` + : base + 'border-color:var(--border-normal);background:var(--background-page,#fff);color:var(--foreground-normal);'; + return h('button', { type: 'button', onClick, style }, [ic ? icon(ic) : null, label].filter(Boolean)); } function subdued(t, extra) { return h('div', { style: `color:var(--foreground-subdued);font-size:13px;${extra || ''}` }, t); } function segToggle(value, opts, onSel) { - return h('div', { style: 'display:flex;width:100%;border-radius:8px;overflow:hidden;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.08);' }, + return h('div', { style: 'display:flex;width:100%;border-radius:8px;overflow:hidden;border:1px solid var(--border-normal);background:var(--background-page,#fff);' }, opts.map((o) => h('button', { type: 'button', onClick: () => onSel(o.key), style: `flex:1;padding:10px 8px;font:inherit;font-size:13px;text-align:center;cursor:pointer;border:none;` - + (value === o.key ? `background:${ACCENT};color:#fff;font-weight:700;` : `background:transparent;color:${ACCENT};`), + + (value === o.key ? `background:${ACCENT};color:#fff;font-weight:500;` : `background:transparent;color:var(--foreground-normal);`), }, o.label))); } -function bereichKopf(txt) { return h('div', { style: `font-size:15px;font-weight:800;color:${ACCENT};margin-bottom:14px;` }, txt); } +function bereichKopf(ic, txt) { return h('div', { style: 'display:flex;align-items:center;gap:7px;font-size:16px;font-weight:500;color:var(--foreground-normal);margin-bottom:14px;' }, [icon(ic), txt]); } function label(txt) { return h('label', { style: 'display:block;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--foreground-subdued);margin-bottom:4px;' }, txt); } const NeuAuftrag = defineComponent({ @@ -564,7 +565,7 @@ const NeuAuftrag = defineComponent({ // Bereich 1 – Produkt const b1 = h('div', { style: BEREICH }, [ - bereichKopf('📄 Produkt'), + bereichKopf('description', 'Produkt'), h('div', { style: 'display:grid;grid-template-columns:repeat(3,1fr);gap:12px;' }, [ h('div', {}, [label('Produkt'), sel(neu.product, produkte.value.map((p) => ({ v: p.key, t: p.name })), (e) => { neu.product = e.target.value; neu.format = neuFormate()[0]?.key || ''; neu.previewSession = null; })]), h('div', {}, [label('Format'), sel(neu.format, neuFormate().map((f) => ({ v: f.key, t: f.name })), (e) => { neu.format = e.target.value; neu.previewSession = null; })]), @@ -594,7 +595,7 @@ const NeuAuftrag = defineComponent({ // Bereich 2 – Kuvert (Empfängertabelle) const b2 = h('div', { style: BEREICH }, [ h('div', { style: 'display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:14px;' }, [ - bereichKopf('✉ Kuvert'), + bereichKopf('mail', 'Kuvert'), identischModus() ? null : h('div', { style: 'width:420px;max-width:100%;' }, [segToggle(neu.adressModus, [{ key: '5zeilen', label: '5 Zeilen' }, { key: 'felder', label: 'Einzelfelder' }, { key: 'individuell', label: 'Ind. Text' }], (k) => { neu.adressModus = k; neu.previewSession = null; })]), ]), // Ohne Kuvert & ohne eigene Platzhalter genügt eine Stückzahl (identische @@ -663,7 +664,7 @@ const NeuAuftrag = defineComponent({ // Bereich 3 – Text (Mustertext-Dropdown direkt darüber) const b3 = h('div', { style: BEREICH }, [ h('div', { style: 'display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:14px;' }, [ - bereichKopf('✍ Text'), + bereichKopf('edit', 'Text'), h('select', { style: WSELECT + 'font-size:13px;padding:7px 10px;width:auto;', onChange: (e) => { if (e.target.value) { musterEinfuegen(e.target.value); e.target.value = ''; } } }, [h('option', { value: '' }, 'Mustertext einfügen …')].concat( musterListe.value.length @@ -717,7 +718,7 @@ const NeuAuftrag = defineComponent({ // Bereich 4 – Kunde const b4 = h('div', { style: BEREICH }, [ - bereichKopf('👤 Kunde'), + bereichKopf('person', 'Kunde'), h('div', { style: 'display:flex;gap:16px;flex-wrap:wrap;' }, [ h('div', { style: 'flex:1 1 280px;max-width:420px;' }, [ label('Auftragsname (optional)'), @@ -763,7 +764,7 @@ const NeuAuftrag = defineComponent({ // Vorschau rechts daneben (eigener hellblauer Bereich). const rechts = h('div', { style: BEREICH + 'flex:0 0 340px;position:sticky;top:20px;align-self:flex-start;' }, [ h('div', { style: 'display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;' }, [ - bereichKopf('👁 Vorschau'), + bereichKopf('visibility', 'Vorschau'), btn(neu.pLaedt ? '…' : 'Aktualisieren', preview), ]), neu.pLaedt ? subdued('Wird erzeugt …') @@ -900,7 +901,7 @@ const NeuAuftrag = defineComponent({ ]); const b1 = h('div', { style: BEREICH }, [ h('div', { style: 'display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:12px;' }, [ - bereichKopf('✍ Unterschriften'), + bereichKopf('draw', 'Unterschriften'), h('div', { style: 'width:280px;' }, [segToggle(sig.modus, [{ key: 'auftrag', label: 'Aus Auftrag' }, { key: 'manuell', label: 'Manuell' }], (k) => { sig.modus = k; sig.pdf = ''; })]), ]), sig.modus === 'auftrag' @@ -918,7 +919,7 @@ const NeuAuftrag = defineComponent({ ]), ]); const b2 = h('div', { style: BEREICH }, [ - bereichKopf('Unterschriften & Position'), + bereichKopf('tune', 'Unterschriften & Position'), subdued(sig.modus === 'auftrag' ? 'Aus dem Kundenkonto vorbelegt (Marker + Feinjustierung), hier überschreibbar. Weitere Unterschrift-SVGs sind ergänzbar.' : 'Unterschrift(en) als SVG hochladen und Marker/Position setzen.', 'margin-bottom:10px;'), @@ -969,7 +970,7 @@ const NeuAuftrag = defineComponent({ const links = h('div', { style: 'flex:1;min-width:0;display:flex;flex-direction:column;gap:16px;' }, [b1, b2, meldung]); const rechts = h('div', { style: BEREICH + 'flex:0 0 420px;position:sticky;top:20px;align-self:flex-start;min-height:200px;' }, [ h('div', { style: 'display:flex;justify-content:space-between;align-items:center;gap:8px;' }, [ - bereichKopf('👁 Vorschau'), + bereichKopf('visibility', 'Vorschau'), sig.pdf ? h('a', { href: sig.pdf, target: '_blank', rel: 'noopener', style: `font-size:12px;color:${ACCENT};text-decoration:none;white-space:nowrap;` }, '↗ Neuer Tab') : null, ]), sig.pdf