Doku: 5.4 abgeschlossen + 5.5 Teil-Stand im Entwicklungsplan

5.4 als erledigt markiert (page-header/empty-state/table-pagination; table-
panel + searchable-select bewusst nicht gebaut). 5.5 Teil-erledigt (alert +
session-status); password-input + rate-limit-trait mit Begruendung zurueck-
gestellt. Wiedereinstieg-Abschnitt + Datum aktualisiert, Perf-Fix vermerkt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-02 08:44:44 +00:00
parent fc91f29a5c
commit 2b9d1b6339

View file

@ -5,18 +5,18 @@
---
## Stand & Wiedereinstieg (zuletzt: 19.06.2026)
## Stand & Wiedereinstieg (zuletzt: 02.07.2026)
**Fertig:** Phasen **0, 1, 2, 3, 4** vollständig · **5.1** vollständig · **5.2 Teil 1** (Form-Rules-Concern) · **5.3 Teil 1+2** (`Contact::displayName()`, `Category::localizedName()`).
Gesamttest-Suite zuletzt grün (**818 passed, 1 skipped**), Pint sauber. Jeder Schritt ist ein eigener Commit (`git log` Präfix „Optimierung X.Y").
**Fertig:** Phasen **0–4** vollständig · **5.1** · **5.2 Teil 1** (Form-Rules-Concern) · **5.3 Teil 1+2** (`Contact::displayName()`, `Category::localizedName()`) + `portalBadgeColor()`-Toter-Code entfernt · **5.4 vollständig** (page-header 33 Seiten, empty-state 5 States, table-pagination 13 Wrapper) · **5.5 Teil-erledigt** (`<x-portal.alert>`, `<x-session-status>`).
Zusätzlich **Perf-Fix** auf `/admin/me/press-releases`: `User::accessibleCompanyIds()` hydratisierte ~107k Company-Models (jetzt `toBase()`-pluck + memoisiert), `accessibleBy()` nutzt company_id-Subquery, Status-Counts via UNION statt OR-GROUP-BY; neue `PressRelease::accessibleStatusCountsFor()`. Und Fix: fehlendes `.search-wrap`-CSS (Suchfeld /admin/me/firmen).
Pint sauber, betroffene Test-Filter grün.
**Hier geht es weiter** — der gesamte Rest ist **Blade-Component-Extraktion** und braucht **visuelle Kontrolle im Browser** (die automatischen Tests prüfen nur Text-Präsenz, keine Optik). Empfehlung: **eine Component pro Commit**, danach im Browser gegenchecken (`vendor/bin/sail npm run dev`). Reihenfolge des Wiedereinstiegs:
**Hier geht es weiter** — der Rest ist teils UI-Extraktion (visuelle/funktionale QA nötig), teils backend-testbar. Empfehlung weiterhin: **eine Component/ein Schritt pro Commit**, im Browser gegenchecken (`vendor/bin/sail npm run dev`).
1. **5.4 Seiten-Struktur-Components** (risikoärmster UI-Einstieg, rein additiv): `<x-portal.page-header>`, `<x-portal.table-panel>`, `<x-portal.empty-state>` usw. — je Component erst die Komponente bauen (Muster: bestehende `<x-portal.stat-card>`), dann eine Admin-Seite umstellen und visuell prüfen, dann die übrigen.
2. **5.3 Rest** (Badges/Formatierung): `portalBadgeColor()` ist **erledigt** — die Helfer waren tot (kein Call-Site) und wurden entfernt, eine `Portal::badgeColor()`-Zentralisierung entfällt. Offen bleibt nur die UI-Extraktion: `PressReleaseStatus`-Badge-Methode + `<x-portal.portal-badge>`/`<x-portal.status-badge>`/`<x-portal.money>`/`<x-portal.datetime>`.
3. **5.2 Rest**: gemeinsamer `UserUpsertService`/`SaveUserAction` (Achtung: Anlegen vs. Bearbeiten divergieren stark — Bearbeiten trägt zusätzlich Profil/Kontakte/Passwort/2FA; nur den wirklich gemeinsamen Persistenz-Kern ziehen) + Blade-Feld-Components (`billing-fields`, `company-link-picker`, `role-checkboxes`, `contact-fields`).
4. **5.5 Flash/Auth-Components**.
5. **Phase 6** (Web-Frontend) — separat, dem User gehörend, vor jedem Löschen bestätigen lassen.
1. **5.5 Rest** (braucht **funktionale Auth-QA**): `<x-password-input>` — 7 Felder über login/register/reset/confirm divergieren (Label-Zeile mit „vergessen?"-Link, `:placeholder`-Bindung, current-/new-password, Alpine-`showPassword` auf Form-Ebene). Rate-Limit-Trait bleibt zurückgestellt (login vs. magic-link divergieren zu stark).
2. **5.2 Rest**: gemeinsamer `UserUpsertService`/`SaveUserAction` (Achtung: Anlegen vs. Bearbeiten divergieren stark — Bearbeiten trägt zusätzlich Profil/Kontakte/Passwort/2FA; nur den wirklich gemeinsamen Persistenz-Kern ziehen) + Blade-Feld-Components (`billing-fields`, `company-link-picker`, `role-checkboxes`, `contact-fields`).
3. **5.3 Rest**: `PressReleaseStatus`-Badge-Methode + `<x-portal.status-badge>`/`<x-portal.money>`/`<x-portal.datetime>` (die Status-Badge-Call-Sites nutzen divergente Farb-Vokabulare — vor Zentralisierung klären).
4. **Phase 6** (Web-Frontend) — separat, dem User gehörend, vor jedem Löschen bestätigen lassen.
**Wichtig beim Wiedereinstieg:** `resources/views/components/layouts/app/sidebar.blade.php` gehört dem User und bleibt aus allen Commits ausgeschlossen (`git reset -q -- …` vor jedem `git add`). `resources/views/web/**` und `components/web/**` erst in Phase 6.
@ -103,8 +103,8 @@ Keine Fachlogik-Änderung. Reduziert erreichbare Demo-Komponenten und Altlasten.
- [x] **5.1 Tabellen-Concerns** — `HasSortableTable::sort()` (5 byte-identische Kopien zentralisiert; `$sortBy`/`$sortDir`-Defaults bleiben in der Komponente, da sie divergieren) und `HasFullTextSearch::supportsFullTextSearch()` (9 byte-identische Kopien — 4 Index-Listen + 5 Firmen-Comboboxen; nicht mehr genutzte `DB`-Imports entfernt). `AdminPerformanceCache::activeInactiveStats(Builder)` bündelt den identischen `SUM(CASE WHEN is_active)`-Block der Benutzer- und Firmenliste. Tests: erweiterte `AdminPerformanceCacheTest`; abgesichert durch 99 `Admin/`-Tests. **Bewusst zurückgestellt:** `HasResettableFilters` (die `updatedXxxFilter()` divergieren — manche normalisieren zusätzlich Werte; ein generischer `updated()`-Hook wäre eine Verhaltensänderung) und eine gemeinsame `hydrateRelationCounts()` (Count-Hydration unterscheidet sich je Tabelle real).
- [~] **5.2 User create/edit entdoppeln** — **Teil 1 erledigt:** `ProvidesUserFormRules`-Concern bündelt die zwischen Anlegen/Bearbeiten identischen Validierungsblöcke (`userIdentityRules(?int $ignore)`, `userRolesAndCompaniesRules()`, `userBillingRules()`); die nur im Bearbeiten genutzten Profil-/Kontakt-Regeln bleiben dort inline. Beide Formulare nutzen die Spreads, 31 User-Tests grün. **Offen (bewusst, mit User abzustimmen):** gemeinsamer `UserUpsertService`/`SaveUserAction` für die ~175-Zeilen-`save()` (Anlegen/Bearbeiten divergieren stark — Bearbeiten trägt zusätzlich Profil, Kontakte, Passwort, 2FA), sowie die Blade-Feld-Components (`billing-fields`, `company-link-picker`, `role-checkboxes`, `contact-fields`) — Markup-Extraktion braucht visuelle QA (analog zur zurückgestellten 2.8).
- [~] **5.3 Formatierungs-Components/Enum** — **Erledigt (Model-Methoden, getestet):** `Contact::displayName()` (6 Relation-Stellen ersetzt; `ContactDisplayNameTest`) und `Category::localizedName()` (4 Stellen; `CategoryLocalizedNameTest`). **Geklärt & erledigt:** Die `portalBadgeColor()`-Helfer in `companies/index`, `companies/show`, `contacts/index` waren toter Code (kein Aufruf) und wurden entfernt — eine `Portal::badgeColor()`-Zentralisierung ist damit gegenstandslos. **Offen (UI-/Blade-lastig, mit User abzustimmen):** `PressReleaseStatus`-Badge-Methode sowie die Blade-Components `<x-portal.portal-badge>`, `<x-portal.status-badge>`, `<x-portal.money>`, `<x-portal.datetime>` (Markup-Extraktion braucht visuelle QA).
- [ ] **5.4 Seiten-Struktur-Components** — `<x-portal.page-header>`, `<x-portal.stat-cards>`, `<x-portal.table-panel>`, `<x-portal.table-pagination>`, `<x-portal.empty-state>`, `<x-portal.searchable-select>`. (Muster: bestehende `<x-portal.stat-card>`/`components.portal.pagination`.)
- [ ] **5.5 Flash/Auth-Components** — `<x-portal.alert :dismissible>` + `<x-session-status>` app-weit; `<x-password-input>`, `<x-form-field>`/`<x-email-input>`; `ensureNotRateLimited()/throttleKey()`-Trait (login/magic-link divergieren); `<x-auth.divider-actions>`.
- [x] **5.4 Seiten-Struktur-Components** — **erledigt.** `<x-portal.page-header>` (Props size sm/md/lg + descriptionWidth, Slots :meta/:actions) über **33 Seiten** ausgerollt; `<x-portal.empty-state>` (Icon-Slot, variant warm/err, Default-Slot für Onboarding) über **5 States**; `<x-portal.table-pagination>` (:paginator) über **13 Wrapper**. **Bewusst nicht gebaut:** `<x-portal.table-panel>` (nur triviales `<article class="panel">`, keine wiederholte Innenstruktur → Over-Engineering) und `<x-portal.searchable-select>` (existiert nirgends — keine `flux:combobox`). **Bewusst bespoke:** companies/show + press-releases/show (Sonder-Header mit Logo/Untertitel).
- [~] **5.5 Flash/Auth-Components** — **Erledigt:** `<x-portal.alert variant=success|error>` (6 Flash-Blöcke: categories/create+edit, footer-codes/index, presets/index) und `<x-session-status>` (login, forgot-password, register, magic-link). **Bewusst bespoke:** companies/show (icon-loses Flash-Design + info-Variante), verify-email/settings-profile (eigene Status-Logik). **Offen (bewusst zurückgestellt, braucht funktionale Auth-QA):** `<x-password-input>` — 7 Felder über login/register/reset/confirm divergieren (Label-Zeile mit „Passwort vergessen?"-Link vs. schlicht, statische vs. `:placeholder`-Bindung, current-/new-password, Alpine-`showPassword`-State auf Form-Ebene); Extraktion verlagert Alpine-State in die Component und muss die Login-/Registrier-/Reset-Flows funktional (nicht nur optisch) verifizieren. `ensureNotRateLimited()/throttleKey()`-Trait bleibt zurückgestellt (login: 1 Key/5 Versuche/Lockout-Event/clear-on-success vs. magic-link: 2 Keys/3+15 Versuche/Stunden-Decay/kein Event — zu wenig gemeinsame Fläche). `<x-form-field>`/`<x-email-input>`/`<x-auth.divider-actions>` optional, geringer Hebel.
---