Uebersicht: Empfaenger als Hauptinfo, Status-getrennte Tabellen, Aufgabe, Versand-Toggle
- Empfaenger (erster/letzter) ist die fuehrende Spalte; Ladefehler behoben (order _in statt Relationsfilter). Auftrag/Kunde sekundaer. - Je Status eine eigene Tabelle (Im Druck / Kuvertierung / Versandfertig / Eingegangen). - Neue Spalte "Aufgabe" (kuvertieren/frankieren/Versandart aus Status abgeleitet). - Versandart pro Auftrag umschaltbar (Sammel/Einzel). - Maschinen-Panel zaehlt nur WARTENDE Jobs (nicht "im Druck"). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -85,12 +85,13 @@ const Board = {
|
||||
machines.value = m.data?.data || [];
|
||||
// Erster/letzter Empfänger je Auftrag (für die Adressspalte).
|
||||
const idx = {};
|
||||
const ids = orders.value.map((x) => x.id);
|
||||
try {
|
||||
const ent = await api.get('/items/order_entries', { params: {
|
||||
limit: -1, sort: 'order,letter_number',
|
||||
filter: { order: { production_status: { _in: AKTIV_STATUS } } },
|
||||
const ent = ids.length ? await api.get('/items/order_entries', { params: {
|
||||
limit: -1, sort: 'letter_number',
|
||||
filter: { order: { _in: ids } },
|
||||
fields: 'order,first_name,last_name,company,city,letter_number,free_text',
|
||||
} });
|
||||
} }) : { data: { data: [] } };
|
||||
for (const e of (ent.data?.data || [])) {
|
||||
const k = String(e.order?.id ?? e.order);
|
||||
if (!k || k === 'null') continue;
|
||||
@@ -111,7 +112,12 @@ const Board = {
|
||||
const eingeplantSet = computed(() => new Set(jobs.value.map((j) => String(j.order?.id)).filter((x) => x && x !== 'null')));
|
||||
const queuedByFormat = computed(() => {
|
||||
const map = {};
|
||||
for (const j of jobs.value) { const f = j.format || '?'; if (!map[f]) map[f] = { n: 0, label: j.format_label || FORMAT_LABEL[f] || f }; map[f].n += 1; }
|
||||
for (const j of jobs.value) {
|
||||
if (j.status !== 'queued') continue; // nur wartende Jobs (nicht „im Druck")
|
||||
const f = j.format || '?';
|
||||
if (!map[f]) map[f] = { n: 0, label: j.format_label || FORMAT_LABEL[f] || f };
|
||||
map[f].n += 1;
|
||||
}
|
||||
return map;
|
||||
});
|
||||
const gefiltert = computed(() => {
|
||||
@@ -154,33 +160,53 @@ const Board = {
|
||||
const th = (t, extra) => h('th', { style: `text-align:left;font-size:12px;font-weight:500;color:var(--foreground-subdued);text-transform:uppercase;letter-spacing:.03em;padding:10px 12px;border-bottom:1px solid var(--border-normal);white-space:nowrap;${extra || ''}` }, t);
|
||||
const td = (kids, extra) => h('td', { style: `padding:11px 12px;border-bottom:1px solid var(--border-subdued);font-size:14px;vertical-align:top;${extra || ''}` }, kids);
|
||||
|
||||
// Nächste konkrete Aufgabe für den Mitarbeiter (aus Status + Kuvert + Versandart).
|
||||
function aufgabe(o) {
|
||||
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 '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' };
|
||||
}
|
||||
}
|
||||
async function setVersand(o, v) {
|
||||
try { await api.patch('/items/orders/' + o.id, { shipping_type: v }); o.shipping_type = v; }
|
||||
catch (e) { err.value = 'Versandart konnte nicht gesetzt 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;' },
|
||||
[icon(einzeln ? 'person' : 'groups'), einzeln ? 'Einzeln' : 'Sammel']);
|
||||
}
|
||||
function zeile(o) {
|
||||
const e = entriesByOrder.value[String(o.id)];
|
||||
const faellig = faelligBis(o.date_created, o.shipping_day);
|
||||
const empf = e
|
||||
? h('div', { style: 'line-height:1.5;' }, [
|
||||
h('div', {}, empfKurz(e.erster) || '—'),
|
||||
e.anzahl > 1 ? h('div', { style: 'color:var(--foreground-subdued);' }, '… ' + empfKurz(e.letzter)) : null,
|
||||
])
|
||||
: h('span', { style: 'color:var(--foreground-subdued);' }, '—');
|
||||
const briefOk = !!o.brief_gedruckt;
|
||||
const kuvOk = !!o.kuvert_gedruckt;
|
||||
const ag = aufgabe(o);
|
||||
const eingeplant = eingeplantSet.value.has(String(o.id));
|
||||
return h('tr', { key: 'o' + o.id }, [
|
||||
td(h('div', { style: 'line-height:1.4;' }, [
|
||||
o.order_name ? h('div', { style: 'font-weight:500;color:var(--primary,#1e50a5);' }, o.order_name) : null,
|
||||
h('div', { style: 'font-weight:500;' }, kundeName(o) || `#${o.order_number}`),
|
||||
h('div', { style: 'font-size:12.5px;color:var(--foreground-subdued);' }, `#${o.order_number} · ${o.entries_count || 0} Empf.`),
|
||||
// Empfänger = Hauptinfo.
|
||||
td(h('div', { style: 'line-height:1.45;' }, [
|
||||
h('div', { style: 'font-weight:500;' }, empfKurz(e?.erster) || '—'),
|
||||
(e && e.anzahl > 1) ? h('div', { style: 'font-size:12.5px;color:var(--foreground-subdued);' }, '… ' + empfKurz(e.letzter)) : null,
|
||||
])),
|
||||
// Auftrag = sekundär.
|
||||
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,
|
||||
h('div', {}, kundeName(o) || `#${o.order_number}`),
|
||||
h('div', { style: 'color:var(--foreground-subdued);' }, `#${o.order_number} · ${o.entries_count || 0} Empf.`),
|
||||
])),
|
||||
td(empf),
|
||||
td(h('span', {}, o.format?.name || FORMAT_LABEL[o.format?.key] || o.format?.key || '—')),
|
||||
td(h('span', { style: `display:inline-flex;align-items:center;gap:5px;font-weight:500;color:${ag.c};` }, [icon(ag.i), ag.t])),
|
||||
td(versandToggle(o)),
|
||||
td(h('span', { style: faellig && faellig.getTime() < Date.now() ? 'color:var(--danger,#c62828);font-weight:500;' : '' }, dmY(faellig))),
|
||||
td(h('div', { style: 'display:flex;flex-direction:column;gap:4px;align-items:flex-start;' }, [
|
||||
druckBadge(briefOk, 'Brief'),
|
||||
o.needs_envelope ? druckBadge(kuvOk, 'Kuvert') : h('span', { style: 'font-size:12px;color:var(--foreground-subdued);' }, 'ohne Kuvert'),
|
||||
])),
|
||||
td(eingeplantSet.value.has(String(o.id))
|
||||
? h('span', { style: 'display:inline-flex;align-items:center;gap:4px;font-size:12.5px;font-weight:500;color:var(--primary,#1e50a5);' }, [icon('check'), 'eingeplant'])
|
||||
: h('span', { style: 'font-size:12.5px;color:var(--foreground-subdued);' }, 'nein')),
|
||||
druckBadge(!!o.brief_gedruckt, 'Brief'),
|
||||
o.needs_envelope ? druckBadge(!!o.kuvert_gedruckt, 'Kuvert') : null,
|
||||
eingeplant ? h('span', { style: 'display:inline-flex;align-items:center;gap:3px;font-size:11.5px;font-weight:500;color:var(--primary,#1e50a5);' }, [icon('check'), 'eingeplant']) : null,
|
||||
].filter(Boolean))),
|
||||
td(statusPill(o)),
|
||||
]);
|
||||
}
|
||||
@@ -218,13 +244,24 @@ const Board = {
|
||||
maschinenPanel(),
|
||||
loading.value
|
||||
? h('div', { style: 'color:var(--foreground-subdued);' }, 'Lädt …')
|
||||
: h('div', { style: 'border:1px solid var(--border-normal);border-radius:12px;overflow:auto;background:var(--background-page,#fff);' }, [
|
||||
h('table', { style: 'width:100%;border-collapse:collapse;' }, [
|
||||
h('thead', {}, h('tr', {}, [th('Auftrag'), th('Empfänger (erster · letzter)'), th('Format'), th('Fällig'), th('Druck'), th('Eingeplant'), th('Status')])),
|
||||
h('tbody', {}, gefiltert.value.length ? gefiltert.value.map(zeile) : [h('tr', {}, h('td', { colspan: 7, style: 'padding:24px;text-align:center;color:var(--foreground-subdued);' }, 'Keine Aufträge.'))]),
|
||||
]),
|
||||
]),
|
||||
h('div', { style: 'margin-top:10px;font-size:12.5px;color:var(--foreground-subdued);' }, `${gefiltert.value.length} von ${orders.value.length} offenen Aufträgen`),
|
||||
: (() => {
|
||||
const GRUPPEN = [['im_druck', 'Im Druck'], ['kuvertieren', 'Kuvertierung'], ['versandfertig', 'Versandfertig'], ['eingegangen', 'Eingegangen']];
|
||||
const kopf = () => h('thead', {}, h('tr', {}, [th('Empfänger (erster · letzter)'), th('Auftrag'), th('Format'), th('Aufgabe'), th('Versand'), th('Fällig'), th('Stand'), th('Status')]));
|
||||
const sek = GRUPPEN.map(([st, label]) => {
|
||||
const list = gefiltert.value.filter((o) => (o.production_status || 'eingegangen') === st);
|
||||
if (!list.length) return null;
|
||||
const m = STATUS_META[st];
|
||||
return h('div', { key: 'sec' + st, style: 'margin-bottom:22px;' }, [
|
||||
h('div', { style: 'display:flex;align-items:center;gap:10px;margin-bottom:10px;' }, [
|
||||
h('span', { style: `display:inline-flex;align-items:center;gap:6px;font-size:16px;font-weight:500;color:${m.fg};` }, [icon(m.icon), label]),
|
||||
h('span', { style: 'font-size:13px;color:var(--foreground-subdued);' }, `${list.length} Auftrag/Aufträge`),
|
||||
]),
|
||||
h('div', { style: 'border:1px solid var(--border-normal);border-radius:12px;overflow:auto;background:var(--background-page,#fff);' },
|
||||
[h('table', { style: 'width:100%;border-collapse:collapse;' }, [kopf(), h('tbody', {}, list.map(zeile))])]),
|
||||
]);
|
||||
}).filter(Boolean);
|
||||
return sek.length ? h('div', {}, sek) : h('div', { style: 'padding:28px;text-align:center;color:var(--foreground-subdued);border:1px solid var(--border-normal);border-radius:12px;background:var(--background-page,#fff);' }, 'Keine offenen Aufträge.');
|
||||
})(),
|
||||
]);
|
||||
return h(resolveComponent('private-view'), { title: 'Übersicht' }, { default: () => inhalt });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user