Mitarbeiter-Uebersicht: Empfaenger ausklappbar + Kommentar; Auftragsnr nur 1x; Board: Versanddatum setzbar
Uebersicht: - Empfaenger-Zelle: erster Empfaenger + Umschalter "alle N Empfaenger" -> klappt eine Detailzeile mit der VOLLEN Empfaengerliste aus (kein gesondertes erster/letzter mehr) - gleiche Detailzeile hat ein Kommentarfeld je Auftrag (internal_note) - Auftragsnummer wird nur noch EINMAL angezeigt (keine Doppelung mehr) - volle Empfaengerliste + internal_note werden mitgeladen Produktionsboard: - Versanddatum je Auftrag unter "Weiteres -> Aufgaben umstellen" setzbar (Datum, leer = schnellstmoeglich). Mitarbeiter-Uebersicht bleibt bei Versandart/-datum weiterhin nur lesbar (wie gewuenscht). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1405,6 +1405,9 @@ const Board = defineComponent({
|
|||||||
h('div', { style: 'font-size:11.5px;font-weight:500;color:#566070;text-transform:uppercase;letter-spacing:.03em;margin-bottom:8px;' }, 'Aufgaben umstellen'),
|
h('div', { style: 'font-size:11.5px;font-weight:500;color:#566070;text-transform:uppercase;letter-spacing:.03em;margin-bottom:8px;' }, 'Aufgaben umstellen'),
|
||||||
h('div', { style: 'display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:14px;' }, [
|
h('div', { style: 'display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:14px;' }, [
|
||||||
h('div', {}, [feldLabel('Versandart'), segToggle(o.shipping_type === 'einzeln' ? 'einzeln' : 'sammel', [{ key: 'einzeln', label: 'Einzelversand' }, { key: 'sammel', label: 'Sammelversand' }], (k) => setOrderFeld(o, { shipping_type: k }))]),
|
h('div', {}, [feldLabel('Versandart'), segToggle(o.shipping_type === 'einzeln' ? 'einzeln' : 'sammel', [{ key: 'einzeln', label: 'Einzelversand' }, { key: 'sammel', label: 'Sammelversand' }], (k) => setOrderFeld(o, { shipping_type: k }))]),
|
||||||
|
h('div', {}, [feldLabel('Versanddatum (leer = schnellstmöglich)'), h('input', { type: 'date', value: o.versanddatum || '',
|
||||||
|
style: 'width:100%;box-sizing:border-box;border:1px solid #b4c0d2;border-radius:8px;padding:9px 11px;font:inherit;font-size:14px;background:#ffffff;color:#1b2430;',
|
||||||
|
onChange: (e) => setOrderFeld(o, { versanddatum: e.target.value || null }) })]),
|
||||||
h('div', {}, [feldLabel('Frankierung'), segToggle(((typeof o.frankiert === 'boolean') ? o.frankiert : (o.shipping_type !== 'sammel')) ? 'ja' : 'nein', [{ key: 'ja', label: 'Frankieren' }, { key: 'nein', label: 'Nicht' }], (k) => setOrderFeld(o, { frankiert: k === 'ja' }))]),
|
h('div', {}, [feldLabel('Frankierung'), segToggle(((typeof o.frankiert === 'boolean') ? o.frankiert : (o.shipping_type !== 'sammel')) ? 'ja' : 'nein', [{ key: 'ja', label: 'Frankieren' }, { key: 'nein', label: 'Nicht' }], (k) => setOrderFeld(o, { frankiert: k === 'ja' }))]),
|
||||||
h('div', {}, [feldLabel('Kuvert schreiben'), segToggle(o.needs_envelope ? 'ja' : 'nein', [{ key: 'ja', label: 'Mit Kuvert' }, { key: 'nein', label: 'Ohne' }], (k) => setOrderFeld(o, { needs_envelope: k === 'ja' }))]),
|
h('div', {}, [feldLabel('Kuvert schreiben'), segToggle(o.needs_envelope ? 'ja' : 'nein', [{ key: 'ja', label: 'Mit Kuvert' }, { key: 'nein', label: 'Ohne' }], (k) => setOrderFeld(o, { needs_envelope: k === 'ja' }))]),
|
||||||
h('div', {}, [feldLabel('Blatt einlegen (kuvertieren)'), segToggle((o.kuvertieren !== false) ? 'ja' : 'nein', [{ key: 'ja', label: 'Blatt rein' }, { key: 'nein', label: 'Nur Kuvert' }], (k) => setOrderFeld(o, { kuvertieren: k === 'ja' }))]),
|
h('div', {}, [feldLabel('Blatt einlegen (kuvertieren)'), segToggle((o.kuvertieren !== false) ? 'ja' : 'nein', [{ key: 'ja', label: 'Blatt rein' }, { key: 'nein', label: 'Nur Kuvert' }], (k) => setOrderFeld(o, { kuvertieren: k === 'ja' }))]),
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ const Board = {
|
|||||||
const entriesByOrder = ref({}); // { orderId: {erster, letzter, anzahl} }
|
const entriesByOrder = ref({}); // { orderId: {erster, letzter, anzahl} }
|
||||||
const sucheEintraege = ref([]); // flache Empfängerliste für die Suche
|
const sucheEintraege = ref([]); // flache Empfängerliste für die Suche
|
||||||
const expandedCharges = reactive({});
|
const expandedCharges = reactive({});
|
||||||
|
const expandedEmpf = reactive({}); // je Auftrag: volle Empfängerliste + Kommentar ausklappen
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const msg = ref(''); const err = ref('');
|
const msg = ref(''); const err = ref('');
|
||||||
const sucheQuery = ref(''); let sucheTimer = null;
|
const sucheQuery = ref(''); let sucheTimer = null;
|
||||||
@@ -92,7 +93,7 @@ const Board = {
|
|||||||
// Sichere Basisfelder (alle auf orders/formats/products – von der
|
// Sichere Basisfelder (alle auf orders/formats/products – von der
|
||||||
// Mitarbeiter-Rolle lesbar). api_client (API-Kundenname) und kuvertieren
|
// Mitarbeiter-Rolle lesbar). api_client (API-Kundenname) und kuvertieren
|
||||||
// sind optional: fehlende Rechte/Felder dürfen die Ansicht NICHT blockieren.
|
// sind optional: fehlende Rechte/Felder dürfen die Ansicht NICHT blockieren.
|
||||||
const basis = 'id,order_number,order_name,production_status,needs_envelope,brief_gedruckt,kuvert_gedruckt,frankiert,'
|
const basis = 'id,order_number,order_name,production_status,needs_envelope,brief_gedruckt,kuvert_gedruckt,frankiert,internal_note,'
|
||||||
+ 'format.key,format.name,shipping_day,shipping_type,versanddatum,date_created,entries_count,source,'
|
+ 'format.key,format.name,shipping_day,shipping_type,versanddatum,date_created,entries_count,source,'
|
||||||
+ 'envelope_labeling,envelope_format,product.name';
|
+ 'envelope_labeling,envelope_format,product.name';
|
||||||
const mitClient = basis + ',api_client.id,api_client.name';
|
const mitClient = basis + ',api_client.id,api_client.name';
|
||||||
@@ -137,8 +138,8 @@ const Board = {
|
|||||||
const idx = {};
|
const idx = {};
|
||||||
for (const e of (ent.data?.data || [])) {
|
for (const e of (ent.data?.data || [])) {
|
||||||
const k = String(e.order?.id ?? e.order); if (!k || k === 'null') continue;
|
const k = String(e.order?.id ?? e.order); if (!k || k === 'null') continue;
|
||||||
if (!idx[k]) idx[k] = { erster: e, letzter: e, anzahl: 1 };
|
if (!idx[k]) idx[k] = { erster: e, letzter: e, anzahl: 1, alle: [e] };
|
||||||
else { idx[k].letzter = e; idx[k].anzahl += 1; }
|
else { idx[k].letzter = e; idx[k].anzahl += 1; if (idx[k].alle.length < 2000) idx[k].alle.push(e); }
|
||||||
}
|
}
|
||||||
entriesByOrder.value = idx;
|
entriesByOrder.value = idx;
|
||||||
} catch { /* Empfänger optional */ }
|
} catch { /* Empfänger optional */ }
|
||||||
@@ -192,6 +193,7 @@ const Board = {
|
|||||||
const setStatus = (o, v) => patchOrder(o, { production_status: v });
|
const setStatus = (o, v) => patchOrder(o, { production_status: v });
|
||||||
const setVersand = (o, v) => patchOrder(o, { shipping_type: v });
|
const setVersand = (o, v) => patchOrder(o, { shipping_type: v });
|
||||||
const setVersanddatum = (o, v) => patchOrder(o, { versanddatum: v || null });
|
const setVersanddatum = (o, v) => patchOrder(o, { versanddatum: v || null });
|
||||||
|
const setNote = (o, v) => patchOrder(o, { internal_note: v });
|
||||||
// Status für einen ganzen Sammelauftrag (alle Teil-Aufträge) auf einmal.
|
// Status für einen ganzen Sammelauftrag (alle Teil-Aufträge) auf einmal.
|
||||||
async function setChargeStatus(c, v) {
|
async function setChargeStatus(c, v) {
|
||||||
try {
|
try {
|
||||||
@@ -324,24 +326,58 @@ const Board = {
|
|||||||
];
|
];
|
||||||
return h('div', { style: 'display:flex;gap:5px;flex-wrap:wrap;' }, steps.map(([l, b, e]) => stepPill(l, b, e)));
|
return h('div', { style: 'display:flex;gap:5px;flex-wrap:wrap;' }, steps.map(([l, b, e]) => stepPill(l, b, e)));
|
||||||
}
|
}
|
||||||
|
// Gibt ein Zeilen-ARRAY zurück: Hauptzeile + (bei Ausklappen) Detailzeile mit
|
||||||
|
// voller Empfängerliste und Kommentarfeld.
|
||||||
function zeile(o, indent) {
|
function zeile(o, indent) {
|
||||||
const e = entriesByOrder.value[String(o.id)];
|
const id = String(o.id);
|
||||||
return h('tr', { key: 'o' + o.id }, [
|
const e = entriesByOrder.value[id];
|
||||||
td(h('div', { style: `line-height:1.45;${indent ? 'padding-left:18px;border-left:2px solid #d2dae6;' : ''}` }, [
|
const auf = !!expandedEmpf[id];
|
||||||
h('div', { style: 'font-weight:500;' }, empfKurz(e?.erster) || '—'),
|
const anzahl = e ? e.anzahl : (o.entries_count || 0);
|
||||||
(e && e.anzahl > 1) ? h('div', { style: 'font-size:12.5px;color:#566070;' }, '… ' + empfKurz(e.letzter)) : null,
|
// Empfänger: erster + Umschalter „alle anzeigen" (kein gesondertes letzter mehr).
|
||||||
])),
|
const empfZelle = h('div', { style: `line-height:1.45;${indent ? 'padding-left:18px;border-left:2px solid #d2dae6;' : ''}` }, [
|
||||||
td(h('div', { style: 'line-height:1.4;font-size:13px;' }, [
|
h('div', { style: 'font-weight:500;' }, empfKurz(e?.erster) || '—'),
|
||||||
o.order_name ? h('div', { style: 'font-weight:500;color:#1E50A5;' }, o.order_name) : null,
|
(anzahl > 1) ? h('button', { type: 'button', onClick: () => { expandedEmpf[id] = !auf; },
|
||||||
h('div', {}, kundeName(o) || `#${o.order_number}`),
|
style: 'cursor:pointer;border:none;background:transparent;padding:2px 0;margin-top:2px;color:#1E50A5;font:inherit;font-size:12.5px;font-weight:600;display:inline-flex;align-items:center;gap:3px;' },
|
||||||
h('div', { style: 'color:#566070;' }, `#${o.order_number} · ${o.entries_count || 0} Empf.`),
|
[icon(auf ? 'expand_less' : 'expand_more'), auf ? 'Empfänger ausblenden' : `alle ${anzahl} Empfänger`]) : null,
|
||||||
])),
|
].filter(Boolean));
|
||||||
|
// Auftrag: Name/Kunde + Nummer NUR EINMAL.
|
||||||
|
const name = o.order_name || kundeName(o);
|
||||||
|
const auftragZelle = h('div', { style: 'line-height:1.4;font-size:13px;' }, [
|
||||||
|
name ? h('div', { style: 'font-weight:500;' + (o.order_name ? 'color:#1E50A5;' : '') }, name) : null,
|
||||||
|
h('div', { style: 'color:#566070;' }, `#${o.order_number} · ${o.entries_count || 0} Empf.`),
|
||||||
|
].filter(Boolean));
|
||||||
|
const haupt = h('tr', { key: 'o' + o.id }, [
|
||||||
|
td(empfZelle),
|
||||||
|
td(auftragZelle),
|
||||||
td(h('span', {}, o.format?.name || FORMAT_LABEL[o.format?.key] || o.format?.key || '—')),
|
td(h('span', {}, o.format?.name || FORMAT_LABEL[o.format?.key] || o.format?.key || '—')),
|
||||||
td(ablaufStrip(o)),
|
td(ablaufStrip(o)),
|
||||||
td(versandToggle(o)),
|
td(versandToggle(o)),
|
||||||
td(datumFeld(o)),
|
td(datumFeld(o)),
|
||||||
td(statusPill(o)),
|
td(statusPill(o)),
|
||||||
]);
|
]);
|
||||||
|
if (!auf) return [haupt];
|
||||||
|
const liste = (e && e.alle) ? e.alle : [];
|
||||||
|
const detail = h('tr', { key: 'd' + o.id }, [
|
||||||
|
h('td', { colspan: 7, style: `padding:0 12px 14px;border-bottom:1px solid #d2dae6;${indent ? 'padding-left:30px;' : ''}` }, [
|
||||||
|
h('div', { style: 'display:grid;grid-template-columns:1fr 1fr;gap:16px;background:#f7f9fc;border:1px solid #d2dae6;border-radius:10px;padding:12px 14px;' }, [
|
||||||
|
h('div', {}, [
|
||||||
|
h('div', { style: 'font-size:11.5px;font-weight:600;color:#566070;text-transform:uppercase;letter-spacing:.03em;margin-bottom:6px;' }, `Empfänger (${liste.length || anzahl})`),
|
||||||
|
h('div', { style: 'max-height:220px;overflow:auto;display:flex;flex-direction:column;gap:3px;' },
|
||||||
|
liste.length ? liste.map((x, i) => h('div', { key: i, style: 'font-size:13px;display:flex;gap:8px;' }, [
|
||||||
|
h('span', { style: 'color:#566070;min-width:28px;' }, (i + 1) + '.'),
|
||||||
|
h('span', {}, empfKurz(x) || '—'),
|
||||||
|
])) : [h('span', { style: 'color:#566070;font-size:13px;' }, 'Keine Empfängerdaten geladen.')]),
|
||||||
|
]),
|
||||||
|
h('div', {}, [
|
||||||
|
h('div', { style: 'font-size:11.5px;font-weight:600;color:#566070;text-transform:uppercase;letter-spacing:.03em;margin-bottom:6px;' }, 'Kommentar'),
|
||||||
|
h('textarea', { rows: 4, placeholder: 'Kommentar zum Auftrag …',
|
||||||
|
style: 'width:100%;box-sizing:border-box;border:1px solid #b4c0d2;border-radius:8px;padding:9px 11px;font:inherit;font-size:13px;background:#ffffff;color:#1b2430;resize:vertical;',
|
||||||
|
value: o.internal_note || '', onChange: (ev) => setNote(o, ev.target.value) }),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
return [haupt, detail];
|
||||||
}
|
}
|
||||||
// Aggregierter Ablauf über alle Aufträge einer Charge.
|
// Aggregierter Ablauf über alle Aufträge einer Charge.
|
||||||
function ablaufStripAgg(orders) {
|
function ablaufStripAgg(orders) {
|
||||||
@@ -398,8 +434,8 @@ const Board = {
|
|||||||
const api = list.filter((o) => o.source === 'api' || o.api_client);
|
const api = list.filter((o) => o.source === 'api' || o.api_client);
|
||||||
const rest = list.filter((o) => !(o.source === 'api' || o.api_client));
|
const rest = list.filter((o) => !(o.source === 'api' || o.api_client));
|
||||||
const rows = [];
|
const rows = [];
|
||||||
for (const c of bildeChargen(api)) { rows.push(chargeZeile(c)); if (expandedCharges[c.key]) for (const o of c.orders) rows.push(zeile(o, true)); }
|
for (const c of bildeChargen(api)) { rows.push(chargeZeile(c)); if (expandedCharges[c.key]) for (const o of c.orders) rows.push(...zeile(o, true)); }
|
||||||
for (const o of rest) rows.push(zeile(o));
|
for (const o of rest) rows.push(...zeile(o));
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
const kopfRow = () => h('thead', {}, h('tr', {}, [th('Empfänger (erster · letzter)'), th('Auftrag'), th('Format'), th('Ablauf'), th('Versand'), th('Versanddatum'), th('Status')]));
|
const kopfRow = () => h('thead', {}, h('tr', {}, [th('Empfänger (erster · letzter)'), th('Auftrag'), th('Format'), th('Ablauf'), th('Versand'), th('Versanddatum'), th('Status')]));
|
||||||
|
|||||||
Reference in New Issue
Block a user