Files
skrift-directus/extensions/directus-extension-skrift-orderentries/dist/index.js
2026-08-24 12:13:58 +02:00

150 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Skrift – Interface „Empfänger (durchsuchbar)"
* ---------------------------------------------------------------------------
* Zeigt im Auftrag alle Empfängerzeilen als kompakte, durchsuchbare Tabelle –
* die Produktionsansicht aus dem alten Job-Manager: pro Zeile die Briefnummer
* und die VOLLE Anschrift bzw. der Freitext (nicht nur der Name). Das Suchfeld
* filtert über die ganze Zeile (Name, Straße, Ort, Land, Platzhalter …).
*
* Rein lesend/suchend – bearbeitet wird eine Zeile weiterhin über das darunter
* liegende o2m-Feld „Empfänger (bearbeiten)".
*
* Bewusst von Hand als ESM geschrieben (kein Build-Schritt): Vue und der
* Extensions-SDK werden von Directus zur Laufzeit bereitgestellt.
*/
import { defineComponent, ref, computed, inject, watch, onMounted, h } from 'vue';
import { useApi } from '@directus/extensions-sdk';
const INLAND = /^(de|deutschland|germany)$/i;
/** Baut die volle Anzeigezeile einer Empfängerzeile. */
function volleZeile(e) {
if (e.free_text && String(e.free_text).trim()) {
return String(e.free_text).trim().split(/\r?\n/).map((s) => s.trim()).filter(Boolean).join(' · ');
}
const name = [e.salutation, e.first_name, e.last_name].filter(Boolean).join(' ');
const strasse = [e.street, e.house_no].filter(Boolean).join(' ');
const ort = [e.zip, e.city].filter(Boolean).join(' ');
const land = e.country && !INLAND.test(String(e.country).trim()) ? e.country : null;
return [name, strasse, ort, land].filter(Boolean).join(' · ');
}
/** Zusätzliche freie Platzhalter als „Key: Wert"-Liste. */
function extras(e) {
if (!e.placeholders || typeof e.placeholders !== 'object') return '';
return Object.entries(e.placeholders)
.filter(([, v]) => v !== null && v !== undefined && String(v).trim() !== '')
.map(([k, v]) => `${k}: ${v}`).join(' · ');
}
const EmpfaengerSuche = defineComponent({
setup() {
const api = useApi();
const values = inject('values', ref({}));
const rows = ref([]);
const loading = ref(false);
const error = ref('');
const geladen = ref(false);
const suche = ref('');
const orderId = () => (values && values.value ? values.value.id : null);
async function laden() {
const id = orderId();
if (!id) { rows.value = []; geladen.value = true; return; }
loading.value = true; error.value = '';
try {
const res = await api.get('/items/order_entries', {
params: {
'filter[order][_eq]': id,
sort: 'letter_number',
limit: -1,
fields: '*',
},
});
const data = (res.data && res.data.data) || [];
rows.value = data.map((e) => ({
nr: e.letter_number,
zeile: volleZeile(e),
extra: extras(e),
}));
} catch (e) {
error.value = (e && e.response && e.response.data && e.response.data.errors &&
e.response.data.errors[0] && e.response.data.errors[0].message) ||
(e && e.message) || 'Empfänger konnten nicht geladen werden.';
} finally {
loading.value = false; geladen.value = true;
}
}
onMounted(laden);
watch(orderId, laden);
const treffer = computed(() => {
const q = suche.value.trim().toLowerCase();
if (!q) return rows.value;
return rows.value.filter((r) =>
(`#${r.nr} ${r.zeile} ${r.extra}`).toLowerCase().includes(q));
});
const hinweis = (txt) => h('div', { style: 'color:var(--foreground-subdued);font-size:14px;padding:8px 0;' }, txt);
return () => {
if (loading.value) return hinweis('Empfänger werden geladen …');
if (error.value) return hinweis(error.value);
if (!geladen.value) return hinweis('');
if (!rows.value.length) return hinweis('Noch keine Empfängerzeilen.');
const suchfeld = h('div', { style: 'display:flex;align-items:center;gap:10px;margin-bottom:10px;' }, [
h('input', {
type: 'search',
value: suche.value,
placeholder: 'Empfänger durchsuchen (Name, Straße, Ort, Platzhalter …)',
onInput: (ev) => { suche.value = ev.target.value; },
style: 'flex:1;padding:8px 12px;border:1px solid var(--border-normal);border-radius:6px;' +
'background:var(--background-page);color:var(--foreground-normal);font:inherit;font-size:14px;',
}),
h('span', { style: 'font-size:12px;color:var(--foreground-subdued);white-space:nowrap;' },
`${treffer.value.length} von ${rows.value.length}`),
]);
const zeilen = treffer.value.map((r) =>
h('div', {
key: r.nr,
style: 'display:flex;align-items:baseline;gap:12px;padding:7px 12px;' +
'border:1px solid var(--border-subdued);border-radius:6px;',
}, [
h('span', {
style: 'min-width:44px;font-family:var(--family-monospace, monospace);font-size:13px;' +
'font-weight:600;color:var(--primary);',
}, `#${r.nr}`),
h('div', { style: 'flex:1;font-size:14px;line-height:1.4;word-break:break-word;' }, [
h('span', {}, r.zeile || '—'),
r.extra
? h('span', { style: 'display:block;font-size:12px;color:var(--foreground-subdued);margin-top:2px;' }, r.extra)
: null,
]),
]),
);
const body = treffer.value.length
? h('div', { style: 'display:flex;flex-direction:column;gap:5px;' }, zeilen)
: hinweis('Keine Treffer.');
return h('div', {}, [suchfeld, body]);
};
},
});
export default {
id: 'skrift-order-entries',
name: 'Empfänger (durchsuchbar)',
icon: 'search',
description: 'Durchsuchbare Empfängertabelle: Briefnummer + volle Anschrift (Produktionsansicht).',
component: EmpfaengerSuche,
types: ['alias'],
localTypes: ['presentation'],
group: 'presentation',
options: null,
};