From 795afc2ecbfc7548b1bf85bcf839d1c560a17d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Gla=CC=88ser?= Date: Mon, 15 Jun 2026 15:05:22 +0200 Subject: [PATCH] Bestellungen-Suche auf Fragment-Reload umstellen --- modules/shared/auth/ui/home.php | 308 +++++++++++++++++++------------- public/index.php | 4 + 2 files changed, 192 insertions(+), 120 deletions(-) diff --git a/modules/shared/auth/ui/home.php b/modules/shared/auth/ui/home.php index 0687af8..6b3190e 100644 --- a/modules/shared/auth/ui/home.php +++ b/modules/shared/auth/ui/home.php @@ -76,7 +76,7 @@ function auth_render_bestellungen_large_table(array $bestellungenTable): string $html[] = '
Bestellungen
'; $html[] = ''; $html[] = ''; - $html[] = ''; + $html[] = ''; $html[] = ''; $html[] = ''; $html[] = ''; @@ -190,6 +190,14 @@ function auth_render_bestellungen_large_table(array $bestellungenTable): string return implode('', $html); } +function auth_render_bestellungen_content(array $bestellungenTable): string +{ + return '
' + . '
' + . auth_render_bestellungen_large_table($bestellungenTable) + . '
'; +} + function render_auth_home_page(array $user, array $otcProducts = [], array $bestellungenTable = []): void { $otcProductRows = ''; @@ -218,9 +226,10 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); + $bestellungenContent = auth_render_bestellungen_content($bestellungenTable); $moduleContentCards = json_encode( [ - 'Bestellungen' => '
' . auth_render_bestellungen_large_table($bestellungenTable), + 'Bestellungen' => $bestellungenContent, ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); @@ -292,13 +301,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best echo '

Bestellungen

