major update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Frontend JavaScript
|
||||
|
||||
Letzte Aktualisierung: 2026-02-21
|
||||
Letzte Aktualisierung: 2026-03-04
|
||||
|
||||
## Ordnerstruktur
|
||||
|
||||
@@ -84,18 +84,185 @@ Hinweise:
|
||||
- `order` nur bekannte Sort-Key-Spalten
|
||||
- Bei Search-/Filter-Änderung wird auf Seite 1 zurückgesetzt.
|
||||
|
||||
## Linting
|
||||
### Filter UX 2.0 (Globaler Standard)
|
||||
|
||||
Prüfen mit:
|
||||
Für Listen gilt standardmäßig:
|
||||
|
||||
```bash
|
||||
npx eslint web/js
|
||||
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
|
||||
<?php
|
||||
$listTitle = t('Users');
|
||||
ob_start();
|
||||
?>
|
||||
<a role="button" class="primary" href="admin/users/create"><?php e(t('Create user')); ?></a>
|
||||
<?php
|
||||
$listTitleActionsHtml = ob_get_clean();
|
||||
require templatePath('partials/app-list-titlebar.phtml');
|
||||
?>
|
||||
```
|
||||
|
||||
Auto-fix (nur für sichere Rule-Fixes):
|
||||
### List Tabs Standard
|
||||
|
||||
```bash
|
||||
npx eslint web/js --fix
|
||||
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`
|
||||
- Confirm-Text über `data-confirm-message`
|
||||
- keine inline `onclick="return confirm(...)"` / `onsubmit="return confirm(...)"` verwenden
|
||||
|
||||
### Detail Validation Summary Standard
|
||||
|
||||
Für serverseitige Formularfehler in Create/Edit-Templates gilt:
|
||||
|
||||
- Fehlerausgabe über `templates/partials/app-details-validation-summary.phtml`.
|
||||
- Keine inline `<div class="notice" data-variant="error">`-Blöcke mit `foreach ($errors as $error)` mehr.
|
||||
- Standardtitel kommt aus `t('Please review the following errors')`.
|
||||
- In Action-Handlern strukturierte Feld-Errors als `$validationSummaryErrors = $errorBag->toArray();` bereitstellen.
|
||||
|
||||
Template-Beispiel:
|
||||
|
||||
```php
|
||||
<?php
|
||||
$validationSummaryErrors = $validationSummaryErrors ?? ($errors ?? []);
|
||||
require templatePath('partials/app-details-validation-summary.phtml');
|
||||
?>
|
||||
```
|
||||
|
||||
Hinweis: `--fix` behebt format-/syntaxnahe Themen (z. B. fehlende Klammern), aber nicht automatisch alle Logikprobleme.
|
||||
Optional strukturierte Einträge:
|
||||
|
||||
- `['message' => '...', 'fieldName' => 'email']`
|
||||
- `['message' => '...', 'fieldId' => 'email']`
|
||||
- `['email' => ['Required']]` (FormErrors-ähnliche Map)
|
||||
|
||||
JS-Verhalten über `initStandardDetailPage(...)`:
|
||||
|
||||
- Klick auf Validation-Link fokussiert das passende Feld.
|
||||
- Bei vorhandener Summary wird initial das erste betroffene Feld (oder Summary) fokussiert.
|
||||
- Betroffene Felder werden zentral mit `aria-invalid="true"` und `aria-describedby` (Summary-ID) markiert.
|
||||
|
||||
## Laufzeit-Check (ohne Node)
|
||||
|
||||
Prüfen im Browser:
|
||||
|
||||
1. DevTools öffnen (Console, optional Preserve log aktivieren).
|
||||
2. Kernseiten laden (`/admin/users`, `/address-book`, Audit-Listen).
|
||||
3. Filter setzen/zurücksetzen, Doppelklick-Navigation und Bulk-Aktionen testen.
|
||||
4. Erwartung: keine JavaScript-Errors und keine Unhandled Promise Rejections.
|
||||
|
||||
Reference in New Issue
Block a user