Files
erp_naurua/modules/shared/auth/ui/home.php
T

1871 lines
259 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../service.php';
function auth_current_path(): string
{
$requestUri = (string) ($_SERVER['REQUEST_URI'] ?? '/');
$path = parse_url($requestUri, PHP_URL_PATH);
if (!is_string($path) || $path === '') {
return '/';
}
return $path;
}
function auth_build_path_with_query(array $query = []): string
{
$query = array_filter(
$query,
static fn ($value): bool => $value !== null && $value !== ''
);
$path = auth_current_path();
$queryString = http_build_query($query, '', '&', PHP_QUERY_RFC3986);
return $queryString === '' ? $path : $path . '?' . $queryString;
}
function auth_format_bestellung_date(?string $value): string
{
if ($value === null || $value === '') {
return '';
}
try {
return (new DateTimeImmutable($value))->format('d.m.Y H:i');
} catch (Throwable) {
return $value;
}
}
function auth_format_bestellung_total(?float $value): string
{
if ($value === null) {
return '';
}
return 'CHF ' . number_format($value, 2, '.', '\'');
}
function auth_format_market_date(?string $earliest, ?string $latest): string
{
if ($earliest === null || $earliest === '') {
return '';
}
$start = (new DateTimeImmutable($earliest))->format('d.m.Y');
if ($latest === null || $latest === '' || $latest === $earliest) {
return $start;
}
return $start . '–' . (new DateTimeImmutable($latest))->format('d.m.Y');
}
function auth_market_status_label(string $status): string
{
return [
'open' => 'Offen',
'waitlist' => 'Warteliste',
'registered' => 'Angemeldet',
'confirmation_pending' => 'Bestätigung ausstehend',
'confirmed' => 'Bestätigt',
'cancelled' => 'Abgesagt',
'completed' => 'Abgeschlossen',
][$status] ?? $status;
}
function auth_market_status_signal_class(string $status): string
{
return [
'waitlist' => 'market-status--red',
'registered' => 'market-status--yellow',
'confirmation_pending' => 'market-status--yellow',
'confirmed' => 'market-status--green',
][$status] ?? '';
}
function auth_render_markets_large_table(array $marketTable): string
{
$rows = $marketTable['rows'] ?? $marketTable;
$search = (string) ($marketTable['search'] ?? '');
$sortColumn = (string) ($marketTable['sort_column'] ?? 'earliest_date');
$sortDirection = strtoupper((string) ($marketTable['sort_direction'] ?? 'ASC'));
$limit = (int) ($marketTable['limit'] ?? 20);
$pageSize = (int) ($marketTable['page_size'] ?? 20);
$hasMore = (bool) ($marketTable['has_more'] ?? (count($rows) >= $limit));
$sortColumns = [
'market_name' => 'Markt',
'earliest_date' => 'Markttag(e)',
'place' => 'Ort',
'status' => 'Status',
];
$html = [];
$html[] = '<section class="sg-card-list-page" aria-label="Marktdurchfü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>';
$html[] = '<span class="sg-search-field-row" data-component="search-field">';
$html[] = '<span class="sg-input-single-line-wrap" data-has-value="' . ($search !== '' ? 'true' : 'false') . '" data-component="single-line-input" data-component-state="' . ($search !== '' ? 'active' : 'inactive-selectable') . '">';
$html[] = '<input class="sg-interaction-element sg-input-single-line sg-search-field-input sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" placeholder="Suche" aria-label="Marktdurchführungen durchsuchen" value="' . auth_escape_html($search) . '" data-markets-search-input>';
$html[] = '<button class="sg-input-clear-button" type="button" aria-label="Eingabe löschen" data-markets-search-clear>×</button>';
$html[] = '</span></span></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">';
$headerIndex = 0;
foreach ($sortColumns as $column => $label) {
$isActive = $column === $sortColumn;
$headerDirection = $isActive ? strtolower($sortDirection) : 'none';
$iconDirection = $isActive ? strtolower($sortDirection) : 'ascending';
$targetDirection = $isActive ? ($sortDirection === 'ASC' ? 'DESC' : 'ASC') : ($column === 'earliest_date' ? 'ASC' : 'ASC');
$ariaLabel = $isActive ? $label . ' ' . ($sortDirection === 'ASC' ? 'aufsteigend sortiert' : 'absteigend sortiert') : $label . ' sortieren';
$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>';
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';
}
$html[] = '<div class="' . $rowClass . '" role="row" data-component-part="large-table-row">';
$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>';
$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>';
$html[] = '</div>';
}
if ($hasMore) {
$html[] = '<div class="sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row sg-large-table__row--load-more" role="row" data-component-part="large-table-row"><div class="sg-large-table__cell sg-large-table__cell--load-more" role="cell"><div class="sg-navigation-card-layout sg-large-table__load-more-layout"><div class="sg-navigation-card-block"><article class="sg-card" data-component="card" data-pattern="navigation-card" aria-label="Mehr laden"><div class="sg-card-segment sg-card-segment--body" data-component-part="card-body" data-pattern-part="navigation-card-segment"><div class="sg-navigation-card-center"><a class="sg-hyperlink" href="#" data-component="hyperlink" data-markets-load-more-trigger="true">Mehr laden</a></div></div></article></div></div></div></div>';
}
$html[] = '</article></section>';
return implode('', $html);
}
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)
. '<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__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'] ?? [];
$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', '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'] ?? '');
$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-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($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><div class="sg-form-sections-card__field-group"><div class="sg-labeled-input-row"><span class="sg-label">Marktname</span><span class="sg-body" data-market-definition-read-name></span></div></div><div class="sg-form-sections-card__field-group"><div class="sg-labeled-input-row"><span class="sg-label">Kategorie/Marktart</span><span class="sg-body" data-market-definition-read-category></span></div></div><div class="sg-form-sections-card__field-group"><div class="sg-labeled-input-row"><span class="sg-label">Website</span><span class="sg-body" data-market-definition-read-website></span></div></div></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">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'] ?? [];
$search = (string) ($bestellungenTable['search'] ?? '');
$sortColumn = (string) ($bestellungenTable['sort_column'] ?? 'order_date');
$sortDirection = strtoupper((string) ($bestellungenTable['sort_direction'] ?? 'DESC'));
$limit = (int) ($bestellungenTable['limit'] ?? 20);
$pageSize = (int) ($bestellungenTable['page_size'] ?? 20);
$hasMore = (bool) ($bestellungenTable['has_more'] ?? false);
$nextLimit = (int) ($bestellungenTable['next_limit'] ?? $limit);
$sortColumns = [
'order_date' => 'Bestelldatum',
'external_ref' => 'Bestellnummer',
'name' => 'Name',
'total_amount' => 'Gesamtsumme',
];
$html = [];
$html[] = '<section class="sg-card-list-page" aria-label="Bestellungen">';
$html[] = '<article class="sg-card sg-large-table sg-large-table--bestellungen" style="margin-top: var(--spacing-large);" data-component="large-table" data-bestellungen-large-table="true" data-bestellungen-search="' . auth_escape_html($search) . '" data-bestellungen-sort-column="' . auth_escape_html($sortColumn) . '" data-bestellungen-sort-direction="' . auth_escape_html($sortDirection) . '" data-bestellungen-limit="' . $limit . '" data-bestellungen-page-size="' . $pageSize . '" role="table" aria-label="Bestellungen">';
$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">Bestellungen</div>';
$html[] = '<span class="sg-search-field-row" data-component="search-field">';
$html[] = '<span class="sg-input-single-line-wrap" data-has-value="' . ($search !== '' ? 'true' : 'false') . '" data-component="single-line-input" data-component-state="' . ($search !== '' ? 'active' : 'inactive-selectable') . '">';
$html[] = '<input class="sg-interaction-element sg-input-single-line sg-search-field-input sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" placeholder="Suche" aria-label="Bestellungen durchsuchen" value="' . auth_escape_html($search) . '" data-large-table-search data-bestellungen-search-input>';
$html[] = '<button class="sg-input-clear-button" type="button" aria-label="Eingabe löschen" data-bestellungen-search-clear>×</button>';
$html[] = '</span>';
$html[] = '</span>';
$html[] = '</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">';
$headerIndex = 0;
foreach ($sortColumns as $column => $label) {
$isActive = $column === $sortColumn;
$headerDirection = $isActive ? strtolower($sortDirection) : 'none';
$iconDirection = $isActive ? strtolower($sortDirection) : 'ascending';
$ariaLabel = $isActive
? $label . ' ' . ($sortDirection === 'ASC' ? 'aufsteigend sortiert' : 'absteigend sortiert')
: $label . ' sortieren';
$targetDirection = $isActive
? ($sortDirection === 'ASC' ? 'DESC' : 'ASC')
: ($column === 'order_date' ? 'DESC' : 'ASC');
$amountHeaderClass = $column === 'total_amount' ? ' sg-large-table__cell--amount' : '';
$html[] = '<div class="sg-large-table__cell sg-large-table__cell--header' . $amountHeaderClass . '" role="columnheader" aria-sort="' . $headerDirection . '" data-sort-key="' . $headerIndex . '">';
$html[] = '<button class="sg-large-table__sort-button" type="button" aria-label="' . auth_escape_html($ariaLabel) . '" data-bestellungen-sort-button="true" data-bestellungen-sort-column="' . auth_escape_html($column) . '" data-bestellungen-sort-direction="' . auth_escape_html($targetDirection) . '">';
$html[] = '<span class="sg-large-table__sort-label">' . auth_escape_html($label) . '</span>';
$html[] = '<span class="sg-large-table__sort-icon" aria-hidden="true" data-direction="' . auth_escape_html($iconDirection) . '"></span>';
$html[] = '</button>';
$html[] = '</div>';
$headerIndex++;
}
$html[] = '</div>';
foreach ($rows as $index => $row) {
$isStriped = $index % 2 === 1;
$rowClass = 'sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row';
if ($isStriped) {
$rowClass .= ' sg-large-table__row--striped-light';
}
$orderNumber = (string) ($row['external_ref'] ?? '');
$orderDate = auth_format_bestellung_date((string) ($row['order_date'] ?? ''));
$totalAmount = auth_format_bestellung_total(isset($row['total_amount']) ? (float) $row['total_amount'] : null);
$firstName = (string) ($row['first_name'] ?? '');
$lastName = (string) ($row['last_name'] ?? '');
$name = trim($firstName . ' ' . $lastName);
$street = (string) ($row['street'] ?? '');
$houseNumber = (string) ($row['house_number'] ?? '');
$zip = (string) ($row['zip'] ?? '');
$city = (string) ($row['city'] ?? '');
$country = (string) ($row['country_name'] ?? '');
$html[] = '<div class="' . $rowClass . '" role="row" data-component-part="large-table-row" data-bestellungen-row="true" data-order-id="' . (int) ($row['id'] ?? 0) . '">';
$html[] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html($orderDate) . '</div>';
$html[] = '<div class="sg-large-table__cell" role="cell">';
$html[] = '<a class="sg-hyperlink" href="#" aria-haspopup="dialog" aria-expanded="false" data-order-drawer-open="true" data-order-id="' . (int) ($row['id'] ?? 0) . '" data-order-number="' . auth_escape_html($orderNumber) . '" data-order-date="' . auth_escape_html($orderDate) . '" data-order-total="' . auth_escape_html($totalAmount) . '" data-order-first-name="' . auth_escape_html($firstName) . '" data-order-last-name="' . auth_escape_html($lastName) . '" data-order-street="' . auth_escape_html($street) . '" data-order-house-number="' . auth_escape_html($houseNumber) . '" data-order-zip="' . auth_escape_html($zip) . '" data-order-city="' . auth_escape_html($city) . '" data-order-country="' . auth_escape_html($country) . '">' . auth_escape_html($orderNumber) . '</a>';
$html[] = '</div>';
$html[] = '<div class="sg-large-table__cell" role="cell">';
$html[] = '<a class="sg-hyperlink" href="#" aria-haspopup="dialog" aria-expanded="false" data-order-drawer-open="true" data-order-id="' . (int) ($row['id'] ?? 0) . '" data-order-number="' . auth_escape_html($orderNumber) . '" data-order-date="' . auth_escape_html($orderDate) . '" data-order-total="' . auth_escape_html($totalAmount) . '" data-order-first-name="' . auth_escape_html($firstName) . '" data-order-last-name="' . auth_escape_html($lastName) . '" data-order-street="' . auth_escape_html($street) . '" data-order-house-number="' . auth_escape_html($houseNumber) . '" data-order-zip="' . auth_escape_html($zip) . '" data-order-city="' . auth_escape_html($city) . '" data-order-country="' . auth_escape_html($country) . '">' . auth_escape_html($name) . '</a>';
$html[] = '</div>';
$html[] = '<div class="sg-large-table__cell sg-large-table__cell--amount" role="cell">' . auth_escape_html($totalAmount) . '</div>';
$html[] = '</div>';
}
if ($hasMore) {
$loadMoreUrl = auth_build_path_with_query([
'bestellungen_search' => $search !== '' ? $search : null,
'bestellungen_sort' => $sortColumn,
'bestellungen_dir' => $sortDirection,
'bestellungen_limit' => $nextLimit,
]);
$html[] = '<div class="sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row sg-large-table__row--load-more" role="row" data-component-part="large-table-row" data-large-table-load-more-row="true">';
$html[] = '<div class="sg-large-table__cell sg-large-table__cell--load-more" role="cell">';
$html[] = '<div class="sg-navigation-card-layout sg-large-table__load-more-layout">';
$html[] = '<div class="sg-navigation-card-block">';
$html[] = '<article class="sg-card" data-component="card" data-pattern="navigation-card" aria-label="Mehr laden">';
$html[] = '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body" data-pattern-part="navigation-card-segment">';
$html[] = '<div class="sg-navigation-card-center">';
$html[] = '<a class="sg-hyperlink" href="' . auth_escape_html($loadMoreUrl) . '" data-component="hyperlink" data-large-table-load-more-trigger="true">Mehr laden</a>';
$html[] = '</div>';
$html[] = '</div>';
$html[] = '</article>';
$html[] = '</div>';
$html[] = '</div>';
$html[] = '</div>';
$html[] = '</div>';
}
$html[] = '</article>';
$html[] = '</section>';
$html[] = '<div class="sg-card-list-page-drawer-backdrop" aria-hidden="true" data-bestellungen-drawer-backdrop="true" data-open="false"></div>';
$html[] = '<aside class="sg-card-list-page-drawer" aria-label="Bestell-Details" aria-hidden="true" data-bestellungen-drawer="true" data-open="false">';
$html[] = '<div class="sg-card-list-page-drawer__header">';
$html[] = '<h2 class="sg-heading-h2 sg-card-list-page-drawer__title" data-order-drawer-title>Bestellung</h2>';
$html[] = '<button class="sg-interaction-element sg-hyperlink" type="button" data-order-drawer-close>schliessen</button>';
$html[] = '</div>';
$html[] = '<div class="sg-card-list-page-drawer__content" data-order-drawer-cards>';
$html[] = '<p class="sg-body" data-order-drawer-loading>Bestelldetails werden geladen …</p>';
$html[] = '</div>';
$html[] = '</aside>';
return implode('', $html);
}
function auth_render_bestellungen_content(array $bestellungenTable): string
{
return '<div data-bestellungen-content="true">'
. '<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-otc-order-open>OTC-Bestellung erfassen</button></div></div></article>'
. auth_render_bestellungen_large_table($bestellungenTable)
. '</div>';
}
function auth_render_lager_table(string $title, array $rows, array $columns, string $tableKey): string
{
$renderForecast = static function (?string $date, string $status, ?string $expiryDate, bool $available): array {
if (!$available) {
return ['text' => '-', 'warning' => false, 'mhdWarning' => false];
}
if ($date === null || $date === '') {
return ['text' => 'Keine Prognose', 'warning' => false, 'mhdWarning' => false];
}
$dateObject = new DateTimeImmutable($date);
$text = $dateObject->format('d.m.Y');
if ($status === 'depleted') {
return ['text' => $text . ' (BESTAND ENDE)', 'warning' => true, 'mhdWarning' => false];
}
$warning = false;
$mhdWarning = false;
if ($expiryDate !== null && $expiryDate !== '') {
$expiryObject = new DateTimeImmutable($expiryDate);
$warning = $dateObject >= $expiryObject->modify('-3 months') && $dateObject <= $expiryObject;
$mhdWarning = $dateObject > $expiryObject;
if ($warning || $mhdWarning) {
$text .= ' (MHD: ' . $expiryObject->format('d.m.Y') . ')';
}
}
return ['text' => $text, 'warning' => $warning, 'mhdWarning' => $mhdWarning];
};
$html = [];
$html[] = '<article class="sg-card sg-large-table sg-large-table--lager" data-component="large-table" data-lager-table="true" data-lager-table-key="' . auth_escape_html($tableKey) . '" role="table" aria-label="' . auth_escape_html($title) . '">';
$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">' . auth_escape_html($title) . '</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 ($columns as $label) {
$html[] = '<div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader"><span class="sg-large-table__sort-label">' . auth_escape_html($label) . '</span></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';
}
$id = (int) ($row['id'] ?? 0);
$name = (string) ($row['name'] ?? $row['display_name'] ?? '');
$value = $tableKey === 'products'
? (string) max(0, (int) round((float) ($row['available_qty'] ?? 0)))
: (string) ($row['item_code'] ?? '');
$currentForecast = ['text' => '', 'warning' => false, 'mhdWarning' => false];
$nextForecast = ['text' => '-', 'warning' => false, 'mhdWarning' => false];
if ($tableKey === 'products') {
$currentForecast = $renderForecast(
$row['sellout_date'] ?? null,
(string) ($row['sellout_forecast_status'] ?? 'no_demand'),
$row['expiry_date'] ?? null,
true
);
$nextForecast = $renderForecast(
$row['next_sellout_date'] ?? null,
(string) ($row['next_sellout_forecast_status'] ?? 'no_demand'),
$row['next_expiry_date'] ?? null,
$row['next_lot_id'] !== null
);
}
$nameData = ' data-lager-drawer-name="' . auth_escape_html($name) . '"';
$valueData = ' data-lager-drawer-value="' . auth_escape_html($value) . '"';
$typeData = ' data-lager-drawer-type="' . auth_escape_html($tableKey === 'products' ? 'Produkt' : 'Artikel') . '"';
$detailData = '';
if ($tableKey === 'products') {
$detailData = ' data-lager-drawer-forecast="' . auth_escape_html($currentForecast['text']) . '"'
. ' data-lager-drawer-forecast-status="' . auth_escape_html((string) ($row['sellout_forecast_status'] ?? 'no_demand')) . '"'
. ' data-lager-drawer-forecast-warning="' . ($currentForecast['warning'] ? 'true' : 'false') . '"'
. ' data-lager-drawer-forecast-mhd-warning="' . ($currentForecast['mhdWarning'] ? 'true' : 'false') . '"'
. ' data-lager-drawer-current-qty="' . auth_escape_html(auth_format_lager_quantity((float) ($row['current_qty'] ?? 0))) . '"'
. ' data-lager-drawer-qty-in="' . auth_escape_html(auth_format_lager_quantity((float) ($row['qty_in'] ?? 0))) . '"'
. ' data-lager-drawer-virtual-shortage-qty="' . auth_escape_html(auth_format_lager_quantity((float) ($row['virtual_shortage_qty'] ?? 0))) . '"'
. ' data-lager-drawer-lot-id="' . auth_escape_html((string) ($row['lot_id'] ?? '')) . '"'
. ' data-lager-drawer-lot-number="' . auth_escape_html((string) ($row['lot_number'] ?? '')) . '"'
. ' data-lager-drawer-manufacturer-party-id="' . auth_escape_html((string) ($row['manufacturer_party_id'] ?? '')) . '"'
. ' data-lager-drawer-manufacturer-name="' . auth_escape_html((string) ($row['manufacturer_name'] ?? '')) . '"'
. ' data-lager-drawer-manufacturer-lot-number="' . auth_escape_html((string) ($row['manufacturer_lot_number'] ?? '')) . '"'
. ' data-lager-drawer-mfg-date="' . auth_escape_html((string) ($row['mfg_date'] ?? '')) . '"'
. ' data-lager-drawer-expiry-date="' . auth_escape_html((string) ($row['expiry_date'] ?? '')) . '"'
. ' data-lager-drawer-purchase-date="' . auth_escape_html((string) ($row['purchase_date'] ?? '')) . '"'
. ' data-lager-drawer-virtual-lot-number="' . auth_escape_html((string) ($row['virtual_lot_number'] ?? '')) . '"';
$openLot = $row['open_lots'][0] ?? [];
$detailData .= ' data-lager-open-lot-id="' . auth_escape_html((string) ($openLot['lot_id'] ?? '')) . '"'
. ' data-lager-open-lot-number="' . auth_escape_html((string) ($openLot['lot_number'] ?? '')) . '"'
. ' data-lager-open-manufacturer-party-id="' . auth_escape_html((string) ($openLot['manufacturer_party_id'] ?? '')) . '"'
. ' data-lager-open-manufacturer-lot-number="' . auth_escape_html((string) ($openLot['manufacturer_lot_number'] ?? '')) . '"'
. ' data-lager-open-mfg-date="' . auth_escape_html((string) ($openLot['mfg_date'] ?? '')) . '"'
. ' data-lager-open-expiry-date="' . auth_escape_html((string) ($openLot['expiry_date'] ?? '')) . '"'
. ' data-lager-open-purchase-date="' . auth_escape_html((string) ($openLot['purchase_date'] ?? '')) . '"';
}
$html[] = '<div class="' . $rowClass . '" role="row" data-component-part="large-table-row" data-lager-row="true" data-lager-id="' . $id . '">';
$html[] = '<div class="sg-large-table__cell" role="cell"><a class="sg-hyperlink" href="#" aria-haspopup="dialog" aria-expanded="false" data-lager-drawer-open="true"' . $nameData . $valueData . $typeData . $detailData . '>' . auth_escape_html($name) . '</a></div>';
$valueCell = $tableKey === 'products'
? '<div class="sg-large-table__cell" role="cell">' . auth_escape_html($value) . '</div>'
: '<div class="sg-large-table__cell" role="cell"><a class="sg-hyperlink" href="#" aria-haspopup="dialog" aria-expanded="false" data-lager-drawer-open="true"' . $nameData . $valueData . $typeData . $detailData . '>' . auth_escape_html($value) . '</a></div>';
$html[] = $valueCell;
if ($tableKey === 'products') {
$forecastClass = $currentForecast['warning']
? ' sg-strong lager-forecast-warning'
: ($currentForecast['mhdWarning'] ? ' sg-strong lager-forecast-mhd-warning' : '');
$nextForecastClass = $nextForecast['warning']
? ' sg-strong lager-forecast-warning'
: ($nextForecast['mhdWarning'] ? ' sg-strong lager-forecast-mhd-warning' : '');
$html[] = '<div class="sg-large-table__cell' . $forecastClass . '" role="cell">' . auth_escape_html($currentForecast['text']) . '</div>';
$html[] = '<div class="sg-large-table__cell" role="cell">' . auth_escape_html((string) max(0, (int) round((float) ($row['next_qty'] ?? 0)))) . '</div>';
$html[] = '<div class="sg-large-table__cell' . $nextForecastClass . '" role="cell">' . auth_escape_html($nextForecast['text']) . '</div>';
}
$html[] = '</div>';
}
$html[] = '</article>';
return implode('', $html);
}
function auth_format_lager_date(?string $value): string
{
if ($value === null || $value === '') {
return 'Nicht hinterlegt';
}
return (new DateTimeImmutable($value))->format(str_contains($value, ':') ? 'd.m.Y H:i' : 'd.m.Y');
}
function auth_format_lager_quantity(float $value): string
{
return fmod($value, 1.0) === 0.0
? number_format($value, 0, ',', '.')
: number_format($value, 2, ',', '.');
}
function auth_render_lager_historical_lots(array $lagerProducts): string
{
$html = [];
foreach ($lagerProducts as $product) {
$productId = (int) ($product['id'] ?? 0);
$html[] = '<div data-lager-historical-product="' . $productId . '" hidden><div class="sg-content-group-box" data-component="content-group-box">';
foreach (($product['historical_lots'] ?? []) as $lot) {
$qtyNet = (float) ($lot['qty_net'] ?? 0);
$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>';
}
$html[] = '</div></div>';
}
return implode('', $html);
}
function auth_render_lager_open_lots(array $lagerProducts, string $manufacturerOptions): string
{
$html = [];
foreach ($lagerProducts as $product) {
$productId = (int) ($product['id'] ?? 0);
$html[] = '<div data-lager-open-product="' . $productId . '" hidden><div class="sg-content-group-box" data-component="content-group-box" data-lager-open-lot-list>';
foreach (($product['open_lots'] ?? []) as $lot) {
$lotId = (int) ($lot['lot_id'] ?? 0);
$selectedClass = !empty($lot['next_active']) ? ' sg-lager-open-lot--next-active' : '';
$selectedText = !empty($lot['next_active']) ? 'Als nächste aktive Charge ausgewählt' : 'Nicht ausgewählt';
$isQualified = trim((string) ($lot['lot_number'] ?? '')) !== ''
&& (int) ($lot['manufacturer_party_id'] ?? 0) > 0
&& trim((string) ($lot['manufacturer_lot_number'] ?? '')) !== ''
&& trim((string) ($lot['mfg_date'] ?? '')) !== ''
&& trim((string) ($lot['expiry_date'] ?? '')) !== ''
&& trim((string) ($lot['purchase_date'] ?? '')) !== ''
&& (float) ($lot['qty_net'] ?? 0) > 0;
$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 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>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Hersteller</span><select class="sg-interaction-element sg-input-single-line" data-lager-open-manufacturer>' . str_replace('value="' . (int) ($lot['manufacturer_party_id'] ?? 0) . '"', 'value="' . (int) ($lot['manufacturer_party_id'] ?? 0) . '" selected', $manufacturerOptions) . '</select></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Hersteller-Chargennummer</span><input class="sg-interaction-element sg-input-single-line" type="text" data-lager-open-manufacturer-lot value="' . auth_escape_html((string) ($lot['manufacturer_lot_number'] ?? '')) . '"></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Herstellungsdatum</span><input class="sg-interaction-element sg-input-single-line" type="date" data-lager-open-mfg-date value="' . auth_escape_html((string) ($lot['mfg_date'] ?? '')) . '"></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">MHD</span><input class="sg-interaction-element sg-input-single-line" type="date" data-lager-open-expiry-date value="' . auth_escape_html((string) ($lot['expiry_date'] ?? '')) . '"></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Einkaufsdatum</span><input class="sg-interaction-element sg-input-single-line" type="date" data-lager-open-purchase-date value="' . auth_escape_html((string) ($lot['purchase_date'] ?? '')) . '"></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Menge</span><input class="sg-interaction-element sg-input-single-line" type="number" min="0" step="1" inputmode="numeric" data-lager-open-target-qty value="' . auth_escape_html((string) ($lot['qty_net'] ?? 0)) . '"></label></div>'
. '</section><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-lager-open-edit-close 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-lager-open-edit-status aria-live="polite"></p></div></form></div></div></article>'
. '<article class="sg-card sg-card--overlay-host sg-delete-confirmation-pattern__floating-card" aria-label="Leere Felder bestätigen" role="dialog" aria-modal="true" aria-labelledby="lager-open-confirmation-title-' . $lotId . '" data-lager-open-confirmation-card 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="lager-open-confirmation-title-' . $lotId . '"><strong>Leere Felder speichern?</strong></p><p class="sg-body sg-delete-confirmation-pattern__text">Die folgenden Felder werden leer gespeichert:</p><div class="sg-delete-confirmation-pattern__list" data-lager-open-confirmation-empty-fields aria-label="Leer zu speichernde Felder"></div><div class="sg-delete-confirmation-pattern__actions"><button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-open-confirmation-cancel>Abbrechen</button><button class="sg-interaction-element sg-button sg-button--process" type="button" data-lager-open-confirmation-submit>Speichern</button></div></div></article>'
. '<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="lager-open-delete-title-' . $lotId . '" data-overlay-dialog="delete" data-lager-open-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="lager-open-delete-title-' . $lotId . '"><strong>Möchtest du diese offene Charge 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-lager-open-delete-confirmation-input data-shared-editable-card-delete-input></label><p class="sg-body" data-lager-open-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-lager-open-delete-confirmation-cancel 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-lager-open-delete-confirmation-submit data-shared-editable-card-delete-submit>Löschen</button></div></div></article></div>';
}
$html[] = '</div></div>';
}
return implode('', $html);
}
function auth_render_lager_content(array $lagerProducts, array $lagerArtikel, array $lagerManufacturers, string $csrfToken, string $sectionTitle = 'Lager'): string
{
$manufacturerOptions = '<option value="">Hersteller auswählen</option>';
foreach ($lagerManufacturers as $manufacturer) {
$manufacturerOptions .= '<option value="' . (int) ($manufacturer['id'] ?? 0) . '">' . auth_escape_html((string) ($manufacturer['name'] ?? '')) . '</option>';
}
$tables = $sectionTitle === 'Lager'
? auth_render_lager_table('Produkte', $lagerProducts, ['Produktname', 'Lager aktuell', 'Verkauf bis aktuell', 'Lager neu', 'Verkauf bis neu'], 'products')
: auth_render_lager_table('Artikel', $lagerArtikel, ['Artikel-Name', 'Artikelnummer'], 'articles');
$openLotCardTemplate = auth_render_lager_open_lots([[
'id' => 0,
'open_lots' => [[
'lot_id' => 0,
'lot_number' => null,
'manufacturer_party_id' => null,
'manufacturer_name' => null,
'manufacturer_lot_number' => null,
'mfg_date' => null,
'expiry_date' => null,
'purchase_date' => null,
'qty_net' => 0,
'qty_in' => 0,
'next_active' => false,
]],
]], $manufacturerOptions);
return '<div data-lager-content="true" data-lager-csrf-token="' . auth_escape_html($csrfToken) . '">'
. '<section class="sg-card-list-page" aria-label="' . auth_escape_html($sectionTitle) . '">'
. $tables
. '</section>'
. '<div class="sg-card-list-page-drawer-backdrop" aria-hidden="true" data-lager-drawer-backdrop="true" data-open="false"></div>'
. '<aside class="sg-card-list-page-drawer" aria-label="Lager-Details" aria-hidden="true" data-lager-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-lager-drawer-title>Lager-Details</h2></div>'
. '<div class="sg-card-list-page-drawer__content">'
. '<article class="sg-card sg-vsf-drawer-card" data-component="card" data-pattern="card" aria-label="Lager-Details">'
. '<header class="sg-card-segment sg-card-segment--header sg-card-segment--darkblue sg-vsf-drawer-card__header"><div class="sg-strong">Lager-Details</div></header>'
. '<div class="sg-card-segment sg-card-segment--body sg-vsf-drawer-card__content"><p class="sg-body"><strong>Name:</strong> <span data-lager-drawer-name-value></span></p><p class="sg-body" data-lager-drawer-value-row><strong data-lager-drawer-value-label>Wert:</strong> <span data-lager-drawer-value-value></span><br><strong>Voraussichtlich leer:</strong> <span data-lager-drawer-forecast-value>Keine Prognose</span></p></div>'
. '</article>'
. '<article class="sg-card sg-card--content-card" data-component="content-card" data-lager-drawer-topic="warning" aria-label="Warnungen">'
. '<div class="sg-card-segment sg-card-segment--header sg-card-segment--darkgreen" data-component-part="card-header" data-lager-drawer-warning-header><div class="sg-strong sg-text-on-dark" data-lager-drawer-warning-title>Keine Warnung</div></div>'
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body"><p class="sg-body" data-lager-drawer-warning-value></p></div>'
. '</article>'
. '<div class="sg-inline-controls-row" data-lager-drawer-topic="new-lot-action"><button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-new-lot-open>Neue Charge anlegen</button></div>'
. '<article class="sg-card sg-card--content-card" data-lager-drawer-topic="new-lot-form" aria-label="Neue Charge anlegen" hidden>'
. '<div class="sg-card-segment sg-card-segment--body sg-card-segment--gray"><div class="sg-form-sections-card-wrapper" data-pattern="form-sections" aria-label="Neue Charge anlegen"><form class="sg-form-sections-card" data-lager-new-lot-form-element><div class="sg-form-sections-card__body" data-pattern-part="form-body"><h2 class="sg-strong sg-form-sections-card__title">Neue Charge anlegen</h2>'
. '<div class="sg-notifications-pattern" data-lager-new-lot-notification-wrapper hidden><article class="sg-card sg-notifications-pattern__card" data-pattern="notification-small" data-component="notification-card"><div class="sg-card-segment sg-card-segment--header sg-card-segment--signal-green sg-notifications-pattern__text-segment sg-notifications-pattern__text-segment--small" data-component-part="card-header"><p class="sg-body" data-lager-new-lot-notification></p></div></article></div>'
. '<section class="sg-form-sections-card__chapter" aria-labelledby="lager-new-charge-data-title"><h2 id="lager-new-charge-data-title" 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-new-lot-number></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Hersteller</span><select class="sg-interaction-element sg-input-single-line" data-lager-new-manufacturer>' . $manufacturerOptions . '</select></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Hersteller-Chargennummer</span><input class="sg-interaction-element sg-input-single-line" type="text" data-lager-new-manufacturer-lot></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Herstellungsdatum</span><input class="sg-interaction-element sg-input-single-line" type="date" data-lager-new-mfg-date></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">MHD</span><input class="sg-interaction-element sg-input-single-line" type="date" data-lager-new-expiry-date></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Einkaufsdatum</span><input class="sg-interaction-element sg-input-single-line" type="date" data-lager-new-purchase-date></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Eingangsmenge</span><input class="sg-interaction-element sg-input-single-line" type="number" min="0" step="1" inputmode="numeric" data-lager-new-receipt-qty placeholder="Menge"></label></div>'
. '</section><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-lager-new-lot-close>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-lager-new-lot-status aria-live="polite"></p></div></form></div></div>'
. '<article class="sg-card sg-card--overlay-host sg-delete-confirmation-pattern__floating-card" aria-label="Leere Felder bestätigen" role="dialog" aria-modal="true" aria-labelledby="lager-new-confirmation-title" data-lager-new-confirmation-card 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="lager-new-confirmation-title"><strong>Leere Felder speichern?</strong></p><p class="sg-body sg-delete-confirmation-pattern__text">Die folgenden Felder werden leer gespeichert:</p><div class="sg-delete-confirmation-pattern__list" data-lager-new-confirmation-empty-fields aria-label="Leer zu speichernde Felder"></div><div class="sg-delete-confirmation-pattern__actions"><button class="sg-interaction-element sg-button sg-button--active" type="button" data-lager-new-confirmation-cancel>Abbrechen</button><button class="sg-interaction-element sg-button sg-button--process" type="button" data-lager-new-confirmation-submit>Speichern</button></div></div></article>'
. '</article>'
. '<template data-lager-open-lot-template>' . $openLotCardTemplate . '</template>'
. '<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 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>'
. '<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 sg-form-inactive-selectable" type="text" data-lager-lot-number readonly></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Hersteller</span><select class="sg-interaction-element sg-input-single-line" id="lager-manufacturer" data-lager-manufacturer>' . $manufacturerOptions . '</select></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Hersteller-Chargennummer</span><input class="sg-interaction-element sg-input-single-line" type="text" id="lager-manufacturer-lot" data-lager-manufacturer-lot></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Herstellungsdatum</span><input class="sg-interaction-element sg-input-single-line" type="date" id="lager-mfg-date" data-lager-mfg-date></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">MHD</span><input class="sg-interaction-element sg-input-single-line" type="date" id="lager-expiry-date" data-lager-expiry-date></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Einkaufsdatum</span><input class="sg-interaction-element sg-input-single-line" type="date" id="lager-purchase-date" data-lager-purchase-date></label></div>'
. '</section><section class="sg-form-sections-card__chapter" aria-labelledby="lager-charge-movements-title"><h2 id="lager-charge-movements-title" class="sg-strong sg-form-sections-card__chapter-title">Lagerbewegungen</h2>'
. '<p class="sg-form-sections-card__sentence">Buchungen werden zusätzlich zur Rückverfolgbarkeit als Lagerbewegung gespeichert. Positive Werte erhöhen den Bestand, negative Werte reduzieren ihn.</p>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Zugang buchen</span><input class="sg-interaction-element sg-input-single-line" type="number" min="0" step="0.01" inputmode="decimal" data-lager-receipt-qty placeholder="Menge"></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Bestandskorrektur (+ / −)</span><input class="sg-interaction-element sg-input-single-line" type="number" step="1" inputmode="numeric" data-lager-adjustment-qty placeholder="+ / − Menge"></label></div>'
. '<div class="sg-form-sections-card__field-group"><label class="sg-labeled-input-row"><span class="sg-label">Abgang buchen</span><input class="sg-interaction-element sg-input-single-line" type="number" min="0" step="0.01" inputmode="decimal" data-lager-out-qty placeholder="Menge"></label></div>'
. '</section><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-lager-edit-close 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-lager-edit-status aria-live="polite"></p></div></form>'
. '</div></div>'
. '</article>'
. '<div data-lager-drawer-topic="historical-lots" aria-label="Vergangene Chargen">' . auth_render_lager_historical_lots($lagerProducts) . '</div>'
. '<article class="sg-card sg-card--content-card" data-component="content-card" data-lager-drawer-topic="virtual-lot" aria-label="Virtuelle 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">Virtuelle Charge</div></div>'
. '<div class="sg-card-segment sg-card-segment--body" data-component-part="card-body"><p class="sg-body"><span data-lager-drawer-virtual-lot-value></span><br><span data-lager-drawer-virtual-lot-qty></span></p></div>'
. '</article>'
. '</div>'
. '</aside>'
. '</div>';
}
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) {
$productId = (int) ($product['id'] ?? 0);
$productName = auth_escape_html((string) ($product['name'] ?? ''));
$productSku = auth_escape_html((string) ($product['sku'] ?? ''));
$availableQty = (int) max(0, (int) round((float) ($product['available_qty'] ?? 0)));
$hasSellableMapping = (bool) ($product['has_sellable_mapping'] ?? false);
$inputId = 'otc-product-' . $productId;
$mappingNote = $hasSellableMapping ? '' : ' (Artikel-Mapping fehlt)';
$disabledAttribute = $hasSellableMapping ? '' : ' disabled aria-disabled="true"';
$otcProductRows .= '<div class="sg-form-sections-card__field-group">';
$otcProductRows .= '<label class="sg-labeled-input-row sg-otc-order-product-row"><span class="sg-label">' . $productName . ($productSku !== '' ? ' (' . $productSku . ')' : '') . $mappingNote . '</span>';
$otcProductRows .= '<input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="number" id="' . $inputId . '" min="0" step="1" data-otc-order-product data-product-id="' . $productId . '" data-title="' . $productName . '" data-available-qty="' . $availableQty . '"' . $disabledAttribute . '>';
$otcProductRows .= '</label>';
$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>';
}
$moduleNavigation = json_encode(
[
'Übersicht' => [],
'ERP' => ['Bestellungen', 'Lager', 'Produkte', 'Artikel', 'Kontakte'],
'Märkte' => ['Marktdurchführungen', 'Märkte'],
'Buchhaltung' => [],
'Kundenberatung' => [],
],
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
$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,
'Marktdurchführungen' => $marketsContent,
'Märkte' => $marketDefinitionsContent,
'Lager' => $lagerContent,
'Artikel' => auth_render_lager_content([], $lagerArtikel, $lagerManufacturers, auth_csrf_token(), 'Artikel'),
],
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
$moduleHeadings = json_encode(
[
'Übersicht' => 'Willkommen bei Naurua Übersicht',
'ERP' => 'Willkommen bei Naurua ERP',
'Märkte' => 'Willkommen bei Naurua Märkte',
'Buchhaltung' => 'Willkommen bei Naurua Buchhaltung',
'Kundenberatung' => 'Willkommen bei Naurua Kundenberatung',
],
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
echo '<!doctype html>';
echo '<html lang="de">';
echo '<head>';
echo '<meta charset="UTF-8">';
echo '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
echo '<title>Naurua ERP</title>';
echo '<link rel="stylesheet" href="/assets/styles.css">';
echo '<link rel="stylesheet" href="/assets/styles.portal.css?v=' . filemtime(__DIR__ . '/../../../../public/assets/styles.portal.css') . '">';
echo '</head>';
echo '<body>';
echo '<section id="pattern-portal-header" class="sg-vsf-list-overview-page-v2">';
echo '<article class="sg-portal-header-pattern-variant" aria-label="Portal Header">';
echo '<header class="sg-portal-header" aria-label="Portal Header" data-pattern="portal-header">';
echo '<div class="sg-portal-header__main" data-pattern-part="portal-header-main">';
echo '<p class="sg-portal-header__brand sg-brand-title" data-pattern-part="portal-header-brand">Naurua ERP</p>';
echo '<div class="sg-portal-header__menu-wrap sg-sandwich-menu-wrap" data-open="false" data-component="sandwich-menu" data-component-size="default" data-component-context="portal-header" data-pattern-part="portal-header-action">';
echo '<button class="sg-interaction-element sg-sandwich-button" type="button" aria-expanded="false" aria-label="Menü öffnen" data-component-part="sandwich-trigger">';
echo '<span class="sg-sandwich-button__icon" aria-hidden="true">';
echo '<span class="sg-sandwich-button__line"></span>';
echo '<span class="sg-sandwich-button__line"></span>';
echo '<span class="sg-sandwich-button__line"></span>';
echo '</span>';
echo '</button>';
echo '<div class="sg-sandwich-menu-panel" aria-label="Ausgeklapptes Menü" data-component-part="sandwich-panel">';
echo '<a class="sg-sandwich-menu-link" href="/" data-component-part="sandwich-menu-link">Startseite</a>';
echo '<a class="sg-sandwich-menu-link" href="/logout.php" data-component-part="sandwich-menu-link">Abmelden</a>';
echo '</div>';
echo '</div>';
echo '<nav class="sg-portal-header__tabs sg-tab-button-group" aria-label="Hauptnavigation" data-component="tab-navigation" data-component-size="large" data-component-context="portal-header" data-pattern-part="portal-header-navigation">';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" aria-selected="false" data-component-part="tab-button" data-component-state="inactive">Übersicht</button>';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" aria-selected="true" data-component-part="tab-button" data-component-state="active">ERP</button>';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" aria-selected="false" data-component-part="tab-button" data-component-state="inactive">Märkte</button>';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" aria-selected="false" data-component-part="tab-button" data-component-state="inactive">Buchhaltung</button>';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" aria-selected="false" data-component-part="tab-button" data-component-state="inactive">Kundenberatung</button>';
echo '</nav>';
echo '</div>';
echo '</header>';
echo '</article>';
echo '<div class="sg-transparent-card" data-component="transparent-card">';
echo '<h1 class="sg-main-heading" data-main-heading aria-live="polite">Willkommen bei Naurua ERP</h1>';
echo '</div>';
echo '<section class="sg-left-navigation-pattern" aria-label="Linke Navigation">';
echo '<div class="sg-left-navigation-pattern__layout" aria-label="Left Navigation Demo">';
echo '<aside class="sg-group-card sg-left-navigation-pattern__group-card sg-left-navigation-pattern__group-card--navigation" data-component="group-card" aria-label="Navigation">';
echo '<div class="sg-group-card__header-row sg-left-navigation-pattern__header-row">';
echo '<h2 class="sg-heading-h2 sg-text-on-dark sg-group-card__heading" data-left-navigation-title aria-live="polite">ERP</h2>';
echo '<button class="sg-interaction-element sg-button sg-button--active sg-left-navigation-pattern__toggle" type="button" data-left-navigation-toggle aria-expanded="true" aria-controls="left-navigation-menu">Ausblenden</button>';
echo '</div>';
echo '<nav class="sg-tab-button-group" id="left-navigation-menu" role="tablist" aria-label="Linksmenue Items" data-component="tab-navigation" data-component-size="large" data-component-variant="linksmenu-items">';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="true" data-component-part="tab-button">Bestellungen</button>';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="false" data-component-part="tab-button">Lager</button>';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="false" data-component-part="tab-button">Produkte</button>';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="false" data-component-part="tab-button">Artikel</button>';
echo '<button class="sg-interaction-element sg-button sg-tab-button" type="button" role="tab" aria-selected="false" data-component-part="tab-button">Kontakte</button>';
echo '</nav>';
echo '</aside>';
echo '<section class="sg-group-card sg-left-navigation-pattern__group-card sg-left-navigation-pattern__group-card--content" data-component="group-card" aria-hidden="true">';
echo '<div class="sg-group-card__header-row sg-left-navigation-pattern__header-row">';
echo '<h2 class="sg-heading-h2 sg-text-on-dark sg-group-card__heading" data-left-navigation-content-title aria-live="polite">Bestellungen</h2>';
echo '</div>';
echo '<div data-left-navigation-content-body>';
echo $bestellungenContent;
echo '</div>';
echo '</section>';
echo '</div>';
echo '</section>';
echo '</section>';
echo '<section class="sg-otc-order-overlay" data-otc-order-overlay data-open="false" aria-hidden="true">';
echo '<div class="sg-delete-confirmation-pattern__stage sg-delete-confirmation-pattern__host sg-object-card" data-pattern="overlay-card" data-dialog-open="false" data-otc-confirmation-stage>';
echo '<article class="sg-card sg-scrollable-card sg-delete-confirmation-pattern__target sg-otc-order-overlay__panel" data-pattern="scrollable-card" aria-labelledby="otc-order-modal-title" role="dialog" aria-modal="true">';
echo '<header class="sg-card-segment sg-card-segment--header sg-card-segment--darkblue sg-scrollable-card__header sg-object-card__header" data-pattern-part="scrollable-card-header">';
echo '<h2 id="otc-order-modal-title" class="sg-strong sg-otc-order-overlay__title">Neuen OTC-Verkauf erfassen</h2>';
echo '<button class="sg-interaction-element sg-button sg-button--active sg-otc-order-overlay__close" type="button" data-otc-order-close>Schliessen</button>';
echo '</header>';
echo '<div class="sg-card-segment sg-card-segment--gray sg-scrollable-card__content sg-object-card__content" data-pattern-part="scrollable-card-content" tabindex="0" aria-label="Scrollbarer Formularinhalt">';
echo '<div class="sg-form-sections-card-wrapper" data-pattern="form-sections" aria-label="Formular mit Abschnitten">';
echo '<form id="otc-order-form" class="sg-form-sections-card" action="#" method="post" data-otc-order-form aria-label="Neuen OTC-Verkauf erfassen">';
echo '<div class="sg-form-sections-card__body" data-pattern-part="form-body">';
echo '<section class="sg-form-sections-card__chapter" aria-labelledby="otc-products-title">';
echo '<h2 id="otc-products-title" class="sg-strong sg-form-sections-card__chapter-title">PRODUKTE</h2>';
echo $otcProductRows;
echo '</section>';
echo '<section class="sg-form-sections-card__chapter" aria-labelledby="otc-order-date-title">';
echo '<h2 id="otc-order-date-title" class="sg-strong sg-form-sections-card__chapter-title">Datum</h2>';
echo '<div class="sg-form-sections-card__field-group">';
echo '<label class="sg-labeled-input-row"><span class="sg-label">Bestelldatum</span>';
echo '<input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="date" id="otc-order-date" value="' . htmlspecialchars(date('Y-m-d'), ENT_QUOTES, 'UTF-8') . '" data-otc-order-date>';
echo '</label>';
echo '</div>';
echo '</section>';
echo '<section class="sg-form-sections-card__chapter" aria-labelledby="otc-payment-title">';
echo '<h2 id="otc-payment-title" class="sg-strong sg-form-sections-card__chapter-title">Bezahlung</h2>';
echo '<div data-otc-payment-rows>';
echo '<div class="sg-form-sections-card__field-group" data-otc-payment-row>';
echo '<label class="sg-labeled-input-row"><span class="sg-label">Bezahlart</span>';
echo '<select class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable sg-pulldown" id="otc-payment-method-1" data-otc-payment-method>';
echo '<option value="twint">Twint</option><option value="card">Kreditkarte</option><option value="cash">Barzahlung</option><option value="paypal">PayPal</option><option value="bank_transfer">Überweisung</option>';
echo '</select>';
echo '</label>';
echo '<label class="sg-labeled-input-row"><span class="sg-label">Betrag (CHF)</span>';
echo '<input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="number" id="otc-payment-amount-1" min="0.01" step="0.01" value="0.00" data-otc-payment-amount>';
echo '</label>';
echo '</div>';
echo '</div>';
echo '<div class="sg-form-sections-card__actions-segment">';
echo '<button class="sg-interaction-element sg-button sg-button--active" type="button" data-otc-payment-add>Bezahlart hinzufügen</button>';
echo '</div>';
echo '</section>';
echo '<section class="sg-form-sections-card__chapter" aria-labelledby="otc-billing-title">';
echo '<h2 id="otc-billing-title" class="sg-strong sg-form-sections-card__chapter-title">Rechnungsadresse</h2>';
echo '<div class="sg-form-sections-card__field-group">';
echo '<label class="sg-labeled-input-row"><span class="sg-label">Vorname</span>';
echo '<input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" id="otc-first-name" value="Fabienne" data-otc-order-first-name>';
echo '</label>';
echo '</div>';
echo '<div class="sg-form-sections-card__field-group">';
echo '<label class="sg-labeled-input-row"><span class="sg-label">Nachname</span>';
echo '<input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" id="otc-last-name" value="Föhn" data-otc-order-last-name>';
echo '</label>';
echo '</div>';
echo '<div class="sg-form-sections-card__field-group">';
echo '<label class="sg-labeled-input-row"><span class="sg-label">Strasse</span>';
echo '<input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" id="otc-street" value="Im Hochrain" data-otc-order-street>';
echo '</label>';
echo '</div>';
echo '<div class="sg-form-sections-card__field-group">';
echo '<label class="sg-labeled-input-row"><span class="sg-label">Hausnummer</span>';
echo '<input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" id="otc-house-number" value="2" data-otc-order-house-number>';
echo '</label>';
echo '</div>';
echo '<div class="sg-form-sections-card__field-group">';
echo '<label class="sg-labeled-input-row"><span class="sg-label">PLZ</span>';
echo '<input class="sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable" type="text" id="otc-zip" value="8102" data-otc-order-zip>';
echo '</label>';
echo '</div>';
echo '<div class="sg-form-sections-card__field-group">';
echo '<label class="sg-labeled-input-row"><span class="sg-label">Ort</span>';
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>';
echo '</div>';
echo '</div>';
echo '</form>';
echo '</div>';
echo '</div>';
echo '<footer class="sg-card-segment sg-card-segment--body sg-scrollable-card__actions-segment" data-pattern-part="scrollable-card-actions">';
echo '<div class="sg-scrollable-card__actions">';
echo '<button class="sg-interaction-element sg-button sg-button--active" type="button" form="otc-order-form" data-otc-order-close>Abbrechen</button>';
echo '<button class="sg-interaction-element sg-button sg-button--process sg-button--process-inactive" type="submit" form="otc-order-form" disabled aria-disabled="true" data-otc-order-submit>Weiter</button>';
echo '</div>';
echo '</footer>';
echo '</article>';
echo '<article class="sg-card sg-card--overlay-host sg-delete-confirmation-pattern__floating-card" aria-label="Verkaufsbestätigung" role="dialog" aria-modal="true" aria-labelledby="otc-confirmation-title" data-otc-confirmation-card hidden>';
echo '<div class="sg-card-segment sg-card-segment--body sg-delete-confirmation-pattern__body">';
echo '<p class="sg-body sg-delete-confirmation-pattern__text" id="otc-confirmation-title"><strong>Verkauf bestätigen</strong></p>';
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>';
echo '</div>';
echo '</div>';
echo '</article>';
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>';
echo 'const portalModuleNavigation = ' . $moduleNavigation . ';';
echo 'const portalModuleContentCards = ' . $moduleContentCards . ';';
echo 'const portalModuleHeadings = ' . $moduleHeadings . ';';
echo "const renderMainHeading = (moduleName) => {";
echo " const heading = document.querySelector('[data-main-heading]');";
echo " if (heading && portalModuleHeadings[moduleName]) { heading.textContent = portalModuleHeadings[moduleName]; }";
echo "};";
echo "const renderLeftNavigation = (moduleName) => {";
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 " 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 === 'Marktdurchführungen') { initMarketsBindings(); }";
echo " if (activeEntry === 'Märkte') { initMarketDefinitionsBindings(); }";
echo " if (activeEntry === 'Lager' || activeEntry === 'Artikel') { initLagerBindings(); }";
echo " menu.innerHTML = '';";
echo " entries.forEach((entry, index) => {";
echo " const button = document.createElement('button');";
echo " button.className = 'sg-interaction-element sg-button sg-tab-button';";
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.textContent = entry;";
echo " menu.appendChild(button);";
echo " });";
echo " menu.querySelectorAll('.sg-tab-button').forEach((button) => {";
echo " button.addEventListener('click', () => {";
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 (contentSection) { contentSection.setAttribute('aria-hidden', 'false'); }";
echo " if (contentBody) { contentBody.innerHTML = portalModuleContentCards[entryName] || ''; }";
echo " if (entryName === 'Bestellungen') { initBestellungenBindings(); }";
echo " if (entryName === 'Marktdurchführungen') { initMarketsBindings(); }";
echo " if (entryName === 'Märkte') { initMarketDefinitionsBindings(); }";
echo " if (entryName === 'Lager' || entryName === 'Artikel') { initLagerBindings(); }";
echo " });";
echo " });";
echo "};";
echo "document.querySelectorAll('.sg-portal-header__tabs').forEach((group) => {";
echo " group.querySelectorAll('.sg-tab-button').forEach((button) => {";
echo " button.addEventListener('click', () => {";
echo " group.querySelectorAll('.sg-tab-button').forEach((otherButton) => {";
echo " const isActive = otherButton === button;";
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 " });";
echo " });";
echo "});";
echo "document.querySelectorAll('.sg-sandwich-menu-wrap').forEach((wrap) => {";
echo " const button = wrap.querySelector('.sg-sandwich-button');";
echo " button.addEventListener('click', (event) => {";
echo " event.stopPropagation();";
echo " const nextState = wrap.dataset.open !== 'true';";
echo " document.querySelectorAll('.sg-sandwich-menu-wrap').forEach((otherWrap) => {";
echo " const otherButton = otherWrap.querySelector('.sg-sandwich-button');";
echo " otherWrap.dataset.open = 'false';";
echo " if (otherButton) { otherButton.setAttribute('aria-expanded', 'false'); }";
echo " });";
echo " wrap.dataset.open = String(nextState);";
echo " button.setAttribute('aria-expanded', String(nextState));";
echo " });";
echo "});";
echo "document.addEventListener('click', (event) => {";
echo " if (event.target.closest('.sg-sandwich-menu-wrap')) {";
echo " return;";
echo " }";
echo " document.querySelectorAll('.sg-sandwich-menu-wrap').forEach((wrap) => {";
echo " const button = wrap.querySelector('.sg-sandwich-button');";
echo " wrap.dataset.open = 'false';";
echo " if (button) { button.setAttribute('aria-expanded', 'false'); }";
echo " });";
echo "});";
echo "(() => {";
echo " const mediaQuery = window.matchMedia('(max-width: 767px)');";
echo " const toggle = document.querySelector('[data-left-navigation-toggle]');";
echo " const menu = document.getElementById('left-navigation-menu');";
echo " if (!toggle || !menu) { return; }";
echo " const setMenuState = (expanded) => {";
echo " menu.hidden = !expanded;";
echo " menu.classList.toggle('sg-left-navigation-pattern__menu--collapsed', !expanded);";
echo " toggle.setAttribute('aria-expanded', String(expanded));";
echo " toggle.textContent = expanded ? 'Ausblenden' : 'Einblenden';";
echo " toggle.classList.add('sg-button--active');";
echo " toggle.classList.remove('sg-button--inactive');";
echo " };";
echo " const syncMode = () => {";
echo " if (mediaQuery.matches) {";
echo " if (toggle.getAttribute('aria-expanded') !== 'true' && toggle.getAttribute('aria-expanded') !== 'false') { setMenuState(true); return; }";
echo " setMenuState(toggle.getAttribute('aria-expanded') !== 'false');";
echo " return;";
echo " }";
echo " menu.hidden = false;";
echo " menu.classList.remove('sg-left-navigation-pattern__menu--collapsed');";
echo " toggle.setAttribute('aria-expanded', 'true');";
echo " toggle.textContent = 'Ausblenden';";
echo " toggle.classList.add('sg-button--active');";
echo " toggle.classList.remove('sg-button--inactive');";
echo " };";
echo " toggle.addEventListener('click', () => { setMenuState(menu.hidden); });";
echo " menu.querySelectorAll('.sg-tab-button').forEach((button) => {";
echo " button.addEventListener('click', () => {";
echo " menu.querySelectorAll('.sg-tab-button').forEach((otherButton) => {";
echo " otherButton.setAttribute('aria-selected', String(otherButton === button));";
echo " });";
echo " });";
echo " });";
echo " syncMode();";
echo " mediaQuery.addEventListener('change', syncMode);";
echo "})();";
echo "let bestellungenTableHandlersInstalled = false;";
echo "let lagerBindingsInstalled = false;";
echo "function initLagerBindings() {";
echo " if (lagerBindingsInstalled) { return; }";
echo " const contentRoot = document.querySelector('[data-left-navigation-content-body]');";
echo " if (!contentRoot || !contentRoot.querySelector('[data-lager-content]')) { return; }";
echo " if (window.SharedEditableActionCard) { window.SharedEditableActionCard.init(contentRoot); }";
echo " lagerBindingsInstalled = true;";
echo " let lagerMobileDetailHost = null;";
echo " let lagerMobileDetailOpen = false;";
echo " const renderLagerMobileDetail = (sourceDrawer, trigger) => { const type = trigger.dataset.lagerDrawerType || 'Lager'; const name = trigger.dataset.lagerDrawerName || ''; const value = trigger.dataset.lagerDrawerValue || ''; sourceDrawer.querySelector('[data-lager-drawer-title]').textContent = type + ': ' + name; sourceDrawer.querySelector('[data-lager-drawer-name-value]').textContent = name; sourceDrawer.querySelector('[data-lager-drawer-value-label]').textContent = type === 'Produkt' ? 'Anzahl im Lager:' : 'Artikelnummer:'; sourceDrawer.querySelector('[data-lager-drawer-value-value]').textContent = value; sourceDrawer.querySelectorAll('[data-lager-drawer-topic]').forEach((card) => { card.hidden = type !== 'Produkt'; }); const newLotForm = sourceDrawer.querySelector('[data-lager-drawer-topic=\"new-lot-form\"]'); if (newLotForm) { newLotForm.hidden = true; } const newLotAction = sourceDrawer.querySelector('[data-lager-drawer-topic=\"new-lot-action\"]'); if (newLotAction) { newLotAction.hidden = false; } if (type === 'Produkt') { const warning = trigger.dataset.lagerDrawerForecastWarning === 'true' || trigger.dataset.lagerDrawerForecastMhdWarning === 'true'; const warningValue = sourceDrawer.querySelector('[data-lager-drawer-warning-value]'); if (warningValue) { warningValue.textContent = warning ? (trigger.dataset.lagerDrawerForecast || 'Keine Prognose') : 'Keine akute Warnung.'; } const warningTitle = sourceDrawer.querySelector('[data-lager-drawer-warning-title]'); if (warningTitle) { warningTitle.textContent = getLagerWarningTitle(trigger); } sourceDrawer.querySelector('[data-lager-drawer-lot-number]').textContent = trigger.dataset.lagerDrawerLotNumber || 'Nicht hinterlegt'; sourceDrawer.querySelector('[data-lager-drawer-current-qty]').textContent = trigger.dataset.lagerDrawerCurrentQty || '0'; sourceDrawer.querySelector('[data-lager-drawer-qty-in]').textContent = trigger.dataset.lagerDrawerQtyIn || '0'; sourceDrawer.querySelector('[data-lager-drawer-manufacturer-name]').textContent = trigger.dataset.lagerDrawerManufacturerName || 'Nicht hinterlegt'; sourceDrawer.querySelector('[data-lager-drawer-manufacturer-lot-number]').textContent = trigger.dataset.lagerDrawerManufacturerLotNumber || 'Nicht hinterlegt'; sourceDrawer.querySelector('[data-lager-drawer-mfg-date]').textContent = trigger.dataset.lagerDrawerMfgDate || 'Nicht hinterlegt'; sourceDrawer.querySelector('[data-lager-drawer-expiry-date]').textContent = trigger.dataset.lagerDrawerExpiryDate || 'Nicht hinterlegt'; sourceDrawer.querySelector('[data-lager-drawer-purchase-date]').textContent = trigger.dataset.lagerDrawerPurchaseDate || 'Nicht hinterlegt'; const productId = trigger.closest('[data-lager-row]')?.dataset.lagerId || ''; sourceDrawer.querySelectorAll('[data-lager-historical-product]').forEach((product) => { product.hidden = product.dataset.lagerHistoricalProduct !== productId; }); sourceDrawer.querySelectorAll('[data-lager-open-product]').forEach((product) => { product.hidden = product.dataset.lagerOpenProduct !== productId; product.querySelectorAll('[data-lager-open-edit-form]').forEach((form) => { form.hidden = true; }); product.querySelectorAll('[data-lager-open-card]').forEach((readView) => { readView.hidden = false; }); }); resetLagerNewLotConfirmation(); resetLagerOpenLotConfirmations(); sourceDrawer.dataset.lagerProductId = productId; populateLagerActiveLotForm(sourceDrawer, trigger); } const host = document.createElement('div'); host.className = 'sg-lager-mobile-detail-host'; host.dataset.lagerMobileDetailHost = '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\" aria-label=\"Navigation Detailansicht zurück oben\"><div class=\"sg-card-segment sg-card-segment--body\" data-component-part=\"card-body\" data-pattern-part=\"navigation-card-segment\"><div class=\"sg-navigation-card-center\"><button class=\"sg-interaction-element sg-hyperlink\" type=\"button\" data-lager-detail-back>zurück</button></div></div></article></div></div>'; const mobileDrawer = sourceDrawer; mobileDrawer.dataset.lagerMobileDrawer = 'true'; mobileDrawer.dataset.open = 'true'; mobileDrawer.setAttribute('aria-hidden', 'false'); host.appendChild(sourceDrawer); return host; };";
echo " const restoreLagerMobileDetail = () => {";
echo " if (lagerMobileDetailHost && lagerMobileDetailHost.parentNode) { const mobileDrawer = lagerMobileDetailHost.querySelector('[data-lager-mobile-drawer]'); if (mobileDrawer) { contentRoot.appendChild(mobileDrawer); mobileDrawer.removeAttribute('data-lager-mobile-drawer'); mobileDrawer.dataset.open = 'false'; mobileDrawer.setAttribute('aria-hidden', 'true'); } lagerMobileDetailHost.parentNode.removeChild(lagerMobileDetailHost); }";
echo " lagerMobileDetailHost = null; lagerMobileDetailOpen = false; document.body.classList.remove('sg-lager-mobile-detail-open');";
echo " };";
echo " const openLagerMobileDetail = (trigger) => {";
echo " restoreLagerMobileDetail(); const sourceDrawer = contentRoot.querySelector('[data-lager-drawer]'); lagerMobileDetailHost = sourceDrawer ? renderLagerMobileDetail(sourceDrawer, trigger) : null; const mobileDrawer = lagerMobileDetailHost?.querySelector('[data-lager-mobile-drawer]'); if (mobileDrawer) { const virtualLot = trigger.dataset.lagerDrawerVirtualLotNumber || ''; const virtualLotValue = mobileDrawer.querySelector('[data-lager-drawer-virtual-lot-value]'); if (virtualLotValue) { virtualLotValue.textContent = virtualLot ? 'Virtuelle Charge: ' + virtualLot : 'Keine virtuelle Fehlbestandscharge vorhanden.'; } }";
echo " if (lagerMobileDetailHost) { document.body.appendChild(lagerMobileDetailHost); document.body.classList.add('sg-lager-mobile-detail-open'); }";
echo " lagerMobileDetailOpen = true; window.history.pushState({ ...(window.history.state || {}), lagerView: 'detail' }, '', window.location.href);";
echo " };";
echo " const closeLagerMobileDetail = () => {";
echo " if (!lagerMobileDetailOpen) { return; }";
echo " if (window.history.state && window.history.state.lagerView === 'detail') { window.history.back(); return; }";
echo " restoreLagerMobileDetail();";
echo " };";
echo " const getDrawer = () => document.querySelector('[data-lager-mobile-drawer]') || contentRoot.querySelector('[data-lager-drawer]');";
echo " const populateLagerActiveLotForm = (drawer, trigger) => { if (!drawer || !trigger) { return; } const values = { '[data-lager-lot-number]': trigger.dataset.lagerDrawerLotNumber || '', '[data-lager-manufacturer]': trigger.dataset.lagerDrawerManufacturerPartyId || '', '[data-lager-manufacturer-lot]': trigger.dataset.lagerDrawerManufacturerLotNumber || '', '[data-lager-mfg-date]': trigger.dataset.lagerDrawerMfgDate || '', '[data-lager-expiry-date]': trigger.dataset.lagerDrawerExpiryDate || '', '[data-lager-purchase-date]': trigger.dataset.lagerDrawerPurchaseDate || '' }; Object.entries(values).forEach(([selector, value]) => { const field = drawer.querySelector(selector); if (field) { field.value = value; } }); drawer.dataset.lagerActiveLotId = trigger.dataset.lagerDrawerLotId || ''; };";
echo " document.addEventListener('click', (event) => { const trigger = event.target.closest('[data-lager-drawer-open][data-lager-drawer-type=\"Produkt\"]'); if (!trigger) { return; } const drawer = getDrawer(); const lotValue = drawer?.querySelector('[data-lager-drawer-virtual-lot-value]'); const qtyValue = drawer?.querySelector('[data-lager-drawer-virtual-lot-qty]'); if (lotValue) { lotValue.textContent = trigger.dataset.lagerDrawerVirtualLotNumber ? 'Virtuelle Charge: ' + trigger.dataset.lagerDrawerVirtualLotNumber : 'Keine virtuelle Fehlbestandscharge vorhanden.'; } if (qtyValue) { qtyValue.textContent = 'Bereits virtuell verkauft: ' + (trigger.dataset.lagerDrawerVirtualShortageQty || '0'); } });";
echo " const setDrawerState = (open) => {";
echo " const drawer = getDrawer(); const backdrop = contentRoot.querySelector('[data-lager-drawer-backdrop]');";
echo " if (!drawer || !backdrop) { return; }";
echo " 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 " };";
echo " const getLagerWarningTitle = (trigger) => trigger.dataset.lagerDrawerForecastStatus === 'depleted' ? 'Warnung: Bestand ist aufgebraucht' : (trigger.dataset.lagerDrawerForecastMhdWarning === 'true' ? 'Warnung: MHD wird vor Bestandsende erreicht' : (trigger.dataset.lagerDrawerForecastWarning === 'true' ? 'Warnung: Bestand wird innerhalb von drei Monaten aufgebraucht sein' : 'Keine Warnung'));";
echo " const closeDrawer = () => { setDrawerState(false); contentRoot.querySelectorAll('[data-lager-drawer-open]').forEach((trigger) => trigger.setAttribute('aria-expanded', 'false')); };";
echo " document.addEventListener('click', (event) => { const trigger = event.target.closest('[data-lager-drawer-open]'); if (!trigger || trigger.dataset.lagerDrawerType !== 'Produkt') { return; } const drawer = getDrawer(); const forecast = drawer?.querySelector('[data-lager-drawer-forecast-value]'); if (forecast) { forecast.textContent = trigger.dataset.lagerDrawerForecast || 'Keine Prognose'; } });";
echo " const getLagerForecastWarningText = (forecast, trigger, text) => { if (forecast?.status === 'depleted' || trigger?.dataset.lagerDrawerForecastStatus === 'depleted') { return 'Der Bestand ist aufgebraucht. Voraussichtlich leer: ' + text + '.'; } if (trigger?.dataset.lagerDrawerForecastMhdWarning === 'true') { const expiry = trigger.dataset.lagerDrawerExpiryDate ? new Date(trigger.dataset.lagerDrawerExpiryDate + 'T00:00:00').toLocaleDateString('de-CH') : 'dem MHD'; const forecastDate = forecast?.date ? new Date(forecast.date + 'T00:00:00').toLocaleDateString('de-CH') : text.replace(/\\s+\\(MHD:.*\\)$/, ''); return 'Das MHD wird am ' + expiry + ' erreicht, bevor der Bestand leer ist. Voraussichtlich leer: ' + forecastDate + '.'; } if (trigger?.dataset.lagerDrawerForecastWarning === 'true') { return 'Voraussichtlich leer: ' + text + '.'; } return 'Keine akute Warnung.'; };";
echo " document.addEventListener('click', (event) => { const trigger = event.target.closest('[data-lager-drawer-open]'); if (!trigger || trigger.dataset.lagerDrawerType !== 'Produkt') { return; } const drawer = getDrawer(); const warningValue = drawer?.querySelector('[data-lager-drawer-warning-value]'); if (warningValue) { warningValue.textContent = getLagerForecastWarningText(null, trigger, trigger.dataset.lagerDrawerForecast || 'Keine Prognose'); } });";
echo " const syncLagerForecast = (forecast, productId) => { const drawer = getDrawer(); const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]') || contentRoot.querySelector('[data-lager-row][data-lager-id=\"' + (productId || '') + '\"] [data-lager-drawer-open]'); if (!forecast || !drawer) { return; } const forecastDate = forecast.date ? new Date(forecast.date + 'T00:00:00') : null; const expiryDate = forecast.expiryDate ? new Date(forecast.expiryDate + 'T00:00:00') : null; const mhdWarning = Boolean(forecastDate && expiryDate && forecastDate > expiryDate); const nearExpiryWarning = Boolean(forecastDate && expiryDate && forecastDate >= new Date(new Date(expiryDate).setMonth(expiryDate.getMonth() - 3)) && forecastDate <= expiryDate); const depleted = forecast.status === 'depleted'; const forecastWarning = depleted || nearExpiryWarning; const date = forecastDate ? forecastDate.toLocaleDateString('de-CH') : 'Keine Prognose'; const text = depleted ? date + ' (BESTAND ENDE)' : (nearExpiryWarning || mhdWarning ? date + ' (MHD: ' + expiryDate.toLocaleDateString('de-CH') + ')' : date); const value = drawer.querySelector('[data-lager-drawer-forecast-value]'); if (value) { value.textContent = text; } const header = drawer.querySelector('[data-lager-drawer-warning-header]'); const title = drawer.querySelector('[data-lager-drawer-warning-title]'); const warningValue = drawer.querySelector('[data-lager-drawer-warning-value]'); if (trigger) { trigger.dataset.lagerDrawerForecast = text; trigger.dataset.lagerDrawerForecastStatus = forecast.status || 'no_demand'; trigger.dataset.lagerDrawerForecastWarning = forecastWarning ? 'true' : 'false'; trigger.dataset.lagerDrawerForecastMhdWarning = mhdWarning ? 'true' : 'false'; if (header) { header.classList.toggle('sg-card-segment--darkgreen', !forecastWarning && !mhdWarning); header.classList.toggle('sg-card-segment--signal-red', forecastWarning); header.classList.toggle('sg-card-segment--signal-yellow', mhdWarning); } if (title) { title.textContent = getLagerWarningTitle(trigger); } if (warningValue) { warningValue.textContent = forecastWarning || mhdWarning ? text : 'Keine akute Warnung.'; warningValue.classList.toggle('sg-strong', forecastWarning || mhdWarning); warningValue.classList.toggle('lager-forecast-warning', forecastWarning); warningValue.classList.toggle('lager-forecast-mhd-warning', mhdWarning); } const row = trigger.closest('[data-lager-row]'); const cells = row?.querySelectorAll('[role=\"cell\"]'); const forecastCell = cells?.[cells.length - 1]; if (forecastCell && cells.length === 3) { forecastCell.textContent = text; } } };";
echo " document.addEventListener('lager-save-complete', (event) => { const forecast = event.detail?.forecast; const productId = event.detail?.productId || ''; const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]') || contentRoot.querySelector('[data-lager-row][data-lager-id=\"' + productId + '\"] [data-lager-drawer-open]'); const drawer = getDrawer(); if (!forecast || !drawer) { return; } const date = forecast.date ? new Date(forecast.date + 'T00:00:00').toLocaleDateString('de-CH') : 'Keine Prognose'; const text = forecast.status === 'depleted' ? date + ' (BESTAND ENDE)' : date; const value = drawer.querySelector('[data-lager-drawer-forecast-value]'); if (value) { value.textContent = text; } if (trigger) { trigger.dataset.lagerDrawerForecast = text; const row = trigger.closest('[data-lager-row]'); const cells = row?.querySelectorAll('[role=\"cell\"]'); const forecastCell = cells?.[cells.length - 1]; if (forecastCell && cells.length === 3) { forecastCell.textContent = text; } } });";
echo " document.addEventListener('click', (event) => {";
echo " const trigger = event.target.closest('[data-lager-drawer-open]');";
echo " if (trigger) { event.preventDefault(); if (window.matchMedia('(max-width: 767px)').matches) { openLagerMobileDetail(trigger); return; } const drawer = getDrawer(); if (!drawer) { return; } const type = trigger.dataset.lagerDrawerType || 'Lager'; const name = trigger.dataset.lagerDrawerName || ''; const value = trigger.dataset.lagerDrawerValue || ''; drawer.querySelector('[data-lager-drawer-title]').textContent = type + ': ' + name; drawer.querySelector('[data-lager-drawer-name-value]').textContent = name; const valueRow = drawer.querySelector('[data-lager-drawer-value-row]'); if (valueRow) { valueRow.hidden = false; } drawer.querySelector('[data-lager-drawer-value-label]').textContent = type === 'Produkt' ? 'Anzahl im Lager:' : 'Artikelnummer:'; drawer.querySelector('[data-lager-drawer-value-value]').textContent = value; drawer.querySelectorAll('[data-lager-drawer-topic]').forEach((card) => { card.hidden = type !== 'Produkt'; }); if (type === 'Produkt') { const forecast = trigger.dataset.lagerDrawerForecast || 'Keine Prognose'; const warning = trigger.dataset.lagerDrawerForecastWarning === 'true' || trigger.dataset.lagerDrawerForecastMhdWarning === 'true'; const warningValue = drawer.querySelector('[data-lager-drawer-warning-value]'); if (warningValue) { warningValue.textContent = warning ? forecast : 'Keine akute Warnung.'; warningValue.classList.toggle('sg-strong', warning); warningValue.classList.toggle('lager-forecast-warning', trigger.dataset.lagerDrawerForecastWarning === 'true'); warningValue.classList.toggle('lager-forecast-mhd-warning', trigger.dataset.lagerDrawerForecastMhdWarning === 'true'); } drawer.querySelector('[data-lager-drawer-lot-number]').textContent = trigger.dataset.lagerDrawerLotNumber || 'Nicht hinterlegt'; drawer.querySelector('[data-lager-drawer-manufacturer-name]').textContent = trigger.dataset.lagerDrawerManufacturerName || 'Nicht hinterlegt'; drawer.querySelector('[data-lager-drawer-manufacturer-lot-number]').textContent = trigger.dataset.lagerDrawerManufacturerLotNumber || 'Nicht hinterlegt'; drawer.querySelector('[data-lager-drawer-mfg-date]').textContent = trigger.dataset.lagerDrawerMfgDate || 'Nicht hinterlegt'; drawer.querySelector('[data-lager-drawer-expiry-date]').textContent = trigger.dataset.lagerDrawerExpiryDate || 'Nicht hinterlegt'; drawer.querySelector('[data-lager-drawer-purchase-date]').textContent = trigger.dataset.lagerDrawerPurchaseDate || 'Nicht hinterlegt'; const virtualLot = trigger.dataset.lagerDrawerVirtualLotNumber || ''; drawer.querySelector('[data-lager-drawer-virtual-lot-value]').textContent = virtualLot ? 'Virtuelle Charge: ' + virtualLot : 'Keine virtuelle Fehlbestandscharge vorhanden.'; populateLagerActiveLotForm(drawer, trigger); const editForm = drawer.querySelector('[data-lager-edit-form]'); if (editForm) { editForm.hidden = true; } } setDrawerState(true); contentRoot.querySelectorAll('[data-lager-drawer-open]').forEach((button) => button.setAttribute('aria-expanded', String(button === trigger))); return; }";
echo " if (event.target.closest('[data-lager-drawer-close]') || event.target.closest('[data-lager-drawer-backdrop]')) { closeDrawer(); }";
echo " });";
echo " document.addEventListener('click', (event) => { const trigger = event.target.closest('[data-lager-drawer-open]'); if (!trigger || trigger.dataset.lagerDrawerType !== 'Produkt' || window.matchMedia('(max-width: 767px)').matches) { return; } const header = contentRoot.querySelector('[data-lager-drawer-warning-header]'); const title = contentRoot.querySelector('[data-lager-drawer-warning-title]'); if (!header || !title) { return; } const forecastWarning = trigger.dataset.lagerDrawerForecastWarning === 'true'; const mhdWarning = trigger.dataset.lagerDrawerForecastMhdWarning === 'true'; title.textContent = getLagerWarningTitle(trigger); header.classList.toggle('sg-card-segment--darkgreen', !forecastWarning && !mhdWarning); header.classList.toggle('sg-card-segment--signal-red', forecastWarning); header.classList.toggle('sg-card-segment--signal-yellow', mhdWarning); });";
echo " document.addEventListener('click', (event) => { const trigger = event.target.closest('[data-lager-drawer-open]'); if (!trigger || trigger.dataset.lagerDrawerType !== 'Produkt' || window.matchMedia('(max-width: 767px)').matches) { return; } setLagerEditMode(false); const drawer = getDrawer(); const currentQty = drawer?.querySelector('[data-lager-drawer-current-qty]'); const qtyIn = drawer?.querySelector('[data-lager-drawer-qty-in]'); const lotNumber = drawer?.querySelector('[data-lager-lot-number]'); if (currentQty) { currentQty.textContent = trigger.dataset.lagerDrawerCurrentQty || '0'; } if (qtyIn) { qtyIn.textContent = trigger.dataset.lagerDrawerQtyIn || '0'; } if (lotNumber) { lotNumber.value = trigger.dataset.lagerDrawerLotNumber || ''; } const productId = trigger.closest('[data-lager-row]')?.dataset.lagerId || ''; contentRoot.querySelectorAll('[data-lager-historical-product]').forEach((product) => { product.hidden = product.dataset.lagerHistoricalProduct !== productId; }); });";
echo " const postLagerChange = async (payload) => { const response = await fetch('/api/lager-charge.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...payload, csrfToken: contentRoot.querySelector('[data-lager-csrf-token]')?.dataset.lagerCsrfToken || '' }) }); const result = await response.json(); if (!response.ok || result.ok !== true) { throw new Error(result.error || 'Speichern fehlgeschlagen'); } syncLagerForecast(result.forecast, result.productId); const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]') || contentRoot.querySelector('[data-lager-row][data-lager-id=\"' + (result.productId || '') + '\"] [data-lager-drawer-open]'); const drawer = getDrawer(); const warningValue = drawer?.querySelector('[data-lager-drawer-warning-value]'); if (warningValue) { warningValue.textContent = getLagerForecastWarningText(result.forecast, trigger, trigger?.dataset.lagerDrawerForecast || 'Keine Prognose'); } return result; };";
echo " document.addEventListener('click', (event) => { const editButton = event.target.closest('[data-lager-edit-open]'); if (editButton) { const drawer = getDrawer(); const editForm = drawer?.querySelector('[data-lager-edit-form]'); if (editForm) { editForm.hidden = false; } return; } const closeButton = event.target.closest('[data-lager-edit-close]'); if (closeButton) { const drawer = getDrawer(); const editForm = drawer?.querySelector('[data-lager-edit-form]'); if (editForm) { editForm.hidden = true; } } });";
echo " const setLagerEditMode = (editing) => { const drawer = getDrawer(); const readView = drawer?.querySelector('[data-lager-read-view]'); const editForm = drawer?.querySelector('[data-lager-edit-form]'); if (readView) { readView.hidden = editing; } if (editForm) { editForm.hidden = !editing; } };";
echo " document.addEventListener('click', (event) => { if (event.target.closest('[data-lager-edit-open]')) { setLagerEditMode(true); } if (event.target.closest('[data-lager-edit-close]')) { clearLagerMovementInputs(); setLagerEditMode(false); } });";
echo " const syncLagerReadView = () => { const drawer = getDrawer(); if (!drawer) { return; } const manufacturer = drawer.querySelector('[data-lager-manufacturer]'); const values = { '[data-lager-drawer-manufacturer-name]': manufacturer?.selectedOptions?.[0]?.textContent || 'Nicht hinterlegt', '[data-lager-drawer-manufacturer-lot-number]': drawer.querySelector('[data-lager-manufacturer-lot]')?.value || 'Nicht hinterlegt', '[data-lager-drawer-mfg-date]': drawer.querySelector('[data-lager-mfg-date]')?.value || 'Nicht hinterlegt', '[data-lager-drawer-expiry-date]': drawer.querySelector('[data-lager-expiry-date]')?.value || 'Nicht hinterlegt', '[data-lager-drawer-purchase-date]': drawer.querySelector('[data-lager-purchase-date]')?.value || 'Nicht hinterlegt' }; Object.entries(values).forEach(([selector, value]) => { const target = drawer.querySelector(selector); if (target) { target.textContent = value; } }); const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]'); if (trigger) { trigger.dataset.lagerDrawerManufacturerName = values['[data-lager-drawer-manufacturer-name]']; trigger.dataset.lagerDrawerManufacturerLotNumber = values['[data-lager-drawer-manufacturer-lot-number]']; trigger.dataset.lagerDrawerMfgDate = values['[data-lager-drawer-mfg-date]']; trigger.dataset.lagerDrawerExpiryDate = values['[data-lager-drawer-expiry-date]']; trigger.dataset.lagerDrawerPurchaseDate = values['[data-lager-drawer-purchase-date]']; } };";
echo " const syncLagerBalanceView = (balance) => { const drawer = getDrawer(); if (!drawer || !balance) { return; } const currentQty = drawer.querySelector('[data-lager-drawer-current-qty]'); const qtyIn = drawer.querySelector('[data-lager-drawer-qty-in]'); if (currentQty) { currentQty.textContent = String(balance.qty_net ?? 0); } if (qtyIn) { qtyIn.textContent = String(balance.qty_in ?? 0); } const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]'); if (trigger) { trigger.dataset.lagerDrawerCurrentQty = String(balance.qty_net ?? 0); trigger.dataset.lagerDrawerQtyIn = String(balance.qty_in ?? 0); } };";
echo " const syncLagerActiveLotView = (lot) => { const drawer = getDrawer(); const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]'); if (!drawer || !lot || !trigger) { return; } const manufacturer = drawer.querySelector('[data-lager-manufacturer]'); if (manufacturer) { manufacturer.value = lot.manufacturer_party_id ?? ''; } const manufacturerName = manufacturer?.selectedOptions?.[0]?.textContent || 'Nicht hinterlegt'; const values = { '[data-lager-drawer-lot-number]': lot.lot_number || 'Nicht hinterlegt', '[data-lager-drawer-manufacturer-name]': manufacturerName, '[data-lager-drawer-manufacturer-lot-number]': lot.manufacturer_lot_number || 'Nicht hinterlegt', '[data-lager-drawer-mfg-date]': lot.mfg_date || 'Nicht hinterlegt', '[data-lager-drawer-expiry-date]': lot.expiry_date || 'Nicht hinterlegt', '[data-lager-drawer-purchase-date]': lot.purchase_date || 'Nicht hinterlegt' }; Object.entries(values).forEach(([selector, value]) => { const target = drawer.querySelector(selector); if (target) { target.textContent = value; } }); const formValues = { '[data-lager-lot-number]': lot.lot_number || '', '[data-lager-manufacturer-lot]': lot.manufacturer_lot_number || '', '[data-lager-mfg-date]': lot.mfg_date || '', '[data-lager-expiry-date]': lot.expiry_date || '', '[data-lager-purchase-date]': lot.purchase_date || '' }; Object.entries(formValues).forEach(([selector, value]) => { const input = drawer.querySelector(selector); if (input) { input.value = value; } }); trigger.dataset.lagerDrawerLotId = String(lot.id || lot.lot_id || ''); trigger.dataset.lagerDrawerLotNumber = lot.lot_number || ''; trigger.dataset.lagerDrawerManufacturerPartyId = String(lot.manufacturer_party_id ?? ''); trigger.dataset.lagerDrawerManufacturerName = manufacturerName; trigger.dataset.lagerDrawerManufacturerLotNumber = lot.manufacturer_lot_number || ''; trigger.dataset.lagerDrawerMfgDate = lot.mfg_date || ''; trigger.dataset.lagerDrawerExpiryDate = lot.expiry_date || ''; trigger.dataset.lagerDrawerPurchaseDate = lot.purchase_date || ''; drawer.dataset.lagerActiveLotId = String(lot.id || lot.lot_id || ''); };";
echo " const clearLagerMovementInputs = () => { const drawer = getDrawer(); if (!drawer) { return; } drawer.querySelectorAll('[data-lager-receipt-qty], [data-lager-adjustment-qty], [data-lager-out-qty]').forEach((input) => { input.value = ''; }); };";
echo " const syncLagerOpenLotCard = (lot, balance, exactCard = null) => { const drawer = getDrawer(); const lotId = lot?.id || lot?.lot_id || ''; const card = exactCard || drawer?.querySelector('[data-lager-open-card=\"' + lotId + '\"]'); if (!card) { return; } const manufacturerSelect = card.querySelector('[data-lager-open-manufacturer]') || drawer?.querySelector('[data-lager-new-manufacturer]'); const qualified = Boolean(lot?.lot_number && lot?.manufacturer_party_id && lot?.manufacturer_lot_number && lot?.mfg_date && lot?.expiry_date && lot?.purchase_date && Number(balance?.qty_net || 0) > 0); const selected = lot?.next_active === true; const values = { '[data-lager-open-card-lot-number]': lot?.lot_number || 'Nicht hinterlegt', '[data-lager-open-card-qty-net]': String(balance?.qty_net ?? 0), '[data-lager-open-card-qty-in]': String(balance?.qty_in ?? 0), '[data-lager-open-card-manufacturer]': manufacturerSelect?.selectedOptions?.[0]?.textContent || 'Nicht hinterlegt', '[data-lager-open-card-manufacturer-lot]': lot?.manufacturer_lot_number || 'Nicht hinterlegt', '[data-lager-open-card-mfg-date]': lot?.mfg_date || 'Nicht hinterlegt', '[data-lager-open-card-expiry-date]': lot?.expiry_date || 'Nicht hinterlegt', '[data-lager-open-card-purchase-date]': lot?.purchase_date || 'Nicht hinterlegt' }; Object.entries(values).forEach(([selector, value]) => { const target = card.querySelector(selector); if (target) { target.textContent = value; } }); card.classList.toggle('sg-lager-open-lot--next-active', selected); const selectedLabel = card.querySelector('[data-lager-open-card-next-active]'); if (selectedLabel) { selectedLabel.textContent = selected ? 'Als nächste aktive Charge ausgewählt' : 'Nicht ausgewählt'; } const selectButton = card.querySelector('[data-lager-open-select]'); if (selectButton) { selectButton.dataset.lagerOpenSelectable = String(qualified); selectButton.disabled = !qualified || selected; selectButton.setAttribute('aria-disabled', String(!qualified || selected)); } };";
echo " const appendLagerOpenLotCard = (lot, balance) => { if (lot?.status === 'current') { syncLagerActiveLotView(lot); syncLagerBalanceView(balance); return null; } const drawer = getDrawer(); const product = drawer?.querySelector('[data-lager-open-product=\"' + (drawer?.dataset.lagerProductId || '') + '\"]'); const list = product?.querySelector('[data-lager-open-lot-list]'); const templateProduct = drawer?.querySelector('[data-lager-open-lot-template]')?.content.querySelector('[data-lager-open-product]'); const card = templateProduct?.querySelector('[data-lager-open-card-wrapper]')?.cloneNode(true); const lotId = String(lot?.id || lot?.lot_id || ''); if (!list || !card || !lotId) { return null; } card.dataset.lagerOpenCardWrapper = lotId; const readView = card.querySelector('[data-lager-open-card]'); if (readView) { readView.dataset.lagerOpenCard = lotId; } const editTitle = card.querySelector('[id^=\"lager-open-charge-data-\"]'); if (editTitle) { editTitle.id = 'lager-open-charge-data-' + lotId; editTitle.closest('section')?.setAttribute('aria-labelledby', editTitle.id); } const confirmationTitle = card.querySelector('[id^=\"lager-open-confirmation-title-\"]'); if (confirmationTitle) { confirmationTitle.id = 'lager-open-confirmation-title-' + lotId; confirmationTitle.closest('[data-lager-open-confirmation-card]')?.setAttribute('aria-labelledby', confirmationTitle.id); } const deleteTitle = card.querySelector('[id^=\"lager-open-delete-title-\"]'); if (deleteTitle) { deleteTitle.id = 'lager-open-delete-title-' + lotId; deleteTitle.closest('[data-lager-open-delete-confirmation]')?.setAttribute('aria-labelledby', deleteTitle.id); } const setValue = (selector, value) => { const input = card.querySelector(selector); if (input) { input.value = value ?? ''; } }; setValue('[data-lager-open-lot-number]', lot.lot_number); setValue('[data-lager-open-manufacturer]', lot.manufacturer_party_id); setValue('[data-lager-open-manufacturer-lot]', lot.manufacturer_lot_number); setValue('[data-lager-open-mfg-date]', lot.mfg_date); setValue('[data-lager-open-expiry-date]', lot.expiry_date); setValue('[data-lager-open-purchase-date]', lot.purchase_date); setValue('[data-lager-open-target-qty]', balance?.qty_net ?? 0); const qualified = Boolean(lot.lot_number && lot.manufacturer_party_id && lot.manufacturer_lot_number && lot.mfg_date && lot.expiry_date && lot.purchase_date && Number(balance?.qty_net || 0) > 0); const selected = lot.next_active === true; card.classList.toggle('sg-lager-open-lot--next-active', selected); const selectedLabel = card.querySelector('[data-lager-open-card-next-active]'); if (selectedLabel) { selectedLabel.textContent = selected ? 'Als nächste aktive Charge ausgewählt' : 'Nicht ausgewählt'; } const selectButton = card.querySelector('[data-lager-open-select]'); if (selectButton) { selectButton.dataset.lagerOpenSelectable = String(qualified); selectButton.disabled = !qualified || selected; selectButton.setAttribute('aria-disabled', String(!qualified || selected)); } list.append(card); syncLagerOpenLotCard(lot, balance, card); return card; };";
echo " const resetLagerNewLotConfirmation = () => { const drawer = getDrawer(); const confirmation = drawer?.querySelector('[data-lager-new-confirmation-card]'); const list = drawer?.querySelector('[data-lager-new-confirmation-empty-fields]'); const form = drawer?.querySelector('[data-lager-new-lot-form-element]'); if (confirmation) { confirmation.hidden = true; } if (list) { list.replaceChildren(); } if (form) { form.dataset.confirmationAccepted = 'false'; } if (drawer) { drawer.dataset.dialogOpen = 'false'; } };";
echo " const resetLagerOpenLotConfirmations = () => { const drawer = getDrawer(); drawer?.querySelectorAll('[data-lager-open-card-wrapper]').forEach((card) => { const confirmation = card.querySelector('[data-lager-open-confirmation-card]'); const list = card.querySelector('[data-lager-open-confirmation-empty-fields]'); const form = card.querySelector('[data-lager-open-edit-form-element]'); if (confirmation) { confirmation.hidden = true; } if (list) { list.replaceChildren(); } if (form) { form.dataset.confirmationAccepted = 'false'; } card.dataset.dialogOpen = 'false'; }); };";
echo " document.addEventListener('click', (event) => { const trigger = event.target.closest('[data-lager-drawer-open]'); if (!trigger || trigger.dataset.lagerDrawerType !== 'Produkt' || window.matchMedia('(max-width: 767px)').matches) { return; } const drawer = getDrawer(); const productId = trigger.closest('[data-lager-row]')?.dataset.lagerId || ''; const openProduct = drawer?.querySelector('[data-lager-open-product=\"' + productId + '\"]'); drawer?.querySelectorAll('[data-lager-open-product]').forEach((product) => { product.hidden = product !== openProduct; product.querySelectorAll('[data-lager-open-edit-form]').forEach((form) => { form.hidden = true; }); product.querySelectorAll('[data-lager-open-card]').forEach((readView) => { readView.hidden = false; }); }); resetLagerNewLotConfirmation(); resetLagerOpenLotConfirmations(); const newForm = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-form\"]'); const newAction = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-action\"]'); if (newForm) { newForm.hidden = true; } if (newAction) { newAction.hidden = false; } const notificationWrapper = drawer?.querySelector('[data-lager-new-lot-notification-wrapper]'); const notification = drawer?.querySelector('[data-lager-new-lot-notification]'); const shortageQty = trigger.dataset.lagerDrawerVirtualShortageQty || '0'; const hasShortage = shortageQty !== '0' && shortageQty !== '0,00'; if (notificationWrapper) { notificationWrapper.hidden = !hasShortage; } if (notification && hasShortage) { notification.textContent = 'Für dieses Produkt bestehen virtuelle Fehlbestände in Höhe von ' + shortageQty + '. Die Eingangsmenge wird nach Aktivierung der Charge zur Nachallokation verwendet.'; } const setValue = (selector, value) => { const field = drawer?.querySelector(selector); if (field) { field.value = value || ''; } }; setValue('[data-lager-new-lot-number]', ''); setValue('[data-lager-new-manufacturer]', ''); setValue('[data-lager-new-manufacturer-lot]', ''); setValue('[data-lager-new-mfg-date]', ''); setValue('[data-lager-new-expiry-date]', ''); setValue('[data-lager-new-purchase-date]', ''); setValue('[data-lager-new-receipt-qty]', ''); if (drawer) { drawer.dataset.lagerOpenLotId = ''; drawer.dataset.lagerProductId = productId; } });";
echo " document.addEventListener('click', (event) => { const openButton = event.target.closest('[data-lager-new-lot-open]'); if (openButton) { const drawer = getDrawer(); const form = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-form\"]'); const action = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-action\"]'); resetLagerNewLotConfirmation(); if (form) { form.hidden = false; } if (action) { action.hidden = true; } return; } if (event.target.closest('[data-lager-new-lot-close]')) { const drawer = getDrawer(); const form = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-form\"]'); const action = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-action\"]'); resetLagerNewLotConfirmation(); if (form) { form.hidden = true; } if (action) { action.hidden = false; } } });";
echo " document.addEventListener('click', (event) => { const editButton = event.target.closest('[data-lager-open-edit-open]'); if (editButton) { const card = editButton.closest('[data-lager-open-card-wrapper]'); const form = card?.querySelector('[data-lager-open-edit-form]'); const readView = card?.querySelector('[data-lager-open-card]'); resetLagerOpenLotConfirmations(); if (form) { form.hidden = false; } if (readView) { readView.hidden = true; } return; } const closeButton = event.target.closest('[data-lager-open-edit-close]'); if (closeButton) { const card = closeButton.closest('[data-lager-open-card-wrapper]'); const form = card?.querySelector('[data-lager-open-edit-form]'); const readView = card?.querySelector('[data-lager-open-card]'); resetLagerOpenLotConfirmations(); if (form) { form.hidden = true; } if (readView) { readView.hidden = false; } } });";
echo " document.addEventListener('input', (event) => { const input = event.target.closest('[data-lager-open-delete-confirmation-input]'); if (!input) { return; } const card = input.closest('[data-lager-open-card-wrapper]'); const submit = card?.querySelector('[data-lager-open-delete-confirmation-submit]'); const valid = input.value === 'DELETE'; if (submit) { submit.disabled = !valid; submit.setAttribute('aria-disabled', String(!valid)); submit.classList.toggle('sg-button--process-inactive', !valid); } });";
echo " document.addEventListener('click', async (event) => { const selectButton = event.target.closest('[data-lager-open-select]'); if (!selectButton) { return; } const card = selectButton.closest('[data-lager-open-card-wrapper]'); const product = card?.closest('[data-lager-open-product]'); const drawer = getDrawer(); const productId = product?.dataset.lagerOpenProduct || drawer?.dataset.lagerProductId || ''; const lotId = card?.dataset.lagerOpenCardWrapper || ''; if (!productId || !lotId) { return; } selectButton.disabled = true; try { const result = await postLagerChange({ action: 'select_open_lot', productId, lotId }); if (result.currentLot?.id && String(result.currentLot.id) === String(lotId)) { card?.remove(); syncLagerActiveLotView(result.currentLot); syncLagerBalanceView({ qty_net: result.currentLot.qty_net, qty_in: result.currentLot.qty_in, qty_out: result.currentLot.qty_out }); return; } product.querySelectorAll('[data-lager-open-card-wrapper]').forEach((candidate) => { const selected = candidate === card; candidate.classList.toggle('sg-lager-open-lot--next-active', selected); const label = candidate.querySelector('[data-lager-open-card-next-active]'); if (label) { label.textContent = selected ? 'Als nächste aktive Charge ausgewählt' : 'Nicht ausgewählt'; } const button = candidate.querySelector('[data-lager-open-select]'); if (button) { button.disabled = selected || button.dataset.lagerOpenSelectable !== 'true'; button.setAttribute('aria-disabled', String(button.disabled)); } }); } catch (error) { const status = card?.querySelector('[data-lager-open-edit-status]'); if (status) { status.textContent = error instanceof Error ? error.message : 'Auswahl fehlgeschlagen'; } selectButton.disabled = false; selectButton.setAttribute('aria-disabled', 'false'); } });";
echo " document.addEventListener('click', async (event) => { const deleteButton = event.target.closest('[data-lager-open-delete]'); if (deleteButton) { const card = deleteButton.closest('[data-lager-open-card-wrapper]'); const confirmation = card?.querySelector('[data-lager-open-delete-confirmation]'); const input = card?.querySelector('[data-lager-open-delete-confirmation-input]'); const submit = card?.querySelector('[data-lager-open-delete-confirmation-submit]'); if (input) { input.value = ''; } if (submit) { submit.disabled = true; submit.setAttribute('aria-disabled', 'true'); submit.classList.add('sg-button--process-inactive'); } if (confirmation) { confirmation.hidden = false; } if (card) { card.dataset.dialogOpen = 'true'; } return; } const cancel = event.target.closest('[data-lager-open-delete-confirmation-cancel]'); if (cancel) { const card = cancel.closest('[data-lager-open-card-wrapper]'); const confirmation = card?.querySelector('[data-lager-open-delete-confirmation]'); if (confirmation) { confirmation.hidden = true; } if (card) { card.dataset.dialogOpen = 'false'; } return; } const submit = event.target.closest('[data-lager-open-delete-confirmation-submit]'); if (!submit || submit.disabled) { return; } const card = submit.closest('[data-lager-open-card-wrapper]'); const confirmation = card?.querySelector('[data-lager-open-delete-confirmation]'); const status = card?.querySelector('[data-lager-open-delete-status]'); if (status) { status.textContent = 'Löschen läuft …'; } submit.disabled = true; try { const result = await postLagerChange({ action: 'delete_open_lot', lotId: card?.dataset.lagerOpenCardWrapper || '' }); if (result.currentLot) { syncLagerActiveLotView(result.currentLot); syncLagerBalanceView({ qty_net: result.currentLot.qty_net, qty_in: result.currentLot.qty_in, qty_out: result.currentLot.qty_out }); } const productId = card?.closest('[data-lager-open-product]')?.dataset.lagerOpenProduct || ''; contentRoot.querySelectorAll('[data-lager-drawer-open]').forEach((trigger) => { if (trigger.closest('[data-lager-row]')?.dataset.lagerId === productId) { trigger.dataset.lagerOpenLotId = ''; } }); const drawer = getDrawer(); if (drawer) { drawer.dataset.lagerOpenLotId = ''; } card?.remove(); } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Löschen fehlgeschlagen'; } submit.disabled = false; } });";
echo " document.addEventListener('click', (event) => { const cancel = event.target.closest('[data-lager-open-confirmation-cancel]'); if (cancel) { const card = cancel.closest('[data-lager-open-card-wrapper]'); const confirmation = card?.querySelector('[data-lager-open-confirmation-card]'); if (confirmation) { confirmation.hidden = true; } if (card) { card.dataset.dialogOpen = 'false'; } const form = card?.querySelector('[data-lager-open-edit-form-element]'); if (form) { form.dataset.confirmationAccepted = 'false'; } return; } const confirm = event.target.closest('[data-lager-open-confirmation-submit]'); if (confirm) { const card = confirm.closest('[data-lager-open-card-wrapper]'); const confirmation = card?.querySelector('[data-lager-open-confirmation-card]'); const form = card?.querySelector('[data-lager-open-edit-form-element]'); if (confirmation) { confirmation.hidden = true; } if (card) { card.dataset.dialogOpen = 'false'; } if (form) { form.dataset.confirmationAccepted = 'true'; form.requestSubmit(); } } });";
echo " document.addEventListener('submit', async (event) => { const openLotForm = event.target.closest('[data-lager-open-edit-form-element]'); if (!openLotForm) { return; } event.preventDefault(); const drawer = getDrawer(); const card = openLotForm.closest('[data-lager-open-card-wrapper]'); const editSection = card?.querySelector('[data-lager-open-edit-form]'); const fields = [{ label: 'Naurua-Chargennummer', input: openLotForm.querySelector('[data-lager-open-lot-number]') }, { label: 'Hersteller', input: openLotForm.querySelector('[data-lager-open-manufacturer]') }, { label: 'Hersteller-Chargennummer', input: openLotForm.querySelector('[data-lager-open-manufacturer-lot]') }, { label: 'Herstellungsdatum', input: openLotForm.querySelector('[data-lager-open-mfg-date]') }, { label: 'MHD', input: openLotForm.querySelector('[data-lager-open-expiry-date]') }, { label: 'Einkaufsdatum', input: openLotForm.querySelector('[data-lager-open-purchase-date]') }, { label: 'Menge', input: openLotForm.querySelector('[data-lager-open-target-qty]') }]; const emptyFields = fields.filter((field) => (field.input?.value || '').trim() === ''); if (emptyFields.length > 0 && openLotForm.dataset.confirmationAccepted !== 'true') { const confirmation = card?.querySelector('[data-lager-open-confirmation-card]'); const emptyFieldsList = card?.querySelector('[data-lager-open-confirmation-empty-fields]'); if (emptyFieldsList) { emptyFieldsList.replaceChildren(...emptyFields.map((field) => { const item = document.createElement('div'); item.className = 'sg-body'; item.textContent = field.label; return item; })); } if (confirmation) { confirmation.hidden = false; } if (card) { card.dataset.dialogOpen = 'true'; } return; } openLotForm.dataset.confirmationAccepted = 'false'; const status = openLotForm.querySelector('[data-lager-open-edit-status]'); if (status) { status.textContent = 'Speichern läuft …'; } try { const targetQtyInput = openLotForm.querySelector('[data-lager-open-target-qty]'); const result = await postLagerChange({ action: 'prepare_lot', productId: drawer?.dataset.lagerProductId || '', lotId: card?.dataset.lagerOpenCardWrapper || '', lotNumber: openLotForm.querySelector('[data-lager-open-lot-number]')?.value || '', manufacturerPartyId: openLotForm.querySelector('[data-lager-open-manufacturer]')?.value || null, manufacturerLotNumber: openLotForm.querySelector('[data-lager-open-manufacturer-lot]')?.value || '', mfgDate: openLotForm.querySelector('[data-lager-open-mfg-date]')?.value || '', purchaseDate: openLotForm.querySelector('[data-lager-open-purchase-date]')?.value || '', expiryDate: openLotForm.querySelector('[data-lager-open-expiry-date]')?.value || '', targetQty: targetQtyInput?.value.trim() === '' ? '0' : targetQtyInput?.value || '' }); if (result.lot?.status === 'current') { card?.remove(); syncLagerActiveLotView(result.lot); syncLagerBalanceView(result.balance); } else { syncLagerOpenLotCard(result.lot, result.balance); } if (editSection) { editSection.hidden = true; } card?.querySelector('[data-lager-open-card]')?.removeAttribute('hidden'); if (status) { status.textContent = ''; } } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Speichern fehlgeschlagen'; } } });";
echo " document.addEventListener('click', (event) => { const cancel = event.target.closest('[data-lager-new-confirmation-cancel]'); if (cancel) { const drawer = getDrawer(); const confirmation = drawer?.querySelector('[data-lager-new-confirmation-card]'); if (confirmation) { confirmation.hidden = true; } if (drawer) { drawer.dataset.dialogOpen = 'false'; } const form = drawer?.querySelector('[data-lager-new-lot-form-element]'); if (form) { form.dataset.confirmationAccepted = 'false'; } return; } const confirm = event.target.closest('[data-lager-new-confirmation-submit]'); if (confirm) { const drawer = getDrawer(); const confirmation = drawer?.querySelector('[data-lager-new-confirmation-card]'); const form = drawer?.querySelector('[data-lager-new-lot-form-element]'); if (confirmation) { confirmation.hidden = true; } if (drawer) { drawer.dataset.dialogOpen = 'false'; } if (form) { form.dataset.confirmationAccepted = 'true'; form.requestSubmit(); } } });";
echo " document.addEventListener('submit', async (event) => { const newLotForm = event.target.closest('[data-lager-new-lot-form-element]'); if (!newLotForm) { return; } event.preventDefault(); const drawer = getDrawer(); const fields = [{ label: 'Naurua-Chargennummer', input: drawer?.querySelector('[data-lager-new-lot-number]') }, { label: 'Hersteller', input: drawer?.querySelector('[data-lager-new-manufacturer]') }, { label: 'Hersteller-Chargennummer', input: drawer?.querySelector('[data-lager-new-manufacturer-lot]') }, { label: 'Herstellungsdatum', input: drawer?.querySelector('[data-lager-new-mfg-date]') }, { label: 'MHD', input: drawer?.querySelector('[data-lager-new-expiry-date]') }, { label: 'Einkaufsdatum', input: drawer?.querySelector('[data-lager-new-purchase-date]') }, { label: 'Eingangsmenge', input: drawer?.querySelector('[data-lager-new-receipt-qty]') }]; const emptyFields = fields.filter((field) => (field.input?.value || '').trim() === ''); if (emptyFields.length > 0 && newLotForm.dataset.confirmationAccepted !== 'true') { const confirmation = drawer?.querySelector('[data-lager-new-confirmation-card]'); const emptyFieldsList = drawer?.querySelector('[data-lager-new-confirmation-empty-fields]'); if (emptyFieldsList) { emptyFieldsList.replaceChildren(...emptyFields.map((field) => { const item = document.createElement('div'); item.className = 'sg-body'; item.textContent = field.label; return item; })); } if (confirmation) { confirmation.hidden = false; } if (drawer) { drawer.dataset.dialogOpen = 'true'; } return; } newLotForm.dataset.confirmationAccepted = 'false'; const status = drawer?.querySelector('[data-lager-new-lot-status]'); if (status) { status.textContent = 'Speichern läuft …'; } try { const result = await postLagerChange({ action: 'prepare_lot', productId: drawer?.dataset.lagerProductId || '', lotId: '', lotNumber: drawer?.querySelector('[data-lager-new-lot-number]')?.value || '', manufacturerPartyId: drawer?.querySelector('[data-lager-new-manufacturer]')?.value || null, manufacturerLotNumber: drawer?.querySelector('[data-lager-new-manufacturer-lot]')?.value || '', mfgDate: drawer?.querySelector('[data-lager-new-mfg-date]')?.value || '', expiryDate: drawer?.querySelector('[data-lager-new-expiry-date]')?.value || '', purchaseDate: drawer?.querySelector('[data-lager-new-purchase-date]')?.value || '', receiptQty: drawer?.querySelector('[data-lager-new-receipt-qty]')?.value || '' }); appendLagerOpenLotCard(result.lot, result.balance); resetLagerNewLotConfirmation(); const form = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-form\"]'); const action = drawer?.querySelector('[data-lager-drawer-topic=\"new-lot-action\"]'); if (form) { form.hidden = true; } if (action) { action.hidden = false; } if (status) { status.textContent = ''; } } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Speichern fehlgeschlagen'; } } });";
echo " document.addEventListener('submit', async (event) => { const editForm = event.target.closest('[data-lager-edit-form-element]'); if (!editForm) { return; } event.preventDefault(); const drawer = getDrawer(); const status = drawer?.querySelector('[data-lager-edit-status]'); if (status) { status.textContent = 'Speichern läuft …'; } try { const result = await postLagerChange({ action: 'update_lot', lotId: drawer?.dataset.lagerActiveLotId || '', manufacturerPartyId: drawer?.querySelector('[data-lager-manufacturer]')?.value || null, manufacturerLotNumber: drawer?.querySelector('[data-lager-manufacturer-lot]')?.value || '', mfgDate: drawer?.querySelector('[data-lager-mfg-date]')?.value || '', expiryDate: drawer?.querySelector('[data-lager-expiry-date]')?.value || '', purchaseDate: drawer?.querySelector('[data-lager-purchase-date]')?.value || '', receiptQty: drawer?.querySelector('[data-lager-receipt-qty]')?.value || '', adjustmentQty: drawer?.querySelector('[data-lager-adjustment-qty]')?.value || '', outQty: drawer?.querySelector('[data-lager-out-qty]')?.value || '' }); syncLagerActiveLotView(result.lot); syncLagerBalanceView(result.balance); clearLagerMovementInputs(); setLagerEditMode(false); if (status) { status.textContent = ''; } } catch (error) { if (status) { status.textContent = error instanceof Error ? error.message : 'Speichern fehlgeschlagen'; } } });";
echo " document.addEventListener('click', (event) => { if (event.target.closest('[data-lager-detail-back]')) { event.preventDefault(); closeLagerMobileDetail(); } });";
echo " document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { closeDrawer(); closeLagerMobileDetail(); } });";
echo " window.addEventListener('popstate', (event) => { if (lagerMobileDetailOpen && (!event.state || event.state.lagerView !== 'detail')) { restoreLagerMobileDetail(); } });";
echo " const syncLagerDisplayedTotal = () => { const drawer = getDrawer(); const trigger = contentRoot.querySelector('[data-lager-drawer-open][aria-expanded=\"true\"]'); if (!drawer || !trigger || trigger.dataset.lagerDrawerType !== 'Produkt') { return; } const active = Number((drawer.querySelector('[data-lager-drawer-current-qty]')?.textContent || '0').replace(',', '.')) || 0; const productId = trigger.closest('[data-lager-row]')?.dataset.lagerId || ''; const openProduct = drawer.querySelector('[data-lager-open-product=\"' + productId + '\"]'); const open = Array.from(openProduct?.querySelectorAll('[data-lager-open-card-qty-net]') || []).reduce((sum, node) => sum + (Number((node.textContent || '0').replace(',', '.')) || 0), 0); const total = active + open; const formatted = Number.isInteger(total) ? String(total) : total.toFixed(2).replace(/\.?0+$/, ''); const value = drawer.querySelector('[data-lager-drawer-value-value]'); if (value && value.textContent !== formatted) { value.textContent = formatted; } trigger.dataset.lagerDrawerValue = formatted; };";
echo " const lagerTotalObserver = new MutationObserver(syncLagerDisplayedTotal);";
echo " lagerTotalObserver.observe(contentRoot, { childList: true, subtree: true, characterData: true });";
echo "}";
echo "let marketsBindingsInstalled = false;";
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;";
echo " const getSearchValue = () => (getSearchInput()?.value || '').trim();";
echo " const getSortColumn = () => getTable()?.dataset.marketsSortColumn || 'earliest_date';";
echo " const getSortDirection = () => (getTable()?.dataset.marketsSortDirection || 'ASC').toUpperCase();";
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) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[character] || character));";
echo " const marketDisplay = (value) => value === null || value === undefined || value === '' ? 'Nicht hinterlegt' : marketEscape(value).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 marketCostItems = (items) => Array.isArray(items) && items.length ? '<ul class=\"sg-bullet-list\" data-component-part=\"bullet-list\">' + items.map((item) => '<li>' + marketDisplay([item.cost_type, item.description].filter(Boolean).join(' – ')) + ': ' + marketMoney(item.gross_amount) + ' brutto, ' + marketMoney(item.net_amount) + ' netto, ' + marketMoney(item.tax_amount) + ' Steuer</li>').join('') + '</ul>' : 'Keine Kostenpositionen';";
echo " const marketCostItemsText = (items) => Array.isArray(items) && items.length ? items.map((item) => [item.cost_type, item.description].filter(Boolean).join(' – ') + ': ' + marketMoney(item.gross_amount) + ' brutto, ' + marketMoney(item.net_amount) + ' netto, ' + marketMoney(item.tax_amount) + ' Steuer').join('\\n') : 'Keine Kostenpositionen';";
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--gray');";
echo " const marketRow = (label, value) => '<div class=\"market-detail-row\"><dt>' + marketEscape(label) + '</dt><dd>' + marketDisplay(value) + '</dd></div>';";
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>' + marketEditForm('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 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>' + marketCostItems(occurrence.cost_items) + '</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' : marketDate(day.date); const selected = day.__draft || (!hasDraftDay && index === 0); const values = day.__draft ? Object.assign({}, day, { __occurrence_id: occurrence.id }) : day; return '<div class=\"sg-content-block-card-group__panel\" id=\"market-day-panel-' + day.id + '\" role=\"tabpanel\" aria-labelledby=\"market-day-tab-' + day.id + '\"' + (selected ? '' : ' hidden') + '><div class=\"sg-content-cards-group\" data-pattern=\"content-cards-group\" aria-label=\"Markttag ' + label + '\">' + marketCard(label, '<dl class=\"market-detail-grid\">' + marketRow('Datum', marketDate(day.date), true) + marketRow('Beobachtungen', day.observations, 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('Umsatz', marketMoney(day.gross_revenue), true) + marketRow('Verkaufte Artikel', day.sold_item_count, true) + '</dl>', 'sg-card-segment--darkgreen', marketEditButton('day', day.id), 'day', day.id, values) + '</div></div>'; }).join(''); const salesCard = marketCard('Umsatz und Artikel', '<dl class=\"market-detail-grid\">' + marketRow('Gesamterlös', marketMoney(salesSummary.gross_revenue)) + marketRow('Gesamtzahl verkaufter Artikel', salesSummary.sold_item_count) + marketRow('Durchschnittlicher Erlös pro Tag', marketMoney(salesSummary.average_revenue_per_day)) + marketRow('Durchschnittlicher Erlös pro verkauftem Artikel', marketMoney(salesSummary.average_revenue_per_item)) + '</dl>', 'sg-card-segment--darkgreen'); return marketStatusCard(occurrence.id, occurrence) + marketCard('Markt und Marktdurchführung', '<dl class=\"market-detail-grid\">' + overview + '</dl>', 'sg-card-segment--darkgreen', marketEditButton('overview', occurrence.id), 'overview', occurrence.id, Object.assign({}, occurrence, { market_name: market.name, category_name: market.category_name || market.category_code, website_url: market.website_url, contact_email: contactPerson.email, contact_phone: contactPerson.phone })) + marketCard('Teilnahme und Zahlung', '<dl class=\"market-detail-grid\">' + participation + '</dl>', 'sg-card-segment--darkgreen', marketEditButton('participation', occurrence.id), 'participation', occurrence.id, occurrence) + marketCard('Infrastruktur', '<dl class=\"market-detail-grid\">' + infrastructure + '</dl>', 'sg-card-segment--darkgreen', marketEditButton('infrastructure', occurrence.id), 'infrastructure', occurrence.id, occurrence) + marketCard('Anreise und Logistik', '<dl class=\"market-detail-grid\">' + travel + '</dl>', 'sg-card-segment--darkgreen', marketEditButton('travel', occurrence.id), 'travel', occurrence.id, occurrence) + salesCard + marketCard('Weitere Angaben', '<dl class=\"market-detail-grid\">' + notes + '</dl>', 'sg-card-segment--darkgreen', marketEditButton('notes', occurrence.id), 'notes', occurrence.id, occurrence) + (days.length ? '<section class=\"sg-content-block-card-group market-days-group\" data-pattern=\"card-gruppe-mit-tastennavigation\"><div class=\"sg-tab-button-group sg-content-block-card-group__tabs\" role=\"tablist\" aria-label=\"Markttage\">' + dayTabs + addDayTab + '</div><div class=\"sg-content-block-card-group__panels\">' + dayPanels + '</div></section>' : '<p class=\"sg-body\">Keine Markttage vorhanden.</p>'); };";
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: marketCostItemsText(values.cost_items), 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 Set(['date']) }; const fields = fieldSets[scope] || []; const formFields = fields.map(([key, label, type, options]) => '<div class=\"sg-form-sections-card__field-group\">' + marketEditField(key, label, type, formValues[key], options, requiredFields[scope]?.has(key) === true) + '</div>').join(''); const draftAttributes = values.__draft ? ' data-market-edit-draft=\"true\" data-market-occurrence-id=\"' + marketEscape(values.__occurrence_id || '') + '\"' : ''; return '<div class=\"sg-form-sections-card-wrapper\" data-pattern=\"form-sections\"><form class=\"sg-form-sections-card market-edit-form\" data-market-edit-form=\"true\" data-market-edit-scope=\"' + scope + '\" data-market-edit-id=\"' + id + '\"' + draftAttributes + '><div class=\"sg-form-sections-card__body\" data-pattern-part=\"form-body\"><div class=\"sg-form-sections-card__section\">' + formFields + '</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-edit-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-edit-status aria-live=\"polite\"></p></form></div>'; };";
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.innerHTML; 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(); };";
echo " let marketMobileDetailHost = null; let marketOpeningMobileDetail = false; let marketDetailOpen = false;";
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) => { 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 tabGroup = tab.closest('[data-pattern=\"card-gruppe-mit-tastennavigation\"]'); tabGroup?.querySelectorAll('[data-market-day-tab]').forEach((candidate) => candidate.setAttribute('aria-selected', String(candidate === tab))); tabGroup?.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.marketEditId)); 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 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 input = card?.querySelector('[data-shared-editable-card-delete-input]'); const status = card?.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 " if (!marketsBindingsInstalled) {";
echo " marketsBindingsInstalled = true;";
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-draft=\"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()); 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) => { 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 " 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 = ''; } };";
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]'); const statusCell = row?.querySelectorAll('[role=\"cell\"]')[4]; if (statusCell) { statusCell.textContent = 'Archiviert'; } 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-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 website = form.querySelector('[data-market-definition-website]'); if (name) { name.value = trigger.dataset.marketDefinitionName || ''; } if (category) { category.value = trigger.dataset.marketDefinitionCategoryId || ''; } if (website) { website.value = trigger.dataset.marketDefinitionWebsite === 'Nicht hinterlegt' ? '' : (trigger.dataset.marketDefinitionWebsite || ''); } 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 website = form.querySelector('[data-market-definition-website]'); if (name) { name.value = activeMarket.trigger.dataset.marketDefinitionName || ''; } if (category) { category.value = activeMarket.trigger.dataset.marketDefinitionCategoryId || ''; } if (website) { website.value = activeMarket.trigger.dataset.marketDefinitionWebsite === 'Nicht hinterlegt' ? '' : (activeMarket.trigger.dataset.marketDefinitionWebsite || ''); } };";
echo " editButton.addEventListener('click', (event) => { event.preventDefault(); openEditCard(); });";
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 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.marketDefinitionWebsite = website; trigger.textContent = market.name || ''; cells.appendChild(trigger); row.appendChild(cells); [category, 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 (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 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.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; }";
echo " const getTable = () => contentRoot.querySelector('[data-bestellungen-large-table]');";
echo " let table = getTable();";
echo " if (!table) { return; }";
echo " let searchTimer = null;";
echo " const getSearchInput = () => {";
echo " const currentTable = getTable();";
echo " return currentTable ? currentTable.querySelector('[data-large-table-search]') : null;";
echo " };";
echo " const getSearchWrap = () => {";
echo " const input = getSearchInput();";
echo " return input ? input.closest('[data-component=\\'single-line-input\\']') : null;";
echo " };";
echo " const getSearchValue = () => {";
echo " const input = getSearchInput();";
echo " return input ? input.value.trim() : '';";
echo " };";
echo " const getSortColumn = () => {";
echo " const currentTable = getTable();";
echo " return currentTable ? (currentTable.dataset.bestellungenSortColumn || 'order_date') : 'order_date';";
echo " };";
echo " const getSortDirection = () => {";
echo " const currentTable = getTable();";
echo " return (currentTable ? (currentTable.dataset.bestellungenSortDirection || 'DESC') : 'DESC').toUpperCase();";
echo " };";
echo " const getCurrentLimit = () => {";
echo " const currentTable = getTable();";
echo " return parseInt((currentTable ? currentTable.dataset.bestellungenLimit : null) || (currentTable ? currentTable.dataset.bestellungenPageSize : null) || '20', 10);";
echo " };";
echo " const getPageSize = () => {";
echo " const currentTable = getTable();";
echo " return parseInt((currentTable ? currentTable.dataset.bestellungenPageSize : null) || '20', 10);";
echo " };";
echo " const isMobileDetailMode = () => window.matchMedia('(max-width: 767px)').matches;";
echo " const escapeHtml = (value) => String(value ?? '').replace(/[&<>\"']/g, (character) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[character] || character));";
echo " let bestellungenMobileDetailHost = null;";
echo " let bestellungenDetailOpen = false; let bestellungenOpeningMobileDetail = false;";
echo " const syncSearchState = () => {";
echo " const input = getSearchInput();";
echo " const wrap = getSearchWrap();";
echo " if (!input || !wrap) { return; }";
echo " const hasValue = input.value.trim().length > 0;";
echo " wrap.setAttribute('data-has-value', String(hasValue));";
echo " wrap.dataset.componentState = hasValue ? 'active' : 'inactive-selectable';";
echo " };";
echo " const renderMobileDetail = () => {";
echo " return '<div class=\"sg-bestellungen-mobile-detail-host\" data-bestellungen-mobile-detail-host=\"true\"><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\" aria-label=\"Navigation Detailansicht zurück oben\"><div class=\"sg-card-segment sg-card-segment--body\" data-component-part=\"card-body\" data-pattern-part=\"navigation-card-segment\"><div class=\"sg-navigation-card-center\"><button class=\"sg-interaction-element sg-hyperlink\" type=\"button\" data-bestellungen-detail-back>zurück</button></div></div></article></div></div></div>';";
echo " };";
echo " const getDrawer = () => document.querySelector('[data-bestellungen-mobile-drawer]') || contentRoot.querySelector('[data-bestellungen-drawer]');";
echo " const getDrawerBackdrop = () => contentRoot.querySelector('[data-bestellungen-drawer-backdrop]');";
echo " const setDrawerState = (open) => {";
echo " const drawer = getDrawer();";
echo " const backdrop = getDrawerBackdrop();";
echo " if (!drawer || !backdrop) { return; }";
echo " drawer.dataset.open = String(open);";
echo " drawer.setAttribute('aria-hidden', String(!open));";
echo " backdrop.dataset.open = String(open);";
echo " backdrop.setAttribute('aria-hidden', String(!open));";
echo " document.body.classList.toggle('sg-card-list-page-drawer-open', open);";
echo " };";
echo " const closeDrawer = () => {";
echo " setDrawerState(false);";
echo " contentRoot.querySelectorAll('[data-order-drawer-open]').forEach((trigger) => {";
echo " trigger.setAttribute('aria-expanded', 'false');";
echo " });";
echo " };";
echo " const openDrawer = (trigger) => {";
echo " const drawer = getDrawer();";
echo " if (!drawer) { return; }";
echo " if (isMobileDetailMode() && !bestellungenOpeningMobileDetail) {";
echo " openMobileDetail(trigger);";
echo " return;";
echo " }";
echo " const drawerTitle = drawer.querySelector('[data-order-drawer-title]');";
echo " const cards = drawer.querySelector('[data-order-drawer-cards]');";
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 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 " };";
echo " setDrawerState(true);";
echo " if (drawerTitle) { drawerTitle.textContent = trigger.dataset.orderNumber ? 'Bestellung ' + trigger.dataset.orderNumber : 'Bestellung'; }";
echo " if (cards) { cards.innerHTML = '<p class=\"sg-body\">Bestelldetails werden geladen …</p>'; }";
echo " fetch('/api/bestellungen/detail.php?id=' + encodeURIComponent(orderId), { credentials: 'same-origin' }).then((response) => response.ok ? response.json() : Promise.reject(new Error('Bestelldetails konnten nicht geladen werden.'))).then((payload) => { if (!payload.ok) { throw new Error('Bestelldetails konnten nicht geladen werden.'); } if (cards) { cards.innerHTML = renderCards(payload.order); } }).catch((error) => { if (cards) { cards.innerHTML = '<p class=\"sg-body\">' + escapeHtml(error.message) + '</p>'; } });";
echo " contentRoot.querySelectorAll('[data-order-drawer-open]').forEach((button) => {";
echo " button.setAttribute('aria-expanded', String(button === trigger));";
echo " });";
echo " };";
echo " const restoreBestellungenList = () => {";
echo " if (bestellungenMobileDetailHost && bestellungenMobileDetailHost.parentNode) { const mobileDrawer = bestellungenMobileDetailHost.querySelector('[data-bestellungen-mobile-drawer]'); if (mobileDrawer) { contentRoot.appendChild(mobileDrawer); mobileDrawer.removeAttribute('data-bestellungen-mobile-drawer'); mobileDrawer.dataset.open = 'false'; mobileDrawer.setAttribute('aria-hidden', 'true'); } document.body.classList.remove('sg-card-list-page-drawer-open');";
echo " bestellungenMobileDetailHost.parentNode.removeChild(bestellungenMobileDetailHost);";
echo " }";
echo " bestellungenMobileDetailHost = null;";
echo " document.body.classList.remove('sg-bestellungen-mobile-detail-open');";
echo " bestellungenDetailOpen = false;";
echo " bindTable();";
echo " };";
echo " const openMobileDetail = (trigger) => {";
echo " restoreBestellungenList();";
echo " const sourceDrawer = contentRoot.querySelector('[data-bestellungen-drawer]');";
echo " if (!sourceDrawer) { return; }";
echo " const host = document.createElement('div');";
echo " host.innerHTML = renderMobileDetail();";
echo " bestellungenMobileDetailHost = host.firstElementChild;";
echo " if (bestellungenMobileDetailHost) { const mobileDrawer = sourceDrawer; mobileDrawer.dataset.bestellungenMobileDrawer = 'true'; mobileDrawer.dataset.open = 'true'; mobileDrawer.setAttribute('aria-hidden', 'false'); bestellungenMobileDetailHost.appendChild(mobileDrawer); document.body.appendChild(bestellungenMobileDetailHost); document.body.classList.add('sg-bestellungen-mobile-detail-open'); }";
echo " bestellungenOpeningMobileDetail = true; openDrawer(trigger); bestellungenOpeningMobileDetail = false;";
echo " bestellungenDetailOpen = true;";
echo " window.history.pushState({ bestellungenView: 'detail', orderId: trigger.dataset.orderId || '' }, '', window.location.href);";
echo " };";
echo " const closeMobileDetail = () => {";
echo " if (!bestellungenDetailOpen) { return; }";
echo " if (window.history.state && window.history.state.bestellungenView === 'detail') {";
echo " window.history.back();";
echo " return;";
echo " }";
echo " restoreBestellungenList();";
echo " };";
echo " const buildUrl = (params) => {";
echo " const url = new URL(window.location.href);";
echo " url.hash = '';";
echo " Object.entries(params).forEach(([key, value]) => {";
echo " if (value === null || value === undefined || value === '') {";
echo " url.searchParams.delete(key);";
echo " return;";
echo " }";
echo " url.searchParams.set(key, String(value));";
echo " });";
echo " return url;";
echo " };";
echo " const buildFragmentUrl = (params) => {";
echo " const url = buildUrl(params);";
echo " return url;";
echo " };";
echo " const replaceFragment = (html) => {";
echo " const doc = new DOMParser().parseFromString(html, 'text/html');";
echo " const fragment = doc.querySelector('[data-bestellungen-content]');";
echo " if (!fragment) {";
echo " return false;";
echo " }";
echo " contentRoot.innerHTML = fragment.innerHTML;";
echo " if (bestellungenMobileDetailHost && bestellungenMobileDetailHost.parentNode) {";
echo " bestellungenMobileDetailHost.parentNode.removeChild(bestellungenMobileDetailHost);";
echo " }";
echo " bestellungenMobileDetailHost = null;";
echo " document.body.classList.remove('sg-bestellungen-mobile-detail-open');";
echo " bestellungenDetailOpen = false;";
echo " table = getTable();";
echo " return !!table;";
echo " };";
echo " const focusSearchInput = () => {";
echo " const input = getSearchInput();";
echo " if (!input) { return; }";
echo " input.focus({ preventScroll: true });";
echo " const end = input.value.length;";
echo " try {";
echo " input.setSelectionRange(end, end);";
echo " } catch (error) {";
echo " void error;";
echo " }";
echo " };";
echo " const loadFragment = async (params) => {";
echo " if (searchTimer) { window.clearTimeout(searchTimer); searchTimer = null; }";
echo " const wasSearchFocused = getSearchInput() ? document.activeElement === getSearchInput() : false;";
echo " const url = buildFragmentUrl(params);";
echo " const response = await fetch(url.toString(), { credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest' } });";
echo " const html = await response.text();";
echo " const replaced = replaceFragment(html);";
echo " if (replaced) {";
echo " const historyUrl = buildUrl(params);";
echo " window.history.replaceState({}, '', historyUrl);";
echo " bindTable();";
echo " if (wasSearchFocused) { focusSearchInput(); }";
echo " }";
echo " };";
echo " const bindTable = () => {";
echo " table = getTable();";
echo " if (!table) { return; }";
echo " const searchInput = getSearchInput();";
echo " const clearButton = table.querySelector('[data-bestellungen-search-clear]');";
echo " if (searchInput && searchInput.dataset.bestellungenBound !== 'true') {";
echo " searchInput.dataset.bestellungenBound = 'true';";
echo " searchInput.addEventListener('input', () => {";
echo " syncSearchState();";
echo " if (searchTimer) { window.clearTimeout(searchTimer); }";
echo " searchTimer = window.setTimeout(() => {";
echo " void loadFragment({";
echo " bestellungen_search: getSearchValue(),";
echo " bestellungen_sort: getSortColumn(),";
echo " bestellungen_dir: getSortDirection(),";
echo " bestellungen_limit: getPageSize(),";
echo " });";
echo " }, 500);";
echo " });";
echo " }";
echo " if (clearButton && clearButton.dataset.bestellungenBound !== 'true') {";
echo " clearButton.dataset.bestellungenBound = 'true';";
echo " clearButton.addEventListener('click', () => {";
echo " if (searchTimer) { window.clearTimeout(searchTimer); searchTimer = null; }";
echo " const input = getSearchInput();";
echo " if (input) { input.value = ''; }";
echo " syncSearchState();";
echo " void loadFragment({";
echo " bestellungen_search: '',";
echo " bestellungen_sort: getSortColumn(),";
echo " bestellungen_dir: getSortDirection(),";
echo " bestellungen_limit: getPageSize(),";
echo " });";
echo " });";
echo " }";
echo " syncSearchState();";
echo " };";
echo " const getCurrentQueryParams = () => ({";
echo " bestellungen_search: getSearchValue(),";
echo " bestellungen_sort: getSortColumn(),";
echo " bestellungen_dir: getSortDirection(),";
echo " bestellungen_limit: getCurrentLimit(),";
echo " });";
echo " const getOpenDrawerOrderId = () => {";
echo " const trigger = Array.from(contentRoot.querySelectorAll('[data-order-drawer-open]')).find((button) => button.getAttribute('aria-expanded') === 'true');";
echo " return trigger ? (trigger.dataset.orderId || '') : '';";
echo " };";
echo " const reloadBestellungenSection = () => loadFragment(getCurrentQueryParams());";
echo " const reloadBestellungenRow = async (orderId) => {";
echo " const targetOrderId = String(orderId || '');";
echo " if (targetOrderId === '') {";
echo " await reloadBestellungenSection();";
echo " return;";
echo " }";
echo " const response = await fetch(buildFragmentUrl(getCurrentQueryParams()).toString(), { credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest' } });";
echo " if (!response.ok) {";
echo " await reloadBestellungenSection();";
echo " return;";
echo " }";
echo " const html = await response.text();";
echo " const doc = new DOMParser().parseFromString(html, 'text/html');";
echo " const newRow = Array.from(doc.querySelectorAll('[data-bestellungen-row]')).find((row) => (row.dataset.orderId || '') === targetOrderId) || null;";
echo " const currentRow = Array.from(contentRoot.querySelectorAll('[data-bestellungen-row]')).find((row) => (row.dataset.orderId || '') === targetOrderId) || null;";
echo " if (!newRow || !currentRow) {";
echo " await reloadBestellungenSection();";
echo " return;";
echo " }";
echo " const openDrawerOrderId = getOpenDrawerOrderId();";
echo " currentRow.outerHTML = newRow.outerHTML;";
echo " if (openDrawerOrderId === targetOrderId) {";
echo " const reopenedTrigger = Array.from(contentRoot.querySelectorAll('[data-order-drawer-open]')).find((button) => (button.dataset.orderId || '') === targetOrderId) || null;";
echo " if (reopenedTrigger) {";
echo " openDrawer(reopenedTrigger);";
echo " }";
echo " }";
echo " };";
echo " if (!bestellungenTableHandlersInstalled) {";
echo " bestellungenTableHandlersInstalled = true;";
echo " document.addEventListener('click', (event) => {";
echo " const detailBackTrigger = event.target.closest('[data-bestellungen-detail-back]');";
echo " if (detailBackTrigger) {";
echo " event.preventDefault();";
echo " closeMobileDetail();";
echo " }";
echo " });";
echo " document.addEventListener('click', (event) => {";
echo " const openTrigger = event.target.closest('[data-order-drawer-open]');";
echo " if (openTrigger) {";
echo " event.preventDefault();";
echo " openDrawer(openTrigger);";
echo " return;";
echo " }";
echo " const closeTrigger = event.target.closest('[data-order-drawer-close]');";
echo " if (closeTrigger) {";
echo " event.preventDefault();";
echo " closeDrawer();";
echo " return;";
echo " }";
echo " const backdrop = event.target.closest('[data-bestellungen-drawer-backdrop]');";
echo " if (backdrop) {";
echo " event.preventDefault();";
echo " closeDrawer();";
echo " return;";
echo " }";
echo " const sortButton = event.target.closest('[data-bestellungen-sort-button]');";
echo " if (sortButton) {";
echo " event.preventDefault();";
echo " void loadFragment({";
echo " bestellungen_search: getSearchValue(),";
echo " bestellungen_sort: sortButton.dataset.bestellungenSortColumn || getSortColumn(),";
echo " bestellungen_dir: sortButton.dataset.bestellungenSortDirection || getSortDirection(),";
echo " bestellungen_limit: getPageSize(),";
echo " });";
echo " return;";
echo " }";
echo " const loadMoreTrigger = event.target.closest('[data-large-table-load-more-trigger]');";
echo " if (loadMoreTrigger) {";
echo " event.preventDefault();";
echo " void loadFragment({";
echo " bestellungen_search: getSearchValue(),";
echo " bestellungen_sort: getSortColumn(),";
echo " bestellungen_dir: getSortDirection(),";
echo " bestellungen_limit: getCurrentLimit() + getPageSize(),";
echo " });";
echo " }";
echo " });";
echo " document.addEventListener('keydown', (event) => {";
echo " if (event.key === 'Escape') {";
echo " closeDrawer();";
echo " }";
echo " });";
echo " window.addEventListener('popstate', (event) => {";
echo " if (bestellungenDetailOpen && (!event.state || event.state.bestellungenView !== 'detail')) {";
echo " restoreBestellungenList();";
echo " }";
echo " });";
echo " if (!window.history.state || window.history.state.bestellungenView !== 'list') {";
echo " window.history.replaceState({ bestellungenView: 'list' }, '', window.location.href);";
echo " }";
echo " }";
echo " bindTable();";
echo "}";
echo "(() => {";
echo " const overlay = document.querySelector('[data-otc-order-overlay]');";
echo " if (!overlay) { return; }";
echo " const form = overlay.querySelector('[data-otc-order-form]');";
echo " const productInputs = Array.from(form.querySelectorAll('[data-otc-order-product]'));";
echo " const orderDateInput = form.querySelector('[data-otc-order-date]');";
echo " const orderDateDefaultValue = orderDateInput ? orderDateInput.value : '';";
echo " const paymentRows = form.querySelector('[data-otc-payment-rows]');";
echo " const addPaymentButton = form.querySelector('[data-otc-payment-add]');";
echo " const paymentOptionValues = ['twint', 'card', 'cash', 'paypal', 'bank_transfer'];";
echo " const paymentOptionLabels = { twint: 'Twint', card: 'Kreditkarte', cash: 'Barzahlung', paypal: 'PayPal', bank_transfer: 'Überweisung' };";
echo " const firstNameInput = form.querySelector('[data-otc-order-first-name]');";
echo " const lastNameInput = form.querySelector('[data-otc-order-last-name]');";
echo " const streetInput = form.querySelector('[data-otc-order-street]');";
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]');";
echo " const successDefaultText = successEl ? successEl.textContent : '';";
echo " const confirmationStage = overlay.querySelector('[data-otc-confirmation-stage]');";
echo " const confirmationCard = overlay.querySelector('[data-otc-confirmation-card]');";
echo " const confirmationProducts = overlay.querySelector('[data-otc-confirmation-products]');";
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) => {";
echo " openTriggers().forEach((trigger) => { trigger.setAttribute('aria-expanded', String(expanded)); });";
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'); }";
echo " };";
echo " const getTodayValue = () => {";
echo " const now = new Date();";
echo " const year = now.getFullYear();";
echo " const month = String(now.getMonth() + 1).padStart(2, '0');";
echo " const day = String(now.getDate()).padStart(2, '0');";
echo " return year + '-' + month + '-' + day;";
echo " };";
echo " const ensureCurrentOrderDate = () => {";
echo " if (!orderDateInput) { return; }";
echo " if (!orderDateInput.value || orderDateInput.value === orderDateDefaultValue) {";
echo " orderDateInput.value = getTodayValue();";
echo " }";
echo " };";
echo " const toggleOverlay = (isOpen) => {";
echo " overlay.dataset.open = String(isOpen);";
echo " overlay.setAttribute('aria-hidden', String(!isOpen));";
echo " document.body.classList.toggle('sg-otc-order-overlay-open', isOpen);";
echo " setTriggerState(isOpen);";
echo " if (isOpen) {";
echo " clearError();";
echo " clearSuccess();";
echo " ensureCurrentOrderDate();";
echo " requestAnimationFrame(() => {";
echo " (productInputs[0] || submitBtn)?.focus();";
echo " });";
echo " return;";
echo " }";
echo " clearError();";
echo " clearSuccess();";
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);";
echo " const isValid = totalQty > 0";
echo " && !!getFieldValue(orderDateInput)";
echo " && paymentRows.querySelectorAll('[data-otc-payment-row]').length > 0";
echo " && !!getFieldValue(firstNameInput)";
echo " && !!getFieldValue(lastNameInput)";
echo " && !!getFieldValue(streetInput)";
echo " && !!getFieldValue(houseNumberInput)";
echo " && !!getFieldValue(zipInput)";
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.';";
echo " } else if (!getFieldValue(orderDateInput)) {";
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;";
echo " errorEl.classList.toggle('hidden', errorMsg === '');";
echo " }";
echo " submitBtn.disabled = !isValid;";
echo " submitBtn.setAttribute('aria-disabled', String(!isValid));";
echo " submitBtn.classList.toggle('sg-button--process-inactive', !isValid);";
echo " return isValid;";
echo " };";
echo " const resetForm = (preserveSuccess = false) => {";
echo " form.reset();";
echo " Array.from(paymentRows.querySelectorAll('[data-otc-payment-row]')).slice(1).forEach((row) => row.remove());";
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 = () => {";
echo " pendingOrderData = null;";
echo " confirmationStage.dataset.dialogOpen = 'false';";
echo " confirmationCard.hidden = true;";
echo " };";
echo " const openConfirmation = (orderData) => {";
echo " pendingOrderData = orderData;";
echo " confirmationProducts.replaceChildren();";
echo " orderData.products.forEach((product) => {";
echo " const input = productInputs.find((candidate) => parseInt(candidate.dataset.productId || '0', 10) === product.productId);";
echo " const item = document.createElement('div');";
echo " item.className = 'sg-body';";
echo " item.textContent = (input?.dataset.title || 'Produkt') + ': ' + product.qty;";
echo " confirmationProducts.appendChild(item);";
echo " });";
echo " confirmationQuantity.textContent = String(orderData.products.reduce((sum, product) => sum + product.qty, 0));";
echo " confirmationTotal.textContent = orderData.payments.reduce((sum, payment) => sum + payment.amount, 0).toFixed(2);";
echo " confirmationStage.dataset.dialogOpen = 'true';";
echo " confirmationCard.hidden = false;";
echo " requestAnimationFrame(() => confirmationSubmit.focus());";
echo " };";
echo " document.addEventListener('click', (event) => {";
echo " const openTrigger = event.target.closest('[data-otc-order-open]');";
echo " if (openTrigger) {";
echo " event.preventDefault();";
echo " toggleOverlay(true);";
echo " return;";
echo " }";
echo " const closeTrigger = event.target.closest('[data-otc-order-close]');";
echo " if (closeTrigger) {";
echo " event.preventDefault();";
echo " toggleOverlay(false);";
echo " }";
echo " });";
echo " [orderDateInput, firstNameInput, lastNameInput, streetInput, houseNumberInput, zipInput, cityInput].forEach((input) => {";
echo " if (!input) { return; }";
echo " input.addEventListener('input', updateFormState);";
echo " input.addEventListener('change', updateFormState);";
echo " });";
echo " productInputs.forEach((input) => {";
echo " input.addEventListener('input', updateFormState);";
echo " input.addEventListener('change', updateFormState);";
echo " });";
echo " const bindPaymentRow = (row) => {";
echo " row.querySelectorAll('select, input').forEach((input) => { input.addEventListener('input', updateFormState); input.addEventListener('change', updateFormState); });";
echo " };";
echo " const paymentOptions = paymentOptionValues.map((value) => '<option value=\"' + value + '\">' + paymentOptionLabels[value] + '</option>').join('');";
echo " const addPaymentRow = () => {";
echo " const number = paymentRows.querySelectorAll('[data-otc-payment-row]').length + 1;";
echo " if (number > paymentOptionValues.length) { updatePaymentAddButton(); return; }";
echo " const nextPaymentMethod = paymentOptionValues[number - 1];";
echo " const row = document.createElement('div');";
echo " row.className = 'sg-form-sections-card__field-group';";
echo " row.dataset.otcPaymentRow = '';";
echo " row.innerHTML = '<label class=\"sg-labeled-input-row\"><span class=\"sg-label\">Bezahlart</span><select class=\"sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable sg-pulldown\" id=\"otc-payment-method-' + number + '\" data-otc-payment-method>' + paymentOptions + '</select></label><label class=\"sg-labeled-input-row\"><span class=\"sg-label\">Betrag (CHF)</span><input class=\"sg-interaction-element sg-input-single-line sg-input-single-line--inactive-selectable sg-form-inactive-selectable\" type=\"number\" id=\"otc-payment-amount-' + number + '\" min=\"0.01\" step=\"0.01\" value=\"0.00\" data-otc-payment-amount></label>' ;";
echo " row.querySelector('[data-otc-payment-method]').value = nextPaymentMethod;";
echo " paymentRows.appendChild(row);";
echo " bindPaymentRow(row);";
echo " updatePaymentAddButton();";
echo " updateFormState();";
echo " };";
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;";
echo " confirmationSubmit.innerHTML = '<span class=\"loading\"></span> Wird verarbeitet...';";
echo " confirmationSubmit.disabled = true;";
echo " clearError();";
echo " try {";
echo " const response = await fetch('/api/otc-order.php', {";
echo " method: 'POST',";
echo " headers: { 'Content-Type': 'application/json' },";
echo " body: JSON.stringify(orderData)";
echo " });";
echo " const result = await response.json();";
echo " if (response.ok && result.ok) {";
echo " submissionSucceeded = true;";
echo " if (successEl) {";
echo " successEl.textContent = result.externalRef ? 'Bestellung erfolgreich erfasst! Bestellnummer: ' + result.externalRef : successDefaultText;";
echo " successEl.classList.remove('hidden');";
echo " }";
echo " closeConfirmation();";
echo " resetForm();";
echo " toggleOverlay(false);";
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 " 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;";
echo " if (!submissionSucceeded) { updateFormState(); }";
echo " }";
echo " };";
echo " form.addEventListener('submit', (event) => {";
echo " event.preventDefault();";
echo " if (!updateFormState()) { return; }";
echo " const products = productInputs";
echo " .filter((input) => getInputValue(input) > 0)";
echo " .map((input) => ({ productId: parseInt(input.dataset.productId || '0', 10), qty: getInputValue(input) }));";
echo " const orderData = {";
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 " 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 " marketOccurrenceId: isMarketLinkEnabled() ? (marketDayInput?.selectedOptions[0]?.dataset.marketOccurrenceId || null) : null,";
echo " marketOccurrenceDayId: isMarketLinkEnabled() ? getFieldValue(marketDayInput) : null";
echo " };";
echo " openConfirmation(orderData);";
echo " });";
echo " confirmationStage.addEventListener('click', (event) => {";
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) {";
echo " window.sgInitHelpIconOverlays({";
echo " closeOnOpenSelectors: ['.sg-sandwich-menu-wrap'],";
echo " outsideClickIgnoreSelectors: ['.sg-sandwich-menu-wrap'],";
echo " });";
echo "}";
echo "renderMainHeading('ERP');";
echo "renderLeftNavigation('ERP');";
echo '</script>';
echo '</body>';
echo '</html>';
}