'; echo ''; echo '
'; - echo '
'; - echo '
'; - echo '
'; - echo ''; - echo '
'; - echo '
'; - echo '
'; + echo $bestellungenContent; echo '
'; echo ''; echo ''; @@ -400,6 +403,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best echo " const activeEntry = entries[0] || '';"; echo " if (contentTitle) { contentTitle.textContent = activeEntry; }"; echo " if (contentBody) { contentBody.innerHTML = portalModuleContentCards[activeEntry] || ''; }"; + echo " if (activeEntry === 'Bestellungen') { initBestellungenBindings(); }"; echo " menu.innerHTML = '';"; echo " entries.forEach((entry, index) => {"; echo " const button = document.createElement('button');"; @@ -419,6 +423,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best 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 " });"; echo " });"; echo "};"; @@ -496,150 +501,213 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $best echo " syncMode();"; echo " mediaQuery.addEventListener('change', syncMode);"; echo "})();"; - echo "(() => {"; - echo " const table = document.querySelector('[data-bestellungen-large-table]');"; + echo "let bestellungenTableHandlersInstalled = false;"; + echo "function initBestellungenBindings() {"; + echo " const contentRoot = document.querySelector('[data-left-navigation-content-body]');"; + echo " if (!contentRoot) { return; }"; + echo " let table = contentRoot.querySelector('[data-bestellungen-large-table]');"; echo " if (!table) { return; }"; - echo " const searchInput = table.querySelector('[data-bestellungen-search-input]');"; - echo " const clearButton = table.querySelector('[data-bestellungen-search-clear]');"; - echo " const sortButtons = Array.from(table.querySelectorAll('[data-bestellungen-sort-button]'));"; - echo " const drawer = document.querySelector('[data-bestellungen-drawer]');"; - echo " const drawerTitle = drawer ? drawer.querySelector('[data-order-drawer-title]') : null;"; - echo " const drawerOrderNumber = drawer ? drawer.querySelector('[data-order-drawer-order-number]') : null;"; - echo " const drawerOrderDate = drawer ? drawer.querySelector('[data-order-drawer-order-date]') : null;"; - echo " const drawerAddressLine1 = drawer ? drawer.querySelector('[data-order-drawer-address-line-1]') : null;"; - echo " const drawerAddressLine2 = drawer ? drawer.querySelector('[data-order-drawer-address-line-2]') : null;"; - echo " const drawerAddressLine3 = drawer ? drawer.querySelector('[data-order-drawer-address-line-3]') : null;"; - echo " const drawerTotal = drawer ? drawer.querySelector('[data-order-drawer-total]') : null;"; - echo " const searchWrap = searchInput ? searchInput.closest('[data-component=\"single-line-input\"]') : null;"; - echo " const defaultLimit = parseInt(table.dataset.bestellungenPageSize || table.dataset.bestellungenLimit || '20', 10);"; - echo " const currentSortColumn = table.dataset.bestellungenSortColumn || 'order_date';"; - echo " const currentSortDirection = (table.dataset.bestellungenSortDirection || 'DESC').toUpperCase();"; echo " let searchTimer = null;"; - echo " const setWrapState = (input) => {"; - echo " if (!searchWrap || !input) { return; }"; - echo " searchWrap.setAttribute('data-has-value', String(input.value.trim().length > 0));"; - echo " searchWrap.dataset.componentState = input.value.trim().length > 0 ? 'active' : 'inactive-selectable';"; + echo " let requestToken = 0;"; + echo " const getSearchInput = () => table.querySelector('[data-large-table-search]');"; + echo " const getSearchWrap = () => {"; + echo " const input = getSearchInput();"; + echo " return input ? input.closest('[data-component=\\'single-line-input\\']') : null;"; echo " };"; - echo " const buildUrl = (params) => {"; - echo " const url = new URL(window.location.href);"; - echo " Object.entries(params).forEach(([key, value]) => {"; - echo " if (value === null || value === undefined || value === '') {"; - echo " url.searchParams.delete(key);"; - echo " } else {"; - echo " url.searchParams.set(key, String(value));"; - echo " }"; - echo " });"; - echo " return url.toString();"; + echo " const getClearButton = () => table.querySelector('.sg-input-clear-button');"; + echo " const getSearchValue = () => {"; + echo " const input = getSearchInput();"; + echo " return input ? input.value.trim() : '';"; echo " };"; - echo " const navigate = (params) => {"; - echo " window.location.assign(buildUrl(params));"; + echo " const getSortColumn = () => table.dataset.bestellungenSortColumn || 'order_date';"; + echo " const getSortDirection = () => (table.dataset.bestellungenSortDirection || 'DESC').toUpperCase();"; + echo " const getCurrentLimit = () => parseInt(table.dataset.bestellungenLimit || table.dataset.bestellungenPageSize || '20', 10);"; + echo " const getPageSize = () => parseInt(table.dataset.bestellungenPageSize || '20', 10);"; + 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 currentSearchValue = () => (searchInput ? searchInput.value.trim() : '');"; - echo " const resetLimit = () => defaultLimit;"; echo " const closeDrawer = () => {"; + echo " const drawer = contentRoot.querySelector('[data-bestellungen-drawer]');"; echo " if (!drawer) { return; }"; echo " drawer.dataset.open = 'false';"; echo " drawer.setAttribute('aria-hidden', 'true');"; - echo " document.querySelectorAll('[data-order-drawer-open]').forEach((trigger) => {"; + echo " contentRoot.querySelectorAll('[data-order-drawer-open]').forEach((trigger) => {"; echo " trigger.setAttribute('aria-expanded', 'false');"; echo " });"; echo " };"; - echo " const setDrawerField = (node, value) => {"; - echo " if (node) { node.textContent = value || ''; }"; - echo " };"; - echo " const formatAddressLine = (street, houseNumber, zip, city, country) => {"; - echo " const streetLine = [street, houseNumber].filter((part) => part && part.length > 0).join(' ').trim();"; - echo " const cityLine = [zip, city].filter((part) => part && part.length > 0).join(' ').trim();"; - echo " return [streetLine, cityLine, country].filter((part) => part && part.length > 0).join(' · ');"; - echo " };"; echo " const openDrawer = (trigger) => {"; + echo " const drawer = contentRoot.querySelector('[data-bestellungen-drawer]');"; echo " if (!drawer) { return; }"; + echo " const drawerTitle = drawer.querySelector('[data-order-drawer-title]');"; + echo " const drawerOrderNumber = drawer.querySelector('[data-order-drawer-order-number]');"; + echo " const drawerOrderDate = drawer.querySelector('[data-order-drawer-order-date]');"; + echo " const drawerAddressLine1 = drawer.querySelector('[data-order-drawer-address-line-1]');"; + echo " const drawerAddressLine2 = drawer.querySelector('[data-order-drawer-address-line-2]');"; + echo " const drawerAddressLine3 = drawer.querySelector('[data-order-drawer-address-line-3]');"; + echo " const drawerTotal = drawer.querySelector('[data-order-drawer-total]');"; echo " const orderNumber = trigger.dataset.orderNumber || '';"; echo " const orderDate = trigger.dataset.orderDate || '';"; echo " const total = trigger.dataset.orderTotal || '';"; - echo " const firstName = trigger.dataset.orderFirstName || '';"; - echo " const lastName = trigger.dataset.orderLastName || '';"; echo " const street = trigger.dataset.orderStreet || '';"; echo " const houseNumber = trigger.dataset.orderHouseNumber || '';"; echo " const zip = trigger.dataset.orderZip || '';"; echo " const city = trigger.dataset.orderCity || '';"; echo " const country = trigger.dataset.orderCountry || '';"; + echo " const streetLine = [street, houseNumber].filter((part) => part && part.length > 0).join(' ').trim();"; + echo " const cityLine = [zip, city].filter((part) => part && part.length > 0).join(' ').trim();"; echo " drawer.dataset.open = 'true';"; echo " drawer.setAttribute('aria-hidden', 'false');"; echo " if (drawerTitle) { drawerTitle.textContent = orderNumber !== '' ? 'Bestellung ' + orderNumber : 'Bestellung'; }"; - echo " setDrawerField(drawerOrderNumber, orderNumber);"; - echo " setDrawerField(drawerOrderDate, orderDate);"; - echo " setDrawerField(drawerAddressLine1, formatAddressLine(street, houseNumber, '', '', ''));"; - echo " setDrawerField(drawerAddressLine2, formatAddressLine('', '', zip, city, ''));"; - echo " setDrawerField(drawerAddressLine3, country);"; - echo " setDrawerField(drawerTotal, total);"; - echo " document.querySelectorAll('[data-order-drawer-open]').forEach((button) => {"; + echo " if (drawerOrderNumber) { drawerOrderNumber.textContent = orderNumber; }"; + echo " if (drawerOrderDate) { drawerOrderDate.textContent = orderDate; }"; + echo " if (drawerAddressLine1) { drawerAddressLine1.textContent = streetLine; }"; + echo " if (drawerAddressLine2) { drawerAddressLine2.textContent = cityLine; }"; + echo " if (drawerAddressLine3) { drawerAddressLine3.textContent = country; }"; + echo " if (drawerTotal) { drawerTotal.textContent = total; }"; + echo " contentRoot.querySelectorAll('[data-order-drawer-open]').forEach((button) => {"; echo " button.setAttribute('aria-expanded', String(button === trigger));"; echo " });"; echo " };"; - echo " sortButtons.forEach((button) => {"; - echo " button.addEventListener('click', () => {"; - echo " const sortColumn = button.dataset.bestellungenSortColumn || currentSortColumn;"; - echo " const sortDirection = button.dataset.bestellungenSortDirection || currentSortDirection;"; - echo " navigate({"; - echo " bestellungen_search: currentSearchValue(),"; - echo " bestellungen_sort: sortColumn,"; - echo " bestellungen_dir: sortDirection,"; - echo " bestellungen_limit: resetLimit(),"; - echo " });"; + echo " const buildUrl = (params) => {"; + echo " const url = new URL(window.location.href);"; + echo " url.searchParams.set('bestellungen_fragment', '1');"; + 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 " });"; - echo " if (searchInput) {"; - echo " searchInput.addEventListener('input', () => {"; - echo " setWrapState(searchInput);"; - echo " if (searchTimer) { window.clearTimeout(searchTimer); }"; - echo " searchTimer = window.setTimeout(() => {"; - echo " navigate({"; - echo " bestellungen_search: currentSearchValue(),"; - echo " bestellungen_sort: currentSortColumn,"; - echo " bestellungen_dir: currentSortDirection,"; - echo " bestellungen_limit: resetLimit(),"; + 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 " table = contentRoot.querySelector('[data-bestellungen-large-table]');"; + echo " return !!table;"; + echo " };"; + echo " const loadFragment = async (params, updateHistory = true) => {"; + echo " if (searchTimer) { window.clearTimeout(searchTimer); searchTimer = null; }"; + echo " const url = buildUrl(params);"; + echo " const activeRequest = ++requestToken;"; + echo " table.setAttribute('aria-busy', 'true');"; + echo " try {"; + echo " const response = await fetch(url.toString(), { headers: { 'X-Requested-With': 'XMLHttpRequest' } });"; + echo " const html = await response.text();"; + echo " if (activeRequest !== requestToken) {"; + echo " return;"; + echo " }"; + echo " const replaced = replaceFragment(html);"; + echo " table = contentRoot.querySelector('[data-bestellungen-large-table]');"; + echo " if (replaced && updateHistory) {"; + echo " const historyUrl = new URL(url.toString());"; + echo " historyUrl.searchParams.delete('bestellungen_fragment');"; + echo " window.history.replaceState({}, '', historyUrl);"; + echo " }"; + echo " bindTable();"; + echo " } finally {"; + echo " if (activeRequest === requestToken) {"; + echo " table = contentRoot.querySelector('[data-bestellungen-large-table]');"; + echo " if (table) {"; + echo " table.removeAttribute('aria-busy');"; + echo " }"; + echo " }"; + echo " }"; + echo " };"; + echo " const bindTable = () => {"; + echo " table = contentRoot.querySelector('[data-bestellungen-large-table]');"; + echo " if (!table) { return; }"; + echo " const searchInput = getSearchInput();"; + echo " const clearButton = getClearButton();"; + 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 " }, 250);"; + 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 " loadFragment({"; + echo " bestellungen_search: '',"; + echo " bestellungen_sort: getSortColumn(),"; + echo " bestellungen_dir: getSortDirection(),"; + echo " bestellungen_limit: getPageSize(),"; + echo " }).then(() => {"; + echo " const nextInput = getSearchInput();"; + echo " if (nextInput) { nextInput.focus(); }"; echo " });"; - echo " }, 250);"; - echo " });"; - echo " }"; - echo " if (clearButton && searchInput) {"; - echo " clearButton.addEventListener('click', () => {"; - echo " if (searchTimer) { window.clearTimeout(searchTimer); searchTimer = null; }"; - echo " searchInput.value = '';"; - echo " setWrapState(searchInput);"; - echo " navigate({"; - echo " bestellungen_search: '',"; - echo " bestellungen_sort: currentSortColumn,"; - echo " bestellungen_dir: currentSortDirection,"; - echo " bestellungen_limit: resetLimit(),"; echo " });"; + echo " }"; + echo " syncSearchState();"; + echo " };"; + echo " if (!bestellungenTableHandlersInstalled) {"; + echo " bestellungenTableHandlersInstalled = true;"; + echo " contentRoot.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 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 " }"; - 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 " if (drawer && drawer.dataset.open === 'true' && !event.target.closest('[data-bestellungen-drawer=\"true\"]')) {"; - echo " closeDrawer();"; - echo " }"; - echo " });"; - echo " document.addEventListener('keydown', (event) => {"; - echo " if (event.key === 'Escape') {"; - echo " closeDrawer();"; - echo " }"; - echo " });"; - echo " setWrapState(searchInput);"; - echo "})();"; + echo " bindTable();"; + echo "}"; echo "(() => {"; echo " const overlay = document.querySelector('[data-otc-order-overlay]');"; echo " if (!overlay) { return; }"; diff --git a/public/index.php b/public/index.php index b7fae9c..920f65a 100644 --- a/public/index.php +++ b/public/index.php @@ -53,6 +53,10 @@ if ($currentUser !== null) { 'limit' => (int) ($_GET['bestellungen_limit'] ?? 20), 'page_size' => 20, ]); + if ((string) ($_GET['bestellungen_fragment'] ?? '') === '1') { + echo auth_render_bestellungen_content($bestellungenTable); + exit; + } render_auth_home_page($currentUser, $otcProducts, $bestellungenTable); exit; }