1727 lines
225 KiB
PHP
1727 lines
225 KiB
PHP
<?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' => 'Name der Veranstaltung',
|
||
'earliest_date' => 'Durchführung',
|
||
'place' => 'Ort',
|
||
'status' => 'Status',
|
||
];
|
||
|
||
$html = [];
|
||
$html[] = '<section class="sg-card-list-page" aria-label="Märkte">';
|
||
$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_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 = []): 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);
|
||
$dayId = (int) ($option['day_id'] ?? 0);
|
||
if ($occurrenceId <= 0 || $dayId <= 0) {
|
||
continue;
|
||
}
|
||
$marketName = (string) ($option['market_name'] ?? '');
|
||
$otcMarketOccurrenceOptions[$occurrenceId] = $marketName;
|
||
$day = (string) ($option['date'] ?? '');
|
||
$dayLabel = $day === '' ? '' : (new DateTimeImmutable($day))->format('d.m.Y');
|
||
$otcMarketDayOptions .= '<option value="' . $dayId . '" data-market-occurrence-id="' . $occurrenceId . '">' . auth_escape_html($dayLabel) . '</option>';
|
||
}
|
||
$otcMarketOccurrenceOptionHtml = '<option value="">Veranstaltung auswählen</option>';
|
||
foreach ($otcMarketOccurrenceOptions as $occurrenceId => $marketName) {
|
||
$otcMarketOccurrenceOptionHtml .= '<option value="' . $occurrenceId . '">' . 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' => ['Märkte', 'Konfiguration'],
|
||
'Buchhaltung' => [],
|
||
'Kundenberatung' => [],
|
||
],
|
||
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
|
||
);
|
||
$bestellungenContent = auth_render_bestellungen_content($bestellungenTable);
|
||
$marketsContent = auth_render_markets_content($marketOccurrencesTable);
|
||
$lagerContent = auth_render_lager_content($lagerProducts, $lagerArtikel, $lagerManufacturers, auth_csrf_token());
|
||
$moduleContentCards = json_encode(
|
||
[
|
||
'Bestellungen' => $bestellungenContent,
|
||
'Märkte' => $marketsContent,
|
||
'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">Veranstaltungsbezug</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 einer Veranstaltung" data-otc-market-link-toggle>';
|
||
echo '<span class="sg-checkbox-field__mark" aria-hidden="true">✓</span>';
|
||
echo '</button><span>Verkauf ist auf einer Veranstaltung</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">Veranstaltung</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">Durchführungstag</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 Veranstaltung 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 '<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 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 (contentBody) { contentBody.innerHTML = portalModuleContentCards[activeEntry] || ''; }";
|
||
echo " if (activeEntry === 'Bestellungen') { initBestellungenBindings(); }";
|
||
echo " if (activeEntry === 'Märkte') { initMarketsBindings(); }";
|
||
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 (contentBody) { contentBody.innerHTML = portalModuleContentCards[entryName] || ''; }";
|
||
echo " if (entryName === 'Bestellungen') { initBestellungenBindings(); }";
|
||
echo " if (entryName === 'Märkte') { initMarketsBindings(); }";
|
||
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) => ({ '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' }[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, specific = false) => '<div class=\"market-detail-row\"><dt>' + marketEscape(label) + (specific ? ' <span class=\"sg-table-label market-day-specific\">(Tagesspezifisch)</span>' : '') + '</dt><dd>' + marketDisplay(value) + '</dd></div>';";
|
||
echo " const marketCard = (title, body, headerClass = 'sg-card-segment--darkgreen', actions = '', 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' || headerClass === 'sg-card-segment--signal-yellow' ? '' : '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('Veranstaltung', market.name) + marketRow('Kategorie', market.category_name || market.category_code) + '<div class=\"market-detail-row\"><dt>Website</dt><dd>' + website + '</dd></div>' + marketRow('Ort', occurrence.place) + marketRow('Adresse', occurrence.address) + marketRow('Gemeinde', occurrence.municipality_reference_id) + 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=\"Durchführungstag hinzufügen\" title=\"Durchführungstag 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=\"Durchführungstag ' + 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('Veranstaltung und Durchfü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_id: market.category_id, 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=\"Durchführungstage\">' + dayTabs + addDayTab + '</div><div class=\"sg-content-block-card-group__panels\">' + dayPanels + '</div></section>' : '<p class=\"sg-body\">Keine Durchführungstage 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', 'Veranstaltung', 'text'], ['category_id', 'Kategorie', 'select', categoryOptions], ['website_url', 'Website', 'url'], ['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(['market_name', 'category_id']), 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 = 'Durchführungstage'; dayGroup.before(heading); } return template.innerHTML; };";
|
||
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 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); 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 + ', Durchfü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 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 " 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('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 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) => ({ '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' }[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 " 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 " };";
|
||
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 occurrenceId = marketOccurrenceInput.value; Array.from(marketDayInput.options).forEach((option, index) => { if (index === 0) { option.hidden = false; option.disabled = false; return; } const matches = occurrenceId !== '' && option.dataset.marketOccurrenceId === occurrenceId; option.hidden = !matches; option.disabled = !matches; }); if (!occurrenceId || !Array.from(marketDayInput.options).some((option) => option.value === marketDayInput.value && !option.disabled)) { marketDayInput.value = ''; } setDisabledSelectState(marketDayInput, !isMarketLinkEnabled() || occurrenceId === ''); };";
|
||
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 = 'Veranstaltung und Durchführungstag 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 if (errorEl) {";
|
||
echo " errorEl.textContent = result.error || 'Unbekannter Fehler';";
|
||
echo " errorEl.classList.remove('hidden');";
|
||
echo " closeConfirmation();";
|
||
echo " }";
|
||
echo " } catch (error) {";
|
||
echo " if (errorEl) {";
|
||
echo " errorEl.textContent = 'Netzwerkfehler: ' + error.message;";
|
||
echo " errorEl.classList.remove('hidden');";
|
||
echo " }";
|
||
echo " closeConfirmation();";
|
||
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() ? getFieldValue(marketOccurrenceInput) : 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>';
|
||
}
|