@@ -97,14 +97,16 @@ function auth_render_markets_large_table(array $marketTable): string
$pageSize = ( int ) ( $marketTable [ 'page_size' ] ? ? 20 );
$hasMore = ( bool ) ( $marketTable [ 'has_more' ] ? ? ( count ( $rows ) >= $limit ));
$sortColumns = [
'market_name' => 'Name der Veranstaltung ' ,
'earliest_date' => 'Durchführung ' ,
'market_name' => 'Markt ' ,
'earliest_date' => 'Markttag(e) ' ,
'place' => 'Ort' ,
'registration_deadline' => 'Anmeldefrist' ,
'status' => 'Status' ,
];
$html = [];
$html [] = '<section class="sg-card-list-page" aria-label="Mä rkte ">' ;
$html [] = '<section class="sg-card-list-page" aria-label="Ma rktdurchführungen ">' ;
$html [] = '<article class="sg-card" data-component="basic-card"><div class="sg-card-segment sg-card-segment--body sg-card-segment--gray" data-component-part="card-body"><div class="sg-component-row sg-basic-card__actions"><button class="sg-interaction-element sg-button sg-button--active" type="button" aria-haspopup="dialog" aria-expanded="false" data-component="button" data-component-state="active" data-market-occurrence-create>Neue Marktdurchführung</button></div></div></article>' ;
$html [] = '<article class="sg-card sg-large-table sg-large-table--markets" data-component="large-table" data-markets-large-table="true" data-markets-search="' . auth_escape_html ( $search ) . '" data-markets-sort-column="' . auth_escape_html ( $sortColumn ) . '" data-markets-sort-direction="' . auth_escape_html ( $sortDirection ) . '" data-markets-limit="' . $limit . '" data-markets-page-size="' . $pageSize . '" role="table" aria-label="Marktdurchführungen">' ;
$html [] = '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkblue sg-large-table__title-segment" data-component-part="large-table-header">' ;
$html [] = '<div class="sg-strong">Marktdurchführungen</div>' ;
@@ -124,6 +126,7 @@ function auth_render_markets_large_table(array $marketTable): string
$html [] = '<div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader" aria-sort="' . $headerDirection . '" data-sort-key="' . $headerIndex . '"><button class="sg-large-table__sort-button" type="button" aria-label="' . auth_escape_html ( $ariaLabel ) . '" data-markets-sort-button="true" data-markets-sort-column="' . auth_escape_html ( $column ) . '" data-markets-sort-direction="' . auth_escape_html ( $targetDirection ) . '"><span class="sg-large-table__sort-label">' . auth_escape_html ( $label ) . '</span><span class="sg-large-table__sort-icon" aria-hidden="true" data-direction="' . auth_escape_html ( $iconDirection ) . '"></span></button></div>' ;
$headerIndex ++ ;
}
$html [] = '<div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader">Verkaufte Produkte</div>' ;
$html [] = '</div>' ;
foreach ( $rows as $index => $row ) {
@@ -135,10 +138,23 @@ function auth_render_markets_large_table(array $marketTable): string
$html [] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html (( string ) ( $row [ 'market_name' ] ? ? '' )) . '</div>' ;
$html [] = '<div class="sg-large-table__cell" role="cell"><a class="sg-hyperlink" href="#" aria-haspopup="dialog" aria-expanded="false" data-market-drawer-open="true" data-market-occurrence-id="' . ( int ) ( $row [ 'id' ] ? ? 0 ) . '">' . auth_escape_html ( auth_format_market_date ( $row [ 'earliest_date' ] ? ? null , $row [ 'latest_date' ] ? ? null )) . '</a></div>' ;
$html [] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html (( string ) ( $row [ 'place' ] ? ? '' )) . '</div>' ;
$registrationDeadline = ( string ) ( $row [ 'registration_deadline' ] ? ? '' );
$registrationDeadlineLabel = '' ;
if ( $registrationDeadline !== '' ) {
try {
$registrationDeadlineLabel = ( new DateTimeImmutable ( $registrationDeadline )) -> format ( 'd.m.Y' );
} catch ( Throwable ) {
$registrationDeadlineLabel = $registrationDeadline ;
}
}
$html [] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html ( $registrationDeadlineLabel ) . '</div>' ;
$status = ( string ) ( $row [ 'status' ] ? ? '' );
$statusSignalClass = trim ( auth_market_status_signal_class ( $status ));
$statusValue = '<span' . ( $statusSignalClass !== '' ? ' class="' . auth_escape_html ( $statusSignalClass ) . '"' : '' ) . '>' . auth_escape_html ( auth_market_status_label ( $status )) . '</span>' ;
$html [] = '<div class="sg-large-table__cell" role="cell">' . $statusValue . '</div>' ;
$soldItemCount = ( int ) round (( float ) ( $row [ 'sold_item_count' ] ? ? 0 ));
$soldGrossRevenue = number_format (( float ) ( $row [ 'sold_gross_revenue' ] ? ? 0 ), 0 , '.' , " ' " );
$html [] = '<div class="sg-large-table__cell" role="cell">' . $soldItemCount . '/' . $soldGrossRevenue . 'CHF</div>' ;
$html [] = '</div>' ;
}
@@ -151,14 +167,63 @@ function auth_render_markets_large_table(array $marketTable): string
function auth_render_markets_content ( array $marketTable ) : string
{
return '<div data-markets-content="true" data-market-csrf-token="' . auth_escape_html ( auth_csrf_token ()) . '">' . auth_render_markets_large_table ( $marketTable )
return '<div data-markets-content="true" data-market-csrf-token="' . auth_escape_html ( auth_csrf_token ()) . '">'
. auth_render_markets_large_table ( $marketTable )
. '<div class="sg-card-list-page-drawer-backdrop" aria-hidden="true" data-market-drawer-backdrop="true" data-open="false"></div>'
. '<aside class="sg-card-list-page-drawer" aria-label="Marktdurchführung-Details" aria-hidden="true" data-market-drawer="true" data-open="false">'
. '<div class="sg-card-list-page-drawer__header"><h2 class="sg-heading-h2 sg-card-list-page-drawer__title" data-market-drawer-title>Marktdurchführung</h2></div>'
. '<div class="sg-card-list-page-drawer__content" data-market-drawer-content><p class="sg-body">Marktdetails werden geladen …</p></div>'
. '</aside></div>' ;
}
function auth_render_market_definitions_content ( array $marketTable ) : string
{
$rows = $marketTable [ 'rows' ] ? ? $marketTable ;
$categories = $marketTable [ 'categories' ] ? ? [];
$organizers = $marketTable [ 'organizers' ] ? ? [];
$html = [];
$html [] = '<div data-market-definitions-content="true" data-market-definitions-csrf-token="' . auth_escape_html ( auth_csrf_token ()) . '"><section class="sg-card-list-page" aria-label="Märkte">' ;
$html [] = '<article class="sg-card" data-component="basic-card"><div class="sg-card-segment sg-card-segment--body sg-card-segment--gray" data-component-part="card-body"><div class="sg-component-row sg-basic-card__actions"><button class="sg-interaction-element sg-button sg-button--active" type="button" aria-haspopup="dialog" aria-expanded="false" data-component="button" data-market-definition-create>Neuer Markt</button></div></div></article>' ;
$html [] = '<article class="sg-card sg-large-table sg-large-table--markets" data-component="large-table" data-market-definitions-table="true" role="table" aria-label="Märkte">' ;
$html [] = '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkblue sg-large-table__title-segment" data-component-part="large-table-header"><div class="sg-strong">Märkte</div></div>' ;
$html [] = '<div class="sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row sg-large-table__row--header" role="row" data-component-part="large-table-header-row">' ;
foreach ([ 'Markt' , 'Kategorie' , 'Veranstalter' , 'Website' , 'Durchführungen' , 'Status' ] as $label ) {
$html [] = '<div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader">' . auth_escape_html ( $label ) . '</div>' ;
}
$html [] = '</div>' ;
foreach ( $rows as $index => $row ) {
$rowClass = 'sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row' ;
if ( $index % 2 === 1 ) {
$rowClass .= ' sg-large-table__row--striped-light' ;
}
$website = ( string ) ( $row [ 'website_url' ] ? ? '' );
$websiteValue = $website === '' ? 'Nicht hinterlegt' : $website ;
$status = ( $row [ 'archived_at' ] ? ? null ) === null ? 'Aktiv' : 'Archiviert' ;
$marketId = ( int ) ( $row [ 'id' ] ? ? 0 );
$marketName = ( string ) ( $row [ 'name' ] ? ? '' );
$organizerName = ( string ) ( $row [ 'organizer_name' ] ? ? '' );
$html [] = '<div class="' . $rowClass . '" role="row" data-component-part="large-table-row" data-market-definition-row data-market-definition-id="' . $marketId . '">' ;
$html [] = '<div class="sg-large-table__cell" role="cell"><a class="sg-hyperlink" href="#" aria-haspopup="dialog" aria-expanded="false" data-market-definition-open data-market-definition-id="' . $marketId . '" data-market-definition-name="' . auth_escape_html ( $marketName ) . '" data-market-definition-category-id="' . ( int ) ( $row [ 'category_id' ] ? ? 0 ) . '" data-market-definition-category-name="' . auth_escape_html (( string ) ( $row [ 'category_name' ] ? ? $row [ 'category_code' ] ? ? '' )) . '" data-market-definition-organizer-id="' . ( int ) ( $row [ 'organizer_id' ] ? ? 0 ) . '" data-market-definition-organizer-name="' . auth_escape_html ( $organizerName ) . '" data-market-definition-website="' . auth_escape_html ( $website ) . '">' . auth_escape_html ( $marketName ) . '</a></div>' ;
$html [] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html (( string ) ( $row [ 'category_name' ] ? ? $row [ 'category_code' ] ? ? '' )) . '</div>' ;
$html [] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html ( $organizerName ) . '</div>' ;
$html [] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html ( $websiteValue ) . '</div>' ;
$html [] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html (( string ) ( $row [ 'occurrence_count' ] ? ? 0 )) . '</div>' ;
$html [] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html ( $status ) . '</div>' ;
$html [] = '</div>' ;
}
$html [] = '</article></section>' ;
$html [] = '<div class="sg-card-list-page-drawer-backdrop" aria-hidden="true" data-market-definition-drawer-backdrop="true" data-open="false"></div>' ;
$html [] = '<aside class="sg-card-list-page-drawer" aria-label="Markt-Details" aria-hidden="true" data-market-definition-drawer="true" data-open="false"><div class="sg-card-list-page-drawer__content" data-market-definition-drawer-content><article class="sg-card sg-card--content-card" data-component="content-card" data-shared-editable-action-card data-market-definition-card><header class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen sg-object-card__header"><h2 class="sg-strong sg-text-on-dark" data-market-definition-read-title>Markt</h2><div class="sg-component-row"><button class="sg-interaction-element sg-button sg-button--active" type="button" data-market-definition-edit data-shared-editable-card-edit>Bearbeiten</button></div></header><div class="sg-card-segment sg-card-segment--body sg-card-segment--white" data-market-definition-read-card data-shared-editable-card-read><dl class="market-detail-grid"><div class="market-detail-row"><dt>Marktname</dt><dd data-market-definition-read-name></dd></div><div class="market-detail-row"><dt>Kategorie/Marktart</dt><dd data-market-definition-read-category></dd></div><div class="market-detail-row"><dt>Veranstalter</dt><dd data-market-definition-read-organizer></dd></div><div class="market-detail-row"><dt>Website</dt><dd data-market-definition-read-website></dd></div></dl></div><div class="sg-card-segment sg-card-segment--body sg-card-segment--gray" data-market-definition-create-card data-shared-editable-card-form hidden><h2 class="sg-strong" data-market-definition-form-title>Markt bearbeiten</h2><div class="sg-form-sections-card-wrapper" data-pattern="form-sections"><form class="sg-form-sections-card" data-market-definition-form><div class="sg-form-sections-card__body" data-pattern-part="form-body"><div class="sg-form-sections-card__section"><div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Marktname<span class="market-required-field-indicator" aria-hidden="true">*</span></span><input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" name="name" required data-market-definition-name></label></div><div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Kategorie/Marktart<span class="market-required-field-indicator" aria-hidden="true">*</span></span><select class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable sg-pulldown" name="category_id" required data-market-definition-category><option value="">Bitte auswählen</option>' ;
foreach ( $categories as $category ) {
$html [] = '<option value="' . ( int ) ( $category [ 'id' ] ? ? 0 ) . '">' . auth_escape_html (( string ) ( $category [ 'name' ] ? ? $category [ 'code' ] ? ? '' )) . '</option>' ;
}
$html [] = '</select></label></div><div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Veranstalter<span class="market-required-field-indicator" aria-hidden="true">*</span></span><select class="sg-interaction-element sg-input-single-line sg-pulldown" name="organizer_id" required data-market-definition-organizer><option value="">Bitte auswählen</option>' ;
foreach ( $organizers as $organizer ) {
$html [] = '<option value="' . ( int ) ( $organizer [ 'id' ] ? ? 0 ) . '">' . auth_escape_html (( string ) ( $organizer [ 'name' ] ? ? '' )) . '</option>' ;
}
$html [] = '<option value="__new__">Neuen Veranstalter anlegen</option></select></label></div><div class="sg-form-sections-card__field-group" data-market-definition-new-organizer hidden><label class="sg-labeled-input-row"><span class="sg-label">Name des neuen Veranstalters<span class="market-required-field-indicator" aria-hidden="true">*</span></span><input class="sg-interaction-element sg-input-single-line" type="text" name="organizer_name" data-market-definition-organizer-name></label><label class="sg-labeled-input-row"><span class="sg-label">E-Mail</span><input class="sg-interaction-element sg-input-single-line" type="email" name="organizer_email" data-market-definition-organizer-email></label><label class="sg-labeled-input-row"><span class="sg-label">Telefon</span><input class="sg-interaction-element sg-input-single-line" type="tel" name="organizer_phone" data-market-definition-organizer-phone></label></div><div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Website</span><input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="url" name="website_url" placeholder="https://" data-market-definition-website></label></div></div></div><footer class="sg-form-sections-card__actions-segment" data-pattern-part="form-actions-segment"><div class="sg-form-sections-card__actions" data-pattern-part="form-actions"><button class="sg-interaction-element sg-button sg-button--active sg-form-sections-card__action" type="button" data-market-definition-cancel data-shared-editable-card-cancel>Abbrechen</button><button class="sg-interaction-element sg-button sg-button--process sg-form-sections-card__action" type="submit">Speichern</button></div></footer><p class="sg-body" data-market-definition-status aria-live="polite"></p></form></div></div></div></article></div></aside></div>' ;
return implode ( '' , $html );
}
function auth_render_bestellungen_large_table ( array $bestellungenTable ) : string
{
$rows = $bestellungenTable [ 'rows' ] ? ? [];
@@ -440,17 +505,17 @@ function auth_render_lager_historical_lots(array $lagerProducts): string
$emptyDate = $qtyNet <= 0 ? auth_format_lager_date ( $lot [ 'last_out_at' ] ? ? null ) : 'Nicht eindeutig' ;
$html [] = '<article class="sg-card sg-card--content-card" data-component="content-card" aria-label="Vergangene Charge">'
. '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen" data-component-part="card-header"><div class="sg-strong sg-text-on-dark">Vergangene Charge</div></div>'
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body">'
. '<p class="sg-body"><strong >Naurua-Chargennummer: </strong> ' . auth_escape_html (( string ) ( $lot [ 'lot_number' ] ? ? 'Nicht hinterlegt' )) . '<br >'
. '<strong >Status: </strong> Abgeschlossen<br >'
. '<strong >Eingangsmenge: </strong> ' . auth_format_lager_quantity (( float ) ( $lot [ 'qty_in' ] ? ? 0 )) . '<br >'
. '<strong >Verbrauchte Menge: </strong> ' . auth_format_lager_quantity (( float ) ( $lot [ 'qty_out' ] ? ? 0 )) . '<br >'
. '<strong >Verbrauchszeitraum: </strong> ' . auth_format_lager_date ( $lot [ 'first_in_at' ] ? ? null ) . ' – ' . auth_format_lager_date ( $lot [ 'last_out_at' ] ? ? null ) . '<br >'
. '<strong >Aufgebraucht am: </strong> ' . $emptyDate . '<br >'
. '<strong >MHD: </strong> ' . auth_format_lager_date ( $lot [ 'expiry_date' ] ? ? null ) . '<br >'
. '<strong >Hersteller: </strong> ' . auth_escape_html (( string ) ( $lot [ 'manufacturer_name' ] ? ? 'Nicht hinterlegt' )) . '<br >'
. '<strong >Hersteller-Chargennummer: </strong> ' . auth_escape_html (( string ) ( $lot [ 'manufacturer_lot_number' ] ? ? 'Nicht hinterlegt' )) . '</p >'
. '</div></article>' ;
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body"><dl class="market-detail-grid"> '
. '<div class="market-detail-row"><dt >Naurua-Chargennummer</dt><dd> ' . auth_escape_html (( string ) ( $lot [ 'lot_number' ] ? ? 'Nicht hinterlegt' )) . '</dd></div >'
. '<div class="market-detail-row"><dt >Status</dt><dd> Abgeschlossen</dd></div >'
. '<div class="market-detail-row"><dt >Eingangsmenge</dt><dd> ' . auth_format_lager_quantity (( float ) ( $lot [ 'qty_in' ] ? ? 0 )) . '</dd></div >'
. '<div class="market-detail-row"><dt >Verbrauchte Menge</dt><dd> ' . auth_format_lager_quantity (( float ) ( $lot [ 'qty_out' ] ? ? 0 )) . '</dd></div >'
. '<div class="market-detail-row"><dt >Verbrauchszeitraum</dt><dd> ' . auth_format_lager_date ( $lot [ 'first_in_at' ] ? ? null ) . ' – ' . auth_format_lager_date ( $lot [ 'last_out_at' ] ? ? null ) . '</dd></div >'
. '<div class="market-detail-row"><dt >Aufgebraucht am</dt><dd> ' . $emptyDate . '</dd></div >'
. '<div class="market-detail-row"><dt >MHD</dt><dd> ' . auth_format_lager_date ( $lot [ 'expiry_date' ] ? ? null ) . '</dd></div >'
. '<div class="market-detail-row"><dt >Hersteller</dt><dd> ' . auth_escape_html (( string ) ( $lot [ 'manufacturer_name' ] ? ? 'Nicht hinterlegt' )) . '</dd></div >'
. '<div class="market-detail-row"><dt >Hersteller-Chargennummer</dt><dd> ' . auth_escape_html (( string ) ( $lot [ 'manufacturer_lot_number' ] ? ? 'Nicht hinterlegt' )) . '</dd></div >'
. '</dl></ div></article>' ;
}
$html [] = '</div></div>' ;
}
@@ -478,15 +543,8 @@ function auth_render_lager_open_lots(array $lagerProducts, string $manufacturerO
$selectButton = '<button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-open-select data-lager-open-selectable="' . ( $isQualified ? 'true' : 'false' ) . '"' . ( $isQualified && empty ( $lot [ 'next_active' ]) ? '' : ' disabled aria-disabled="true"' ) . '>Als nächste aktive auswählen</button>' ;
$html [] = '<div class="sg-delete-confirmation-pattern__stage sg-delete-confirmation-pattern__host' . $selectedClass . '" data-pattern="overlay-card" data-dialog-open="false" data-lager-open-card-wrapper="' . $lotId . '"><article class="sg-card sg-card--content-card sg-delete-confirmation-pattern__target" data-component="content-card" data-pattern="overlay-card" data-shared-editable-action-card data-shared-editable-delete-token="DELETE" aria-label="Offene Charge">'
. '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen sg-object-card__header" data-component-part="card-header"><div class="sg-strong sg-text-on-dark">Offene Charge</div><div class="sg-component-row">' . $selectButton . '<button class="sg-interaction-element sg-button sg-button--warning" type="button" data-component="button" data-component-variant="warning" data-lager-open-delete data-shared-editable-card-delete>Löschen</button><button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-open-edit-open data-shared-editable-card-edit>Bearbeiten</button></div></div>'
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body" data-lager-open-card="' . ( int ) ( $lot [ 'lot_id' ] ? ? 0 ) . '" data-shared-editable-card-read><p class="sg-body"><strong >Auswahl: </strong> <span data-lager-open-card-next-active>' . auth_escape_html ( $selectedText ) . '</span><br><strong >Naurua-Chargennummer: </strong> <span data-lager-open-card-lot-number>' . auth_escape_html (( string ) ( $lot [ 'lot_number' ] ? ? 'Nicht hinterlegt' )) . '</span><br >'
. '<strong>Status:</strong> Vorbereitet<br >'
. '<strong>Bestand:</strong> <span data-lager-open-card-qty-net>' . auth_format_lager_quantity (( float ) ( $lot [ 'qty_net' ] ? ? 0 )) . '</span><br>'
. '<strong>Eingangsmenge:</strong> <span data-lager-open-card-qty-in>' . auth_format_lager_quantity (( float ) ( $lot [ 'qty_in' ] ? ? 0 )) . '</span><br>'
. '<strong>Hersteller:</strong> <span data-lager-open-card-manufacturer>' . auth_escape_html (( string ) ( $lot [ 'manufacturer_name' ] ? ? 'Nicht hinterlegt' )) . '</span><br>'
. '<strong>Hersteller-Chargennummer:</strong> <span data-lager-open-card-manufacturer-lot>' . auth_escape_html (( string ) ( $lot [ 'manufacturer_lot_number' ] ? ? 'Nicht hinterlegt' )) . '</span><br>'
. '<strong>Herstellungsdatum:</strong> <span data-lager-open-card-mfg-date>' . auth_format_lager_date ( $lot [ 'mfg_date' ] ? ? null ) . '</span><br>'
. '<strong>MHD:</strong> <span data-lager-open-card-expiry-date>' . auth_format_lager_date ( $lot [ 'expiry_date' ] ? ? null ) . '</span><br>'
. '<strong>Einkaufsdatum:</strong> <span data-lager-open-card-purchase-date>' . auth_format_lager_date ( $lot [ 'purchase_date' ] ? ? null ) . '</span></p></div>'
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body" data-lager-open-card="' . ( int ) ( $lot [ 'lot_id' ] ? ? 0 ) . '" data-shared-editable-card-read><dl class="market-detail-grid"><div class="market-detail-row"><dt >Auswahl</dt><dd> <span data-lager-open-card-next-active>' . auth_escape_html ( $selectedText ) . '</span></dd></div><div class="market-detail-row"><dt >Naurua-Chargennummer</dt><dd> <span data-lager-open-card-lot-number>' . auth_escape_html (( string ) ( $lot [ 'lot_number' ] ? ? 'Nicht hinterlegt' )) . '</span></dd></div >'
. '<div class="market-detail-row"><dt>Status</dt><dd>Vorbereitet</dd></div><div class="market-detail-row"><dt>Bestand</dt><dd><span data-lager-open-card-qty-net>' . auth_format_lager_quantity (( float ) ( $lot [ 'qty_net' ] ? ? 0 )) . '</span></dd></div><div class="market-detail-row"><dt>Eingangsmenge</dt><dd><span data-lager-open-card-qty-in>' . auth_format_lager_quantity (( float ) ( $lot [ 'qty_in' ] ? ? 0 )) . '</span></dd></div><div class="market-detail-row"><dt>Hersteller</dt><dd><span data-lager-open-card-manufacturer>' . auth_escape_html (( string ) ( $lot [ 'manufacturer_name' ] ? ? 'Nicht hinterlegt' )) . '</span></dd></div><div class="market-detail-row"><dt>Hersteller-Chargennummer</dt><dd><span data-lager-open-card-manufacturer-lot>' . auth_escape_html (( string ) ( $lot [ 'manufacturer_lot_number' ] ? ? 'Nicht hinterlegt' )) . '</span></dd></div><div class="market-detail-row"><dt>Herstellungsdatum</dt><dd><span data-lager-open-card-mfg-date>' . auth_format_lager_date ( $lot [ 'mfg_date' ] ? ? null ) . '</span></dd></div><div class="market-detail-row"><dt>MHD</dt><dd><span data-lager-open-card-expiry-date>' . auth_format_lager_date ( $lot [ 'expiry_date' ] ? ? null ) . '</span></dd></div><div class="market-detail-row"><dt>Einkaufsdatum</dt><dd><span data-lager-open-card-purchase-date>' . auth_format_lager_date ( $lot [ 'purchase_date' ] ? ? null ) . '</span></dd></div></dl></div >'
. '<div class="sg-card-segment sg-card-segment--body sg-card-segment--gray" data-lager-open-edit-form data-shared-editable-card-form hidden><div class="sg-form-sections-card-wrapper" data-pattern="form-sections" aria-label="Offene Charge bearbeiten"><form class="sg-form-sections-card" data-lager-open-edit-form-element><div class="sg-form-sections-card__body" data-pattern-part="form-body"><h2 class="sg-strong sg-form-sections-card__title">Offene Charge bearbeiten</h2>'
. '<section class="sg-form-sections-card__chapter" aria-labelledby="lager-open-charge-data-' . $lotId . '"><h2 id="lager-open-charge-data-' . $lotId . '" class="sg-strong sg-form-sections-card__chapter-title">Chargendaten</h2>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Naurua-Chargennummer</span><input class="sg-interaction-element sg-input-single-line" type="text" data-lager-open-lot-number value="' . auth_escape_html (( string ) ( $lot [ 'lot_number' ] ? ? '' )) . '"></label></div>'
@@ -568,7 +626,7 @@ function auth_render_lager_content(array $lagerProducts, array $lagerArtikel, ar
. '<div data-lager-drawer-topic="open-lots" aria-label="Offene Chargen">' . auth_render_lager_open_lots ( $lagerProducts , $manufacturerOptions ) . '</div>'
. '<article class="sg-card sg-card--content-card" data-component="content-card" data-lager-drawer-topic="lot" data-shared-editable-action-card aria-label="Aktive Charge">'
. '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen sg-object-card__header" data-component-part="card-header"><div class="sg-strong sg-text-on-dark">Aktive Charge</div><button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-edit-open data-shared-editable-card-edit>Bearbeiten</button></div>'
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body" data-lager-read-view data-shared-editable-card-read><p class="sg-body"><strong >Naurua-Chargennummer: </strong> <span data-lager-drawer-lot-number></span><br><strong >Aktueller Bestand: </strong> <span data-lager-drawer-current-qty></span><br><strong >Eingangsmenge: </strong> <span data-lager-drawer-qty-in></span><br><strong >Hersteller: </strong> <span data-lager-drawer-manufacturer-name></span><br><strong >Hersteller-Chargennummer: </strong> <span data-lager-drawer-manufacturer-lot-number></span><br><strong >Herstellungsdatum: </strong> <span data-lager-drawer-mfg-date></span><br><strong >MHD: </strong> <span data-lager-drawer-expiry-date></span><br><strong >Einkaufsdatum: </strong> <span data-lager-drawer-purchase-date></span></p ></div>'
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body" data-lager-read-view data-shared-editable-card-read><dl class="market-detail-grid"><div class="market-detail-row"><dt >Naurua-Chargennummer</dt><dd> <span data-lager-drawer-lot-number></span></dd></div><div class="market-detail-row"><dt >Aktueller Bestand</dt><dd> <span data-lager-drawer-current-qty></span></dd></div><div class="market-detail-row"><dt >Eingangsmenge</dt><dd> <span data-lager-drawer-qty-in></span></dd></div><div class="market-detail-row"><dt >Hersteller</dt><dd> <span data-lager-drawer-manufacturer-name></span></dd></div><div class="market-detail-row"><dt >Hersteller-Chargennummer</dt><dd> <span data-lager-drawer-manufacturer-lot-number></span></dd></div><div class="market-detail-row"><dt >Herstellungsdatum</dt><dd> <span data-lager-drawer-mfg-date></span></dd></div><div class="market-detail-row"><dt >MHD</dt><dd> <span data-lager-drawer-expiry-date></span></dd></div><div class="market-detail-row"><dt >Einkaufsdatum</dt><dd> <span data-lager-drawer-purchase-date></span></dd></div></dl ></div>'
. '<div class="sg-card-segment sg-card-segment--body sg-card-segment--gray" data-lager-edit-form data-shared-editable-card-form hidden><div class="sg-form-sections-card-wrapper" data-pattern="form-sections" aria-label="Aktive Charge bearbeiten">'
. '<form class="sg-form-sections-card" data-lager-edit-form-element><div class="sg-form-sections-card__body" data-pattern-part="form-body"><h2 class="sg-strong sg-form-sections-card__title">Aktive Charge bearbeiten</h2>'
. '<section class="sg-form-sections-card__chapter" aria-labelledby="lager-charge-traceability-title"><h2 id="lager-charge-traceability-title" class="sg-strong sg-form-sections-card__chapter-title">Rückverfolgbarkeit</h2>'
@@ -596,7 +654,7 @@ function auth_render_lager_content(array $lagerProducts, array $lagerArtikel, ar
. '</div>' ;
}
function render_auth_home_page ( array $user , array $otcProducts = [], array $bestellungenTable = [], array $marketOccurrencesTable = [], array $lagerProducts = [], array $lagerArtikel = [], array $lagerManufacturers = []) : void
function render_auth_home_page ( array $user , array $otcProducts = [], array $otcMarketOptions = [], array $bestellungenTable = [], array $marketOccurrencesTable = [], array $lagerProducts = [], array $lagerArtikel = [], array $lagerManufacturers = [], array $marketDefinitionsTable = []) : void
{
$otcProductRows = '' ;
foreach ( $otcProducts as $product ) {
@@ -616,6 +674,26 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
$otcProductRows .= '</div>' ;
}
$otcMarketOccurrenceOptions = [];
$otcMarketDayOptions = '' ;
foreach ( $otcMarketOptions as $option ) {
$occurrenceId = ( int ) ( $option [ 'occurrence_id' ] ? ? 0 );
$marketDefinitionId = ( int ) ( $option [ 'market_definition_id' ] ? ? 0 );
$dayId = ( int ) ( $option [ 'day_id' ] ? ? 0 );
if ( $occurrenceId <= 0 || $marketDefinitionId <= 0 || $dayId <= 0 ) {
continue ;
}
$marketName = ( string ) ( $option [ 'market_name' ] ? ? '' );
$otcMarketOccurrenceOptions [ $marketDefinitionId ] = $marketName ;
$day = ( string ) ( $option [ 'date' ] ? ? '' );
$dayLabel = $day === '' ? '' : ( new DateTimeImmutable ( $day )) -> format ( 'd.m.Y' );
$otcMarketDayOptions .= '<option value="' . $dayId . '" data-market-definition-id="' . $marketDefinitionId . '" data-market-occurrence-id="' . $occurrenceId . '">' . auth_escape_html ( $dayLabel ) . '</option>' ;
}
$otcMarketOccurrenceOptionHtml = '<option value="">Markt auswählen</option>' ;
foreach ( $otcMarketOccurrenceOptions as $marketDefinitionId => $marketName ) {
$otcMarketOccurrenceOptionHtml .= '<option value="' . $marketDefinitionId . '">' . auth_escape_html ( $marketName ) . '</option>' ;
}
if ( $otcProductRows === '' ) {
$otcProductRows = '<p class="sg-body sg-form-sections-card__sentence">Keine aktiven OTC-Produkte erfasst.</p>' ;
}
@@ -624,7 +702,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
[
'Übersicht' => [],
'ERP' => [ 'Bestellungen' , 'Lager' , 'Produkte' , 'Artikel' , 'Kontakte' ],
'Märkte' => [ 'Mä rkte' , 'Konfiguration '],
'Märkte' => [ 'Ma rktdurchführungen' , 'Märkte '],
'Buchhaltung' => [],
'Kundenberatung' => [],
],
@@ -632,11 +710,13 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
);
$bestellungenContent = auth_render_bestellungen_content ( $bestellungenTable );
$marketsContent = auth_render_markets_content ( $marketOccurrencesTable );
$marketDefinitionsContent = auth_render_market_definitions_content ( $marketDefinitionsTable );
$lagerContent = auth_render_lager_content ( $lagerProducts , $lagerArtikel , $lagerManufacturers , auth_csrf_token ());
$moduleContentCards = json_encode (
[
'Bestellungen' => $bestellungenContent ,
'Mä rkte ' => $marketsContent ,
'Ma rktdurchführungen ' => $marketsContent ,
'Märkte' => $marketDefinitionsContent ,
'Lager' => $lagerContent ,
'Artikel' => auth_render_lager_content ([], $lagerArtikel , $lagerManufacturers , auth_csrf_token (), 'Artikel' ),
],
@@ -793,6 +873,32 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo '<input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" id="otc-city" value="Oberengstringen" data-otc-order-city>' ;
echo '</label>' ;
echo '</div>' ;
echo '</section>' ;
echo '<section class="sg-form-sections-card__chapter" aria-labelledby="otc-market-link-title">' ;
echo '<h2 id="otc-market-link-title" class="sg-strong sg-form-sections-card__chapter-title">Marktbezug</h2>' ;
echo '<div class="sg-form-sections-card__field-group">' ;
echo '<label class="sg-checkbox-field-option sg-checkbox-field-option--inactive-selectable sg-body" data-component="checkbox-field" data-component-state="inactive-selectable">' ;
echo '<button class="sg-checkbox-field sg-checkbox-field--inactive-selectable" type="button" role="checkbox" aria-checked="false" aria-label="Verkauf ist auf einem Markttag" data-otc-market-link-toggle>' ;
echo '<span class="sg-checkbox-field__mark" aria-hidden="true">✓</span>' ;
echo '</button><span>Verkauf ist auf einem Markttag</span>' ;
echo '<input type="hidden" name="market_sale" value="0" data-otc-market-link-enabled>' ;
echo '</label>' ;
echo '</div>' ;
echo '<div class="sg-form-sections-card__field-group">' ;
echo '<label class="sg-labeled-input-row"><span class="sg-label">Markt</span>' ;
echo '<select class="sg-interaction-element sg-input-single-line sg-input-single-line--disabled sg-form-disabled sg-pulldown sg-pulldown--disabled" disabled aria-disabled="true" data-otc-market-occurrence>' ;
echo $otcMarketOccurrenceOptionHtml ;
echo '</select>' ;
echo '</label>' ;
echo '</div>' ;
echo '<div class="sg-form-sections-card__field-group">' ;
echo '<label class="sg-labeled-input-row"><span class="sg-label">Markttag</span>' ;
echo '<select class="sg-interaction-element sg-input-single-line sg-input-single-line--disabled sg-form-disabled sg-pulldown sg-pulldown--disabled" disabled aria-disabled="true" data-otc-market-day>' ;
echo '<option value="">Zuerst Markt auswählen</option>' . $otcMarketDayOptions ;
echo '</select>' ;
echo '</label>' ;
echo '</div>' ;
echo '</section>' ;
echo '<div class="sg-form-sections-card__field-group">' ;
echo '<div class="sg-body sg-otc-order-form__status hidden" data-otc-order-success>Bestellung erfolgreich erfasst! Die Bestellnummer wird automatisch generiert.</div>' ;
echo '<div class="sg-body sg-otc-order-form__status sg-otc-order-form__status--error hidden" data-otc-order-error></div>' ;
@@ -814,6 +920,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo '<div class="sg-delete-confirmation-pattern__list" data-otc-confirmation-products aria-label="Produkte"></div>' ;
echo '<p class="sg-body sg-delete-confirmation-pattern__text"><strong>Gesamtzahl Flaschen: <span data-otc-confirmation-quantity>0</span></strong></p>' ;
echo '<p class="sg-body sg-delete-confirmation-pattern__text"><strong>Gesamtbetrag: CHF <span data-otc-confirmation-total>0.00</span></strong></p>' ;
echo '<p class="sg-body sg-otc-order-form__status sg-otc-order-form__status--error hidden" data-otc-confirmation-error aria-live="polite"></p>' ;
echo '<div class="sg-delete-confirmation-pattern__actions">' ;
echo '<button class="sg-interaction-element sg-button sg-button--active" type="button" data-otc-confirmation-cancel>Abbrechen</button>' ;
echo '<button class="sg-interaction-element sg-button sg-button--process" type="button" data-otc-confirmation-submit>Verkaufen</button>' ;
@@ -823,7 +930,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo '</div>' ;
echo '</section>' ;
echo '<script src="/scripts/help-icon-overlays.js"></script>' ;
echo '<script src="/scripts/shared-editable-action-card.js"></script>' ;
echo '<script src="/scripts/shared-editable-action-card.js?v=20260920-2 "></script>' ;
echo '<script>' ;
echo 'const portalModuleNavigation = ' . $moduleNavigation . ';' ;
echo 'const portalModuleContentCards = ' . $moduleContentCards . ';' ;
@@ -832,19 +939,23 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " const heading = document.querySelector('[data-main-heading]'); " ;
echo " if (heading && portalModuleHeadings[moduleName]) { heading.textContent = portalModuleHeadings[moduleName]; } " ;
echo " }; " ;
echo " const renderLeftNavig ation = (moduleName) => { " ;
echo " const updatePortalLoc ation = (moduleName, entryName = '') => { const url = new URL(window.location.href); url.searchParams.set('portal_module', moduleName); if (entryName) { url.searchParams.set('portal_section', entryName); } else { url.searchParams.delete('portal_section'); } window.history.replaceState( { ...(window.history.state || { }), portalModule: moduleName, portalSection: entryName }, '', url); }; " ;
echo " const renderLeftNavigation = (moduleName, requestedEntry = '') => { " ;
echo " const title = document.querySelector('[data-left-navigation-title]'); " ;
echo " const contentTitle = document.querySelector('[data-left-navigation-content-title]'); " ;
echo " const contentSection = document.querySelector('.sg-left-navigation-pattern__group-card--content'); " ;
echo " const contentBody = document.querySelector('[data-left-navigation-content-body]'); " ;
echo " const menu = document.getElementById('left-navigation-menu'); " ;
echo " if (title) { title.textContent = moduleName; } " ;
echo " if (!menu) { return; } " ;
echo " const entries = portalModuleNavigation[moduleName] || []; " ;
echo " const activeEntry = entries[0] || ''; " ;
echo " const activeEntry = entries.includes(requestedEntry) ? requestedEntry : ( entries[0] || '') ; " ;
echo " if (contentTitle) { contentTitle.textContent = activeEntry; } " ;
echo " if (contentSection) { contentSection.setAttribute('aria-hidden', 'false'); } " ;
echo " if (contentBody) { contentBody.innerHTML = portalModuleContentCards[activeEntry] || ''; } " ;
echo " if (activeEntry === 'Bestellungen') { initBestellungenBindings(); } " ;
echo " if (activeEntry === 'Mä rkte ') { initMarketsBindings(); } " ;
echo " if (activeEntry === 'Ma rktdurchführungen ') { initMarketsBindings(); } " ;
echo " if (activeEntry === 'Märkte') { initMarketDefinitionsBindings(); } " ;
echo " if (activeEntry === 'Lager' || activeEntry === 'Artikel') { initLagerBindings(); } " ;
echo " menu.innerHTML = ''; " ;
echo " entries.forEach((entry, index) => { " ;
@@ -853,7 +964,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " button.type = 'button'; " ;
echo " button.setAttribute('role', 'tab'); " ;
echo " button.setAttribute('data-component-part', 'tab-button'); " ;
echo " button.setAttribute('aria-selected', String(index === 0 )); " ;
echo " button.setAttribute('aria-selected', String(entry === activeEntry )); " ;
echo " button.textContent = entry; " ;
echo " menu.appendChild(button); " ;
echo " }); " ;
@@ -862,12 +973,15 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " menu.querySelectorAll('.sg-tab-button').forEach((otherButton) => { " ;
echo " otherButton.setAttribute('aria-selected', String(otherButton === button)); " ;
echo " }); " ;
echo " const entryName = button.textContent.trim(); " ;
echo " if (contentTitle) { contentTitle.textContent = entryName; } " ;
echo " if (contentBody ) { contentBody.innerHTML = portalModule ContentCards[ entryName] || '' ; } " ;
echo " if (entryName === 'Bestellungen') { initBestellungenBindings( ); }" ;
echo " if (entryName === 'Märkte') { initMarketsBindings() ; }" ;
echo " if (entryName === 'Lager' || entryName === 'Artikel ') { initLa ger Bindings(); } " ;
echo " const entryName = button.textContent.trim(); " ;
echo " updatePortalLocation(moduleName, entryName) ; " ;
echo " if (contentTitle ) { contentTitle.text Content = entryName; } " ;
echo " if (contentSection) { contentSection.setAttribute('aria-hidden', 'false' ); }" ;
echo " if (contentBody) { contentBody.innerHTML = portalModuleContentCards[entryName] || '' ; }" ;
echo " if (entryName === 'Bestellungen ') { initBestellun gen Bindings(); } " ;
echo " if (entryName === 'Marktdurchführungen') { initMarketsBindings(); } " ;
echo " if (entryName === 'Märkte') { initMarketDefinitionsBindings(); } " ;
echo " if (entryName === 'Lager' || entryName === 'Artikel') { initLagerBindings(); } " ;
echo " }); " ;
echo " }); " ;
echo " }; " ;
@@ -879,8 +993,10 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " otherButton.setAttribute('aria-selected', String(isActive)); " ;
echo " otherButton.dataset.componentState = isActive ? 'active' : 'inactive'; " ;
echo " }); " ;
echo " renderMainHeading( button.textContent.trim()) ; " ;
echo " renderLeftNavigation(button.textContent.trim() ); " ;
echo " const moduleName = button.textContent.trim(); " ;
echo " updatePortalLocation(moduleName ); " ;
echo " renderMainHeading(moduleName); " ;
echo " renderLeftNavigation(moduleName); " ;
echo " }); " ;
echo " }); " ;
echo " }); " ;
@@ -1026,6 +1142,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " function initMarketsBindings() { " ;
echo " const contentRoot = document.querySelector('[data-left-navigation-content-body]'); " ;
echo " if (!contentRoot || !contentRoot.querySelector('[data-markets-large-table]')) { return; } " ;
echo " if (window.SharedEditableActionCard) { window.SharedEditableActionCard.init(contentRoot); } " ;
echo " const getTable = () => contentRoot.querySelector('[data-markets-large-table]'); " ;
echo " let searchTimer = null; " ;
echo " const getSearchInput = () => getTable()?.querySelector('[data-markets-search-input]') || null; " ;
@@ -1035,17 +1152,30 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " const getCurrentLimit = () => parseInt(getTable()?.dataset.marketsLimit || '20', 10); " ;
echo " const getPageSize = () => parseInt(getTable()?.dataset.marketsPageSize || '20', 10); " ;
echo " const marketEscape = (value) => String(value ?? '').replace(/[&<> \" ']/g, (character) => ( { '&': '&', '<': '<', '>': '>', ' \" ': '"', \" ' \" : ''' }[character] || character)); " ;
echo " const marketDisplay = (value) => value === null || value === undefined || value === '' ? 'Nicht hinterlegt' : marketEscape(value); " ;
echo " const marketDisplay = (value) => { if ( value === null || value === undefined || value === '') { return 'Nicht hinterlegt'; } const escaped = marketEscape(value); const linked = escaped.replace(/https: \\ / \\ /erpnaurua \\ .imhochrain \\ .ch \\ /files \\ .php \\ ?path=[^ \\ s<]+/g, (url) => '<a class= \" sg-hyperlink \" href= \" ' + url + ' \" target= \" _blank \" rel= \" noopener \" >' + url + '</a>'); return linked.replace(/ \\ r? \\ n/g, '<br>'); }; " ;
echo " const marketBoolean = (value) => value === true || value === 'true' || value === 1 || value === '1' ? 'Ja' : value === false || value === 'false' || value === 0 || value === '0' ? 'Nein' : 'Nicht hinterlegt'; " ;
echo " const marketMoney = (value) => value === null || value === undefined || value === '' ? 'Nicht hinterlegt' : 'CHF ' + Number(value).toFixed(2); " ;
echo " const marketContactName = (contact) => { if (!contact) { return 'Nicht hinterlegt'; } return marketDisplay([contact.first_name, contact.last_name].filter(Boolean).join(' ')); }; " ;
echo " const marketDate = (value) => { if (!value) { return 'Nicht hinterlegt'; } const parts = String(value).split('-'); return parts.length === 3 ? parts[2] + '.' + parts[1] + '.' + parts[0] : marketEscape(value); }; " ;
echo " const marketStatusLabel = (value) => ( { open: 'Offen', waitlist: 'Warteliste', registered: 'Angemeldet', confirmation_pending: 'Bestätigung ausstehend', confirmed: 'Bestätigt', cancelled: 'Abgesagt', completed: 'Abgeschlossen' }[value] || value || 'Nicht hinterlegt'); " ;
echo " const marketStatusSegment = (value) => ( { waitlist: 'sg-card-segment--signal-red', registered: 'sg-card-segment--signal-yellow', confirmation_pending: 'sg-card-segment--signal-yellow', confirmed: 'sg-card-segment--signal-green' }[value] || 'sg-card-segment--darkblue '); " ;
echo " const marketRow = (label, value, specific = false ) => '<div class= \" market-detail-row \" ><dt>' + marketEscape(label) + (specific ? ' <span class= \" sg-table-label market-day-specific \" >(Tagesspezifisch)</span>' : '') + '</dt><dd>' + marketDisplay(value) + '</dd></div>';" ;
echo " const marketCard = (title, body, headerClass = 'sg-card-segment--darkgreen', actions = '') => '<article class= \" sg-card sg-card--content-card market-detail-card \" data-component= \" content-card \" ><header class= \" sg-card-segment sg-card-segment--header ' + headerClass + ' \" data-component-part =\" card-header \" ><div class= \" sg-strong sg-text-on-dark \" >' + title + '</div>' + actions + '</header><div class= \" sg-card-segment sg-card-segment--body \" data-component-part= \" card-body \" >' + body + '</div></article>' ;" ;
echo " const marketEditButton = (scope, id ) => '<button class= \" sg-interaction-element sg-hyperlink \" type= \" button \" data-market-edit= \" true \" data-market-edit-scope= \" ' + scope + ' \" data-market-edit-id= \" ' + marketEscape(id) + ' \" >bearbeiten</button>' ;" ;
echo " const renderMarketDetail = (payload) => { const market = payload.market || { }; const occurrence = payload.occurrence || { }; const days = Array.isArray(occurrence.days) ? occurrence.days : []; const website = market.website_url ? '<a class= \" sg-hyperlink \" href= \" ' + marketEscape(market.website_url) + ' \" target= \" _blank \" rel= \" noopener \" >' + marketEscape(market.website_url) + '</a>' : 'Nicht hinterlegt'; const overview = marketRow('Veranstaltung', market.name) + marketRow('Kategorie', market.category_name || market.category_code) + '<div class= \" market-detail-row \" ><dt>Website</dt><dd>' + website + '</dd></div>' + marketRow('Ort', occurrence.place) + marketRow('Adresse', occurrence.address) + marketRow('Gemeinde', occurrence.municipality_reference_id); const dates = days.map((day) => marketDate(day.date)).join(' – '); const participation = marketRow('Anmeldung / Anfrage', occurrence.request_or_registration_date) + marketRow('Anmeldefrist', occurrence.registration_deadline) + marketRow('Bestätigung', occurrence.confirmation_date) + marketRow('Zahlungsfrist', occurrence.payment_deadline) + marketRow('Zahlungsstatus', occurrence.payment_status); const infrastructure = marketRow('Standgrösse', occurrence.stand_size) + marketRow('Tisch vorhanden', occurrence.table_available) + marketRow('Tisch mietbar', occurrence.table_rentable) + marketRow('Eigener Tisch erforderlich', occurrence.own_table_required) + marketRow('Überdachung', occurrence.shelter_available) + marketRow('Strom', occurrence.power_available) + marketRow('Wasser', occurrence.water_available) + marketRow('Beleuchtung', occurrence.lighting_available); const travel = marketRow('Reiseadresse', occurrence.travel_address) + marketRow('Distanz (km)', occurrence.travel_distance_km) + marketRow('Reisedauer (Minuten)', occurrence.travel_duration_minutes) + marketRow('Parkmöglichkeiten', occurrence.parking_options) + marketRow('Anlieferung', occurrence.delivery_access) + marketRow('Direktes Entladen', occurrence.direct_unload_possible) + marketRow('Lagerung', occurrence.storage_options); const notes = marketRow('Teilnahmebedingungen', occurrence.other_participation_conditions) + marketRow('Rücktrittsbedingungen', occurrence.cancellation_terms) + marketRow('Umsatzbeteiligung', occurrence.revenue_share_terms) + marketRow('Notizen', occurrence.notes); const dayTabs = days.map((day, index) => '<button class= \" sg-interaction-element sg-button sg-tab-button \" type= \" button \" role= \" tab \" aria-selected= \" ' + (index === 0 ? 'true' : 'false') + ' \" aria-controls= \" market-day-panel-' + day.id + ' \" id= \" market-day-tab-' + day.id + ' \" data-market-day-tab= \" true \" >' + marketDate(day.date) + '</button>').join(''); const dayPanels = days.map((day, index) => '<div class= \" sg-content-block-card-group__panel \" id= \" market-day-panel-' + day.id + ' \" role= \" tabpanel \" aria-labelledby= \" market-day-tab-' + day.id + ' \" ' + (index === 0 ? '' : ' hidden') + '><div class= \" sg-content-cards-group \" data-pattern= \" content-cards-group \" aria-label= \" Durchführungstag ' + marketDate(day.date) + ' \" ><article class= \" sg-card sg-card--content-card \" ><header class= \" sg-card-segment sg-card-segment--header sg-card-segment--darkgreen \" ><div class= \" sg-strong sg-text-on-dark \" >' + marketDate(day.date) + '</div>' + marketEditButton('day', day.id) + '</header><div class= \" sg-card-segment sg-card-segment--body \" ><dl class= \" market-detail-grid \" >' + marketRow('Datum', marketDate(day.date), true) + marketRow('Standzeit von', day.stand_start_time, true) + marketRow('Standzeit bis', day.stand_end_time, true) + marketRow('Aufbau von', day.setup_start_time, true) + marketRow('Aufbau bis', day.setup_end_time, true) + marketRow('Anlieferung von', day.delivery_start_time, true) + marketRow('Anlieferung bis', day.delivery_end_time, true) + marketRow('Beobachtungen', day.observations, true) + '</dl></div></article></div></div>').join(''); return '<article class= \" sg-card sg-card--content-card market-status-card \" ><header class= \" sg-card-segment sg-card-segment--header ' + marketStatusSegment(occurrence.status) + ' \" ><div class= \" sg-strong sg-text-on-dark \" >Status: ' + marketEscape(marketStatusLabel(occurrence.status)) + '</div>' + marketEditButton('occurrence', occurrence.id) + '</header><div class= \" sg-card-segment sg-card-segment--body \" ><dl class= \" market-detail-grid \" >' + marketRow('Datumsbereich', dates) + marketRow('Anzahl Tage', days.length) + '</dl></div></article>' + marketCard('Veranstaltung und Durchführung', '<dl class= \" market-detail-grid \" >' + overview + '</dl>', 'sg-card-segment--darkgreen') + marketCard('Teilnahme und Zahlung', '<dl class= \" market-detail-grid \" >' + participation + '</dl>', 'sg-card-segment--darkgreen') + marketCard('Infrastruktur', '<dl class= \" market-detail-grid \" >' + infrastructure + '</dl>', 'sg-card-segment--darkgreen') + marketCard('Anreise und Logistik', '<dl class= \" market-detail-grid \" >' + travel + '</dl>', 'sg-card-segment--darkgreen') + marketC
Line truncated
echo " const marketEditForm = (scope, id, values) => { const fields = scope === 'day' ? [['date', 'Datum', 'date'], ['stand_start_time', 'Standzeit von', 'time'], ['stand_end_time', 'Standzeit bis', 'time'], ['setup_start_time', 'Aufbau von', 'time'], ['setup_end_time', 'Aufbau bis', 'time'], ['delivery_start_time', 'Anlieferung von', 'time'], ['delivery_end_time', 'Anlieferung bis', 'time'], ['observations', 'Beobachtungen', 'text']] : [['place', 'Ort', 'text'], ['address', 'Adresse', 'text'], ['registration_deadline', 'Anmeldefrist', 'date'], ['request_or_registration_date', 'Anmeldung / Anfrage', 'date'], ['confirmation_date', 'Bestätigung', 'date'], ['payment_deadline', 'Zahlungsfrist', 'date'], ['notes', 'Notizen', 'text']]; return '<form class= \" market-edit-form \" data-market-edit-form= \" true \" data-market-edit-scope= \" ' + scope + ' \" data-market-edit-id= \" ' + id + ' \" ><dl class= \" market-detail-grid \" >' + fields.map(([key, label, type]) => '<div class= \" market-detail-row \" ><dt>' + marketEscape(label ) + '</dt ><dd><input class=\" sg-interaction-element sg-input-single-line \" type= \" ' + type + ' \" name= \" ' + key + ' \" value= \" ' + marketEscape(values[key] || '') + ' \" > </dd ></div>').join('') + '</dl><div class= \" sg-component-row \" ><button class= \" sg-interaction-element sg-button sg-button--process \" type =\" submit \" >speichern</button><button class= \" sg-interaction-element sg-button sg-button--active \" type= \" button \" data-market-edit-cancel>abbrechen</button><span class= \" sg-body \" data- market-e dit-status></span> </div></form >'; };" ;
echo " const marketStatusSegment = (value) => ( { waitlist: 'sg-card-segment--signal-red', registered: 'sg-card-segment--signal-yellow', confirmation_pending: 'sg-card-segment--signal-yellow', confirmed: 'sg-card-segment--signal-green' }[value] || 'sg-card-segment--gray '); " ;
echo " const marketRow = (label, value) => '<div class= \" market-detail-row \" ><dt>' + marketEscape(label) + '</dt><dd>' + marketDisplay(value) + '</dd></div>'; " ;
echo " const initializeMunicipalityFields = (scope) => { scope.querySelectorAll('select[name= \" municipality_reference_id \" ]').forEach((select) => { if (!select.querySelector('option[value= \" __new__ \" ]')) { const option = document.createElement('option'); option.value = '__new__'; option.textContent = 'Neue Gemeinde'; select.append(option); } const form = select.closest('form'); if (!form) { return; } let fields = form.querySelector('[data-market-new-municipality]'); if (!fields) { fields = document.createElement('div'); fields.className = 'sg-form-sections-card__field-group'; fields.dataset.marketNewMunicipality = ''; fields.hidden = true; fields.innerHTML = '<label class= \" sg-labeled-input-row \" ><span class =\" sg-label \" >Gemeinde</span><input class= \" sg-interaction-element sg-input-single-line \" type= \" text \" name= \" municipality_name \" data-market-new-municipality-name></label>'; select.closest('.sg-form-sections-card__field-group')?.after(fields); } const sync = () => { const visible = select.value === '__new__'; fields.hidden = !visible; const input = fields.querySelector('[data-market-new-municipality-name]'); if (input) { input.disabled = !visible; input.setAttribute('aria-disabled', String(!visible)); } }; if (select.dataset.municipalityBound !== 'true') { select.addEventListener('change', sync); select.dataset.municipalityBound = 'true'; } sync(); }); } ;" ;
echo " const syncMunicipalityReadValue = (scope) => { const drawer = scope.querySelector('[data-market-drawer]'); if (!drawer?.dataset.marketPayload) { return; } let payload = { }; try { payload = JSON.parse(drawer.dataset.marketPayload); } catch (error) { return; } const value = payload.occurrence?.municipality_name || 'Nicht hinterlegt'; scope.querySelectorAll('.market-detail-row').forEach((row) => { if (row.querySelector('dt')?.textContent?.trim() !== 'Gemeinde') { return; } const output = row.querySelector('dd'); if (output && output.textContent !== value) { output.textContent = value; } }); } ;" ;
echo " const marketCard = (title, body, headerClass = 'sg-card-segment--darkgreen', actions = '', scope = '', id = '', values = null) => { const header = '<header class= \" sg-card-segment sg-card-segment--header ' + headerClass + ' sg-object-card__header \" data-component-part= \" card-header \" ><div class= \" sg-strong sg-text-on-dark \" >' + title + '</div>' + (actions ? '<div class= \" sg-component-row \" >' + actions + '</div>' : '') + '</header>'; return '<article class= \" sg-card sg-card--content-card market-detail-card \" data-component= \" content-card \" ' + (scope ? ' data-shared-editable-action-card' : '') + '>' + header + '<div class= \" sg-card-segment sg-card-segment--body \" data-component-part= \" card-body \" ' + (scope ? ' data-shared-editable-card-read' : '') + '>' + body + '</div>' + (scope ? '<div class= \" sg-card-segment sg-card-segment--body sg-card-segment--gray \" data-shared-editable-card-form hidden>' + marketEditForm(scope, id, values) + '</div>' : '') + '</article>'; }; " ;
echo " const marketStatusCard = ( id, values) => { const headerClass = marketStatusSegment(values.status); const textClass = headerClass === 'sg-card-segment--gray' ? '' : 'sg-text-on-dark'; const actions = marketEditButton('status', id); const header = '<header class= \" sg-card-segment sg-card-segment--header ' + headerClass + ' sg-object-card__header \" data-component-part= \" card-header \" ><div class= \" sg-strong ' + textClass + ' \" >Status: ' + marketEscape(marketStatusLabel(values.status) ) + '</div ><div class=\" sg-component-row \" > ' + actions + ' </div ></header>'; return '<article class= \" sg-card sg-card--content-card market-detail-card \" data-component =\" content-card \" data-shared-editable-action-card>' + header + '<div class= \" sg-card-segment sg-card-segment--body sg-card-segment--gray \" data-shared-editable-card-form hidden>' + marketE ditForm('status', id, values) + ' </div></article >'; };" ;
echo " const marketEditButton = () => '<button class= \" sg-interaction-element sg-button sg-button--active \" type= \" button \" data-shared-editable-card-edit>Bearbeiten</button>'; " ;
echo " const marketHumanizeError = (error) => { const message = error instanceof Error ? error.message : 'Speichern fehlgeschlagen.'; const labels = { table_available: 'Tisch vorhanden', table_rentable: 'Tisch mietbar', own_table_required: 'Eigener Tisch erforderlich', shelter_available: 'Überdachung', power_available: 'Strom', water_available: 'Wasser', lighting_available: 'Beleuchtung', direct_unload_possible: 'Direktes Entladen' }; const match = message.match(/^([a-z_]+) muss boolean sein \\ . $ /); return match && labels[match[1]] ? labels[match[1]] + ' muss Ja oder Nein sein.' : message; }; " ;
echo " document.addEventListener('submit', (event) => { const form = event.target.closest('[data-market-edit-form]'); if (!form) { return; } ['table_available', 'table_rentable', 'own_table_required', 'shelter_available', 'power_available', 'water_available', 'lighting_available', 'direct_unload_possible'].forEach((field) => { const control = form.elements.namedItem(field); if (!control) { return; } const label = control.selectedOptions?.[0]?.textContent?.trim(); control.value = label === 'Ja' ? '1' : label === 'Nein' ? '0' : control.value === '' || label === 'Nicht hinterlegt' ? '' : control.value; }); }, true); " ;
echo " const marketEditStatusObserver = new MutationObserver(() => { contentRoot.querySelectorAll('[data-market-edit-status], [data-market-create-status]').forEach((status) => { const message = status.textContent.trim(); if (!message || message === 'Speichern läuft …') { return; } const humanMessage = marketHumanizeError(new Error(message)); if (humanMessage !== message) { status.textContent = humanMessage; } status.classList.add('sg-form-validation-text'); status.setAttribute('role', 'alert'); }); }); marketEditStatusObserver.observe(contentRoot, { childList: true, characterData: true, subtree: true }); " ;
echo " const marketEditField = (key, label, type, value, options = [], required = false, minDate = '') => { const current = value === null || value === undefined ? '' : String(value); const requiredAttributes = required ? ' required aria-required= \" true \" ' : ''; const minAttribute = type === 'date' && minDate ? ' min= \" ' + marketEscape(minDate) + ' \" ' : ''; const disabledAttributes = ' disabled aria-disabled= \" true \" '; const control = type === 'readonly' ? '<input class= \" sg-interaction-element sg-input-single-line sg-input-single-line--disabled sg-form-disabled \" type= \" text \" name= \" ' + key + ' \" value= \" ' + marketEscape(current) + ' \" ' + disabledAttributes + '>' : type === 'readonly-textarea' ? '<textarea class= \" sg-input-multi-line sg-form-disabled \" name= \" ' + key + ' \" rows= \" 4 \" ' + disabledAttributes + '>' + marketEscape(current) + '</textarea>' : type === 'textarea' ? '<textarea class= \" sg-interaction-element sg-input-multi-line \" name= \" ' + key + ' \" rows= \" 4 \" ' + requiredAttributes + '>' + marketEscape(current) + '</textarea>' : type === 'select' ? '<select class= \" sg-interaction-element sg-input-single-line \" name= \" ' + key + ' \" ' + requiredAttributes + '>' + options.map(([optionValue, optionLabel, contactEmail = '', contactPhone = '']) => '<option value= \" ' + marketEscape(optionValue) + ' \" data-contact-email= \" ' + marketEscape(contactEmail) + ' \" data-contact-phone= \" ' + marketEscape(contactPhone) + ' \" ' + (String(optionValue) === current ? ' selected' : '') + '>' + marketEscape(optionLabel) + '</option>').join('') + '</select>' : '<input class= \" sg-interaction-element sg-input-single-line \" type= \" ' + type + ' \" name= \" ' + key + ' \" value= \" ' + marketEscape(current) + ' \" ' + requiredAttributes + minAttribute + '>'; const requiredIndicator = required ? '<span class= \" market-required-field-indicator \" aria-hidden= \" true \" >*</span>' : ''; return '<label class= \" sg-labeled-input-row \" ><span class= \" sg-label \" >' + marketEscape(label) + requiredIndicator + '</span>' + control + '</label>'; }; " ;
echo " const marketDeletePattern = (id, body) => '<div class= \" sg-delete-confirmation-pattern__stage sg-delete-confirmation-pattern__host \" data-pattern= \" overlay-card \" data-dialog-open= \" false \" ><div class= \" sg-delete-confirmation-pattern__target \" data-shared-editable-action-card data-shared-editable-delete-token= \" DELETE \" data-market-delete-occurrence-id= \" ' + marketEscape(id) + ' \" ><div class= \" sg-component-row \" ><button class= \" sg-interaction-element sg-button sg-button--warning \" type= \" button \" data-component= \" button \" data-component-variant= \" warning \" data-shared-editable-card-delete data-market-delete-button>Löschen</button></div>' + body + '<article class= \" sg-card sg-card--overlay-host sg-delete-confirmation-pattern__floating-card \" aria-label= \" Löschbestätigung \" role= \" dialog \" aria-modal= \" true \" aria-labelledby= \" market-delete-title-' + marketEscape(id) + ' \" data-overlay-dialog= \" delete \" data-shared-editable-card-delete-confirmation hidden><div class= \" sg-card-segment sg-card-segment--body sg-delete-confirmation-pattern__body \" ><p class= \" sg-body sg-delete-confirmation-pattern__text \" id= \" market-delete-title-' + marketEscape(id) + ' \" ><strong>Möchtest du diese Marktdurchführung wirklich löschen?</strong></p><p class= \" sg-body sg-delete-confirmation-pattern__text \" >Du kannst das nicht rückgängig machen. Bestätige durch Eingabe von <span class= \" sg-delete-confirmation-pattern__code \" >DELETE</span>.</p><label class= \" sg-labeled-input-row sg-delete-confirmation-pattern__input-row \" ><span class= \" sg-label \" >Bestätigung</span><input class= \" sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable \" type= \" text \" placeholder= \" DELETE \" aria-label= \" Löschbestätigung durch DELETE \" data-shared-editable-card-delete-input></label><p class= \" sg-body \" data-market-delete-status aria-live= \" polite \" ></p><div class= \" sg-delete-confirmation-pattern__actions \" ><button class= \" sg-interaction-element sg-button sg-button--active \" type= \" button \" data-shared-editable-card-delete-cancel>Abbrechen</button><button class= \" sg-interaction-element sg-button sg-button--process sg-button--process-inactive \" type= \" button \" disabled aria-disabled= \" true \" data-shared-editable-card-delete-submit data-market-delete-submit>Löschen</button></div></div></article></div></div>' ; " ;
echo " const marketDeletePatternWithTitle = (id, title, body) => { const template = document.createElement('template'); template.innerHTML = marketDeletePattern(id, body); const target = template.content.querySelector('[data-market-delete-occurrence-id]'); const row = target?.querySelector(':scope > .sg-component-row'); if (target && row) { const card = document.createElement('article'); card.className = 'sg-card sg-card--content-card market-delete-confirmation-card'; const header = document.createElement('header'); header.className = 'sg-card-segment sg-card-segment--header sg-card-segment--gray sg-object-card__header'; const heading = document.createElement('h2'); heading.className = 'sg-heading-h2 market-delete-confirmation-card__title'; heading.textContent = title; header.append(heading, row); card.append(header); target.prepend(card); } return template.innerHTML; }; " ;
echo " const marketEditForm = (scope, id, values) => { const boolOptions = [['', 'Nicht hinterlegt'], ['1', 'Ja'], ['0', 'Nein']]; const statusOptions = [['open', 'Offen'], ['waitlist', 'Warteliste'], ['registered', 'Angemeldet'], ['confirmation_pending', 'Bestätigung ausstehend'], ['confirmed', 'Bestätigt'], ['cancelled', 'Abgesagt'], ['completed', 'Abgeschlossen']]; const referenceOptions = values.reference_options || { }; const categoryOptions = [['', 'Nicht hinterlegt'], ...(Array.isArray(referenceOptions.categories) ? referenceOptions.categories.map((option) => [option.id, option.name]) : [])]; const municipalityOptions = [['', 'Nicht hinterlegt'], ...(Array.isArray(referenceOptions.municipalities) ? referenceOptions.municipalities.map((option) => [option.id, option.name]) : [])]; const partyOptions = [['', 'Nicht hinterlegt'], ...(Array.isArray(referenceOptions.parties) ? referenceOptions.parties.map((option) => [option.id, option.name]) : [])]; const contactOptions = [['', 'Nicht hinterlegt'], ...(Array.isArray(referenceOptions.contacts) ? referenceOptions.contacts.map((option) => [option.id, [[option.first_name, option.last_name].filter(Boolean).join(' ') || 'Nicht hinterlegt', option.party_name].filter(Boolean).join(' – '), option.email || '', option.phone || '']) : [])]; const costSummary = values.cost_summary || { }; const formValues = Object.assign( { }, values, { gross_cost: marketMoney(costSummary.gross_amount), net_cost: marketMoney(costSummary.net_amount), tax_cost: marketMoney(costSummary.tax_amount), open_cost: marketMoney(costSummary.open_amount), cost_items_text: values.cost_items_text, gross_revenue_display: marketMoney(values.gross_revenue), sold_item_count_display: marketDisplay(values.sold_item_count) }); const fieldSets = { status: [['status', 'Status', 'select', statusOptions]], overview: [['market_name', 'Markt', 'readonly'], ['category_name', 'Kategorie/Marktart', 'readonly'], ['website_url', 'Website', 'readonly'], ['place', 'Ort', 'text'], ['address', 'Adresse', 'textarea'], ['municipality_reference_id', 'Gemeinde', 'select', municipalityOptions], ['organization_contact_id', 'Veranstalter', 'select', partyOptions], ['contact_person_id', 'Kontaktperson', 'select', contactOptions], ['contact_email', 'Kontakt E-Mail', 'email'], ['contact_phone', 'Kontakt Telefon', 'tel']], participation: [['request_or_registration_date', 'Anmeldung / Anfrage', 'date'], ['registration_deadline', 'Anmeldefrist', 'date'], ['confirmation_date', 'Bestätigung', 'date'], ['payment_deadline', 'Zahlungsfrist', 'date'], ['payment_status', 'Zahlungsstatus', 'select', [['', 'Nicht hinterlegt'], ['paid', 'Bezahlt'], ['unpaid', 'Unbezahlt']]], ['payment_date', 'Zahlungsdatum', 'date'], ['paid_amount', 'Bezahlter Betrag', 'number'], ['payment_method_code', 'Zahlungsart', 'text'], ['gross_cost', 'Gesamtkosten brutto', 'readonly'], ['net_cost', 'Gesamtkosten netto', 'readonly'], ['tax_cost', 'Steuern', 'readonly'], ['open_cost', 'Offener Betrag', 'readonly'], ['cost_items_text', 'Kostenpositionen', 'readonly-textarea']], infrastructure: [['stand_size', 'Standgrösse', 'text'], ['table_available', 'Tisch vorhanden', 'select', boolOptions], ['table_rentable', 'Tisch mietbar', 'select', boolOptions], ['own_table_required', 'Eigener Tisch erforderlich', 'select', boolOptions], ['shelter_available', 'Überdachung', 'select', boolOptions], ['power_available', 'Strom', 'select', boolOptions], ['water_available', 'Wasser', 'select', boolOptions], ['lighting_available', 'Beleuchtung', 'select', boolOptions], ['other_infrastructure', 'Weitere Infrastruktur', 'textarea'], ['own_stand_requirements', 'Anforderungen an den eigenen Stand', 'textarea']], travel: [['travel_address', 'Reiseadresse', 'textarea'], ['travel_distance_km', 'Distanz (km)', 'number'], ['travel_duration_minutes', 'Reisedauer (Minuten)', 'number'], ['parking_options', 'Parkmöglichkeiten', 'textarea'], ['delivery_access', 'Anlieferung', 'textarea'], ['direct_unload_possible', 'Direktes Entladen', 'select', boolOptions], ['storage_options', 'Lagerung', 'textarea'], ['travel_notes', 'Besondere Anreisehinweise', 'textarea']], notes: [['cancellation_terms', 'Rücktrittsbedingungen', 'textarea'], ['revenue_share_terms', 'Umsatzbeteiligung', 'textarea'], ['other_participation_conditions', 'Teilnahmebedingungen', 'textarea'], ['notes', 'Notizen', 'textarea'], ['contract_folder_url', 'Vertragsordner', 'url']], day: [['date', 'Datum', 'date'], ['observations', 'Beobachtungen', 'textarea'], ['stand_start_time', 'Standzeit von', 'time'], ['stand_end_time', 'Standzeit bis', 'time'], ['setup_start_time', 'Aufbau von', 'time'], ['setup_end_time', 'Aufbau bis', 'time'], ['delivery_start_time', 'Anlieferung von', 'time'], ['delivery_end_time', 'Anlieferung bis', 'time'], ['gross_revenue_display', 'Umsatz', 'readonly'], ['sold_item_count_display', 'Verkaufte Artikel', 'readonly']] }; const requiredFields = { status: new Set(['status']), overview: new Set(), day: new
Line truncated
echo " const marketDeletePatternWithTitleAndDays = (id, title, body) => { const html = marketDeletePatternWithTitle(id, title, body); const template = document.createElement('template'); template.innerHTML = html; const target = template.content.querySelector('[data-market-delete-occurrence-id]'); const dayGroup = target?.querySelector('.market-days-group'); if (dayGroup) { const heading = document.createElement('h2'); heading.className = 'sg-heading-h2 market-days-group__title'; heading.textContent = 'Markttage'; dayGroup.before(heading); } return template.innerHTML; }; " ;
echo " const marketCreateForm = (marketDefinitions) => { const options = [['', 'Bitte auswählen'], ...(Array.isArray(marketDefinitions) ? marketDefinitions.map((market) => [market.id, market.name + (market.category_name ? ' – ' + market.category_name : '')]) : [])]; const fields = '<div class= \" sg-form-sections-card__field-group \" >' + marketEditField('market_definition_id', 'Markt', 'select', '', options, true) + '</div><div class= \" sg-form-sections-card__field-group \" >' + marketEditField('date', 'Erster Markttag', 'date', '', [], true) + '</div>'; return '<article class= \" sg-card sg-card--content-card market-detail-card \" data-component= \" content-card \" ><header class= \" sg-card-segment sg-card-segment--header sg-card-segment--darkgreen sg-object-card__header \" data-component-part= \" card-header \" ><div class= \" sg-strong sg-text-on-dark \" >Pflichtangaben für neue Marktdurchführung</div></header><div class= \" sg-card-segment sg-card-segment--body sg-card-segment--gray \" ><div class= \" sg-form-sections-card-wrapper \" data-pattern= \" form-sections \" ><form class= \" sg-form-sections-card market-create-form \" data-market-create-form= \" true \" ><div class= \" sg-form-sections-card__body \" data-pattern-part= \" form-body \" ><div class= \" sg-form-sections-card__section \" >' + fields + '</div></div><footer class= \" sg-form-sections-card__actions-segment \" data-pattern-part= \" form-actions-segment \" ><div class= \" sg-form-sections-card__actions \" data-pattern-part= \" form-actions \" ><button class= \" sg-interaction-element sg-button sg-button--active sg-form-sections-card__action \" type= \" button \" data-market-create-cancel>Abbrechen</button><button class= \" sg-interaction-element sg-button sg-button--process sg-form-sections-card__action \" type= \" submit \" >Speichern</button></div></footer><p class= \" sg-body \" data-market-create-status aria-live= \" polite \" ></p></form></div></div></article>'; }; " ;
echo " const buildUrl = (params) => { const url = new URL(window.location.href); Object.entries(params).forEach(([key, value]) => { if (value === null || value === undefined || value === '') { url.searchParams.delete(key); } else { url.searchParams.set(key, String(value)); } }); return url; }; " ;
echo " const replaceFragment = (html) => { const doc = new DOMParser().parseFromString(html, 'text/html'); const fragment = doc.querySelector('[data-markets-content]'); if (!fragment) { return false; } contentRoot.innerHTML = fragment.inn erHTML; return Boolean(getTable()); }; " ;
echo " const replaceFragment = (html) => { const doc = new DOMParser().parseFromString(html, 'text/html'); const fragment = doc.querySelector('[data-markets-content]'); if (!fragment) { return false; } contentRoot.innerHTML = fragment.out erHTML; return Boolean(getTable()); }; " ;
echo " const loadFragment = async (params) => { if (searchTimer) { window.clearTimeout(searchTimer); searchTimer = null; } const input = getSearchInput(); const wasFocused = input ? document.activeElement === input : false; const response = await fetch(buildUrl( { ...params, market_fragment: '1' }).toString(), { credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest' } }); if (!response.ok) { return; } const replaced = replaceFragment(await response.text()); if (!replaced) { return; } window.history.replaceState( { }, '', buildUrl(params)); bindTable(); if (wasFocused) { const nextInput = getSearchInput(); nextInput?.focus( { preventScroll: true }); if (nextInput) { nextInput.setSelectionRange(nextInput.value.length, nextInput.value.length); } } }; " ;
echo " const syncSearchState = () => { const input = getSearchInput(); const wrap = input?.closest('[data-component= \ 'single-line-input \ ']'); if (!input || !wrap) { return; } const hasValue = input.value.trim() !== ''; wrap.setAttribute('data-has-value', String(hasValue)); wrap.dataset.componentState = hasValue ? 'active' : 'inactive-selectable'; }; " ;
echo " const bindTable = () => { const table = getTable(); if (!table) { return; } const input = getSearchInput(); const clearButton = table.querySelector('[data-markets-search-clear]'); if (input && input.dataset.marketsBound !== 'true') { input.dataset.marketsBound = 'true'; input.addEventListener('input', () => { syncSearchState(); if (searchTimer) { window.clearTimeout(searchTimer); } searchTimer = window.setTimeout(() => { void loadFragment( { market_search: getSearchValue(), market_sort: getSortColumn(), market_dir: getSortDirection(), market_limit: getPageSize() }); }, 500); }); } if (clearButton && clearButton.dataset.marketsBound !== 'true') { clearButton.dataset.marketsBound = 'true'; clearButton.addEventListener('click', () => { const currentInput = getSearchInput(); if (currentInput) { currentInput.value = ''; } syncSearchState(); void loadFragment( { market_search: '', market_sort: getSortColumn(), market_dir: getSortDirection(), market_limit: getPageSize() }); }); } syncSearchState(); }; " ;
@@ -1053,22 +1183,138 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " const getMarketDrawer = () => document.querySelector('[data-market-mobile-drawer]') || contentRoot.querySelector('[data-market-drawer]'); " ;
echo " const getMarketBackdrop = () => contentRoot.querySelector('[data-market-drawer-backdrop]'); " ;
echo " const setMarketDrawerState = (open) => { const drawer = getMarketDrawer(); const backdrop = getMarketBackdrop(); if (!drawer || !backdrop) { return; } drawer.dataset.open = String(open); drawer.setAttribute('aria-hidden', String(!open)); backdrop.dataset.open = String(open); backdrop.setAttribute('aria-hidden', String(!open)); document.body.classList.toggle('sg-card-list-page-drawer-open', open); }; " ;
echo " const applyMarketTabs = (root) => { root.querySelectorAll('[data-market-day-tab]').forEach((tab) => { if (tab.dataset.marketTabBound === 'true') { return; } tab.dataset.marketTabBound = 'true'; tab.addEventListener('click', () => { const g roup = tab.closest('[data-pattern= \" card-gruppe-mit-tastennavigation \" ]'); g roup?.querySelectorAll('[data-market-day-tab]').forEach((candidate) => candidate.setAttribute('aria-selected', String(candidate === tab))); g roup?.querySelectorAll('[role= \" tabpanel \" ]').forEach((panel) => { panel.hidden = panel.id !== tab.getAttribute('aria-controls'); }); tab.focus(); }); }); }; " ;
echo " const renderMarketEdit = (scope, id, payload) => { const drawer = getMarketDrawer(); const source = scope === 'day' ? (payload.occurrence.days || []).find((day) => String(day.id) === String(id)) : payload.occurrence; const card = drawer? .querySelector('[data-market-edit= \" true \" ][data-market-edit-scope= \" ' + scope + ' \" ][data- market-e dit-id= \" ' + id + ' \" ]')?.closest('article'); if (card && source) { card.querySelector('[data-component-part= \" card-body \" ]')?.replaceChildren(); card .querySelector('[data-component-part= \" card-body \" ]').innerHTML = marketEditForm(scope, id, source); } };" ;
echo " const openMarketDrawer = async (trigger ) => { const drawer = getMarketDrawer(); if (!drawer) { return; } const title = drawer.querySelector('[data-market-drawer-title]'); const content = drawer .querySelector('[data-market-drawer-content]'); setMarketDrawerState(true); if (title) { title.textContent = 'Marktdurchführung'; } if (content) { content.innerHTML = '<p class =\" sg-body \" >Marktdetails werden geladen …</p>'; } try { const response = await fetch('/api/market-occurrence.php?id=' + encodeURIComponent(trigger.dataset.marketOccurrenceId || ''), { credentials: 'same-origin' }); const payload = await response.json(); if (!response.ok || !payload.ok) { throw new Error(payload.error || 'Marktdetails konnten nicht geladen werden.'); } drawer.dataset.marketPayload = JSON.stringify(payload); if (title) { title.textContent = payload.market?.name || 'Marktdurchführung'; } if (content) { content.innerHTML = renderMarketDetail(payload); applyMarketTabs(content); content.querySelectorAll('[data-market-edit]').forEach((button) => button.addEventListener('click', () => { const scope = button.dataset.marketEditScope || ''; const id = button.dataset.marketEditId || ''; const source = scope === 'day' ? (payload.occurrence.days || []).find((day ) => String(day .id) === String(id)) : payload.occurrence; const card = button.closest('article'); const body = card?.querySelector('[data-component-part= \" card-body \" ]'); if (body && source) { body.innerHTML = marketEditForm(scope, id, source); } })); } } catch (error) { if (content) { content.innerHTML = '<p class= \" sg-body \" >' + marketEscape(error.message) + '</p>'; } } contentRoot.querySelectorAll('[data-market-drawer-open]').forEach((button) => button.setAttribute('aria-expanded', String(button === trigger)) ); };" ;
echo " const applyMarketTabs = (root) => { const group = root.querySelector('[data-pattern= \" card-gruppe-mit-tastennavigation \" ] [role= \" tablist \" ]'); if (group) { group.dataset.component = 'tab-navigation'; group.dataset.componentSize = 'large'; group.querySelectorAll('[data-market-day-tab]').forEach((tab) => { tab.dataset.componentPart = 'tab-button'; tab.classList.remove('sg-button--active'); }); } root.querySelectorAll('[data-market-day-tab]').forEach((tab) => { if (tab.dataset.marketTabBound === 'true') { return; } tab.dataset.marketTabBound = 'true'; tab.addEventListener('click', () => { const tabG roup = tab.closest('[data-pattern= \" card-gruppe-mit-tastennavigation \" ]'); tabG roup?.querySelectorAll('[data-market-day-tab]').forEach((candidate) => candidate.setAttribute('aria-selected', String(candidate === tab))); tabG roup?.querySelectorAll('[role= \" tabpanel \" ]').forEach((panel) => { panel.hidden = panel.id !== tab.getAttribute('aria-controls'); }); tab.focus(); }); }); }; " ;
echo " const syncMarketDayAddButton = (root = contentRoot) => { if (!root.querySelector('[data-market-inherit-row]')) { enhanceMarketDayInheritance(root); } const drawer = getMarketDrawer(); const payload = drawer?.dataset.marketPayload ? JSON.parse(drawer.dataset.marketPayload) : null; const days = Array.isArray (payload? .occurrence? .days) ? payload.occurrence.days.filter((day) => !day.__draft).slice().sort((left, right) => String(left.date).localeCompare(String(right.date))) : []; const nextDate = (date) => { const value = new Date(String(date) + 'T00:00:00Z'); value.setUTCDate(value.getUTCDate() + 1); return value.toISOString().slice(0, 10); }; root .querySelectorAll ('[data-market-edit-form ][data-market-edit-scope=\" day \" ]').forEach((form) => { const input = form.querySelector('[name= \" date \" ]'); if (!input) { return; } const index = days.findIndex((day) => String(day.id) === String(form.dataset. marketE ditId)); const minimum = form.dataset.marketEditDraft === 'true' ? days.at(-1)?.date : index > 0 ? days[index - 1].date : ''; input.min = minimum ? nextDate(minimum) : ''; }); const addButton = root.querySelector('[data-market-day-add]'); if (!addButton) { return; } const editing = Array.from(root .querySelectorAll ('[data-market-edit-form]')).some((form) => form.closest('[data-shared-editable-card-form]')?.hidden === false); addButton.disabled = editing; addButton.setAttribute('aria-disabled', String(editing)); addButton.classList.toggle('sg-button--active', !editing); addButton.classList.toggle('sg-button--inactive', editing); };" ;
echo " const enhanceMarketDayInheritance = (root ) => { const drawer = getMarketDrawer(); const payload = drawer?.dataset.marketPayload ? JSON.parse(drawer.dataset.marketPayload) : null; const days = Array.isArray(payload?.occurrence?.days) ? payload.occurrence.days.filter((day) => !day.__draft).slice().sort((left, right) => String(left.date).localeCompare(String(right.date))) : []; const firstDayId = days[0]?.id; root .querySelectorAll ('[data-market-edit-form][data-market-edit-scope= \" day \" ]').forEach((form) => { const dayId = form.dataset.marketEditId; if (!dayId || String(dayId) === String(firstDayId)) { return; } const dateGroup = form.querySelector('[name= \" date \" ]')?.closest('.sg-form-sections-card__field-group'); const observationsGroup = form.querySelector('[name =\" observations \" ]')?.closest('.sg-form-sections-card__field-group'); if (!dateGroup || !observationsGroup) { return; } let row = form.querySelector('[data-market-inherit-row]'); if (!row) { row = document.createElement('div'); row.className = 'sg-form-sections-card__field-group'; row.dataset.marketInheritRow = 'true'; row.innerHTML = '<label class= \" sg-checkbox-field-option sg-body \" data-component= \" checkbox-field \" data-component-state= \" default \" ><button class= \" sg-checkbox-field \" type= \" button \" role= \" checkbox \" aria-checked= \" false \" aria-label= \" Ab hier alle Werte aus Tag 1 übernehmen \" data-market-inherit-toggle><span class= \" sg-checkbox-field__mark \" aria-hidden= \" true \" >✓</span></button><span>Ab hier alle Werte aus Tag 1 übernehmen</span><input type= \" hidden \" name= \" inherit_from_first_day \" value= \" 0 \" ></label>'; observationsGroup.after(row); row.querySelector('[data-market-inherit-toggle]').addEventListener('click', (event) => { event.stopImmediatePropagation(); const toggle = row.querySelector('[data-market-inherit-toggle]'); const checked = toggle.getAttribute('aria-checked') !== 'true'; toggle.setAttribute('aria-checked', String(checked)); toggle.classList.toggle('sg-form-active', checked); row.querySelector('[name= \" inherit_from_first_day \" ]').value = checked ? '1' : '0'; form.querySelectorAll('input, select, textarea').forEach((control) => { if (control.name !== 'date' && control.name !== 'inherit_from_first_day' && control.name !== 'observations') { control.disabled = checked; control.setAttribute('aria-disabled', String(checked)); control.classList.toggle('sg-form-disabled', checked); control.classList.toggle('sg-input-single-line--disabled', checked && control.tagName === 'INPUT'); } }); syncMarketDayAddButton(root); }); } const day = days.find((candidate ) => String(candidate .id) === String(dayId)); const inherited = Boolean(day?.inherit_from_first_day); const toggle = row.querySelector('[data-market-inherit-toggle]'); toggle.setAttribute('aria-checked', String(inherited)); toggle.classList.toggle('sg-form-active', inherited); row.querySelector('[name= \" inherit_from_first_day \" ]').value = inherited ? '1' : '0'; form.querySelectorAll('input, select, textarea').forEach((control) => { if (control.name !== 'date' && control.name !== 'inherit_from_first_day' && control.name !== 'observations') { control.disabled = inherited; control.setAttribute('aria-disabled', String(inherited)); control.classList.toggle('sg-form-disabled', inherited); control.classList.toggle('sg-input-single-line--disabled', inherited && control.tagName === 'INPUT'); } }); } ); };" ;
echo " const marketDayDeleteConfirmation = (id, label) => '<article class= \" sg-card sg-card--overlay-host sg-delete-confirmation-pattern__floating-card \" aria-label= \" Löschbestätigung für Markttag \" role= \" dialog \" aria-modal= \" true \" aria-labelledby= \" market-day-delete-title-' + marketEscape(id) + ' \" data-overlay-dialog= \" delete \" data-market-day-delete-confirmation data-shared-editable-card-delete-confirmation hidden><div class= \" sg-card-segment sg-card-segment--body sg-delete-confirmation-pattern__body \" ><p class= \" sg-body sg-delete-confirmation-pattern__text \" id= \" market-day-delete-title-' + marketEscape(id) + ' \" ><strong>Möchtest du den Markttag ' + marketEscape(label) + ' wirklich löschen?</strong></p><p class= \" sg-body sg-delete-confirmation-pattern__text \" >Du kannst das nicht rückgängig machen. Bestätige durch Eingabe von <span class= \" sg-delete-confirmation-pattern__code \" >DELETE</span>.</p><label class= \" sg-labeled-input-row sg-delete-confirmation-pattern__input-row \" ><span class= \" sg-label \" >Bestätigung</span><input class= \" sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable \" type= \" text \" placeholder= \" DELETE \" aria-label= \" Löschbestätigung durch DELETE \" data-shared-editable-card-delete-input></label><p class= \" sg-body \" data-market-day-delete-status aria-live= \" polite \" ></p><div class= \" sg-delete-confirmation-pattern__actions \" ><button class= \" sg-interaction-element sg-button sg-button--active \" type= \" button \" data-shared-editable-card-delete-cancel>Abbrechen</button><button class= \" sg-interaction-element sg-button sg-button--process sg-button--process-inactive \" type= \" button \" disabled aria-disabled= \" true \" data-shared-editable-card-delete-submit data-market-day-delete-submit>Löschen</button></div></div></article>'; " ;
echo " const enhanceMarketDayDeleteControls = (root) => { const cards = Array.from(root.querySelectorAll('.market-days-group .sg-content-cards-group > [data-shared-editable-action-card]')); cards.forEach((card, index) => { if (index === 0 || card.querySelector('[data-market-edit-form][data-market-edit-draft= \" true \" ]')) { return; } const form = card.querySelector('[data-market-edit-form][data-market-edit-scope= \" day \" ]'); const actions = card.querySelector('.sg-object-card__header .sg-component-row'); if (!form || !actions || actions.querySelector('[data-market-day-delete-button]')) { return; } const dayId = form.dataset.marketEditId || ''; const label = card.querySelector('.sg-object-card__header .sg-strong')?.textContent?.trim() || 'diesen Markttag'; if (!dayId) { return; } const stage = document.createElement('div'); stage.className = 'sg-delete-confirmation-pattern__stage sg-delete-confirmation-pattern__host'; stage.dataset.pattern = 'overlay-card'; stage.dataset.dialogOpen = 'false'; card.classList.add('sg-delete-confirmation-pattern__target'); card.parentNode.replaceChild(stage, card); stage.appendChild(card); card.dataset.marketDayDeleteId = dayId; card.dataset.sharedEditableDeleteToken = 'DELETE'; actions.insertAdjacentHTML('afterbegin', '<button class= \" sg-interaction-element sg-button sg-button--warning \" type= \" button \" data-component= \" button \" data-component-variant= \" warning \" data-shared-editable-card-delete data-market-day-delete-button>Löschen</button>'); card.insertAdjacentHTML('beforeend', marketDayDeleteConfirmation(dayId, label)); }); }; " ;
echo " const rerenderMarketDetail = (payload) => { const drawer = getMarketDrawer(); const content = drawer?.querySelector('[data-market-drawer-content]'); if (!drawer || !content) { return; } drawer.dataset.marketPayload = JSON.stringify(payload); content.innerHTML = renderMarketDetailWithActions(payload); applyMarketTabs(content); enhanceMarketDayDeleteControls(content); syncMarketDayAddButton(content); }; " ;
echo " const addMarketDayDraft = () => { const drawer = getMarketDrawer(); const payload = drawer?.dataset.marketPayload ? JSON.parse(drawer.dataset.marketPayload) : null; if (!payload?.occurrence) { return; } const days = Array.isArray(payload.occurrence.days) ? payload.occurrence.days : []; if (days.some((day) => day.__draft)) { return; } days.push( { id: 'draft-' + Date.now(), date: '', __draft: true }); payload.occurrence.days = days; rerenderMarketDetail(payload); const draftEdit = contentRoot.querySelector('[data-market-edit-form][data-market-edit-draft= \" true \" ]')?.closest('[data-shared-editable-action-card]')?.querySelector('[data-shared-editable-card-edit]'); draftEdit?.click(); window.setTimeout(() => syncMarketDayAddButton(contentRoot), 0); }; " ;
echo " const discardMarketDayDraft = () => { const drawer = getMarketDrawer(); const payload = drawer?.dataset.marketPayload ? JSON.parse(drawer.dataset.marketPayload) : null; if (!payload?.occurrence || !Array.isArray(payload.occurrence.days)) { return; } const days = payload.occurrence.days.filter((day) => !day.__draft); if (days.length === payload.occurrence.days.length) { return; } payload.occurrence.days = days; rerenderMarketDetail(payload); }; " ;
echo " const closeMarketDrawer = () => { setMarketDrawerState(false); contentRoot.querySelectorAll('[data-market-drawer-open]').forEach((button) => button.setAttribute('aria-expanded', 'false')); }; " ;
echo " const renderMarketDetail = (payload) => { const market = payload.market || { }; const occurrence = payload.occurrence || { }; const days = Array.isArray(occurrence.days) ? occurrence.days : []; const hasDraftDay = days.some((day) => day.__draft); const organizer = occurrence.organizer || { }; const organization = organizer.organization || { }; const contactPerson = organizer.contact_person || { }; const website = market.website_url ? '<a class= \" sg-hyperlink \" href= \" ' + marketEscape(market.website_url) + ' \" target= \" _blank \" rel= \" noopener \" >' + marketEscape(market.website_url) + '</a>' : 'Nicht hinterlegt'; const contract = occurrence.contract_folder_url ? '<a class= \" sg-hyperlink \" href= \" ' + marketEscape(occurrence.contract_folder_url) + ' \" target= \" _blank \" rel= \" noopener \" >Vertragsordner öffnen</a>' : 'Nicht hinterlegt'; const overview = marketRow('Markt', market.name) + marketRow('Kategorie', market.category_name || market.category_code) + '<div class= \" market-detail-row \" ><dt>Website</dt><dd>' + website + '</dd></div>' + marketRow('Ort', occurrence.place) + marketRow('Adresse', occurrence.address) + marketRow('Gemeinde', occurrence.municipality_reference_id) + marketRow('Veranstalter', organization.name) + marketRow('Kontaktperson', marketContactName(contactPerson)) + marketRow('Kontakt E-Mail', contactPerson.email) + marketRow('Kontakt Telefon', contactPerson.phone); const dates = days.map((day) => marketDate(day.date)).filter((date) => date !== 'Nicht hinterlegt').join(' – '); const costSummary = occurrence.cost_summary || { }; const participation = marketRow('Anmeldung / Anfrage', occurrence.request_or_registration_date) + marketRow('Anmeldefrist', occurrence.registration_deadline) + marketRow('Bestätigung', occurrence.confirmation_date) + marketRow('Zahlungsfrist', occurrence.payment_deadline) + marketRow('Zahlungsstatus', occurrence.payment_status) + marketRow('Zahlungsdatum', occurrence.payment_date) + marketRow('Bezahlter Betrag', marketMoney(occurrence.paid_amount)) + marketRow('Zahlungsart', occurrence.payment_method_code) + marketRow('Gesamtkosten brutto', marketMoney(costSummary.gross_amount)) + marketRow('Gesamtkosten netto', marketMoney(costSummary.net_amount)) + marketRow('Steuern', marketMoney(costSummary.tax_amount)) + marketRow('Offener Betrag', marketMoney(costSummary.open_amount)) + '<div class= \" market-detail-row \" ><dt>Kostenpositionen</dt><dd>' + marketDisplay(occurrence.cost_items_text) + '</dd></div>'; const infrastructure = marketRow('Standgrösse', occurrence.stand_size) + marketRow('Tisch vorhanden', marketBoolean(occurrence.table_available)) + marketRow('Tisch mietbar', marketBoolean(occurrence.table_rentable)) + marketRow('Eigener Tisch erforderlich', marketBoolean(occurrence.own_table_required)) + marketRow('Überdachung', marketBoolean(occurrence.shelter_available)) + marketRow('Strom', marketBoolean(occurrence.power_available)) + marketRow('Wasser', marketBoolean(occurrence.water_available)) + marketRow('Beleuchtung', marketBoolean(occurrence.lighting_available)) + marketRow('Weitere Infrastruktur', occurrence.other_infrastructure) + marketRow('Anforderungen an den eigenen Stand', occurrence.own_stand_requirements); const travel = marketRow('Reiseadresse', occurrence.travel_address) + marketRow('Distanz (km)', occurrence.travel_distance_km) + marketRow('Reisedauer (Minuten)', occurrence.travel_duration_minutes) + marketRow('Parkmöglichkeiten', occurrence.parking_options) + marketRow('Anlieferung', occurrence.delivery_access) + marketRow('Direktes Entladen', marketBoolean(occurrence.direct_unload_possible)) + marketRow('Lagerung', occurrence.storage_options) + marketRow('Besondere Anreisehinweise', occurrence.travel_notes); const notes = marketRow('Teilnahmebedingungen', occurrence.other_participation_conditions) + marketRow('Rücktrittsbedingungen', occurrence.cancellation_terms) + marketRow('Umsatzbeteiligung', occurrence.revenue_share_terms) + marketRow('Notizen', occurrence.notes) + '<div class= \" market-detail-row \" ><dt>Vertragsordner</dt><dd>' + contract + '</dd></div>'; const salesSummary = occurrence.sales_summary || { }; const dayTabs = days.map((day, index) => { const label = day.__draft ? 'Neuer Tag' : marketDate(day.date); const selected = day.__draft || (!hasDraftDay && index === 0); return '<button class= \" sg-interaction-element sg-button sg-button--active sg-tab-button \" type= \" button \" role= \" tab \" aria-selected= \" ' + (selected ? 'true' : 'false') + ' \" aria-controls= \" market-day-panel-' + day.id + ' \" id= \" market-day-tab-' + day.id + ' \" data-market-day-tab= \" true \" >' + label + '</button>'; }).join(''); const addDayTab = '<button class= \" sg-interaction-element sg-button sg-button--active sg-tab-button market-day-add-button \" type= \" button \" data-market-day-add= \" true \" aria-label= \" Markttag hinzufügen \" title= \" Markttag hinzufügen \" >+</button>'; const dayPanels = days.map((day, index) => { const label = day.__draft ? 'Neuer Tag' : marketDa
Line truncated
echo " const renderMarketDetailWithActions = (payload) => marketDeletePatternWithTitleAndDays(payload.occurrence?.id || '', marketDrawerTitle(payload), renderMarketDetail(payload)); " ;
echo " const marketDrawerTitle = (payload) => { const marketName = payload.market?.name || 'Marktdurchführung'; const dates = (Array.isArray(payload.occurrence?.days) ? payload.occurrence.days : []).map((day) => marketDate(day.date)).filter(Boolean); return dates.length ? marketName + ', Marktdurchführung ' + dates.join(', ') : marketName; }; " ;
echo " const openMarketDrawer = async (trigger) => { const drawer = getMarketDrawer(); if (!drawer) { return; } const title = drawer.querySelector('[data-market-drawer-title]'); const content = drawer.querySelector('[data-market-drawer-content]'); setMarketDrawerState(true); if (title) { title.textContent = 'Marktdurchführung'; } if (content) { content.innerHTML = '<p class= \" sg-body \" >Marktdetails werden geladen …</p>'; } try { const response = await fetch('/api/market-occurrence.php?id=' + encodeURIComponent(trigger.dataset.marketOccurrenceId || ''), { credentials: 'same-origin' }); const payload = await response.json(); if (!response.ok || !payload.ok) { throw new Error(payload.error || 'Marktdetails konnten nicht geladen werden.'); } drawer.dataset.marketPayload = JSON.stringify(payload); if (title) { title.textContent = marketDrawerTitle(payload); } if (content) { content.innerHTML = renderMarketDetailWithActions(payload); applyMarketTabs(content); syncMarketDayAddButton(content); } } catch (error) { if (content) { content.innerHTML = '<p class= \" sg-body \" >' + marketEscape(error.message) + '</p>'; } } contentRoot.querySelectorAll('[data-market-drawer-open]').forEach((button) => button.setAttribute('aria-expanded', String(button === trigger))); }; " ;
echo " const openNewMarketDrawer = async () => { const drawer = getMarketDrawer(); if (!drawer) { return; } const title = drawer.querySelector('[data-market-drawer-title]'); const content = drawer.querySelector('[data-market-drawer-content]'); setMarketDrawerState(true); if (title) { title.textContent = 'Neue Marktdurchführung'; } if (content) { content.innerHTML = '<p class= \" sg-body \" >Pflichtangaben werden geladen …</p>'; } try { const response = await fetch('/api/market-occurrence.php?action=create_options', { credentials: 'same-origin' }); const payload = await response.json(); if (!response.ok || !payload.ok) { throw new Error(payload.error || 'Pflichtangaben konnten nicht geladen werden.'); } if (content) { content.innerHTML = marketCreateForm(payload.market_definitions || []); } } catch (error) { if (content) { content.innerHTML = '<p class= \" sg-body \" >' + marketEscape(error.message) + '</p>'; } } }; " ;
echo " const restoreMarketList = () => { if (marketMobileDetailHost?.parentNode) { const drawer = marketMobileDetailHost.querySelector('[data-market-mobile-drawer]'); if (drawer) { contentRoot.appendChild(drawer); drawer.removeAttribute('data-market-mobile-drawer'); drawer.dataset.open = 'false'; drawer.setAttribute('aria-hidden', 'true'); } marketMobileDetailHost.parentNode.removeChild(marketMobileDetailHost); } marketMobileDetailHost = null; marketDetailOpen = false; document.body.classList.remove('sg-market-mobile-detail-open'); bindTable(); }; " ;
echo " const openMarketMobileDetail = (trigger) => { restoreMarketList(); const sourceDrawer = contentRoot.querySelector('[data-market-drawer]'); if (!sourceDrawer) { return; } const host = document.createElement('div'); host.className = 'sg-market-mobile-detail-host'; host.dataset.marketMobileDetailHost = 'true'; host.innerHTML = '<div class= \" sg-navigation-card-layout sg-mobile-detail-back-card \" ><div class= \" sg-navigation-card-block \" ><article class= \" sg-card \" data-component= \" card \" data-pattern= \" navigation-card \" ><div class= \" sg-card-segment sg-card-segment--body \" ><div class= \" sg-navigation-card-center \" ><button class= \" sg-interaction-element sg-hyperlink \" type= \" button \" data-market-detail-back>zurück</button></div></div></article></div></div>'; sourceDrawer.dataset.marketMobileDrawer = 'true'; sourceDrawer.dataset.open = 'true'; sourceDrawer.setAttribute('aria-hidden', 'false'); host.appendChild(sourceDrawer); document.body.appendChild(host); marketMobileDetailHost = host; document.body.classList.add('sg-market-mobile-detail-open'); marketOpeningMobileDetail = true; void openMarketDrawer(trigger).finally(() => { marketOpeningMobileDetail = false; }); marketDetailOpen = true; window.history.pushState( { marketView: 'detail' }, '', window.location.href); }; " ;
echo " const deleteMarketOccurrence = async (submit) => { if (submit.disabled) { return; } const card = submit.closest('[data-shared-editable-action-card]'); const confirmation = Array.from(card?.children || []).find((child) => child.matches('[data-shared-editable-card-delete-confirmation]')); const input = confirmation?.querySelector('[data-shared-editable-card-delete-input]'); const status = confirmation?.querySelector('[data-market-delete-status]'); const occurrenceId = card?.dataset.marketDeleteOccurrenceId || ''; if (!card || !input || input.value !== 'DELETE' || !occurrenceId) { return; } if (status) { status.textContent = 'Löschen läuft …'; } submit.disabled = true; submit.setAttribute('aria-disabled', 'true'); try { const response = await fetch('/api/market-occurrence.php', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': contentRoot.querySelector('[data-market-csrf-token]')?.dataset.marketCsrfToken || '' }, body: JSON.stringify( { action: 'delete', id: occurrenceId, confirmation: input.value }) }); const result = await response.json(); if (!response.ok || !result.ok) { throw new Error(result.error || 'Löschen fehlgeschlagen.'); } closeMarketDrawer(); if (marketDetailOpen) { restoreMarketList(); } await loadFragment( { market_search: getSearchValue(), market_sort: getSortColumn(), market_dir: getSortDirection(), market_limit: getCurrentLimit() }); } catch (error) { if (status) { status.textContent = marketEscape(error.message); } submit.disabled = false; submit.setAttribute('aria-disabled', 'false'); } }; " ;
echo " const deleteMarketDay = async (submit) => { if (submit.disabled) { return; } const card = submit.closest('[data-shared-editable-action-card]'); const input = card?.querySelector('[data-shared-editable-card-delete-input]'); const status = card?.querySelector('[data-market-day-delete-status]'); const dayId = card?.dataset.marketDayDeleteId || ''; if (!card || !input || input.value !== 'DELETE' || !dayId) { return; } if (status) { status.textContent = 'Löschen läuft …'; } submit.disabled = true; submit.setAttribute('aria-disabled', 'true'); try { const response = await fetch('/api/market-occurrence.php', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': contentRoot.querySelector('[data-market-csrf-token]')?.dataset.marketCsrfToken || '' }, body: JSON.stringify( { action: 'delete_day', id: dayId, confirmation: input.value }) }); const result = await response.json(); if (!response.ok || !result.ok) { throw new Error(result.error || 'Löschen fehlgeschlagen.'); } const trigger = contentRoot.querySelector('[data-market-drawer-open][aria-expanded= \" true \" ]'); if (trigger) { await openMarketDrawer(trigger); } } catch (error) { if (status) { status.textContent = marketEscape(error.message); } submit.disabled = false; submit.setAttribute('aria-disabled', 'false'); } }; " ;
echo " const marketDayDeleteObserver = new MutationObserver(() => enhanceMarketDayDeleteControls(contentRoot)); marketDayDeleteObserver.observe(contentRoot, { childList: true, subtree: true }); " ;
echo " const municipalityFieldObserver = new MutationObserver(() => initializeMunicipalityFields(contentRoot)); municipalityFieldObserver.observe(contentRoot, { childList: true, subtree: true }); initializeMunicipalityFields(contentRoot); " ;
echo " const municipalityReadObserver = new MutationObserver(() => syncMunicipalityReadValue(contentRoot)); municipalityReadObserver.observe(contentRoot, { childList: true, subtree: true }); syncMunicipalityReadValue(contentRoot); " ;
echo " if (!marketsBindingsInstalled) { " ;
echo " marketsBindingsInstalled = true; " ;
echo " document.addEventListener('click', (event) => { const sortButton = event.target.closest('[data-markets-sort-button]'); if (sortButton) { event.preventDefault(); void loadFragment( { market_search: getSearchValue(), market_sort: sortButton.dataset.marketsSortColumn || getSortColumn(), market_dir: sortButton.dataset.marketsSortDirection || getSortDirection(), market_limit: getPageSize() } ); return; } const loadMore = event.target.closest('[data-markets-load-more-trigger]'); if (loadMore) { event.preventDefault(); void loadFragment( { market_search: getSearchValue(), market_sort: getSortColumn(), market_dir: getSortDirection(), market_limit: getCurrentLimit() + getPageSize() } ); return; } const trigger = event.target.closest('[data-market-drawer-open]'); if (trigger) { event.preventDefault(); if (window.matchMedia('(max-width: 767px)').matches && !marketOpeningMobileDetail) { openMarketMobileDetail(trigger); } else { void openMarketDrawer(trigger); } return; } if (event.target.closest('[data-market-drawer-close]')) { closeMarketDrawer(); return; } if (event.target.closest('[data-market-detail-back]')) { if (window.history.state?.marketView === 'detail') { window.history.back(); } else { restoreMarketList(); } return; } const edit = event.target.closest('[data-market-edit]'); if (edit) { const drawer = getMarketDrawer(); const current = drawer?.dataset.marketPayload ? JSON.parse(drawer.dataset.marketPayload) : null; if (current) { const scope = edit.dataset.marketEditScope || ''; const id = edit.dataset.marketEditId || ''; const source = scope === 'day' ? (current.occurrence.days || []).find((day) => String(day.id) === String(id)) : current.occurrence; const card = edit.closest('article'); const body = card?.querySelector('[data-component-par t=\" card-body \" ]'); if (body && source) { body.innerHTML = marketEditForm(scope, id, source); } } return; } if (event.target.closest('[data-market-edit-cancel]')) { const drawer = getMarketDrawer(); if (drawer?.dataset.marketPayload) { const payload = JSON.parse(drawer.dataset.marketPayload); const content = drawer.querySelector('[data-market-drawer-content]'); content.innerHTML = renderMarketDetail(payload); applyMarketTabs(content ); } } });" ;
echo " document.addEventListener('submit', async (event) => { const form = event.target.closest('[data-market-edit-form]'); if (!form) { return; } event.preventDefault(); const status = form.querySelector('[data-market-edit-status]'); const fields = Object.fromEntries(new FormData(form).entries()); if (status) { status.textContent = 'Speichern läuft …'; } try { const response = await fetch('/api/market-occurrence.php', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': contentRoot.querySelector('[data-market-csrf-token]')?.dataset.marketCsrfToken || '' }, body: JSON.stringify( { scope: form.dataset.marketEditScope, id: form.dataset.marketEditId, fields }) }); const result = await response.json(); if (!response.ok || !result.ok) { throw new Error(result.error || 'Speichern fehlgeschlagen.'); } const trigger = contentRoot.querySelector('[data-market-drawer-open][aria-expanded= \" true \" ]'); if (trigger) { await openMarketDrawer(trigger); } } catch (error) { if (status) { status.textContent = marketEscape(error.message); } } });" ;
echo " document.addEventListener('click', (event) => { const dayAdd = event.target.closest('[data-market-day-add]'); if (dayAdd) { event.preventDefault(); addMarketDayDraft( ); return; } const editButton = event.target.closest('[data-shared-editable-card-edit]'); if (editButton) { window.setTimeout(() => syncMarketDayAddButton(contentRoot), 0 ); return; } const cancelButton = event.target.closest('[data-shared-editable-card-cancel]'); if (cancelButton) { if (cancelButton.closest('[data-market-edit-form][data-market-edit-draf t=\" true \" ]')) { window.setTimeout(discardMarketDayDraft, 0); } else { window.setTimeout(() => syncMarketDayAddButton(contentRoot), 0 ); } } });" ;
echo " document.addEventListener('click', (event) => { const deleteDaySubmit = event.target.closest('[data-market-day-delete-submit]'); if (deleteDaySubmit) { void deleteMarketDay(deleteDaySubmit); return; } const createButton = event.target.closest('[data-market-occurrence-create]'); if (createButton) { event.preventDefault(); void openNewMarketDrawer(); return; } if (event.target.closest('[data-market-create-cancel]')) { closeMarketDrawer(); } });" ;
echo " document.addEventListener('submit', async (event) => { const form = event.target.closest('[data-market-create-form]'); if (!form) { return; } event.preventDefault(); event.stopImmediatePropagation(); const status = form.querySelector('[data-market-create-status]'); const fields = Object.fromEntries(new FormData(form).entries()); if (status) { status.textContent = 'Speichern läuft …'; } try { const response = await fetch('/api/market-occurrence.php', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': contentRoot.querySelector('[data-market-csrf-token]')?.dataset.marketCsrfToken || '' }, body: JSON.stringify( { action: 'create', fields }) }); const result = await response.json(); if (!response.ok || !result.ok) { throw new Error(result.error || 'Speichern fehlgeschlagen.'); } const detailResponse = await fetch('/api/market-occurrence.php?id=' + encodeURIComponent(result.occurrence_id), { credentials: 'same-origin' }); const payload = await detailResponse.json(); if (!detailResponse.ok || !payload.ok) { throw new Error(payload.error || 'Die neue Durchführung konnte nicht geladen werden.'); } const drawer = getMarketDrawer(); const title = drawer?.querySelector('[data-market-drawer-title]'); const content = drawer?.querySelector('[data-market-drawer-content]'); if (drawer) { drawer.dataset.marketPayload = JSON.stringify(payload); } if (title) { title.textContent = marketDrawerTitle(payload); } if (content) { content.innerHTML = renderMarketDetailWithActions(payload); applyMarketTabs(content); syncMarketDayAddButton(content); } } catch (error) { if (status) { status.textContent = marketEscape(error.message); } } }); " ;
echo " document.addEventListener('submit', async (event) => { const form = event.target.closest('[data-market-edit-form][data-market-edit-draft= \" true \" ]'); if (!form) { return; } event.preventDefault(); event.stopImmediatePropagation(); const status = form.querySelector('[data-market-edit-status]'); const fields = Object.fromEntries(new FormData(form).entries()); if (status) { status.textContent = 'Speichern läuft …'; } try { const response = await fetch('/api/market-occurrence.php', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': contentRoot.querySelector('[data-market-csrf-token]')?.dataset.marketCsrfToken || '' }, body: JSON.stringify( { action: 'create_day', id: form.dataset.marketOccurrenceId, fields }) }); const result = await response.json(); if (!response.ok || !result.ok) { throw new Error(result.error || 'Speichern fehlgeschlagen.'); } const trigger = contentRoot.querySelector('[data-market-drawer-open][aria-expanded= \" true \" ]'); if (trigger) { await openMarketDrawer(trigger); } } catch (error) { if (status) { status.textContent = marketEscape(error.message); } } }); " ;
echo " document.addEventListener('click', (event) => { const deleteSubmit = event.target.closest('[data-market-delete-submit]'); if (deleteSubmit) { void deleteMarketOccurrence(deleteSubmit); return; } const sortButton = event.target.closest('[data-markets-sort-button]'); if (sortButton) { event.preventDefault(); void loadFragment( { market_search: getSearchValue(), market_sort: sortButton.dataset.marketsSortColumn || getSortColumn(), market_dir: sortButton.dataset.marketsSortDirection || getSortDirection(), market_limit: getPageSize() }); return; } const loadMore = event.target.closest('[data-markets-load-more-trigger]'); if (loadMore) { event.preventDefault(); void loadFragment( { market_search: getSearchValue(), market_sort: getSortColumn(), market_dir: getSortDirection(), market_limit: getCurrentLimit() + getPageSize() }); return; } const trigger = event.target.closest('[data-market-drawer-open]'); if (trigger) { event.preventDefault(); if (window.matchMedia('(max-width: 767px)').matches && !marketOpeningMobileDetail) { openMarketMobileDetail(trigger); } else { void openMarketDrawer(trigger); } return; } if (event.target.closest('[data-market-drawer-close]')) { closeMarketDrawer(); return; } if (event.target.closest('[data-market-detail-back]')) { if (window.history.state?.marketView === 'detail') { window.history.back(); } else { restoreMarketList(); } return; } }); " ;
echo " document.addEventListener('submit', async (event) => { const form = event.target.closest('[data-market-edit-form]'); if (!form) { return; } event.preventDefault(); const status = form.querySelector('[data-market-edit-status]'); const fields = Object.fromEntries(new FormData(form).entries()); const occurrenceId = form.dataset.marketEditId || ''; if (status) { status.textContent = 'Speichern läuft …'; } try { const response = await fetch('/api/market-occurrence.php', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': contentRoot.querySelector('[data-market-csrf-token]')?.dataset.marketCsrfToken || '' }, body: JSON.stringify( { scope: form.dataset.marketEditScope, id: occurrenceId, fields }) }); const result = await response.json(); if (!response.ok || !result.ok) { throw new Error(result.error || 'Speichern fehlgeschlagen.'); } await loadFragment( { market_search: getSearchValue(), market_sort: getSortColumn(), market_dir: getSortDirection(), market_limit: getCurrentLimit() }); const refreshedTrigger = contentRoot.querySelector('[data-market-drawer-open][data-market-occurrence-id= \" ' + occurrenceId + ' \" ]'); if (refreshedTrigger) { await openMarketDrawer(refreshedTrigger); } } catch (error) { if (status) { status.textContent = marketEscape(error.message); } } }); " ;
echo " document.addEventListener('click', (event) => { if (event.target.closest('[data-market-drawer-backdrop]')) { closeMarketDrawer(); } }); " ;
echo " document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { closeMarketDrawer(); restoreMarketList(); } }); " ;
echo " window.addEventListener('popstate', () => { if (marketDetailOpen) { restoreMarketList(); } }); " ;
echo " } " ;
echo " const normalizeOccurrenceOverviewForm = (root) => { root.querySelectorAll('[data-market-edit-form][data-market-edit-scope=overview]').forEach((form) => { if (form.dataset.marketOccurrenceFieldsNormalized === 'true') { return; } const organizerSelect = form.querySelector('[name=organization_contact_id]'); const contactSelect = form.querySelector('[name=contact_person_id]'); const organizerField = organizerSelect?.closest('.sg-form-sections-card__field-group'); const contactField = contactSelect?.closest('.sg-form-sections-card__field-group'); if (organizerField && organizerSelect) { const value = organizerSelect.selectedOptions[0]?.textContent || 'Nicht hinterlegt'; organizerField.innerHTML = '<label class= \" sg-labeled-input-row \" ><span class= \" sg-label \" >Veranstalter</span><input class= \" sg-interaction-element sg-input-single-line sg-input-single-line--disabled sg-form-disabled \" type= \" text \" name= \" organizer_name \" value= \" ' + marketEscape(value) + ' \" disabled aria-disabled= \" true \" ></label>'; } if (contactField && contactSelect) { const value = contactSelect.selectedOptions[0]?.textContent || ''; contactField.innerHTML = '<label class= \" sg-labeled-input-row \" ><span class= \" sg-label \" >Kontaktperson</span><input class= \" sg-interaction-element sg-input-single-line \" type= \" text \" name= \" contact_person_name \" value= \" ' + marketEscape(value) + ' \" ></label>'; } form.dataset.marketOccurrenceFieldsNormalized = 'true'; }); }; " ;
echo " const marketOccurrenceFieldObserver = new MutationObserver(() => normalizeOccurrenceOverviewForm(contentRoot)); marketOccurrenceFieldObserver.observe(contentRoot, { childList: true, subtree: true }); " ;
echo " const normalizeMarketSimpleCostFields = (root) => { root.querySelectorAll('[data-market-edit-form][data-market-edit-scope=participation]').forEach((form) => { if (form.dataset.marketSimpleCostsNormalized === 'true') { return; } const drawer = getMarketDrawer(); let payload = { }; try { payload = drawer?.dataset.marketPayload ? JSON.parse(drawer.dataset.marketPayload) : { }; } catch (error) { } const item = Array.isArray(payload.occurrence?.cost_items) ? payload.occurrence.cost_items[0] : null; const total = form.querySelector('[name=gross_cost]'); const text = form.querySelector('[name=cost_items_text]'); const totalLabel = total?.closest('.sg-labeled-input-row')?.querySelector('.sg-label'); const textLabel = text?.closest('.sg-labeled-input-row')?.querySelector('.sg-label'); if (total) { total.disabled = false; total.removeAttribute('disabled'); total.type = 'number'; total.step = '0.01'; total.min = '0'; total.required = true; total.classList.remove('sg-input-single-line--disabled', 'sg-form-disabled'); total.value = payload.occurrence?.cost_summary?.gross_amount ?? item?.gross_amount ?? ''; } if (text) { text.disabled = false; text.removeAttribute('disabled'); text.classList.remove('sg-form-disabled'); text.value = payload.occurrence?.cost_items_text ?? item?.description ?? ''; } if (totalLabel) { totalLabel.textContent = 'Gesamtkosten'; } if (textLabel) { textLabel.textContent = 'Kostenpositionen'; } form.dataset.marketSimpleCostsNormalized = 'true'; }); }; " ;
echo " const marketSimpleCostFieldsObserver = new MutationObserver(() => normalizeMarketSimpleCostFields(contentRoot)); marketSimpleCostFieldsObserver.observe(contentRoot, { childList: true, subtree: true }); normalizeMarketSimpleCostFields(contentRoot); " ;
echo " const normalizeMarketTaxFields = (root) => { root.querySelectorAll('[data-market-edit-form][data-market-edit-scope=participation]').forEach((form) => { if (form.dataset.marketTaxFieldsNormalized === 'true') { return; } const total = form.querySelector('[name=gross_cost]'); const totalGroup = total?.closest('.sg-form-sections-card__field-group'); if (!totalGroup) { return; } const drawer = getMarketDrawer(); let payload = { }; try { payload = drawer?.dataset.marketPayload ? JSON.parse(drawer.dataset.marketPayload) : { }; } catch (error) { } const item = Array.isArray(payload.occurrence?.cost_items) ? payload.occurrence.cost_items[0] : null; const taxStatusGroup = document.createElement('div'); taxStatusGroup.className = 'sg-form-sections-card__field-group'; taxStatusGroup.innerHTML = marketEditField('tax_status', 'Steuerstatus', 'select', item?.tax_status || 'taxable', [['taxable', 'Steuerpflichtig'], ['tax_free', 'Steuerfrei']], true); const taxRateGroup = document.createElement('div'); taxRateGroup.className = 'sg-form-sections-card__field-group'; taxRateGroup.innerHTML = marketEditField('tax_rate', 'Steuersatz', 'number', item?.tax_rate ?? '8.10', [], true); totalGroup.after(taxStatusGroup, taxRateGroup); const taxStatus = taxStatusGroup.querySelector('[name=tax_status]'); const taxRate = taxRateGroup.querySelector('[name=tax_rate]'); const sync = () => { const taxFree = taxStatus?.value === 'tax_free'; if (taxFree && taxRate) { taxRate.value = '0.00'; } if (taxRate) { taxRate.disabled = taxFree; taxRate.setAttribute('aria-disabled', String(taxFree)); } }; taxStatus?.addEventListener('change', sync); sync(); form.dataset.marketTaxFieldsNormalized = 'true'; }); }; " ;
echo " const marketTaxFieldsObserver = new MutationObserver(() => normalizeMarketTaxFields(contentRoot)); marketTaxFieldsObserver.observe(contentRoot, { childList: true, subtree: true }); normalizeMarketTaxFields(contentRoot); " ;
echo " const marketTaxRateAttributesObserver = new MutationObserver(() => { contentRoot.querySelectorAll('[data-market-edit-form][data-market-edit-scope=participation] [name=tax_rate]').forEach((input) => { input.step = '0.01'; input.min = '0'; }); }); marketTaxRateAttributesObserver.observe(contentRoot, { childList: true, subtree: true }); " ;
echo " const marketPaidAmountObserver = new MutationObserver(() => { contentRoot.querySelectorAll('[data-market-edit-form][data-market-edit-scope=participation] [name=paid_amount]').forEach((input) => { input.disabled = false; input.removeAttribute('disabled'); input.type = 'number'; input.step = '0.01'; input.min = '0'; input.classList.remove('sg-input-single-line--disabled', 'sg-form-disabled'); }); }); marketPaidAmountObserver.observe(contentRoot, { childList: true, subtree: true }); " ;
echo " const marketPaidAmountPositionObserver = new MutationObserver(() => { contentRoot.querySelectorAll('[data-market-edit-form][data-market-edit-scope=participation]').forEach((form) => { const paidGroup = form.querySelector('[name=paid_amount]')?.closest('.sg-form-sections-card__field-group'); const taxGroup = form.querySelector('[name=tax_cost]')?.closest('.sg-form-sections-card__field-group'); if (paidGroup && taxGroup && taxGroup.nextElementSibling !== paidGroup) { taxGroup.after(paidGroup); } }); }); marketPaidAmountPositionObserver.observe(contentRoot, { childList: true, subtree: true }); " ;
echo " const syncMarketSimpleCostTotals = (form) => { const gross = Number.parseFloat(form.querySelector('[name=gross_cost]')?.value || '0'); const rate = form.querySelector('[name=tax_status]')?.value === 'tax_free' ? 0 : Number.parseFloat(form.querySelector('[name=tax_rate]')?.value || '0'); const tax = Number.isFinite(gross) && Number.isFinite(rate) ? Math.round((gross - gross / (1 + rate / 100)) * 100) / 100 : 0; const net = Number.isFinite(gross) ? Math.round((gross - tax) * 100) / 100 : 0; const format = (value) => 'CHF ' + value.toFixed(2); const netField = form.querySelector('[name=net_cost]'); const taxField = form.querySelector('[name=tax_cost]'); if (netField) { netField.value = format(net); } if (taxField) { taxField.value = format(tax); } }; " ;
echo " document.addEventListener('input', (event) => { const field = event.target.closest('[data-market-edit-form][data-market-edit-scope=participation] [name=gross_cost], [data-market-edit-form][data-market-edit-scope=participation] [name=tax_rate]'); if (field) { syncMarketSimpleCostTotals(field.closest('[data-market-edit-form]')); } }); " ;
echo " document.addEventListener('change', (event) => { const field = event.target.closest('[data-market-edit-form][data-market-edit-scope=participation] [name=tax_status]'); if (field) { syncMarketSimpleCostTotals(field.closest('[data-market-edit-form]')); } }); " ;
echo " bindTable(); " ;
echo " } " ;
echo " function initMarketDefinitionsBindings() { " ;
echo " const contentRoot = document.querySelector('[data-left-navigation-content-body]'); " ;
echo " const definitionContent = contentRoot?.querySelector('[data-market-definitions-content]'); " ;
echo " const drawer = definitionContent?.querySelector('[data-market-definition-drawer]'); " ;
echo " const readCard = drawer?.querySelector('[data-market-definition-read-card]'); " ;
echo " const card = drawer?.querySelector('[data-market-definition-create-card]'); " ;
echo " const form = drawer?.querySelector('[data-market-definition-form]'); " ;
echo " const editButton = drawer?.querySelector('[data-market-definition-edit]'); " ;
echo " if (!contentRoot || !definitionContent || !drawer || !readCard || !card || !form || !editButton || definitionContent.dataset.marketDefinitionsBound === 'true') { return; } " ;
echo " definitionContent.dataset.marketDefinitionsBound = 'true'; " ;
echo " let activeMarket = null; " ;
echo <<< JS
const prepareMarketDefinitionDeleteCard = () => {
const existingCard = drawer . querySelector ( '[data-market-definition-card]' );
if ( ! existingCard || drawer . querySelector ( '[data-market-definition-delete-id]' )) {
return ;
}
const stage = document . createElement ( 'div' );
stage . className = 'sg-delete-confirmation-pattern__stage sg-delete-confirmation-pattern__host' ;
stage . dataset . pattern = 'overlay-card' ;
stage . dataset . dialogOpen = 'false' ;
const target = document . createElement ( 'div' );
target . className = 'sg-delete-confirmation-pattern__target' ;
target . dataset . sharedEditableActionCard = '' ;
target . dataset . sharedEditableDeleteToken = 'DELETE' ;
target . dataset . marketDefinitionDeleteId = '' ;
target . dataset . marketDefinitionCard = '' ;
const titleCard = document . createElement ( 'article' );
titleCard . className = 'sg-card sg-card--content-card market-delete-confirmation-card' ;
const header = document . createElement ( 'header' );
header . className = 'sg-card-segment sg-card-segment--header sg-card-segment--gray sg-object-card__header' ;
const heading = document . createElement ( 'h2' );
heading . className = 'sg-heading-h2 market-delete-confirmation-card__title' ;
heading . dataset . marketDefinitionReadTitle = '' ;
heading . dataset . marketDefinitionDrawerTitle = '' ;
heading . textContent = 'Markt' ;
const actions = document . createElement ( 'div' );
actions . className = 'sg-component-row' ;
const deleteButton = document . createElement ( 'button' );
deleteButton . className = 'sg-interaction-element sg-button sg-button--warning' ;
deleteButton . type = 'button' ;
deleteButton . dataset . sharedEditableCardDelete = '' ;
deleteButton . dataset . marketDefinitionDelete = '' ;
deleteButton . textContent = 'Löschen' ;
actions . append ( deleteButton );
header . append ( heading , actions );
titleCard . append ( header );
existingCard . querySelector ( '[data-market-definition-read-title]' ) ? . remove ();
existingCard . removeAttribute ( 'data-shared-editable-action-card' );
const confirmation = document . createElement ( 'article' );
confirmation . className = 'sg-card sg-card--overlay-host sg-delete-confirmation-pattern__floating-card' ;
confirmation . setAttribute ( 'aria-label' , 'Löschbestätigung' );
confirmation . setAttribute ( 'role' , 'dialog' );
confirmation . setAttribute ( 'aria-modal' , 'true' );
confirmation . dataset . overlayDialog = 'delete' ;
confirmation . dataset . sharedEditableCardDeleteConfirmation = '' ;
confirmation . hidden = true ;
confirmation . innerHTML = '<div class="sg-card-segment sg-card-segment--body sg-delete-confirmation-pattern__body"><p class="sg-body sg-delete-confirmation-pattern__text"><strong>Möchtest du diesen Markt wirklich löschen?</strong></p><p class="sg-body sg-delete-confirmation-pattern__text">Du kannst das nicht rückgängig machen. Bestätige durch Eingabe von <span class="sg-delete-confirmation-pattern__code">DELETE</span>.</p><label class="sg-labeled-input-row sg-delete-confirmation-pattern__input-row"><span class="sg-label">Bestätigung</span><input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" placeholder="DELETE" aria-label="Löschbestätigung durch DELETE" data-shared-editable-card-delete-input></label><p class="sg-body" data-market-definition-delete-status aria-live="polite"></p><div class="sg-delete-confirmation-pattern__actions"><button class="sg-interaction-element sg-button sg-button--active" type="button" data-shared-editable-card-delete-cancel>Abbrechen</button><button class="sg-interaction-element sg-button sg-button--process sg-button--process-inactive" type="button" disabled aria-disabled="true" data-shared-editable-card-delete-submit data-market-definition-delete-submit>Löschen</button></div></div>' ;
target . append ( titleCard , existingCard , confirmation );
stage . append ( target );
drawer . querySelector ( '[data-market-definition-drawer-content]' ) ? . append ( stage );
};
JS ;
echo " prepareMarketDefinitionDeleteCard(); " ;
echo " const sharedCard = drawer.querySelector('[data-market-definition-card]'); " ;
echo " if (window.SharedEditableActionCard) { window.SharedEditableActionCard.init(definitionContent); } " ;
echo " const setDrawerState = (open) => { drawer.dataset.open = String(open); drawer.setAttribute('aria-hidden', String(!open)); if (activeMarket?.trigger) { activeMarket.trigger.setAttribute('aria-expanded', String(open)); } const backdrop = definitionContent.querySelector('[data-market-definition-drawer-backdrop]'); if (backdrop) { backdrop.dataset.open = String(open); backdrop.setAttribute('aria-hidden', String(!open)); } document.body.classList.toggle('sg-card-list-page-drawer-open', open); }; " ;
echo " const resetForm = () => { form.reset(); const status = form.querySelector('[data-market-definition-status]'); if (status) { status.textContent = ''; } syncNewOrganizerFields(); }; " ;
echo " const syncNewOrganizerFields = () => { const select = form.querySelector('[data-market-definition-organizer]'); const fields = form.querySelector('[data-market-definition-new-organizer]'); if (!select || !fields) { return; } fields.hidden = select.value !== '__new__'; }; " ;
echo " const closeDrawer = () => { resetForm(); if (sharedCard && window.SharedEditableActionCard) { window.SharedEditableActionCard.resetDeleteConfirmation(sharedCard); window.SharedEditableActionCard.setEditMode(sharedCard, false); } activeMarket = null; setDrawerState(false); }; " ;
echo " const deleteMarketDefinition = async (submit) => { if (submit.disabled || !activeMarket?.id) { return; } const card = submit.closest('[data-shared-editable-action-card]'); const input = card?.querySelector('[data-shared-editable-card-delete-input]'); const status = card?.querySelector('[data-market-definition-delete-status]'); if (!card || !input || input.value !== 'DELETE') { return; } if (status) { status.textContent = 'Löschen läuft …'; } submit.disabled = true; submit.setAttribute('aria-disabled', 'true'); try { const response = await fetch('/api/market-definition.php', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': definitionContent.dataset.marketDefinitionsCsrfToken || '' }, body: JSON.stringify( { action: 'delete', id: activeMarket.id, confirmation: input.value }) }); const result = await response.json(); if (!response.ok || !result.ok) { throw new Error(result.error || 'Löschen fehlgeschlagen.'); } const row = activeMarket.trigger.closest('[data-market-definition-row]'); row?.remove(); closeDrawer(); } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Löschen fehlgeschlagen.'; } submit.disabled = false; submit.setAttribute('aria-disabled', 'false'); } }; " ;
echo " const showReadCard = (trigger) => { activeMarket = { id: trigger.dataset.marketDefinitionId || '', trigger }; drawer.querySelector('[data-market-definition-read-title]').textContent = trigger.dataset.marketDefinitionName || 'Markt'; readCard.querySelector('[data-market-definition-read-name]').textContent = trigger.dataset.marketDefinitionName || 'Nicht hinterlegt'; readCard.querySelector('[data-market-definition-read-category]').textContent = trigger.dataset.marketDefinitionCategoryName || 'Nicht hinterlegt'; readCard.querySelector('[data-market-definition-read-organizer]').textContent = trigger.dataset.marketDefinitionOrganizerName || 'Nicht hinterlegt'; readCard.querySelector('[data-market-definition-read-website]').textContent = trigger.dataset.marketDefinitionWebsite || 'Nicht hinterlegt'; const title = form.querySelector('[data-market-definition-form-title]'); if (title) { title.textContent = 'Markt bearbeiten'; } const name = form.querySelector('[data-market-definition-name]'); const category = form.querySelector('[data-market-definition-category]'); const organizer = form.querySelector('[data-market-definition-organizer]'); const website = form.querySelector('[data-market-definition-website]'); if (name) { name.value = trigger.dataset.marketDefinitionName || ''; } if (category) { category.value = trigger.dataset.marketDefinitionCategoryId || ''; } if (organizer) { organizer.value = trigger.dataset.marketDefinitionOrganizerId || ''; } if (website) { website.value = trigger.dataset.marketDefinitionWebsite === 'Nicht hinterlegt' ? '' : (trigger.dataset.marketDefinitionWebsite || ''); } syncNewOrganizerFields(); if (sharedCard && window.SharedEditableActionCard) { window.SharedEditableActionCard.setEditMode(sharedCard, false); } setDrawerState(true); }; " ;
echo " definitionContent.addEventListener('click', (event) => { const trigger = event.target.closest('[data-market-definition-open]'); if (!trigger || !definitionContent.contains(trigger)) { return; } event.preventDefault(); event.stopPropagation(); showReadCard(trigger); }, true); " ;
echo " definitionContent.addEventListener('click', (event) => { const deleteSubmit = event.target.closest('[data-market-definition-delete-submit]'); if (deleteSubmit) { void deleteMarketDefinition(deleteSubmit); return; } const trigger = event.target.closest('[data-market-definition-open]'); if (trigger) { const deleteButton = drawer.querySelector('[data-market-definition-delete]'); if (deleteButton) { deleteButton.hidden = false; } } if (event.target.closest('[data-market-definition-create]')) { const deleteButton = drawer.querySelector('[data-market-definition-delete]'); if (deleteButton) { deleteButton.hidden = true; } } }); " ;
echo " const openEditCard = () => { if (!activeMarket) { return; } const title = form.querySelector('[data-market-definition-form-title]'); if (title) { title.textContent = 'Markt bearbeiten'; } const name = form.querySelector('[data-market-definition-name]'); const category = form.querySelector('[data-market-definition-category]'); const organizer = form.querySelector('[data-market-definition-organizer]'); const website = form.querySelector('[data-market-definition-website]'); if (name) { name.value = activeMarket.trigger.dataset.marketDefinitionName || ''; } if (category) { category.value = activeMarket.trigger.dataset.marketDefinitionCategoryId || ''; } if (organizer) { organizer.value = activeMarket.trigger.dataset.marketDefinitionOrganizerId || ''; } if (website) { website.value = activeMarket.trigger.dataset.marketDefinitionWebsite === 'Nicht hinterlegt' ? '' : (activeMarket.trigger.dataset.marketDefinitionWebsite || ''); } syncNewOrganizerFields(); }; " ;
echo " editButton.addEventListener('click', (event) => { event.preventDefault(); openEditCard(); }); " ;
echo " form.querySelector('[data-market-definition-organizer]')?.addEventListener('change', syncNewOrganizerFields); " ;
echo " const appendMarketRow = (market) => { const table = contentRoot.querySelector('[data-market-definitions-table]'); if (!table) { return; } const row = document.createElement('div'); row.className = 'sg-card-segment sg-card-segment--body sg-large-table__row'; row.setAttribute('role', 'row'); row.dataset.componentPart = 'large-table-row'; row.dataset.marketDefinitionRow = ''; row.dataset.marketDefinitionId = String(market.id || ''); const category = form.querySelector('[data-market-definition-category]')?.selectedOptions[0]?.textContent || ''; const organizer = form.querySelector('[data-market-definition-organizer]')?.selectedOptions[0]?.textContent || ''; const website = market.website_url || 'Nicht hinterlegt'; const cells = document.createElement('div'); cells.className = 'sg-large-table__cell'; const trigger = document.createElement('a'); trigger.className = 'sg-hyperlink'; trigger.href = '#'; trigger.setAttribute('aria-haspopup', 'dialog'); trigger.setAttribute('aria-expanded', 'false'); trigger.dataset.marketDefinitionOpen = ''; trigger.dataset.marketDefinitionId = String(market.id || ''); trigger.dataset.marketDefinitionName = market.name || ''; trigger.dataset.marketDefinitionCategoryId = String(market.category_id || ''); trigger.dataset.marketDefinitionCategoryName = category; trigger.dataset.marketDefinitionOrganizerId = String(market.organizer_id || ''); trigger.dataset.marketDefinitionOrganizerName = organizer; trigger.dataset.marketDefinitionWebsite = website; trigger.textContent = market.name || ''; cells.appendChild(trigger); row.appendChild(cells); [category, organizer, website, '0', 'Aktiv'].forEach((value) => { const cell = document.createElement('div'); cell.className = 'sg-large-table__cell'; cell.setAttribute('role', 'cell'); cell.textContent = value; row.appendChild(cell); }); table.appendChild(row); }; " ;
echo " document.addEventListener('click', (event) => { if (event.target.closest('[data-market-definition-create]')) { event.preventDefault(); activeMarket = null; resetForm(); const title = form.querySelector('[data-market-definition-form-title]'); if (title) { title.textContent = 'Neuen Markt anlegen'; } if (sharedCard && window.SharedEditableActionCard) { window.SharedEditableActionCard.setEditMode(sharedCard, true); } setDrawerState(true); form.querySelector('[data-market-definition-name]')?.focus(); return; } if (event.target.closest('[data-market-definition-edit]')) { event.preventDefault(); openEditCard(); return; } if (event.target.closest('[data-market-definition-cancel]')) { event.preventDefault(); if (!activeMarket) { closeDrawer(); } return; } if (event.target.closest('[data-market-definition-drawer-backdrop]')) { event.preventDefault(); closeDrawer(); } }); " ;
echo " document.addEventListener('keydown', (event) => { if (event.key === 'Escape' && drawer.dataset.open === 'true') { closeDrawer(); } }); " ;
echo " form.addEventListener('submit', async (event) => { event.preventDefault(); const status = form.querySelector('[data-market-definition-status]'); const submit = form.querySelector('[type=submit]'); const fields = Object.fromEntries(new FormData(form).entries()); const isUpdate = Boolean(activeMarket?.id); if (fields.organizer_id === '__new__' && !String(fields.organizer_name || '').trim()) { if (status) { status.textContent = 'Name des neuen Veranstalters ist erforderlich.'; } return; } if (status) { status.textContent = 'Speichern läuft …'; } if (submit) { submit.disabled = true; } try { const response = await fetch('/api/market-definition.php', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': definitionContent.dataset.marketDefinitionsCsrfToken || '' }, body: JSON.stringify( { action: isUpdate ? 'update' : 'create', id: isUpdate ? activeMarket.id : null, fields }) }); const result = await response.json(); if (!response.ok || !result.ok) { throw new Error(result.error || 'Speichern fehlgeschlagen.'); } if (isUpdate) { const trigger = activeMarket.trigger; const category = form.querySelector('[data-market-definition-category]')?.selectedOptions[0]; const organizer = form.querySelector('[data-market-definition-organizer]')?.selectedOptions[0]; const website = result.market.website_url || 'Nicht hinterlegt'; trigger.textContent = result.market.name; trigger.dataset.marketDefinitionName = result.market.name; trigger.dataset.marketDefinitionCategoryId = String(result.market.category_id); trigger.dataset.marketDefinitionCategoryName = category?.textContent || ''; trigger.dataset.marketDefinitionOrganizerId = String(result.market.organizer_id); trigger.dataset.marketDefinitionOrganizerName = organizer?.textContent || ''; trigger.dataset.marketDefinitionWebsite = website; showReadCard(trigger); } else { appendMarketRow(result.market); closeDrawer(); } } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Speichern fehlgeschlagen.'; } } finally { if (submit) { submit.disabled = false; } } }); " ;
echo " } " ;
echo " function initBestellungenBindings() { " ;
echo " const contentRoot = document.querySelector('[data-left-navigation-content-body]'); " ;
echo " if (!contentRoot) { return; } " ;
@@ -1149,12 +1395,12 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " const orderId = trigger.dataset.orderId || ''; " ;
echo " const formatAmount = (value, currency) => value === null || value === undefined ? 'Nicht vorhanden' : currency + ' ' + Number(value).toFixed(2); " ;
echo " const formatDate = (value) => value ? new Date(value).toLocaleString('de-CH', { dateStyle: 'short', timeStyle: 'short' }) : 'Nicht vorhanden'; " ;
echo " const value = (label, content) => '<p class= \" sg-body \" ><strong >' + escapeHtml(label) + ': </strong> ' + escapeHtml(content || 'Nicht vorhanden') + '</p >'; " ;
echo " const value = (label, content) => '<div class= \" market-detail-row \" ><dt >' + escapeHtml(label) + '</dt><dd> ' + escapeHtml(content || 'Nicht vorhanden') + '</dd></div >'; " ;
echo " const renderCards = (order) => { " ;
echo " const c = order.customer || { }; const a = order.amounts || { }; const address = [[c.street, c.house_number].filter(Boolean).join(' '), [c.zip, c.city].filter(Boolean).join(' '), c.country_name].filter(Boolean).join('<br>'); " ;
echo " const lines = (order.lines || []).map((line) => '<div class= \" sg-card-segment sg-card-segment--body \" ><p class= \" sg-body \" ><strong>' + escapeHtml(line.item_name) + '</strong> · ' + escapeHtml( line.item_code) + ' · Menge: ' + escapeHtml( String(line.qty)) + '</p>' + value('Positionsstatus', line.line_status) + value('Einzelpreis', formatAmount(line.unit_price, a.currency || 'CHF')) + value('Positionssumme', formatAmount(line.line_total, a.currency || 'CHF')) + '</div>').join(''); " ;
echo " const allocations = (order.lines || []).flatMap((line) => (line.product_allocations || []).map((item) => '<div class= \" sg-body \" ><strong>' + escapeHtml(item.product_name) + ':</strong> ' + escapeHtml( item.lot_number || 'Nicht zugeordnet') + ' · ' + escapeHtml( item.allocated_qty === null ? 'Nicht zugeordnet' : String(item.allocated_qty)) + ' · ' + escapeHtml( item.allocation_status || 'Nicht vorhanden') + ( item.lot_status === 'virtual_shortage' ? ' · Virtuelle Fehlbestandscharge' : '') + '</div >')).join(''); " ;
echo " return '<article class= \" sg-card sg-card--content-card \" data-component= \" content-card \" aria-label= \" Bestellübersicht \" ><header class= \" sg-card-segment sg-card-segment--header sg-card-segment--darkgreen \" data-component-part= \" card-header \" ><div class= \" sg-strong sg-text-on-dark \" >Bestellübersicht</div></header><div class= \" sg-card-segment sg-card-segment--body \" data-component-part= \" card-body \" >' + value('Bestellnummer', order.external_ref) + value('Bestelldatum', formatDate(order.order_date)) + value('Verkaufskanal', order.order_source) + value('Bestellstatus', order.order_status) + value('Zahlungsstatus', order.payment_status) + value('Zahlungsart', order.payment_method) + value('Versandart', order.shipping_method) + '<p class= \" sg-body \" ><strong>Kunde:</strong> ' + escapeHtml( [c.first_name, c.last_name].filter(Boolean).join(' ') || 'Nicht vorhanden' ) + '</p><p class=\" sg-body \" ><strong >Lieferadresse: </strong><br >' + address + '</p >' + value('Netto', formatAmount(a.net, a.currency || 'CHF')) + value('Versand', formatAmount(a.shipping, a.currency || 'CHF')) + value('Rabatt', formatAmount(a.discount, a.currency || 'CHF')) + value('Steuer', formatAmount(a.tax, a.currency || 'CHF')) + value('Gesamtsumme', formatAmount(a.total, a.currency || 'CHF')) + '</div></article><article class= \" sg-card sg-card--content-card \" data-component= \" content-card \" aria-label= \" Bestellte Artikel \" ><header class= \" sg-card-segment sg-card-segment--header sg-card-segment--darkgreen \" data-component-part= \" card-header \" ><div class= \" sg-strong sg-text-on-dark \" >Bestellte Artikel</div></header>' + (lines || '<div class= \" sg-card-segment sg-card-segment--body \" data-component-part= \" card-body \" ><p class= \" sg-body \" >Keine Artikel vorhanden.</p></div>') + '</article><article class= \" sg-card sg-card--content-card \" data-component= \" content-card \" aria-label= \" Chargenzuordnung \" ><header class= \" sg-card-segment sg-card-segment--header sg-card-segment--darkgreen \" data-component-part= \" card-header \" ><div class= \" sg-strong sg-text-on-dark \" >Chargenzuordnung</div></header><div class= \" sg-card-segment sg-card-segment--body \" data-component-part= \" card-body \" >' + (allocations || '<p class= \" sg-body \" >Keine Chargenzuordnung vorhanden.</p>') + '</div></article>'; " ;
echo " const c = order.customer || { }; const a = order.amounts || { }; const address = [[c.street, c.house_number].filter(Boolean).join(' '), [c.zip, c.city].filter(Boolean).join(' '), c.country_name].filter(Boolean).map(escapeHtml). join('<br>'); " ;
echo " const lines = (order.lines || []).map((line) => '<div class= \" sg-card-segment sg-card-segment--body \" ><dl class= \" market-detail-grid \" >' + value(line.item_name, [ line.item_code, ' Menge: ' + String(line.qty)].filter(Boolean).join(' · ')) + value('Positionsstatus', line.line_status) + value('Einzelpreis', formatAmount(line.unit_price, a.currency || 'CHF')) + value('Positionssumme', formatAmount(line.line_total, a.currency || 'CHF')) + '</dl></ div>').join(''); " ;
echo " const allocations = (order.lines || []).flatMap((line) => (line.product_allocations || []).map((item) => '<dl class= \" market-detail-grid \" >' + value(item.product_name, [ item.lot_number || 'Nicht zugeordnet', item.allocated_qty === null ? 'Nicht zugeordnet' : String(item.allocated_qty), item.allocation_status || 'Nicht vorhanden', item.lot_status === 'virtual_shortage' ? 'Virtuelle Fehlbestandscharge' : ''].filter(Boolean).join(' · ') ) + '</dl >')).join(''); " ;
echo " return '<article class= \" sg-card sg-card--content-card \" data-component= \" content-card \" aria-label= \" Bestellübersicht \" ><header class= \" sg-card-segment sg-card-segment--header sg-card-segment--darkgreen \" data-component-part= \" card-header \" ><div class= \" sg-strong sg-text-on-dark \" >Bestellübersicht</div></header><div class= \" sg-card-segment sg-card-segment--body \" data-component-part= \" card-body \" ><dl class= \" market-detail-grid \" > ' + value('Bestellnummer', order.external_ref) + value('Bestelldatum', formatDate(order.order_date)) + value('Verkaufskanal', order.order_source) + value('Bestellstatus', order.order_status) + value('Zahlungsstatus', order.payment_status) + value('Zahlungsart', order.payment_method) + value('Versandart', order.shipping_method) + value('Kunde', [c.first_name, c.last_name].filter(Boolean).join(' ')) + '<div class=\" market-detail-row \" ><dt >Lieferadresse</dt><dd >' + ( address || 'Nicht vorhanden') + '</dd></div >' + value('Netto', formatAmount(a.net, a.currency || 'CHF')) + value('Versand', formatAmount(a.shipping, a.currency || 'CHF')) + value('Rabatt', formatAmount(a.discount, a.currency || 'CHF')) + value('Steuer', formatAmount(a.tax, a.currency || 'CHF')) + value('Gesamtsumme', formatAmount(a.total, a.currency || 'CHF')) + '</dl></ div></article><article class= \" sg-card sg-card--content-card \" data-component= \" content-card \" aria-label= \" Bestellte Artikel \" ><header class= \" sg-card-segment sg-card-segment--header sg-card-segment--darkgreen \" data-component-part= \" card-header \" ><div class= \" sg-strong sg-text-on-dark \" >Bestellte Artikel</div></header>' + (lines || '<div class= \" sg-card-segment sg-card-segment--body \" data-component-part= \" card-body \" ><p class= \" sg-body \" >Keine Artikel vorhanden.</p></div>') + '</article><article class= \" sg-card sg-card--content-card \" data-component= \" content-card \" aria-label= \" Chargenzuordnung \" ><header class= \" sg-card-segment sg-card-segment--header sg-card-segment--darkgreen \" data-component-part= \" card-header \" ><div class= \" sg-strong sg-text-on-dark \" >Chargenzuordnung</div></header><div class= \" sg-card-segment sg-card-segment--body \" data-component-part= \" card-body \" >' + (allocations || '<p class= \" sg-body \" >Keine Chargenzuordnung vorhanden.</p>') + '</div></article>'; " ;
echo " }; " ;
echo " setDrawerState(true); " ;
echo " if (drawerTitle) { drawerTitle.textContent = trigger.dataset.orderNumber ? 'Bestellung ' + trigger.dataset.orderNumber : 'Bestellung'; } " ;
@@ -1409,6 +1655,10 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " const houseNumberInput = form.querySelector('[data-otc-order-house-number]'); " ;
echo " const zipInput = form.querySelector('[data-otc-order-zip]'); " ;
echo " const cityInput = form.querySelector('[data-otc-order-city]'); " ;
echo " const marketLinkToggle = form.querySelector('[data-otc-market-link-toggle]'); " ;
echo " const marketLinkEnabledInput = form.querySelector('[data-otc-market-link-enabled]'); " ;
echo " const marketOccurrenceInput = form.querySelector('[data-otc-market-occurrence]'); " ;
echo " const marketDayInput = form.querySelector('[data-otc-market-day]'); " ;
echo " const submitBtn = overlay.querySelector('[data-otc-order-submit]'); " ;
echo " const errorEl = form.querySelector('[data-otc-order-error]'); " ;
echo " const successEl = form.querySelector('[data-otc-order-success]'); " ;
@@ -1419,6 +1669,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " const confirmationQuantity = overlay.querySelector('[data-otc-confirmation-quantity]'); " ;
echo " const confirmationTotal = overlay.querySelector('[data-otc-confirmation-total]'); " ;
echo " const confirmationSubmit = overlay.querySelector('[data-otc-confirmation-submit]'); " ;
echo " const confirmationError = overlay.querySelector('[data-otc-confirmation-error]'); " ;
echo " let pendingOrderData = null; " ;
echo " const openTriggers = () => document.querySelectorAll('[data-otc-order-open]'); " ;
echo " const setTriggerState = (expanded) => { " ;
@@ -1426,6 +1677,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " }; " ;
echo " const clearError = () => { " ;
echo " if (errorEl) { errorEl.textContent = ''; errorEl.classList.add('hidden'); } " ;
echo " if (confirmationError) { confirmationError.textContent = ''; confirmationError.classList.add('hidden'); } " ;
echo " }; " ;
echo " const clearSuccess = () => { " ;
echo " if (successEl) { successEl.textContent = successDefaultText; successEl.classList.add('hidden'); } " ;
@@ -1462,6 +1714,10 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " }; " ;
echo " const getInputValue = (input) => parseInt(input.value || '0', 10) || 0; " ;
echo " const getFieldValue = (input) => (input ? input.value.trim() : ''); " ;
echo " const isMarketLinkEnabled = () => marketLinkToggle?.getAttribute('aria-checked') === 'true'; " ;
echo " const setDisabledSelectState = (select, disabled) => { if (!select) { return; } select.disabled = disabled; select.setAttribute('aria-disabled', String(disabled)); select.classList.toggle('sg-form-disabled', disabled); select.classList.toggle('sg-input-single-line--disabled', disabled); select.classList.toggle('sg-pulldown--disabled', disabled); select.classList.toggle('sg-form-inactive-selectable', !disabled); select.classList.toggle('sg-input-single-line--inactive-selectable', !disabled); }; " ;
echo " const syncMarketDayOptions = () => { if (!marketOccurrenceInput || !marketDayInput) { return; } const marketDefinitionId = marketOccurrenceInput.value; Array.from(marketDayInput.options).forEach((option, index) => { if (index === 0) { option.hidden = false; option.disabled = false; return; } const matches = marketDefinitionId !== '' && option.dataset.marketDefinitionId === marketDefinitionId; option.hidden = !matches; option.disabled = !matches; }); if (!marketDefinitionId || !Array.from(marketDayInput.options).some((option) => option.value === marketDayInput.value && !option.disabled)) { marketDayInput.value = ''; } setDisabledSelectState(marketDayInput, !isMarketLinkEnabled() || marketDefinitionId === ''); }; " ;
echo " const syncMarketLinkState = () => { const enabled = isMarketLinkEnabled(); if (marketLinkEnabledInput) { marketLinkEnabledInput.value = enabled ? '1' : '0'; } setDisabledSelectState(marketOccurrenceInput, !enabled); if (!enabled && marketOccurrenceInput) { marketOccurrenceInput.value = ''; } syncMarketDayOptions(); }; " ;
echo " const updatePaymentAddButton = () => { addPaymentButton.disabled = paymentRows.querySelectorAll('[data-otc-payment-row]').length >= paymentOptionValues.length; }; " ;
echo " const updateFormState = () => { " ;
echo " const totalQty = productInputs.reduce((sum, input) => sum + getInputValue(input), 0); " ;
@@ -1473,7 +1729,8 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " && !!getFieldValue(streetInput) " ;
echo " && !!getFieldValue(houseNumberInput) " ;
echo " && !!getFieldValue(zipInput) " ;
echo " && !!getFieldValue(cityInput); " ;
echo " && !!getFieldValue(cityInput) " ;
echo " && (!isMarketLinkEnabled() || (!!getFieldValue(marketOccurrenceInput) && !!getFieldValue(marketDayInput))); " ;
echo " let errorMsg = ''; " ;
echo " if (totalQty === 0) { " ;
echo " errorMsg = 'Mindestens ein Produkt mit Menge > 0 erforderlich.'; " ;
@@ -1481,6 +1738,8 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " errorMsg = 'Bestelldatum auswählen.'; " ;
echo " } else if (!getFieldValue(firstNameInput) || !getFieldValue(lastNameInput) || !getFieldValue(streetInput) || !getFieldValue(houseNumberInput) || !getFieldValue(zipInput) || !getFieldValue(cityInput)) { " ;
echo " errorMsg = 'Alle Rechnungsadress-Felder ausfüllen.'; " ;
echo " } else if (isMarketLinkEnabled() && (!getFieldValue(marketOccurrenceInput) || !getFieldValue(marketDayInput))) { " ;
echo " errorMsg = 'Markt und Markttag auswählen.'; " ;
echo " } " ;
echo " if (errorEl) { " ;
echo " errorEl.textContent = errorMsg; " ;
@@ -1497,6 +1756,8 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " updatePaymentAddButton(); " ;
echo " clearError(); " ;
echo " if (!preserveSuccess) { clearSuccess(); } " ;
echo " if (marketLinkToggle) { marketLinkToggle.setAttribute('aria-checked', 'false'); marketLinkToggle.classList.remove('sg-form-active'); marketLinkToggle.classList.add('sg-checkbox-field--inactive-selectable'); } " ;
echo " syncMarketLinkState(); " ;
echo " if (!preserveSuccess) { updateFormState(); } " ;
echo " }; " ;
echo " const closeConfirmation = () => { " ;
@@ -1563,6 +1824,9 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " addPaymentButton.addEventListener('click', addPaymentRow); " ;
echo " bindPaymentRow(paymentRows.querySelector('[data-otc-payment-row]')); " ;
echo " updatePaymentAddButton(); " ;
echo " marketLinkToggle?.addEventListener('click', (event) => { event.stopImmediatePropagation(); const enabled = marketLinkToggle.getAttribute('aria-checked') !== 'true'; marketLinkToggle.setAttribute('aria-checked', String(enabled)); marketLinkToggle.classList.toggle('sg-form-active', enabled); marketLinkToggle.classList.toggle('sg-checkbox-field--inactive-selectable', !enabled); syncMarketLinkState(); updateFormState(); }); " ;
echo " marketOccurrenceInput?.addEventListener('change', () => { syncMarketDayOptions(); updateFormState(); }); " ;
echo " marketDayInput?.addEventListener('change', updateFormState); " ;
echo " const submitOrder = async (orderData) => { " ;
echo " let submissionSucceeded = false; " ;
echo " const originalBtnText = confirmationSubmit.innerHTML; " ;
@@ -1585,17 +1849,15 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " closeConfirmation(); " ;
echo " resetForm(); " ;
echo " toggleOverlay(false); " ;
echo " } else if (errorEl) { " ;
echo " errorEl.textContent = result.error || 'Unbekannter Fehler'; " ;
echo " errorEl.classList.remove('hidden'); " ;
echo " closeConfirmation(); " ;
echo " } else { " ;
echo " const message = result.error || 'Unbekannter Fehler'; " ;
echo " if (errorEl) { errorEl.textContent = message; errorEl.classList.remove('hidden'); } " ;
echo " if (confirmationError) { confirmationError.textContent = message; confirmationError.classList.remove('hidden'); } " ;
echo " } " ;
echo " } catch (error) { " ;
echo " if (errorEl) { " ;
echo " errorEl.textContent = 'Netzwerkfehler: ' + error.message; " ;
echo " errorEl .classList.remove('hidden'); " ;
echo " } " ;
echo " closeConfirmation(); " ;
echo " const message = 'Netzwerkfehler: ' + error.message; " ;
echo " if (errorEl) { errorEl.textContent = message; errorEl.classList.remove('hidden'); } " ;
echo " if (confirmationError) { confirmationError.textContent = message; confirmationError .classList.remove('hidden'); } " ;
echo " } finally { " ;
echo " confirmationSubmit.innerHTML = originalBtnText; " ;
echo " confirmationSubmit.disabled = false; " ;
@@ -1612,14 +1874,16 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " products: products, " ;
echo " orderDate: orderDateInput ? orderDateInput.value : '', " ;
echo " payments: Array.from(paymentRows.querySelectorAll('[data-otc-payment-row]')).map((row) => ( { paymentMethod: row.querySelector('[data-otc-payment-method]').value, amount: parseFloat(row.querySelector('[data-otc-payment-amount]').value || '0') || 0 })), " ;
echo " billing: { " ;
echo " billing: { " ;
echo " firstName: getFieldValue(firstNameInput), " ;
echo " lastName: getFieldValue(lastNameInput), " ;
echo " street: getFieldValue(streetInput), " ;
echo " houseNumber: getFieldValue(houseNumberInput), " ;
echo " zip: getFieldValue(zipInput), " ;
echo " city: getFieldValue(cityInput) " ;
echo " } " ;
echo " city: getFieldValue(cityInput) " ;
echo " }, " ;
echo " marketOccurrenceId: isMarketLinkEnabled() ? (marketDayInput?.selectedOptions[0]?.dataset.marketOccurrenceId || null) : null, " ;
echo " marketOccurrenceDayId: isMarketLinkEnabled() ? getFieldValue(marketDayInput) : null " ;
echo " }; " ;
echo " openConfirmation(orderData); " ;
echo " }); " ;
@@ -1627,6 +1891,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " if (event.target.closest('[data-otc-confirmation-cancel]')) { closeConfirmation(); return; } " ;
echo " if (event.target.closest('[data-otc-confirmation-submit]') && pendingOrderData) { submitOrder(pendingOrderData); } " ;
echo " }); " ;
echo " syncMarketLinkState(); " ;
echo " updateFormState(); " ;
echo " })(); " ;
echo " if (window.sgInitHelpIconOverlays) { " ;
@@ -1635,8 +1900,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " outsideClickIgnoreSelectors: ['.sg-sandwich-menu-wrap'], " ;
echo " }); " ;
echo " } " ;
echo " renderMainHeading('ERP' );" ;
echo " renderLeftNavigation('ERP'); " ;
echo " (() => { const url = new URL(window.location.href); const requestedModule = url.searchParams.get('portal_module') || 'ERP'; const moduleButton = Array.from(document.querySelectorAll('.sg-portal-header__tabs .sg-tab-button')).find((button) => button.textContent.trim() === requestedModule) || document.querySelector('.sg-portal-header__tabs .sg-tab-button'); if (!moduleButton) { return; } const moduleName = moduleButton.textContent.trim(); document.querySelectorAll('.sg-portal-header__tabs .sg-tab-button').forEach((button) => { const active = button === moduleButton; button.setAttribute('aria-selected', String(active)); button.dataset.componentState = active ? 'active' : 'inactive'; }); renderMainHeading(moduleName); renderLeftNavigation(moduleName, url.searchParams.get('portal_section') || ''); })( );" ;
echo '</script>' ;
echo '</body>' ;
echo '</html>' ;