@@ -1,11 +1,13 @@
/**
/**
* Skrift – Modul „Übersicht" (Mitarbeiter, read-only )
* Skrift – Modul „Übersicht" (Mitarbeiter)
* ===========================================================================
* ===========================================================================
* Kompakte Tabelle aller offenen Aufträge – ohne Blättern direkt er sichtlich :
* Secondary-Sidebar mit zwei An sichten :
* Auftrag (Name/Nummer) · erster+letzter Empfänger · Format · fällig ·
* • Übersicht – kompakte, nach Status getrennte Tabellen (API-Aufträge
* Status · Druck (Brief/Kuvert getrennt) · eingeplant.
* gruppiert). Je Auftrag: Empfänger (erster/letzter), Format, Aufgabe,
* Mitarbeiter planen NICHT, dürfen aber: Status setzen, „Format liegt ein →
* Versandart (umschaltbar), Versanddatum (editierbar), Druck Brief/Kuvert,
* Warteschlange starten" (setzt das Maschinenformat) und suchen/filtern .
* eingeplant, Status. Maschinen-Panel: „Format einlegen → starten" .
* • Suche – Empfänger suchen, Schriftstückart wählen, direkt in die
* Warteschlange (Nachdruck nach Fehlern).
*
*
* Von Hand als ESM (kein Build) – Vue + SDK stellt Directus bereit.
* Von Hand als ESM (kein Build) – Vue + SDK stellt Directus bereit.
*/
*/
@@ -24,9 +26,11 @@ const STATUS_META = {
versendet : { bg : 'rgba(46,125,50,.14)' , fg : 'var(--success,#2e7d32)' , icon : 'local_shipping' } ,
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' } ,
storniert : { bg : 'rgba(198,40,40,.12)' , fg : 'var(--danger,#c62828)' , icon : 'cancel' } ,
} ;
} ;
// Aktiv = noch nicht abgeschlossen.
const AKTIV _STATUS = [ 'eingegangen' , 'im_druck' , 'kuvertieren' , 'versandfertig' ] ;
const AKTIV _STATUS = [ 'eingegangen' , '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' } ;
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' } ;
const ENVELOPE _TPL = { dinlang : 'kuvert_a_v1' , c6 : 'kuvert_b_v1' , c4 : 'kuvert_c4_v1' } ;
const WOCHENTAG = { montag : 1 , dienstag : 2 , mittwoch : 3 , donnerstag : 4 , freitag : 5 , samstag : 6 , sonntag : 0 } ;
const WOCHENTAG = { montag : 1 , dienstag : 2 , mittwoch : 3 , donnerstag : 4 , freitag : 5 , samstag : 6 , sonntag : 0 } ;
function faelligBis ( dateCreated , shippingDay ) {
function faelligBis ( dateCreated , shippingDay ) {
@@ -46,60 +50,79 @@ const Board = {
const api = useApi ( ) ;
const api = useApi ( ) ;
const icon = ( name , extra = { } ) => h ( resolveComponent ( 'v-icon' ) , { name , small : true , ... extra } ) ;
const icon = ( name , extra = { } ) => h ( resolveComponent ( 'v-icon' ) , { name , small : true , ... extra } ) ;
const view = ref ( 'uebersicht' ) ; // 'uebersicht' | 'suche'
const orders = ref ( [ ] ) ;
const orders = ref ( [ ] ) ;
const jobs = ref ( [ ] ) ;
const jobs = ref ( [ ] ) ;
const machines = ref ( [ ] ) ;
const machines = ref ( [ ] ) ;
const entriesByOrder = ref ( { } ) ;
const templates = ref ( [ ] ) ;
const entriesByOrder = ref ( { } ) ; // { orderId: {erster, letzter, anzahl} }
const sucheEintraege = ref ( [ ] ) ; // flache Empfängerliste für die Suche
const expandedCharges = reactive ( { } ) ;
const loading = ref ( false ) ;
const loading = ref ( false ) ;
const msg = ref ( '' ) ; const err = ref ( '' ) ;
const msg = ref ( '' ) ; const err = ref ( '' ) ;
const sucheQuery = ref ( '' ) ;
const sucheQuery = ref ( '' ) ; let sucheTimer = null ;
let sucheTimer = null ;
const sucheSetzen = ( v ) => { clearTimeout ( sucheTimer ) ; sucheTimer = setTimeout ( ( ) => { sucheQuery . value = v ; } , 220 ) ; } ;
const sucheSetzen = ( v ) => { clearTimeout ( sucheTimer ) ; sucheTimer = setTimeout ( ( ) => { sucheQuery . value = v ; } , 250 ) ; } ;
const sucheTyp = ref ( 'brief' ) ;
function kundeName ( o ) {
const aktiveMaschinen = computed ( ( ) => machines . value . filter ( ( m ) => m . active !== false ) ) ;
return [ o . customer ? . company , [ o . customer ? . first _name , o . customer ? . last _name ] . filter ( Boolean ) . join ( ' ' ) ] . filter ( Boolean ) . join ( ' · ' ) ;
function kundeName ( o ) { return [ o . customer ? . company , [ o . customer ? . first _name , o . customer ? . last _name ] . filter ( Boolean ) . join ( ' ' ) ] . filter ( Boolean ) . join ( ' · ' ) ; }
}
function empfKurz ( e ) {
function empfKurz ( e ) {
if ( ! e ) return '' ;
if ( ! e ) return '' ;
const name = e . company || [ e . first _name , e . last _name ] . filter ( Boolean ) . join ( ' ' )
const name = e . company || [ e . first _name , e . last _name ] . filter ( Boolean ) . join ( ' ' )
|| ( e . free _text ? String ( e . free _text ) . split ( '\n' ) . map ( ( z ) => z . trim ( ) ) . filter ( Boolean ) [ 0 ] : '' ) ;
|| ( e . free _text ? String ( e . free _text ) . split ( '\n' ) . map ( ( z ) => z . trim ( ) ) . filter ( Boolean ) [ 0 ] : '' ) ;
return [ name , e . city ] . filter ( Boolean ) . join ( ', ' ) ;
return [ name , e . city ] . filter ( Boolean ) . join ( ', ' ) ;
}
}
function entryName ( e ) {
return [ e . salutation , e . first _name , e . last _name ] . filter ( Boolean ) . join ( ' ' )
|| e . company || ( e . free _text ? String ( e . free _text ) . split ( '\n' ) [ 0 ] . trim ( ) : '' ) || '—' ;
}
async function laden ( leise = false ) {
async function laden ( leise = false ) {
if ( ! leise ) { loading . value = true ; err . value = '' ; }
if ( ! leise ) { loading . value = true ; err . value = '' ; }
try {
try {
const [ o , j , m ] = await Promise . all ( [
const [ o , j , m , t ] = await Promise . all ( [
api . get ( '/items/orders' , { params : {
api . get ( '/items/orders' , { params : {
limit : - 1 , sort : 'date_created' ,
limit : - 1 , sort : 'date_created' ,
filter : { production _status : { _in : AKTIV _STATUS } } ,
filter : { production _status : { _in : AKTIV _STATUS } } ,
fields : 'id,order_number,order_name,production_status,needs_envelope,brief_gedruckt,kuvert_gedruckt,'
fields : 'id,order_number,order_name,production_status,needs_envelope,brief_gedruckt,kuvert_gedruckt,'
+ 'format.key,format.name,shipping_day,date_created,entries_count,'
+ 'format.key,format.name,shipping_day,shipping_type,versanddatum, date_created,entries_count,source, '
+ 'envelope_labeling,envelope_format,api_client.id,api_client.name,product.name,'
+ 'customer.first_name,customer.last_name,customer.company' ,
+ 'customer.first_name,customer.last_name,customer.company' ,
} } ) ,
} } ) ,
api . get ( '/items/jobs' , { params : { limit : - 1 , filter : { status : { _in : [ 'queued' , 'printing' ] } } , fields : 'id,type,status,format,format_label,order.id' } } ) . catch ( ( ) => ( { data : { data : [ ] } } ) ) ,
api . get ( '/items/jobs' , { params : { limit : - 1 , filter : { status : { _in : [ 'queued' , 'printing' ] } } , fields : 'id,type,status,format,format_label,order.id' } } ) . catch ( ( ) => ( { data : { data : [ ] } } ) ) ,
api . get ( '/items/machines' , { params : { limit : - 1 , filter : { active : { _eq : true } } , fields : 'id,name,current_format,status' } } ) . catch ( ( ) => ( { data : { data : [ ] } } ) ) ,
api . get ( '/items/machines' , { params : { limit : - 1 , filter : { active : { _eq : true } } , fields : 'id,name,current_format,status,active ' } } ) . catch ( ( ) => ( { data : { data : [ ] } } ) ) ,
api . get ( '/items/format_templates' , { params : { limit : - 1 , fields : 'id,key,name,type,default_format,search_default' } } ) . catch ( ( ) => ( { data : { data : [ ] } } ) ) ,
] ) ;
] ) ;
orders . value = o . data ? . data || [ ] ;
orders . value = o . data ? . data || [ ] ;
jobs . value = j . data ? . data || [ ] ;
jobs . value = j . data ? . data || [ ] ;
machines . value = m . data ? . data || [ ] ;
machines . value = m . data ? . data || [ ] ;
// Erster/letzter Empfänger je Auftrag (für die Adressspalte).
templates . value = t . data ? . data || [ ] ;
const idx = { } ;
// Empfänger (erster/letzter + flache Suchliste) aus order_entries.
const idx = { } ; const flach = [ ] ;
const ids = orders . value . map ( ( x ) => x . id ) ;
const ids = orders . value . map ( ( x ) => x . id ) ;
try {
try {
const ent = ids . length ? await api . get ( '/items/order_entries' , { params : {
const ent = ids . length ? await api . get ( '/items/order_entries' , { params : {
limit : - 1 , sort : 'letter_number' ,
limit : - 1 , sort : 'letter_number' ,
filter : { order : { _in : ids } } ,
filter : { order : { _in : ids } } ,
fields : 'order,first_name,last_name,company,city,letter_number ,free_text' ,
fields : 'order,letter_number,salutation, first_name,last_name,company,street,house_no,zip,city,country ,free_text' ,
} } ) : { data : { data : [ ] } } ;
} } ) : { data : { data : [ ] } } ;
for ( const e of ( ent . data ? . data || [ ] ) ) {
for ( const e of ( ent . data ? . data || [ ] ) ) {
const k = String ( e . order ? . id ? ? e . order ) ;
const k = String ( e . order ? . id ? ? e . order ) ; if ( ! k || k === 'null' ) continue ;
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 } ;
else { idx [ k ] . letzter = e ; idx [ k ] . anzahl += 1 ; }
else { idx [ k ] . letzter = e ; idx [ k ] . anzahl += 1 ; }
const adr = [ ] ;
const person = [ e . salutation , e . first _name , e . last _name ] . filter ( Boolean ) . join ( ' ' ) ;
if ( person ) adr . push ( person ) ; else if ( e . company ) adr . push ( e . company ) ;
const strasse = [ e . street , e . house _no ] . filter ( Boolean ) . join ( ' ' ) ; if ( strasse ) adr . push ( strasse ) ;
const ortZeile = [ e . zip , e . city ] . filter ( Boolean ) . join ( ' ' ) ; if ( ortZeile ) adr . push ( ortZeile ) ;
if ( e . country && ! /^(de|d|deu|deutschland|germany)$/i . test ( String ( e . country ) . trim ( ) ) ) adr . push ( e . country ) ;
if ( ! adr . length && e . free _text ) adr . push ( ... String ( e . free _text ) . split ( '\n' ) . map ( ( z ) => z . trim ( ) ) . filter ( Boolean ) ) ;
const hay = [ e . salutation , e . first _name , e . last _name , e . company , e . street , e . house _no , e . zip , e . city , e . country , e . free _text ] . filter ( Boolean ) . join ( ' ' ) . toLowerCase ( ) ;
flach . push ( { orderId : k , nr : Number ( e . letter _number ) || ( idx [ k ] . anzahl ) , name : entryName ( e ) , adr , hay } ) ;
}
}
} catch { /* Empfänger optional */ }
} catch { /* Empfänger optional */ }
entriesByOrder . value = idx ;
entriesByOrder . value = idx ;
sucheEintraege . value = flach ;
} catch ( e ) {
} catch ( e ) {
if ( ! leise ) err . value = e ? . response ? . data ? . errors ? . [ 0 ] ? . message || e ? . message || 'Laden fehlgeschlagen.' ;
if ( ! leise ) err . value = e ? . response ? . data ? . errors ? . [ 0 ] ? . message || e ? . message || 'Laden fehlgeschlagen.' ;
} finally { if ( ! leise ) loading . value = false ; }
} finally { if ( ! leise ) loading . value = false ; }
@@ -109,15 +132,11 @@ const Board = {
onMounted ( ( ) => { refreshTimer = setInterval ( ( ) => laden ( true ) , 20000 ) ; } ) ;
onMounted ( ( ) => { refreshTimer = setInterval ( ( ) => laden ( true ) , 20000 ) ; } ) ;
onUnmounted ( ( ) => clearInterval ( refreshTimer ) ) ;
onUnmounted ( ( ) => clearInterval ( refreshTimer ) ) ;
const orderById = computed ( ( ) => { const m = { } ; for ( const o of orders . value ) m [ String ( o . id ) ] = o ; return m ; } ) ;
const eingeplantSet = computed ( ( ) => new Set ( jobs . value . map ( ( j ) => String ( j . order ? . id ) ) . filter ( ( x ) => x && x !== 'null' ) ) ) ;
const eingeplantSet = computed ( ( ) => new Set ( jobs . value . map ( ( j ) => String ( j . order ? . id ) ) . filter ( ( x ) => x && x !== 'null' ) ) ) ;
const queuedByFormat = computed ( ( ) => {
const queuedByFormat = computed ( ( ) => {
const map = { } ;
const map = { } ;
for ( const j of jobs . value ) {
for ( const j of jobs . value ) { if ( j . status !== 'queued' ) continue ; const f = j . format || '?' ; if ( ! map [ f ] ) map [ f ] = { n : 0 , label : j . format _label || FORMAT _LABEL [ f ] || f } ; map [ f ] . n += 1 ; }
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 ;
return map ;
} ) ;
} ) ;
const gefiltert = computed ( ( ) => {
const gefiltert = computed ( ( ) => {
@@ -125,42 +144,75 @@ const Board = {
let list = orders . value ;
let list = orders . value ;
if ( q ) list = list . filter ( ( o ) => {
if ( q ) list = list . filter ( ( o ) => {
const e = entriesByOrder . value [ String ( o . id ) ] ;
const e = entriesByOrder . value [ String ( o . id ) ] ;
const hay = [ o . order _number , o . order _name , kundeName ( o ) , empfKurz ( e ? . erster ) , empfKurz ( e ? . letzter ) , o . format ? . name ] . filter ( Boolean ) . join ( ' ' ) . toLowerCase ( ) ;
return [ o . order _number , o . order _name , kundeName ( o ) , empfKurz ( e ? . erster ) , empfKurz ( e ? . letzter ) , o . format ? . name ] . filter ( Boolean ) . join ( ' ' ) . toLowerCase ( ) . includes ( q ) ;
return hay . includes ( q ) ;
} ) ;
} ) ;
return list ;
return list ;
} ) ;
} ) ;
function flash ( t ) { msg . value = t ; setTimeout ( ( ) => { if ( msg . value === t ) msg . value = '' ; } , 4000 ) ; }
function flash ( t ) { msg . value = t ; setTimeout ( ( ) => { if ( msg . value === t ) msg . value = '' ; } , 4000 ) ; }
async function setStatus ( o , v ) {
async function patchOrder ( o , felder ) { try { await api . patch ( '/items/orders/' + o . id , felder ) ; Object . assign ( o , felder ) ; } catch ( e ) { err . value = 'Speichern fehlgeschlagen.' ; } }
try { await api . patch ( '/items/orders/' + o . id , { production _status : v } ) ; o . production _status = v ; }
const setStatus = ( o , v ) => patchOrder ( o , { production _status : v } ) ;
catch ( e ) { er r . value = 'Status konnte nicht gesetzt werden.' ; }
const setVersand = ( o , v ) => patchOrd er( o , { shipping _type : v } ) ;
}
const setVersanddatum = ( o , v ) => patchOrder ( o , { versanddatum : v || null } ) ;
async function formatStarten ( machine , fmt ) {
async function formatStarten ( machine , fmt ) {
try {
try { await api . patch ( '/items/machines/' + machine . id , { current _format : fmt } ) ; machine . current _format = fmt ; flash ( ` ${ machine . name } : Format „ ${ FORMAT _LABEL [ fmt ] || fmt } " eingelegt → Warteschlange läuft. ` ) ; }
await api . patch ( '/items/machines/' + machine . id , { current _format : fmt } ) ;
catch ( e ) { err . value = 'Format konnte nicht gestartet werden.' ; }
machine . current _format = fmt ;
flash ( ` ${ machine . name } : Format „ ${ FORMAT _LABEL [ fmt ] || fmt } " eingelegt → Warteschlange läuft. ` ) ;
} catch ( e ) { err . value = 'Format konnte nicht gestartet werden.' ; }
}
}
// ── Charge-Bildung (API-Aufträge gruppieren) ──────────────────────────────
function bildeChargen ( list ) {
const map = new Map ( ) ;
for ( const o of list ) {
const key = ` ${ o . api _client ? . id ? ? 'x' } | ${ o . product ? . name || '?' } | ${ o . shipping _day || '' } | ${ String ( o . date _created || '' ) . slice ( 0 , 10 ) } ` ;
let c = map . get ( key ) ;
if ( ! c ) { c = { key , kundeName : o . api _client ? . name || 'API-Kunde' , produkt : o . product ? . name || '?' , versandtag : o . shipping _day || '' , orders : [ ] } ; map . set ( key , c ) ; }
c . orders . push ( o ) ;
}
return [ ... map . values ( ) ] ;
}
// ── Reprint (Suche) ───────────────────────────────────────────────────────
const tplSuchStandard = ( typ ) => templates . value . find ( ( t ) => t . type === typ && t . search _default ) || null ;
const tplByKey = ( key , typ ) => templates . value . find ( ( t ) => t . key === key && t . type === typ ) || templates . value . find ( ( t ) => t . type === typ ) || null ;
const tplForFormat = ( fmt , typ ) => ( fmt && templates . value . find ( ( t ) => t . type === typ && t . default _format && t . default _format === fmt ) ) || null ;
async function schnelldruck ( o , nr , typ ) {
const machine = aktiveMaschinen . value [ 0 ] ? . id || null ;
if ( ! machine ) { err . value = 'Keine aktive Maschine verfügbar.' ; return ; }
const tpl = typ === 'umschlag'
? ( tplSuchStandard ( 'umschlag' ) || tplForFormat ( o . envelope _format , 'umschlag' ) || tplByKey ( ENVELOPE _TPL [ o . envelope _format ] || 'kuvert_a_v1' , 'umschlag' ) )
: ( tplSuchStandard ( 'brief' ) || tplForFormat ( o . format ? . key , 'brief' ) || tplByKey ( LETTER _TPL [ o . format ? . key ] || 'a4_single' , 'brief' ) ) ;
if ( ! tpl ? . id ) { err . value = ` Kein Standard-Template für ${ typ === 'umschlag' ? 'Kuvert' : 'Schriftstück' } – bitte in den Einstellungen zuordnen. ` ; return ; }
try {
const sections = { } ; sections [ typ === 'umschlag' ? 'umschlag' : 'brief' ] = { enabled : true , template : tpl . id , numbers : nr ? String ( nr ) : '' } ;
await api . post ( '/skrift-orders/enqueue' , { order : o . id , machine , mode : 'queue' , sections } ) ;
flash ( ` # ${ o . order _number } · ${ typ === 'umschlag' ? 'Kuvert' : 'Schriftstück' } ${ nr ? ` Nr. ${ nr } ` : '' } → Warteschlange. ` ) ;
await laden ( true ) ;
} catch ( e ) { err . value = e ? . response ? . data ? . error || e ? . message || 'Einreihen fehlgeschlagen.' ; }
}
const sucheTreffer = computed ( ( ) => {
const q = sucheQuery . value . trim ( ) . toLowerCase ( ) ; if ( ! q ) return [ ] ;
const res = [ ] ;
for ( const e of sucheEintraege . value ) {
const o = orderById . value [ e . orderId ] ; if ( ! o ) continue ;
if ( ! ( e . hay . includes ( q ) || String ( o . order _number ) . toLowerCase ( ) . includes ( q ) ) ) continue ;
res . push ( { e , o } ) ; if ( res . length >= 150 ) break ;
}
return res ;
} ) ;
// ── Render-Helfer ─────────────────────────────────────────────────────────
// ── Render-Helfer ─────────────────────────────────────────────────────────
const statusPill = ( o ) => {
const statusPill = ( o ) => {
const k = o . production _status || 'eingegangen' ;
const k = o . production _status || 'eingegangen' ; const m = STATUS _META [ k ] || STATUS _META . eingegangen ;
const m = STATUS _META [ k ] || STATUS _META . eingegangen ;
return h ( 'div' , { style : ` display:inline-flex;align-items:center;gap:5px;border-radius:8px;padding:5px 8px 5px 10px;background: ${ m . bg } ;color: ${ m . fg } ;font-weight:500;font-size:13px; ` } , [
return h ( 'div' , { style : ` display:inline-flex;align-items:center;gap:5px;border-radius:8px;padding:5px 8px 5px 10px;background: ${ m . bg } ;color: ${ m . fg } ;font-weight:500;font-size:13px; ` } , [
icon ( m . icon ) ,
icon ( m . icon ) ,
h ( 'select' , { style : ` border:none;background:transparent;color: ${ m . fg } ;font:inherit;font-size:13px;font-weight:500;cursor:pointer; ` , value : k , onChange : ( e ) => setStatus ( o , e . target . value ) } ,
h ( 'select' , { style : ` border:none;background:transparent;color: ${ m . fg } ;font:inherit;font-size:13px;font-weight:500;cursor:pointer; ` , value : k , onChange : ( e ) => setStatus ( o , e . target . value ) } ,
Object . entries ( STATUS _LABEL ) . map ( ( [ kk , v ] ) => h ( 'option' , { value : kk , selected : k === kk } , v ) ) ) ,
Object . entries ( STATUS _LABEL ) . map ( ( [ kk , v ] ) => h ( 'option' , { value : kk , selected : k === kk } , v ) ) ) ,
] ) ;
] ) ;
} ;
} ;
const druckBadge = ( ok , text ) => h ( 'span' , { style : ` display:inline-flex;align-items:center;gap:4px;font-size:12.5px;font-weight:500;padding:2px 8px;border-radius:999px;background: ${ ok ? 'rgba(46,125,50,.14)' : 'rgba(198,40,40,.12)' } ;color: ${ ok ? 'var(--success,#2e7d32)' : 'var(--danger,#c62828)' } ; ` } ,
const druckBadge = ( ok , text ) => h ( 'span' , { style : ` display:inline-flex;align-items:center;gap:4px;font-size:12.5px;font-weight:500;padding:2px 8px;border-radius:999px;background: ${ ok ? 'rgba(46,125,50,.14)' : 'rgba(198,40,40,.12)' } ;color: ${ ok ? 'var(--success,#2e7d32)' : 'var(--danger,#c62828)' } ; ` } , [ icon ( ok ? 'check' : 'close' ) , text ] ) ;
[ icon ( ok ? 'check' : 'close' ) , text ] ) ;
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 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 ) ;
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 ) {
function aufgabe ( o ) {
const einzeln = o . shipping _type === 'einzeln' ;
const einzeln = o . shipping _type === 'einzeln' ;
switch ( o . production _status ) {
switch ( o . production _status ) {
@@ -171,28 +223,27 @@ const Board = {
default : return { t : STATUS _LABEL [ o . production _status ] || '' , c : 'var(--foreground-subdued)' , i : 'info' } ;
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 ) {
function versandToggle ( o ) {
const einzeln = o . shipping _type === 'einzeln' ;
const einzeln = o . shipping _type === 'einzeln' ;
return h ( 'button' , { type : 'button' , title : 'Umschalten Sammel/Einzel' , onClick : ( ) => setVersand ( o , einzeln ? 'sammel' : '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;' } ,
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' ] ) ;
[ icon ( einzeln ? 'person' : 'groups' ) , einzeln ? 'Einzeln' : 'Sammel' ] ) ;
}
}
function zeile ( o ) {
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 ) } ) ;
}
function zeile ( o , indent ) {
const e = entriesByOrder . value [ String ( o . id ) ] ;
const e = entriesByOrder . value [ String ( o . id ) ] ;
const faellig = faelligBis ( o . date _created , o . shipping _day ) ;
const ag = aufgabe ( o ) ;
const ag = aufgabe ( o ) ;
const eingeplant = eingeplantSet . value . has ( String ( o . id ) ) ;
const eingeplant = eingeplantSet . value . has ( String ( o . id ) ) ;
return h ( 'tr' , { key : 'o' + o . id } , [
return h ( 'tr' , { key : 'o' + o . id } , [
// Empfänger = Hauptinfo.
td ( h ( 'div' , { style : ` line-height:1.45; ${ indent ? 'padding-left:18px;border-left:2px solid var(--border-subdued);' : '' } ` } , [
td ( h ( 'div' , { style : 'line-height:1.45;' } , [
h ( 'div' , { style : 'font-weight:500;' } , empfKurz ( e ? . erster ) || '—' ) ,
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 ,
( 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;' } , [
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:var(--primary,#1e50a5);' } , o . order _name ) : null ,
h ( 'div' , { } , kundeName ( o ) || ` # ${ o . order _number } ` ) ,
h ( 'div' , { } , kundeName ( o ) || ` # ${ o . order _number } ` ) ,
@@ -201,7 +252,7 @@ const Board = {
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 ( h ( 'span' , { style : ` display:inline-flex;align-items:center;gap:5px;font-weight:500;color: ${ ag . c } ; ` } , [ icon ( ag . i ) , ag . t ] ) ) ,
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 ( versandToggle ( o ) ) ,
td ( h ( 'span' , { style : faellig && faellig . getTime ( ) < Date . now ( ) ? 'color:var(--danger,#c62828);font-weight:500;' : '' } , dmY ( faellig ) ) ) ,
td ( datumFeld ( o ) ) ,
td ( h ( 'div' , { style : 'display:flex;flex-direction:column;gap:4px;align-items:flex-start;' } , [
td ( h ( 'div' , { style : 'display:flex;flex-direction:column;gap:4px;align-items:flex-start;' } , [
druckBadge ( ! ! o . brief _gedruckt , 'Brief' ) ,
druckBadge ( ! ! o . brief _gedruckt , 'Brief' ) ,
o . needs _envelope ? druckBadge ( ! ! o . kuvert _gedruckt , 'Kuvert' ) : null ,
o . needs _envelope ? druckBadge ( ! ! o . kuvert _gedruckt , 'Kuvert' ) : null ,
@@ -210,60 +261,145 @@ const Board = {
td ( statusPill ( o ) ) ,
td ( statusPill ( o ) ) ,
] ) ;
] ) ;
}
}
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 : 8 , 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 ( '/' ) } ` ) ,
] ) ,
] ) ,
] ) ;
}
// Zeilen einer Status-Gruppe: API gruppiert (Charge-Kopf + bei Aufklappen Mitglieder), Rest einzeln.
function gruppenZeilen ( list ) {
const api = list . filter ( ( o ) => o . source === 'api' || o . api _client ) ;
const rest = list . filter ( ( o ) => ! ( o . source === 'api' || o . api _client ) ) ;
const rows = [ ] ;
for ( const c of bildeChargen ( api ) ) { rows . push ( chargeHeaderRow ( 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 ) ) ;
return rows ;
}
const kopfRow = ( ) => h ( 'thead' , { } , h ( 'tr' , { } , [ th ( 'Empfänger (erster · letzter)' ) , th ( 'Auftrag' ) , th ( 'Format' ) , th ( 'Aufgabe' ) , th ( 'Versand' ) , th ( 'Versanddatum' ) , th ( 'Stand' ) , th ( 'Status' ) ] ) ) ;
const tabelle = ( list ) => 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;' } , [ kopfRow ( ) , h ( 'tbody' , { } , gruppenZeilen ( list ) ) ] ) ] ) ;
const sektionKopf = ( label , m , n ) => 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);' } , ` ${ n } Auftrag/Aufträge ` ) ,
] ) ;
function maschinenPanel ( ) {
function maschinenPanel ( ) {
const qf = queuedByFormat . value ;
const qf = queuedByFormat . value ; const formate = Object . keys ( qf ) ;
const formate = Object . keys ( qf ) ;
return h ( 'div' , { style : 'border:1px solid var(--border-normal);border-radius:12px;background:var(--background-page,#fff);margin-bottom:18px;overflow:hidden;' } , [
return h ( 'div' , { style : 'border:1px solid var(--border-normal);border-radius:12px;background:var(--background-page,#fff);margin-bottom:18px;overflow:hidden;' } , [
h ( 'div' , { style : 'display:flex;align-items:center;gap:6px;font-size:13px;font-weight:500;color:var(--foreground-subdued);padding:10px 14px;border-bottom:1px solid var(--border-subdued);background:var(--background-subdued,#f6f8fb);' } , [ icon ( 'print' ) , 'Maschinen · Format einlegen und starten' ] ) ,
h ( 'div' , { style : 'display:flex;align-items:center;gap:6px;font-size:13px;font-weight:500;color:var(--foreground-subdued);padding:10px 14px;border-bottom:1px solid var(--border-subdued);background:var(--background-subdued,#f6f8fb);' } , [ icon ( 'print' ) , 'Maschinen · Format einlegen und starten' ] ) ,
h ( 'div' , { style : 'padding:14px;display:flex;flex-direction:column;gap:14px;' } ,
h ( 'div' , { style : 'padding:14px;display:flex;flex-direction:column;gap:14px;' } ,
m achines . value . length ? m achines . value . map ( ( m ) => h ( 'div' , { key : 'm' + m . id , style : 'display:flex;align-items:center;gap:12px;flex-wrap:wrap;' } , [
aktiveMas chinen . value . length ? aktiveMas chinen . 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 , m . current _format ? h ( 'span' , { style : 'color:var(--foreground-subdued);font-weight:400;font-size:13px;' } , ` · eingelegt: ${ FORMAT _LABEL [ m . current _format ] || m . current _format } ` ) : h( 'span' , { style : 'color:var(--foreground-subdued);font-weight:400;font-size:13px;' } , ' · kein Format eingelegt' ) ] ) ,
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' , {
... ( formate . length ? formate . map ( ( f ) => h ( 'button' , { type : 'button' , onClick : ( ) => formatStarten ( m , f ) ,
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:var(--primary,#1e50a5);background:var(--primary,#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 } ) ` ] ) )
} , [ 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 Jobs in der Warteschlange.' ) ] ) ,
] ) )
] ) ) : [ h ( 'span' , { style : 'color:var(--foreground-subdued);font-size:13px;' } , 'Keine aktive Maschine.' ) ] ) ,
: [ h ( 'span' , { style : 'color:var(--foreground-subdued);font-size:13px;' } , 'Keine aktive Maschine.' ) ] ) ,
] ) ;
] ) ;
}
}
return ( ) => {
function uebersichtInhalt ( ) {
const inhalt = h ( 'div' , { style : 'padding:24px 28px;' } , [
if ( loading . value ) return h ( 'div' , { style : 'color:var(--foreground-subdued);' } , 'Lädt …' ) ;
h ( 'div' , { style : 'display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:18px;' } , [
const GRUPPEN = [ [ 'im_druck' , 'Im Druck' ] , [ 'kuvertieren' , 'Kuvertierung' ] , [ 'eingegangen' , 'Eingegangen' ] ] ;
h ( 'div' , { style : 'position:relative;flex:1;min-width:240px;' } , [
const sek = [ ] ;
h ( 'input' , { type : 'search' , p laceholder : 'Auftrag, Kunde, Empfänger, Format …' ,
for ( const [ st , label ] of GRUPPEN ) {
style : 'width:100%;box-sizing:border-box;border:1px solid var(--border-normal);border-radius:10px;padding:11px 14px;font:inherit;font-size:15px;background:var(--background-page);color:var(--foreground-normal);' ,
const list = gefiltert . value . filter ( ( o ) => ( o . production _status || 'eingegangen' ) === st ) ;
if ( ! list . length ) continue ;
sek . push ( h ( 'div' , { key : 'sec' + st , style : 'margin-bottom:22px;' } , [ sektionKopf ( label , STATUS _META [ st ] , list . length ) , tabelle ( list ) ] ) ) ;
}
// Versandfertig: unterteilt in „schnellstmöglich" (ohne Datum) und „mit Datum".
const vf = gefiltert . value . filter ( ( o ) => o . production _status === 'versandfertig' ) ;
if ( vf . length ) {
const ohne = vf . filter ( ( o ) => ! o . versanddatum ) ;
const mit = vf . filter ( ( o ) => o . versanddatum ) . sort ( ( a , b ) => String ( a . versanddatum ) . localeCompare ( String ( b . versanddatum ) ) ) ;
sek . push ( h ( 'div' , { key : 'secvf' , style : 'margin-bottom:22px;' } , [
sektionKopf ( 'Versandfertig' , STATUS _META . versandfertig , vf . length ) ,
ohne . length ? h ( 'div' , { style : 'margin-bottom:12px;' } , [ h ( 'div' , { style : 'font-size:13px;font-weight:500;color:var(--foreground-subdued);margin-bottom:6px;' } , 'Schnellstmöglich (' + ohne . length + ')' ) , tabelle ( ohne ) ] ) : null ,
mit . length ? h ( 'div' , { } , [ h ( 'div' , { style : 'font-size:13px;font-weight:500;color:var(--foreground-subdued);margin-bottom:6px;' } , 'Mit Versanddatum (' + mit . length + ')' ) , tabelle ( mit ) ] ) : null ,
] . filter ( Boolean ) ) ) ;
}
return sek . length ? h ( 'div' , { } , [ maschinenPanel ( ) , ... sek ] ) : h ( 'div' , { } , [ maschinenPanel ( ) , 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.' ) ] ) ;
}
function sucheInhalt ( ) {
const TYPEN = [ [ 'brief' , 'Schriftstück' , 'description' ] , [ 'umschlag' , 'Kuvert' , 'mail' ] , [ 'objekt' , 'Druckobjekt' , 'attach_file' ] ] ;
const typWahl = h ( 'div' , { } , [
h ( 'div' , { style : 'font-size:14px;font-weight:500;margin-bottom:8px;' } , '1. Was möchten Sie?' ) ,
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 ] ) ;
} ) ) ,
] ) ;
const feld = h ( 'div' , { style : 'margin-top:18px;' } , [
h ( 'div' , { style : 'font-size:14px;font-weight:500;margin-bottom:8px;' } , '2. Empfänger suchen' ) ,
h ( 'input' , { type : 'search' , placeholder : 'Name, Straße, Ort oder Auftragsnummer …' ,
style : 'width:100%;box-sizing:border-box;background:var(--background-page);color:var(--foreground-normal);border:2px solid var(--border-normal);border-radius:12px;font:inherit;font-size:22px;padding:16px 20px;outline:none;' ,
onInput : ( e ) => sucheSetzen ( e . target . value ) } ) ,
onInput : ( e ) => sucheSetzen ( e . target . value ) } ) ,
] ) ;
const rahmen = ( kinder ) => h ( 'div' , { } , [
h ( 'div' , { style : 'background:var(--background-page,#fff);border:1px solid var(--border-normal);border-radius:12px;padding:20px 22px;margin-bottom:16px;' } , [ typWahl , feld ] ) ,
kinder ,
] ) ;
if ( ! sucheQuery . value . trim ( ) ) return rahmen ( h ( 'div' , { style : 'color:var(--foreground-subdued);' } , 'Suchbegriff eingeben – durchsucht die offenen Aufträge.' ) ) ;
const treffer = sucheTreffer . value ;
if ( ! treffer . length ) return rahmen ( h ( 'div' , { style : 'color:var(--foreground-subdued);' } , 'Keine Treffer.' ) ) ;
const trefferZeile = ( { e , o } ) => {
const gesamt = entriesByOrder . value [ String ( o . id ) ] ? . anzahl || 1 ;
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' ] )
: ( 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' ] ) ;
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 ) ) ,
h ( 'div' , { style : 'display:flex;gap:8px;flex-wrap:wrap;margin-top:5px;align-items:center;font-size:13px;color:var(--foreground-subdued);' } , [
h ( 'span' , { } , ` Auftrag # ${ o . order _number } ` ) ,
gesamt > 1 ? h ( 'span' , { style : 'background:var(--background-subdued);border-radius:999px;padding:2px 9px;' } , ` Nr. ${ e . nr } von ${ gesamt } ` ) : null ,
h ( 'span' , { style : ` display:inline-flex;align-items:center;gap:4px;color: ${ ( STATUS _META [ o . production _status ] || STATUS _META . eingegangen ) . fg } ; ` } , [ icon ( ( STATUS _META [ o . production _status ] || STATUS _META . eingegangen ) . icon ) , STATUS _LABEL [ o . production _status ] || '' ] ) ,
] ) ,
] ) ,
] ) ,
aktion ,
] ) ;
} ;
return rahmen ( h ( 'div' , { } , treffer . map ( trefferZeile ) ) ) ;
}
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);' ) ,
} , [ icon ( ic ) , label ] ) ;
return ( ) => {
const navi = h ( 'div' , { style : 'padding:14px 12px;' } , [
navItem ( 'uebersicht' , 'Übersicht' , 'table_view' ) ,
navItem ( 'suche' , 'Suche' , 'search' ) ,
] ) ;
const inhalt = h ( 'div' , { style : 'padding:24px 28px;' } , [
view . value === 'uebersicht' ? h ( 'div' , { style : 'display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:18px;' } , [
h ( 'input' , { type : 'search' , placeholder : 'Auftrag, Kunde, Empfänger, Format …' , style : 'flex:1;min-width:240px;box-sizing:border-box;border:1px solid var(--border-normal);border-radius:10px;padding:11px 14px;font:inherit;font-size:15px;background:var(--background-page);color:var(--foreground-normal);' , onInput : ( e ) => sucheSetzen ( e . target . value ) } ) ,
h ( 'button' , { type : 'button' , onClick : ( ) => laden ( ) , style : 'cursor:pointer;border:1px solid var(--border-normal);background:var(--background-page,#fff);border-radius:8px;padding:9px 16px;font:inherit;font-size:14px;font-weight:500;display:inline-flex;align-items:center;gap:6px;' } , [ icon ( 'refresh' ) , 'Aktualisieren' ] ) ,
h ( 'button' , { type : 'button' , onClick : ( ) => laden ( ) , style : 'cursor:pointer;border:1px solid var(--border-normal);background:var(--background-page,#fff);border-radius:8px;padding:9px 16px;font:inherit;font-size:14px;font-weight:500;display:inline-flex;align-items:center;gap:6px;' } , [ icon ( 'refresh' ) , 'Aktualisieren' ] ) ,
] ) ,
] ) : null ,
msg . value ? h ( 'div' , { style : 'background:var(--success-25,rgba(46,125,50,.14));color:var(--success,#2e7d32);padding:10px 14px;border-radius:8px;margin-bottom:12px;font-size:14px;' } , msg . value ) : null ,
msg . value ? h ( 'div' , { style : 'background:var(--success-25,rgba(46,125,50,.14));color:var(--success,#2e7d32);padding:10px 14px;border-radius:8px;margin-bottom:12px;font-size:14px;' } , msg . value ) : null ,
err . value ? h ( 'div' , { style : 'background:var(--danger-25,rgba(198,40,40,.12));color:var(--danger,#c62828);padding:10px 14px;border-radius:8px;margin-bottom:12px;font-size:14px;' } , err . value ) : null ,
err . value ? h ( 'div' , { style : 'background:var(--danger-25,rgba(198,40,40,.12));color:var(--danger,#c62828);padding:10px 14px;border-radius:8px;margin-bottom:12px;font-size:14px;' } , err . value ) : null ,
maschinenPanel ( ) ,
view . value === 'suche' ? sucheInhalt ( ) : uebersichtInhalt ( ) ,
loading . value
? h ( 'div' , { style : 'color:var(--foreground-subdued);' } , 'Lädt …' )
: ( ( ) => {
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 h ( resolveComponent ( 'private-view' ) , { title : view . value === 'suche' ? 'Suche' : 'Übersicht' } , { navigation : ( ) => navi , default : ( ) => inhalt } ) ;
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 } ) ;
} ;
} ;
} ,
} ,
} ;
} ;