# Frontend JavaScript Letzte Aktualisierung: 2026-03-06 ## Ordnerstruktur - `/web/js/core` - technische Basismodule (DOM/Factories) - `/web/js/components` - wiederverwendbare UI-Features - `/web/js/pages` - seitenspezifische Hilfen (z. B. Listeninitialisierung) ## Initialisierung - `/web/js/app-boot.js` - wird im `
` geladen - setzt früh UI-States (no-js/js, Sidebar-/Contrast-LocalStorage) - `/web/js/app-init.js` - Modul-Entrypoint für Standardseiten - importiert Komponenten zentral - `/web/js/app-login-init.js` - reduzierter Entrypoint für Loginseiten ## Modulkonvention (components/pages) Empfohlenes Muster: 1. `init...()` Funktion exportieren 2. Früher Guard auf fehlende Elemente 3. Keine stillen Fehler verschlucken 4. Nur DOM/UI-Verhalten, keine Fachlogik Template für neue Komponenten: - bestehende Komponenten als Referenz verwenden (`/web/js/components/*.js`) ## Page Module Contract (Hard Cut) Für seitenbezogene Initialisierung gilt verbindlich: - Kein inline `` bereitstellen. - Page-Module lesen Config zentral über `readPageConfig(...)` aus `web/js/core/app-page-config.js`. - JS-`src` in `pages/**` und `templates/**` nur über `assetVersion('...js...')`. ## Defensive DOM-Nutzung Nicht jede Seite hat jedes Element. - Vor Zugriff immer Element-Existenz prüfen. - Optional zentrale Helper (`app-dom.js`) verwenden. - Module sollen auf nicht-passenden Seiten sauber no-op sein. ## Grid.js-Integration - Gemeinsame Initialisierung über Core/Page-Utilities. - Vendor-spezifische CSS-Anpassungen in: - `/web/css/vendor-overrides/gridjs.css` - Bei dynamischen Rows mit Lightbox/Tooltips: nach Render Refresh triggern. ### Grid URL State Sync `createServerGrid(...)` synchronisiert bei `urlSync: true` den Grid-Zustand zentral in die URL. - Search + Filter (bestehendes Verhalten) - Sortierung über `order` + `dir` - Pagination über `page` (1-basiert) Optionale Konfiguration: ```js createServerGrid({ // ... urlSync: true, urlState: { pageParam: 'page', sortOrderParam: 'order', sortDirParam: 'dir', cleanFirstPage: true, syncPage: true, syncSort: true } }); ``` Hinweise: - `page=1` wird bei `cleanFirstPage: true` nicht in der URL gehalten. - Übernommene URL-Werte werden validiert: - `page >= 1` - `dir` nur `asc|desc` - `order` nur bekannte Sort-Key-Spalten - Bei Search-/Filter-Änderung wird auf Seite 1 zurückgesetzt. ### Row Interaction (Globaler Standard) Listen mit `rowDblClick.getUrl(...)` nutzen standardmäßig eine zugängliche Row-Interaktion: - sichtbare Action-Spalte rechts (`Open`/`Edit`) - Doppelklick bleibt als Fallback aktiv - `Enter` auf fokussierter Zeile navigiert zur selben URL - Action-Header/Tooltip zeigt den Enter-Hinweis Optionale Konfiguration über `createServerGrid(...)`: ```js createServerGrid({ // ... rowDblClick: { getUrl: (rowData) => new URL(`admin/users/edit/${rowData?.cells?.[1]?.data}`, appBase).toString(), }, rowInteraction: { enabled: true, showActionButton: true, keepDblClick: true, enableEnterOnRow: true, actionColumnLabel: 'Actions', resolveActionLabel: (url) => (url.includes('/edit/') ? 'Edit' : 'Open'), } }); ``` Defaults: - `rowInteraction.enabled`: `true`, wenn `rowDblClick.getUrl` vorhanden ist - `showActionButton`: `true` - `keepDblClick`: `true` - `enableEnterOnRow`: `true` - `actionColumnLabel`: `language.actions.column` oder `'Actions'` Hinweis: - Falls eine Seite `actions.enabled: true` explizit setzt, wird keine zusätzliche automatische Row-Action-Spalte injiziert. ### Grid Error Recovery Bei Grid-Fehlern rendert die Factory automatisch einen Retry-CTA im Error-State. - Label kommt aus `language.errorRetry` (Fallback: `Retry`). - Klick auf Retry lädt den Grid-Server-Request erneut. ### Filter UX 2.0 (Globaler Standard) Für Listen gilt standardmäßig: 1. Quick Search sichtbar außerhalb des Drawers. 2. Restliche Filter im rechten Drawer (`data-filter-drawer-*`). 3. Drawer arbeitet `apply-first` (Draft bis `Apply`). 4. Aktive Filter werden als Chips angezeigt (`data-active-filter-chips`). 5. Filter-Reset erfolgt über Chips (`Clear all filters`), nicht im Drawer. JS-Verkabelung: - Standard-Entrypoint ist `initStandardListPage(...)` aus `web/js/core/app-grid-factory.js`. - Der Wrapper kapselt intern `createServerGrid(...)` + (bei `mode: 'drawer'`) `initListFilterExperience(...)`. - Bei Apply/Clear wird URL mit `pushState` fortgeschrieben. - Save-Filter-Usecases lesen den angewandten Zustand über `gridConfig.baseUrl()`. - Chip-State-Logik ist zentral im Wrapper verdrahtet (Default: `app-list-filter-state.js`). Search-only-Ausnahme: - Listen ohne zusätzliche Filter initialisieren über denselben Wrapper mit `mode: 'search-only'` (ohne Drawer/Chips). ### Standard-List-API Für Listen-Templates gilt: - `initStandardListPage({ grid, filters, hooks })` verwenden. - `grid.filterSchema` serverseitig aus `filter-schema.php` (`gridSchemaClientFilters(...)`) übergeben. - Optional zusätzliche Spezialfilter über `grid.extraFilters`. - Bei Drawer-Listen `filters.mode: 'drawer'`, bei Search-only `filters.mode: 'search-only'`. Regel: - keine direkten Aufrufe von `gridFiltersFromSchema(...)` in Listen-Templates - keine direkten Aufrufe von `initListFilterExperience(...)` in Listen-Templates - keine inline `filters: [...]`-Arrays in Listen-Templates - MultiSelect-Filter immer als CSV-Key (`*_ids`, `actions`, `event_types` etc.) Serverseitig passendes Template-Markup: - normale Select-Filter über `selectFilter(...)` - MultiSelect-Filter über `multiSelectFilter(...)` ### List Titlebar Standard Für Listen-Titlebars gilt: - Titlebar über `templates/partials/app-list-titlebar.phtml` rendern. - Titel über `$listTitle` setzen. - Aktionen über `$listTitleActionsHtml` als Slot (captured HTML) übergeben. Beispiel: ```php ``` ### List Tabs Standard Für Tenant- oder Kontext-Tabs in Listen gilt: - Tabs über `templates/partials/app-list-tabs.phtml` rendern. - Tab-Items über `$listTabsItems` mit `href`, `label`, `active` übergeben. - Optional `id` über `$listTabsId` setzen. ### List Purge Action Standard Für Purge-Buttons in Titlebar-Actions gilt: - Purge-Markup über `templates/partials/app-list-purge-action.phtml` rendern. - Formular/CSRF/Button/Confirm werden zentral übergeben via: - `$listPurgeEnabled` - `$listPurgeFormId` - `$listPurgeAction` - `$listPurgeConfirmMessage` - `$listPurgeButtonLabel` ### Aside Grouped Navigation + Persisted Details State Für gruppierte Sidebar-Navigation mit einklappbaren `details` gilt: - Zentraler Storage-Helper: `web/js/core/app-details-open-state.js` (`initPersistedDetailsGroup(...)`). - Standardgruppen über `details[data-details-key]`. - Persistenz-Aktivierung über: - `data-details-storage="..."` (allgemein, z. B. Detailseiten) - `data-aside-details-storage="..."` (Aside-Panels) - Optionales Aktiv-Verhalten im Aside über `data-aside-details-open-active="1"`: - aktive Gruppe bleibt immer offen - effektive Open-Keys = `persistedOpenKeys ∪ activeOpenKeys` Admin-Aside-Standard: - Storage-Key: `aside-admin-sections-v1` - Gruppenkeys: - `admin-organization` - `admin-roles-permissions` - `admin-automation` - `admin-monitoring` - `admin-logs` - `admin-system` ### Detail Page Standard Für Create/Edit-Detailseiten gilt: - Hauptformular über `data-standard-detail-form="1"` explizit opt-in markieren. - Initialisierung läuft zentral über `initStandardDetailPage(...)` aus `web/js/core/app-detail-page-factory.js`. - Auto-Init erfolgt über `web/js/components/app-standard-detail-page.js` (in `app-init.js` eingebunden). Standardverhalten: - Dirty-State über serialisierte `FormData`-Diffs (initial vs. aktuell). - `beforeunload`-Warnung nur bei ungespeicherten Änderungen. - Back/Cancel-Link in `.app-details-titlebar h1 a` fragt bei Dirty-State per Confirm. - `Cmd/Ctrl+S` triggert primären Save/Create-Button (`data-detail-save-primary="1"`). - Submit-Lock/Confirm laufen zentral über die Detail-Action-Policy. - Bei Dirty-State wird in der Titlebar automatisch ein kleiner Hinweis mit Feldanzahl gezeigt. ### Detail Action Policy Für standardisierte Detailseiten gilt zusätzlich: - `initStandardDetailPage(...)` bindet intern `initDetailActionPolicy(...)` aus `web/js/core/app-details-action-policy.js`. - Aktivierung pro Seite über `data-detail-action-policy="1"` auf `.app-details-titlebar` (Default im Shared-Partial aktiv). - Confirm nicht mehr inline per `onclick/onsubmit`, sondern über `data-detail-confirm-message`. - Aktionssemantik über `data-detail-action-kind` (z. B. `save`, `save-close`, `delete`, `danger`). - Optionaler Lock-Override über `data-detail-submit-lock="none"` (Default: `form`). ### Global Confirm Actions Für allgemeine Form-/Button-Confirms außerhalb der Detail-Policy gilt: - zentral über `web/js/components/app-confirm-actions.js` - Dialog-Engine zentral über `web/js/core/app-confirm-dialog.js` (`confirmDialog.confirm(...)`) - Confirm-Text über `data-confirm-message` bzw. auf Detail-Aktionen `data-detail-confirm-message` - optionale Dialog-Meta über - `data-confirm-title`, `data-confirm-confirm-label`, `data-confirm-cancel-label`, `data-confirm-variant`, `data-confirm-focus` - analog für Detail-Aktionen: `data-detail-confirm-*` - kein direktes `window.confirm(...)` in App-JS - keine inline `onclick="return confirm(...)"` / `onsubmit="return confirm(...)"` verwenden ### Frontend Telemetry Standard Für UX-/Fehler-Telemetrie im Frontend gilt: - zentral über `web/js/core/app-telemetry.js` (`telemetry.capture(...)`) - `warnOnce(...)` sendet standardisiert `frontend.warn_once` - AJAX-/Fetch-Fehler laufen zentral über den globalen Fetch-Hook als `frontend.ajax_error` - keine neuen Console-only-Warnpfade für relevante UX-Probleme ohne Telemetrie - Payloads nur mit Whitelist-Feldern und ohne sensitive Rohdaten (keine Tokens/Query-Parameter/Stacktraces) ### Detail Validation Summary Standard Für serverseitige Formularfehler in Create/Edit-Templates gilt: - Fehlerausgabe über `templates/partials/app-details-validation-summary.phtml`. - Keine inline `