Add SSE live updates for bestellungen

This commit is contained in:
2026-06-16 19:43:19 +02:00
parent c0f819fd3d
commit a463632772
3 changed files with 211 additions and 1 deletions
+25
View File
@@ -352,3 +352,28 @@ SQL;
'next_limit' => min($totalCount, $limit + $pageSize),
];
}
function get_sales_order_realtime_snapshot(PDO $pdo, int $limit = 1000): array
{
$limit = max(1, min(2000, $limit));
$stmt = $pdo->prepare(
'SELECT id, created_at, updated_at
FROM public.sales_order
ORDER BY updated_at DESC NULLS LAST, id DESC
LIMIT :limit'
);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->execute();
$rows = [];
foreach ($stmt->fetchAll() as $row) {
$rows[] = [
'id' => (int) ($row['id'] ?? 0),
'created_at' => (string) ($row['created_at'] ?? ''),
'updated_at' => (string) ($row['updated_at'] ?? ''),
];
}
return $rows;
}
+71 -1
View File
@@ -124,7 +124,7 @@ function auth_render_bestellungen_large_table(array $bestellungenTable): string
$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">';
$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>';
@@ -509,6 +509,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " mediaQuery.addEventListener('change', syncMode);";
echo "})();";
echo "let bestellungenTableHandlersInstalled = false;";
echo "let bestellungenRealtimeSource = null;";
echo "function initBestellungenBindings() {";
echo " const contentRoot = document.querySelector('[data-left-navigation-content-body]');";
echo " if (!contentRoot) { return; }";
@@ -677,6 +678,74 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
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 " const connectBestellungenRealtime = () => {";
echo " if (!window.EventSource) { return null; }";
echo " if (bestellungenRealtimeSource) {";
echo " bestellungenRealtimeSource.close();";
echo " }";
echo " const source = new EventSource('/api/realtime/bestellungen.php', { withCredentials: true });";
echo " source.addEventListener('bestellungen.changed', (event) => {";
echo " const payload = parseJsonRealtimeEvent(event.data);";
echo " if (!payload) { return; }";
echo " const kind = String(payload.kind || '');";
echo " const orderId = payload.orderId !== undefined && payload.orderId !== null ? String(payload.orderId) : '';";
echo " if (kind === 'updated' && orderId !== '') {";
echo " void reloadBestellungenRow(orderId);";
echo " return;";
echo " }";
echo " void reloadBestellungenSection();";
echo " });";
echo " bestellungenRealtimeSource = source;";
echo " return source;";
echo " };";
echo " const parseJsonRealtimeEvent = (raw) => {";
echo " if (typeof raw !== 'string' || raw.trim() === '') { return null; }";
echo " try {";
echo " const parsed = JSON.parse(raw);";
echo " return parsed && typeof parsed === 'object' ? parsed : null;";
echo " } catch (error) {";
echo " return null;";
echo " }";
echo " };";
echo " if (!bestellungenTableHandlersInstalled) {";
echo " bestellungenTableHandlersInstalled = true;";
echo " contentRoot.addEventListener('click', (event) => {";
@@ -727,6 +796,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best
echo " });";
echo " }";
echo " bindTable();";
echo " connectBestellungenRealtime();";
echo "}";
echo "(() => {";
echo " const overlay = document.querySelector('[data-otc-order-overlay]');";