# Fortschritts-Log — Hub × FluxUI > Tagebuch der Umsetzung. Neue Einträge **oben** anfügen. > Format pro Eintrag: Datum · Phase · Was wurde gemacht · Wer · Notizen --- ## 2026-06-12 · PM-Vorschau-Umbau + Profil-Feinschliff ✅ - **Was**: (1) PM-Detailseite (Customer) nach Kevins Review umgebaut: Status-Workflow direkt unter den Header, farblich abgehoben (Soft-Hintergrund + 3px-Border je Status: Entwurf hub, Überarbeiten err, In Prüfung warn, neu: Veröffentlicht ok); danach Pressekontakte + Status & Verlauf, dann erst Titelbild und Inhalt. Metadaten ergänzt: Portal, Kategorie, Sprache als Kacheln, Themen (Keywords) als Badges, Backlink — alles im Verlaufs-Panel. (2) **Inhalts-Typografie**: Die Detailseiten nutzten `prose`-Klassen, aber das Tailwind-Typography- Plugin ist gar nicht installiert → Text erschien unformatiert (Diff Editor vs. Vorschau). Neue Klasse `.pr-content` in hub-components.css bildet die Editor-Typografie nach (Absätze, Listen, H2-H4, Links, Blockquote); Customer- UND Admin-Detailseite umgestellt, Lesebreite max 760px. (3) Profil: Pflicht-Badges an Pflichtfeldern, Fokus-Fix nach dem Speichern (blur statt Autofokus auf „Anzeigename"), Submit-Modal-Bestätigungen von rohen Checkboxen auf Flux-Switches (Alpine-State via change-Event, Button-Freischaltung unverändert). - **Dateien**: `customer/press-releases/show.blade.php` (Umbau), `admin/press-releases/show.blade.php` (.pr-content), `resources/css/shared/hub-components.css` (.pr-content), `customer/profile.blade.php`, `components/press-release-submit-modal.blade.php`. - **Build/Test**: Suite 546 passed / 4 skipped, Pint clean, npm run build ok. - **Offene Fragen**: Web-Detailseiten (`web/release-detail` u. a.) nutzen ebenfalls wirkungslose `prose`-Klassen — beim Web-Relaunch auf `.pr-content` (oder Typography-Plugin) umstellen. ## 2026-06-12 · User-Panel-Restarbeiten (Kevins Liste) ✅ - **Was**: Alle Punkte aus `docs/user-admin/User-Panel-Restarbeiten.md` (Status-Tabelle dort): (1) PM-Anlage ohne Firma zeigt eine Meldung mit „Firma anlegen"-Button statt des leeren Editors. (2) Profil-Seite neu gegliedert (Persönliche Daten / Konto / Rechnungsadresse / Einstellungen); Rechnungsadresse vervollständigt um Anrede, Vorname, Nachname, Firmenname (Migration `billing_addresses` + Snapshot-Spalte `company` in `invoice_billing_addresses`, von MAN-Lauf und STR-Spiegelung durchgereicht); „Persönliche Daten übernehmen"-Button gegen die Doppel-Eingabe. (3) Doppelte Validierungsmeldung behoben: fehlende Pflichtfelder melden einzeln unter dem Feld. (4) USt-ID-Validierung: Formatprüfung sofort (hartes Speicher-Gate) + eVatR-Online-Bestätigung (BZSt-REST-API, neuer `VatIdValidationService`, ENV `BILLING_OWN_VAT_ID`, 6h-Cache nur für definitive Ergebnisse, Ausfälle degradieren sanft). (5) Rechnungsadresse ist Pflicht für jeden Checkout (Redirect aufs Profil mit Hinweis); USt-ID wird als Stripe-Tax-ID übergeben. (6) Firmenübersicht zeigt Logos auch für Legacy-Firmen (zentrale logoUrl-Auflösung statt verkürzter Fast-Variante); „Letzte PM" mit Jahreszahl. (7) Checkboxen → Flux-Switches (Boilerplate-Override, Footer-Code); Token-Abilities bleiben Checkbox-Gruppe. Befund zu den Profil-Schaltern: `show_stats`/`disable_footer_code` werden noch nirgends ausgewertet (greifen mit Web-Relaunch) — steht jetzt in den Beschreibungen. - **Dateien**: `customer/profile.blade.php` (Neufassung), `customer/press-releases/create.blade.php` (Guard), `customer/press-kits/index.blade.php` (Logo/Datum), `app/Services/Billing/VatIdValidationService.php` (neu) + `VatIdCheckStatus`-Enum, `VatResolver` (isPlausibleVatId public), `StripeCheckoutService` (Tax-ID-Sync), `CheckoutController` (Guard), `User::hasCompleteBillingAddress()`, `BillingAddress::isComplete()`, Migration, `config/billing.php` (`own_vat_id`). - **Build/Test**: Suite 546 passed / 4 skipped, Pint clean; 14 neue Tests (VatId-Service, PM-Guard, Profil-Validierung, Checkout-Guard). - **Offene Fragen**: `BILLING_OWN_VAT_ID` in .env setzen (eigene USt-ID), sonst bleibt die Online-Prüfung aus; Entscheidung zu den beiden Profil-Schaltern (behalten vs. bis Relaunch ausblenden). ## 2026-06-12 · Responsive-Härtung (Block 3, Punkt 1) ✅ - **Was**: Systemische Responsive-Fehler behoben (Screenshots in `dev/frontend/responsive/`): (1) Das starre Inline-Grid `style="grid-template-columns:1fr auto"` der Seiten-Header (48 Seiten) konnte nie umbrechen — die Aktions-Spalte quetschte die Titel-Spalte (Buchstaben-Umbrüche im Firmennamen, Buttons liefen aus der Box). Ersetzt durch die neue CSS-Klasse `.page-header` (hub-components.css): unterhalb lg stapeln Titel und Aktionen, Aktions-Zeilen bekommen flex-wrap. (2) Firmenkontext-Leiste im App-Layout stapelt jetzt erst ab lg nebeneinander (vorher sm → Überlappung von „Firmenkontext"/ „Aktive Firma" zwischen 640–1024px). (3) Stat-Cards: Label/Meta-Zeile mit flex-wrap (keine Kollision mehr auf schmalen Karten), `.stat-num` mit `overflow-wrap:anywhere` + 26px unter 480px (Text-Werte wie „Businessportal24" sprengen die Karte nicht mehr). (4) KPI-Grids, die schon ab sm auf 4 Spalten gingen (Customer-Dashboard, User-Show, PM-Index), erst ab xl vierspaltig. - **Dateien**: `resources/css/shared/hub-components.css` (.page-header, .stat-num), `resources/views/components/portal/stat-card.blade.php`, `resources/views/components/layouts/app.blade.php`, 48 Seiten-Views (Header-Klasse per sed), 3 KPI-Grids. - **Build/Test**: `npm run build` ok (beide Bundles), Suite 532 passed / 4 skipped. - **Offene Fragen**: Weitere Stellen aus Kevins Klick-Durchgang folgen (Liste Block 3). ## 2026-06-12 · Admin-Zahlungsmodul (P8-Rest) · Zahlungen + Tarif-Verwaltung ✅ - **Was**: Den Phase-8-Platzhalter `/admin/payments` durch das echte Zahlungsmodul ersetzt: KPI-Reihe (aktive Abos, MRR netto, Umsatz 30 Tage brutto, offene Einzel-PMs), Tabellen für Stripe-Abos (mit Tarif-Auflösung über die Price-IDs), Einmalkäufe (Typ/Status/PM-Link) und den lokalen Rechnungsausgang (STR-/MAN-Badge), User-Suche über alle drei Bereiche. Neu: `/admin/payments/plans` — Tarif-Verwaltung mit Edit-Modal (Name, Netto-Preise, PM-Kontingent, Tageslimit, aktiv/inaktiv, Sortierung) und **Sofort-Sync nach Stripe** über den neuen `StripePlanSyncService`: Preisänderung legt ein neues Price-Objekt an und deaktiviert das alte (Stripe-Preise sind unveränderlich), Namensänderung aktualisiert das Produkt, unverknüpfte Tarife werden komplett angelegt. Bestandsabos behalten ihren Preis (Hinweis in UI und Speichermeldung). Buchungs-Seite zieht die Preise ohnehin live aus `plans` → Änderungen wirken sofort überall. Sidebar: eigener Eintrag „Tarife & Pakete" unter Billing. - **Dateien**: `resources/views/livewire/admin/payments/index.blade.php` (Neufassung), `resources/views/livewire/admin/payments/plans.blade.php` (neu), `app/Services/Billing/StripePlanSyncService.php` (neu), `routes/admin.php`, Sidebar. - **Build/Test**: Suite 532 passed / 4 skipped, Pint clean; 13 neue Tests (`AdminPlansPageTest`, `AdminPaymentsPageTest`), Stripe im Test gemockt. - **Offene Fragen**: Refund-Workflow aus dem Admin (vorerst über das Stripe-Dashboard); Einzel-PM-Preis bleibt Config/ENV-basiert. - **Nächster Schritt**: User-Panel-Restarbeiten (Kevin sammelt Liste), Login/Registrierungs-Flow durchtesten, 9G Tageslimit. ## 2026-06-12 · Phase 9F · Tarif-Seite + Checkout-UI ✅ - **Was**: „Buchungen & Add-ons" vom Credit-Konzept-Mock auf echte Daten umgestellt: 4-Tier-Raster aus `plans` (Alpine Monat/Jahr-Toggle, „2 Monate gratis"), Checkout-Buttons auf die 9E-Routen, Einzel-PM als separater No-Abo-Block, Aktueller-Tarif-Panel (Abo / Bestandstarif unbegrenzt / offene Einzelkäufe / leer) mit Kontingent-Kachel, „Abo verwalten" → Stripe Billing Portal (neue Route `me.checkout.billing-portal`), aktive Buchungen + Verlauf real. Credit-Pakete/Marktplatz/Platzierungen entfernt (→ 9I bzw. Phase 2). Stripe Tax im Dashboard aktiviert („SaaS – business use", exklusiv). **Feinschliff nach Review (Kevin)**: Aktueller-Tarif-Card nur bei vorhandener Buchung (kein irreführendes „Unbegrenzt" vor dem Launch; Kontingent-Kachel nur als echte Zahl), Tarif-Cards plakativer (Icon je Tarif, größerer Preis, Trennlinie, mehr Abstand zum Button), „Prüfung und Veröffentlichung inklusive" ohne „KI", „Aktive Buchungen"-Panel entfernt (Info steht im Tarif-Panel), Verlauf als eigene, klar abgegrenzte Sektion. - **Dateien**: `resources/views/livewire/customer/bookings.blade.php` (Neufassung), `app/Http/Controllers/CheckoutController.php` + `app/Services/Billing/StripeCheckoutService.php` (Billing Portal), `routes/customer.php`. - **Build/Test**: Suite 519 passed / 4 skipped, Pint clean; 9 neue Tests in `BookingsPageTest`, `PanelConsolidationTest` auf neue Seite angepasst. - **Offene Fragen**: Stripe Tax + Produkt-Sync vor Relaunch im Live-Mode wiederholen. - **Nächster Schritt**: 9G Tageslimit (`plans.daily_limit` beim Veröffentlichen), dann 9H Einzel-PM-Abo-Brücke, 9I Launch-Credits. ## 2026-06-12 · Phase 9E · Stripe-Anbindung komplett ✅ - **Was**: Produkt-Sync nach Stripe (Tarife + Einzel-PM, Netto-Preise, Test-Mode), Webhook-Verarbeitung (STR-Spiegelung + Einmalkauf-Erfüllung; Endpoint `pressekonto.com/stripe/webhook` registriert, Secret gesetzt), Checkout-Flows als Backend (`me.checkout.subscription`, `me.checkout.single-pm`; Stripe Tax via `Cashier::calculateTaxes()`), Slot-Logik vom Stub auf Plan-Kontingent umgestellt: Abo → Tarif-Quote, danach Einmalkauf-Verbrauch (consumed + PM-Verknüpfung), **Grandfathered = unbegrenzt** (Entscheidung 12.06.2026, Bestandsschutz); Stub-Spalte `users.press_release_quota` entfernt. - **Dateien**: `app/Http/Controllers/CheckoutController.php`, `app/Services/Billing/StripeCheckoutService.php`, `app/Listeners/ProcessStripeWebhook.php`, `app/Console/Commands/SyncStripePlans.php`, `app/Models/User.php`, `app/Services/PressRelease/PressReleaseService.php`, `routes/customer.php`, `config/billing.php`, Buchungs-Seite (Rückmeldung), Submit-Modal/Views (Kontingent-Anzeige). - **Build/Test**: Suite 510 passed / 4 skipped, Pint clean; Stripe-Sync live gegen Test-Mode gelaufen (Einzel-PM: `STRIPE_PRICE_SINGLE_PM` in .env). - **Offene Fragen**: Stripe Tax im Dashboard aktivieren (Ursprungsadresse), sonst schlägt der Checkout fehl; Live-Mode-Sync vor Relaunch. - **Nächster Schritt**: 9F Tarif-Seite/Buchungs-UI an die Checkout-Routen anbinden (Mock ablösen), danach 9G Tageslimit. ## 2026-06-12 · Phase 9D · Tarif-Datenmodell, Rechnungskreise & USt ✅ Zentrale Doku: `docs/user-admin/Billing-und-Rechnungskreise.md`. Plan: `docs/PHASE-9-FLOW-UND-TARIFE-PLAN.md` (9D ✅, 9E in Arbeit). **Tarif-Datenmodell** - Laravel Cashier ^16.5 (freigegeben), `User` ist Billable, Cashier-Migrationen published + ausgeführt. - `plans` (4 Tiers, Netto-Preise, Kontingente, Tageslimits, Seeder), `single_purchases` (Einzel-PM, Extra-PM, Boost, PDF-Nachweis). - `hasActiveBooking()` prüft hybrid: Cashier-Abo ∨ bezahlter Einmalkauf ∨ aktive Legacy-Vereinbarung. **Hybride Rechnungskreise (Entscheidung 12.06.)** - `InvoiceNumberGenerator`: atomare fortlaufende Nummern, STR- (Stripe) / MAN- (manuell); Alt-Archiv `legacy_invoices` bleibt unverändert. - MAN-Fälligkeitslauf `billing:generate-manual-invoices` (täglich 04:30): Periodenende → Rechnung mit Adress-Snapshot → Periode weiter. **Legacy-Migration (P6.6, Runbook entsperrt)** - `legacy:grandfather-subscriptions`: aktive jährliche Vereinbarungen aus dem Rechnungsarchiv (22 im Test-Snapshot, 4 sofort fällig) als `grandfathered` nach `user_payment_options` — Replay-fähig für den Lauf kurz vor Relaunch. **USt (Einwand 12.06.: alle neuen Preise netto; Legacy war brutto)** - `VatResolver`: DE immer Steuer, EU nur mit USt-ID befreit (Reverse Charge + Pflichthinweis `invoices.tax_note`), Drittland befreit. - `vat_id` an Rechnungsadresse + Rechnungs-Snapshot; Netto-Ableitung der Legacy-Beträge (199 € brutto → 167,23 € netto + 31,77 € USt — Brutto bleibt für DE-Bestandskunden identisch). - Offen: VIES-Validierung, PDF-Layout, Steuerberater-Abnahme. **Verifikation**: Suite 490 passed / 4 skipped (39 neue Billing-Tests über 4 Commits). Pint clean. Dry-Runs gegen Echtdaten validiert. --- ## 2026-06-12 · Phase 9 · Veröffentlichungs-Flow Block 1 (9A–9C) ✅ Plan-Doc: `docs/PHASE-9-FLOW-UND-TARIFE-PLAN.md`. Grundlage: `docs/Decision-Update Preisstruktur & Veröffentlichungs-Flow.md` (+ Entscheidung 12.06.: Gelb geht direkt live). Vorab Block 0: Repo aufgeräumt (3 Artefakt-Dateien im Root entfernt), der unkommittierte Stand vom 29.05.–11.06. in drei Commits gesichert (Vite/Multi-Domain-Infra, User-Panel/KI-Pipeline, Doku-Sync). **9A — Gelb-Routing Direkt-Live** - `routeByClassification()`: Gelb durchläuft denselben Auto-Publish-Pfad wie Grün (`autoPublishApproved()`); nur Rot wird abgelehnt. - Scheduler publiziert fällige gelbe + grüne PMs; unklassifizierte bleiben als Fallback in der manuellen Queue. **9B — Slot-Verbrauch bei Veröffentlichung** - Increment aus `submitForReview()` entfernt; `publish()` und `changeStatusFromAdmin()` zählen idempotent beim ersten `published`-Übergang (Prüfung über Status-Logs). Rot kostet nichts. - Submit-Guard: Einreichen erfordert freien Slot (`QuotaExceededException`, API 422). **9C — Submit-Gate + Funnel-Fix** - `User::hasActiveBooking()`-Stub hinter `config/billing.php` (`enforce_booking`, Default aus) — Tarif-Modul ersetzt nur den Rumpf. - Einreichungs-Modal zeigt ohne Buchung einen Buchungs-Hinweis mit CTA zur Buchungs-Seite; Server-Guard (`BookingRequiredException`), API antwortet 402. - **Befund + Fix**: Customer-Create legte PMs bei „Zur Prüfung senden" direkt mit Status `review` an — vorbei an Blacklist, Quota, KI und Status-Log. Jetzt: immer Draft anlegen, dann `submitForReview()`. **Verifikation**: Suite 451 passed / 4 skipped (9 neue Tests: Quota-Semantik, Gelb-Routing, Gate via Service/API/Modal). Pint clean. Nächster Schritt: Review-Stopp, dann Block 2 (9D–9J: Tarif-Datenmodell, Stripe/Cashier — Dependency-Freigabe nötig, Tarif-UI, Tageslimit, Einzel-PM, Launch-Credits). --- ## 2026-05-29 · Phase 8 · User-Panel-Konsolidierung abgeschlossen (8F–8K) ✅ Abschluss von Phase 8. Die erste Hälfte (8A–8E: Show-Page-Lücken, Listen-Indikatoren, Pressekontakt-Warnung, Firmen-Liste auf Mockup-Niveau) war bereits im Commit „Optimierung der User und Admin Panels" enthalten, aber undokumentiert. Heute der zweite Block plus Doku-Sync. Roadmap-Doc: `20-PHASE-8-USER-PANEL.md`. Plan: `docs/PHASE-8-USER-PANEL-PLAN.md`. **8D — Doku-Sync**: `docs/user-admin/checkliste-user-backend.md`, `docs/STATUS-ABGLEICH-USER-PANEL.md` und `Admin-User.md` auf den echten IST-Stand gezogen (8A–8E waren als „offen" markiert, sind aber umgesetzt). **8F — SVG-Titelbild-Platzhalter** - 9 Varianten (3 Muster × 3 Hub-Farben) in `public/images/press-release-placeholders/`. - `App\Enums\PressReleasePlaceholder` (Default, Seed-deterministisch). - `` + Picker-Modal (`components.press-release-placeholder-picker`, dispatcht `placeholder-selected`). **8G — Titelbild-Schema + Cover-Resolver** - Migration `placeholder_variant` auf `press_releases` (nullable). - Model-`creating`-Hook setzt deterministisch eine Variante. - `App\Services\PressRelease\PressReleaseCoverImage` (`coverUrl`/`coverIsPlaceholder`). - Hero-Bild in Customer-/Admin-Show; Vorschau + Picker in Create/Edit. **8H — Bild-Upload mit Lizenz-Pflichtfeldern** - Migration: `author`, `license_type`, `license_url`, `persons_consent`, `rights_confirmed_at` auf `press_release_images`. - `App\Enums\ImageLicenseType` (CC/kommerziell erzwingen Lizenz-URL). - Image-Manager: Urheber (Pflicht), Lizenztyp (Pflicht), Lizenz-URL (bedingt), Personen-Einwilligung, Rechte-Bestätigung (Pflicht). - Abweichung: Upload-Control bleibt `flux:input type=file` statt `flux:file-upload` (Stabilität). Lizenzerfassung vollständig. **8J — Quota-Stub (vor 8I, damit Modal darauf aufsetzt)** - Migration: `users.press_release_quota` (3), `..._used_this_month` (0). - `User::pressReleaseQuotaRemaining()`, Decrement in `PressReleaseService::submitForReview()`. - Command `press-releases:reset-monthly-quota` + Scheduler (`monthlyOn(1, '00:05')`). **8I — Veröffentlichungs-Modal (Customer-Show)** - „Zur Prüfung einreichen" öffnet jetzt ein FluxUI-Modal statt `wire:confirm`: Rechtshinweis (Platzhalter, anwaltlich zu prüfen), Kontingent-Badge, 3 Bestätigungs-Checkboxen (Submit via Alpine disabled bis alle gesetzt) → ruft das bestehende `submitForReview()`. **8K — Abschluss** - Neue Tests: `PressReleasePlaceholderTest` (8), `PressReleaseImageLicenseTest` (3), `PressReleaseQuotaTest` (3), `PressReleasePublishModalPhase8iTest` (2). - Suite: **375 passed, 4 skipped**. Pint clean. `npm run build:portal` clean. --- ## 2026-05-29 · Wartung · Test-Regression-Fix + Phase-7-Doku nachgezogen Review der Gesamt-Umsetzung. Zwei Befunde behoben: ### Fix — `ProfileUpdateTest > profile page is displayed` Seit dem Customer-Portal-Umbau ist `/settings/profile` ein **Redirect** auf `/admin/me/profile` (route `me.profile`), die Profil-Pflege liegt jetzt in der Volt-Komponente `customer.profile`. Der Starter-Kit-Test machte aber weiterhin `GET /settings/profile`→`assertOk()` und lief deshalb auf 302 statt 200 (undokumentierte Regression aus dem Commit „Optimierung der User und Admin Panels", 2026-05-22). Test umgestellt auf `assertRedirect('/admin/me/profile')`. Das Rendern der Zielseite ist bereits durch `CustomerProfileSecurityTest` (Volt-Komponententest) abgedeckt, also keine Doppelung. Die übrigen 4 Tests der Datei nutzen weiterhin `Volt::test('settings.profile')` / `delete-user-form` (Komponenten existieren und sind funktional). ### Doku-Sync Phase 7 war im Code vollständig umgesetzt (siehe Eintrag unten), aber im Log nicht erfasst und in den Status-Tabellen widersprüchlich (`19-PHASE-7` „✅", `03-WEITERE-PHASEN` „🟡 in Planung", `README` noch auf Stand Phase 2). Nachgezogen: - Phase-7-Eintrag in diesem Log ergänzt. - `README.md` Status-Tabelle auf Phase 0–7 aktualisiert. - `03-WEITERE-PHASEN.md` Phase 7 von „🟡 In Planung" auf „✅ abgeschlossen" + Gesamt-Status-Tabelle ergänzt. **Validierung**: - `php artisan test --compact` → 359 passed, 3 skipped, 1 failed (weiterhin nur der pre-existing `ApiDocumentationTest`, fehlende `docs/api/v1.yml`) - `vendor/bin/pint tests/Feature/Settings/ProfileUpdateTest.php` → fixed (EOF-Blankline) - `npm run build:portal` → grün (436.51 KB CSS / 58.95 KB gzip) --- ## 2026-05-22 · Phase 7 · Press-Release-Form-Refactor ✅ (retroaktiv dokumentiert) > Großes Modul-Refactor außerhalb der ursprünglichen hub-flux- > Roadmap (0–6). Vorlage: > `dev/frontend/tailwind_v3/User Neue Mitteilung presseportale.html`. > Plan-Doc: `19-PHASE-7-PRESS-RELEASE-FORM.md`. > Dieser Eintrag wurde am 2026-05-29 nachgetragen — die Arbeit > selbst entstand am 21./22.05. (Commit „Optimierung der User und > Admin Panels"). **7A — Migrations + Models** - `add_phase7_fields_to_press_releases` (subtitle, boilerplate_override, scheduled_at, embargo_at — alle nullable) - `create_press_release_attachments_table` (analog press_release_images, mit sort_order, soft-deletes) - `add_boilerplate_to_companies` (companies.boilerplate) - Models + Factory + Relationen + Casts. **7B — Editor + Sanitizer** - `composer require mews/purifier ^3.4` (approved) - `App\Services\PressRelease\PressReleaseHtmlSanitizer` (Allowlist p/br/h2/h3/strong/em/u/ul/ol/li/blockquote/a) - `` → `` mit reduzierter Toolbar. - Test: `PressReleaseHtmlSanitizerTest`. **7C/7D — Customer Create + Edit Form (UI)** - 2-Spalter mit sticky Settings-Sidebar (Status & Absenden, Portal read-only-Badge, Pressekontakt-Single-Select, Themen-Tags, Veröffentlichung, SEO). - Linke Spalte: Firma-Selector, Titel/Untertitel mit Counter-Pillen (`.pr-meter`), `flux:editor`, Medien, Anhänge, Boilerplate-Box mit Override-Toggle. - Hub-Form-Bausteine in `hub-components.css` ergänzt (`.pr-form-label`, `.pr-meter`, `.pr-bald-badge`, `.pr-ai-hint`, `.pr-check-row`, `.pr-boiler`, `.pr-tag-chip`, `.pr-pub-opt` …). - Live-Re-Validation (`updated()` re-validiert Felder mit bestehendem Error) + Sammel-Toast bei Validierungsfehler. - Neues JS-Asset `portal-form-hooks` (Build). - Tests: `CustomerPressReleaseCreatePhase7Test` (8), `CustomerPressReleaseEditPhase7Test` (9). **7E — Anhänge-Manager** - `App\Services\PressRelease\PressReleaseAttachmentStorage` - Komponente `livewire/components/press-release-attachments-manager.blade.php` (upload/remove/reorder, PDF/DOCX/XLSX/PPTX, Tile-Layout). **7F — Scheduling + Embargo** - UI: Radio „Geplanter Termin" + `datetime-local`, Embargo-Switch + Date-Picker. - Validation: scheduled_at min. 5 Min Zukunft, embargo_at Zukunft (nur wenn Toggle aktiv). - `PressReleaseService::publish()` → `resolvePublishedAt()` (published_at > scheduled_at > embargo_at-Verschiebung > now). - Command `press-releases:publish-scheduled` (`App\Console\Commands\PublishScheduledPressReleases`, `--dry-run`, `--limit=N`) + Scheduler-Eintrag in `routes/console.php` (`everyFiveMinutes`, `withoutOverlapping`, `runInBackground`). - Tests: `PressReleaseSchedulingTest` (11), `CustomerPressReleaseSchedulingFormTest` (5). **Effekt auf die Suite**: von dokumentierten ~231 auf ~360 Tests gewachsen. Admin-Create/Edit ziehen das Layout vorerst NUR optisch mit; Scheduling/Embargo-UI bleibt Customer-seitig (laut Plan-Doc Out-of-Scope für Admin in Phase 7). --- ## 2026-05-20 · Phase 6 · Auth-Cleanup ✅ Mit Phase 6 ist die hub-flux-Roadmap (Phase 0–6) **vollständig abgeschlossen**. Schlanker Cleanup-Task ohne Risiko. **Inventur** (Referenzen geprüft via `rg` über `resources/` + `routes/`): | Datei | Status | |---|---| | `components/layouts/app/sidebar.blade.php` | aktiv (primäres Portal-Layout) | | `components/layouts/auth/pressekonto.blade.php` | aktiv (6 Auth-Pages) | | `components/layouts/auth.blade.php` | nur in `login-simple`/`test-simple` → weg | | `components/layouts/auth/simple.blade.php` | Starter-Kit-Rest mit hardcoded `class="dark"` → weg | | `components/layouts/auth/split.blade.php` | dito → weg | | `components/layouts/auth/card.blade.php` | dito → weg | | `components/layouts/app/header.blade.php` | nirgends referenziert → weg | | `livewire/auth/login-simple.blade.php` | Debug-Page ohne Route → weg | | `test-simple.blade.php` | Debug-Page ohne Route → weg | **Gelöscht**: 7 Files, insgesamt ~17 KB Code **Aktualisiert**: `_docs/FORTIFY-SANCTUM-SETUP.md` (Code-Beispiel von `components.layouts.auth` auf `components.layouts.auth.pressekonto`). **Side-Effect**: Portal-CSS-Bundle ist ~2.4 KB kleiner geworden (422.62 KB statt 425.07 KB), weil weniger Klassen via Tailwind JIT entdeckt werden. **Validierung**: - `php artisan view:clear && npm run build:portal` → grün - `vendor/bin/pint --dirty --format agent` → passed - Tests: 230/231 (nur der bekannte pre-existing `ApiDocumentationTest`) **Roadmap-Update**: - `03-WEITERE-PHASEN.md` Phase 6 von „⚪ optional (Auth-Konsolidierung)" auf „✅ abgeschlossen (Auth-Cleanup)" umgestellt - Status-Tabelle: Phase 6 ✅ - „Nächste Schritte" angepasst: kein hub-flux-Item mehr offen, nur noch eigenständige Initiativen (Dark-Mode-Smoke-Test, PM-Form-Wizard-Refactor, Web-Frontend-Block) **Lesson**: Mit der Anti-Flash-Bridge aus Phase 5 wären die hardcoded `class="dark"`-Layouts zu einer Stolperfalle für Light-Mode-User geworden, sobald jemand sie reaktiviert hätte. Cleanup zum richtigen Zeitpunkt. Plan-Doc: `18-PHASE-6-AUTH-CLEANUP.md` --- ## 2026-05-20 · Phase 5 · Dark-Mode-Bugfixes (User-Findings) Nach dem ersten visuellen Smoke-Test des Users zwei Folge-Fixes geliefert, plus die Anti-Flash-Bridge: ### Fix 1 — Logo „presse" im Dark Mode `components/web/brand-mark.blade.php`: Bei `variant="auto"` (Default) wird die Name-Farbe jetzt über CSS-Custom-Property `--brand-mark-name-color` mit Light-Mode-Fallback gesetzt. Im Portal-Dark-Mode setzt `design-tokens.css` `.dark`-Block die Variable auf `#ffffff`. Hub-Frontend ist Light-Only, dort bleibt die Variable undefiniert und der Marken- Standardwert greift. ### Fix 2 — Navlist-Flackern beim Klick `portal.css` Navlist-Item-Block: - Hover im Dark Mode auf `var(--color-hub-soft)` (Light-Mode-Hover `var(--color-bg)` ist dunkler als die Sidebar — wirkte als „Eindrücken" statt Hervorheben) - `:active`, `:focus` explizit auf `var(--color-hub-soft)` mit `outline: none` → verhindert Browser-Default-Tap-Flash - `-webkit-tap-highlight-color: transparent` für mobile Browser - `:focus-visible` mit Hub-Ring für Tastatur-A11y bleibt erhalten ### Fix 3 — Anti-Flash-Bridge (FOLT bei `wire:navigate`) Bei `wire:navigate` morpht Livewire das DOM, das neue HTML kommt vom Server **ohne** `class="dark"` (Server kennt LocalStorage nicht). Folge: kurzer weißer Theme-Flash, bis JS die Klasse wieder anhängt. User hat das richtig diagnostiziert. Lösung: 1. **JS-Bridge** in `partials/head.blade.php` + `partials/admin-head.blade.php`: wrappt `window.Flux.applyAppearance`, spiegelt den effektiv applizierten Modus (bei `system` aus DOM-Klasse abgelesen → `dark`/`light`) in ein `flux_appearance`-Cookie. Schreibt auch beim initialen Page-Load. 2. **Server-Side Render**: ``-Tag in `components/layouts/app/sidebar.blade.php` und `layouts/admin-master.blade.php` setzt `class="dark"` direkt, basierend auf dem Cookie: `@class(['dark' => request()->cookie('flux_appearance') === 'dark'])` 3. **Bonus**: hardcoded `class="dark"` aus `admin-master.blade.php` raus (war Light-User-Bug-Falle). Erstbesuch: einmaliger Flash (Cookie noch nicht gesetzt). Alle weiteren Pageloads: kein Flash mehr. ### Latente Bug-Fixes nebenbei - `customer/tokens.blade.php` Z. 137: `--color-ink-deep` Token war nie definiert → durch `--color-panel-dark-2` ersetzt (konstant dunkel in beiden Modi) - `customer/security.blade.php` 2FA-QR: erklärender Kommentar für `bg-white` (Scan-Tauglichkeit) **Validierung**: - Build, Pint, Tests: 230/231 (nur ApiDocumentationTest) --- ## 2026-05-20 · Phase 5 · Dark Mode ✅ Phase 5 (Dark Mode) ist abgeschlossen. Die meiste Arbeit war faktisch schon in Phase 0–4 erledigt — die konsequente Token-Disziplin zahlt sich jetzt voll aus. **Was schon da war (Stand bei Phase-5-Start):** - `shared/design-tokens.css` hat einen kompletten `.dark { … }`-Block (Z. 182–248) mit allen Surfaces, Hub-Blau (heller), Bernstein (heller, mit konstantem `--color-accent-warm`), Ink-Skala (invertiert), Status-Farben, Bridge-Dots, Schatten und `color-scheme: dark` - `portal.css` hat `@custom-variant dark (&:where(.dark, .dark *))` + Dark-Mode-Shadow-Overrides für Primary-Buttons - `hub-components.css` ist 100 % token-basiert - `panel-dark` nutzt `--color-panel-dark*` (konstant) - `@fluxAppearance` in beiden Head-Partials integriert - Switcher in Sidebar (Desktop + Mobile) + `settings/appearance.blade.php` **Was im Rahmen von Phase 5 gefixt wurde:** 1. **`customer/tokens.blade.php` Z. 137** — Token-Anzeige nutzte ein nicht existentes `--color-ink-deep`. Damit war der Code-Block bisher transparent (im Dark Mode wäre `--color-ink` hell → weißer Text unlesbar). Umgestellt auf `--color-panel-dark-2` (konstant dunkel, weißer Text in beiden Modi korrekt). 2. **`customer/security.blade.php` Z. 270** — 2FA-QR-Code in `bg-white`. Bewusst belassen (QR-Codes brauchen schwarz-auf-weiß für Scan-Tauglichkeit), erklärender Kommentar ergänzt. **Was unkritisch ist (kein Fix):** - Alle weiteren `bg-white` / `text-white`-Treffer in `customer/dashboard.blade.php` sind im `panel-dark` Block (konstant dunkel) oder auf `--color-accent` (Bernstein in beiden Modi) — passt - `admin/dashboard.blade.php` Quick-Action Hover (`group-hover:bg-hub group-hover:text-white`) — Hub-Bg ist in beiden Modi dunkel genug für weißen Text - `customer/press-kits/show.blade.php` Logo-Bg `bg-white` — bewusst, weil Firmen-Logos einen weißen Bg brauchen - Sidebar-`dark:bg-zinc-*` Helper aus dem Starter-Kit — Zinc-Skala ist in `portal.css` auf warmes Buchpapier gemapped, läuft **Außerhalb Phase 5:** - `shared-styles.css` hat `.dark .card`, `.dark .slider-*`, `.dark .highlight-*`, `.dark .section-*` — diese sind für den Web-Bereich (Hub-Frontend, presseecho, businessportal24). `portal.css` importiert `shared-styles.css` **nicht** — keine Kollision. - Web-Frontend bleibt Light-Only (per Roadmap-Definition) **Validierung:** - Build: `npm run build:portal` → grün - Pint: `vendor/bin/pint --dirty --format agent` → passed - Tests: 230/231 (1 pre-existing `ApiDocumentationTest`, kein Bezug zur UI-Migration) **Empfehlung:** Im Browser einmal User-Menü → Erscheinung → „Dunkel" + Dashboard/Listen/Detail/Security (QR)/Tokens visuell durchklicken für den Smoke-Test (Pest hat keine echte Rendering-Pipeline). Roadmap-Update: - `03-WEITERE-PHASEN.md` Phase 5 von „⚪ später" auf „✅ abgeschlossen" gesetzt - Status-Tabelle aktualisiert - Empfehlungen verschoben: Phase 6 Auth-Cleanup + manueller Smoke-Test nach oben Plan-Doc: `17-PHASE-5-DARK-MODE.md` --- ## 2026-05-20 · Phase 4 · offiziell abgeschlossen ✅ Mit dem Abschluss von 4J ist Phase 4 (Listen/Detail-Pages durchgehen) in allen 10 Sub-Päckchen 4A–4J durch: | ID | Bereich | |---|---| | 4A | Press-Releases Listen (Admin + Customer) | | 4B | Press-Releases Detail/Show | | 4C | Press-Releases Forms (create/edit) | | 4D | Companies (Admin) | | 4E | Profile/Settings (Admin + Customer) | | 4F | Restliche Admin-Bereiche (12 Module) | | 4G | Restliche Customer-Bereiche (5 Module) | | 4H | Customer „Meine Pressemitteilungen" Mockup-Stil | | 4I | Admin „Pressemitteilungen" — Patterns übertragen | | 4J | Dashboard-PM-Listen mit 4H/4I-Patterns | **Roadmap-Cleanup**: - `03-WEITERE-PHASEN.md` Phase 4 von „🚧 iterativ" auf „✅ komplett abgeschlossen" gesetzt - Phase 2 + 3 (Customer- + Admin-Dashboard) waren faktisch schon in Phase 1 umgesetzt — Status retroaktiv auf ✅ korrigiert (mit Hinweis auf 4J-Verfeinerung) - Neuer „Gesamt-Status"-Block in `03-WEITERE-PHASEN.md` mit Phasen-Tabelle, Sub-Päckchen-Tabelle und konkreten Empfehlungen für die nächsten Schritte (Phase 5 Dark Mode > Phase 6 Auth-Cleanup > Web-Frontend-Block als eigene Roadmap) - Erkenntnis: Das Mockup `Veröffentlichen Tailwind.html` ist eine **Public-Facing-Landing-Page** von businessportal24 (Web-Frontend), nicht ein Backend-PM-Wizard. Gehört in den separaten Web-Frontend-Block, nicht in Phase 4. **Verifikationsstand**: - 230/231 Tests grün (1 pre-existing `ApiDocumentationTest`-Fail, unverändert seit 4D) - Build (Portal) sauber: 424 KB CSS / 16 KB JS gzipped - Pint sauber - 16 Plan-Dokumente in `dev/frontend/hub-flux/` (`00`–`16` + `PROGRESS.md` + `README.md`) --- ## 2026-05-20 · Phase 4J · Dashboard-PM-Listen mit 4H/4I-Patterns - **Anlass**: Nach 4H/4I sahen die Voll-Listen exakt wie das Mockup aus, aber die kompakten PM-Listen in den Dashboards waren noch im alten Stand (Badge rechts, Sub-Zeile ohne Portal). 4J schließt diese Inkonsistenz. - **Befund**: Beide Dashboards waren bereits in Phase 1 großzügig Hub-styled (Page-Header, KPI-Reihe, ``, ``, `panel-dark` Brand-Bridge usw.) — die Roadmap-Status „⚪ später" für Phase 2 + 3 waren also veraltet und wurden im Zuge dieses Päckchens auf ✅ gesetzt. - **Plan-Dokument**: `16-PHASE-4J-DASHBOARD-LISTS.md`. ### Customer-Dashboard (`customer/dashboard.blade.php`) - Volt: `recent` Select um `portal` + `published_at` erweitert - `recent`-Liste: - Portal-Pills (`pe`/`bp`) neben dem Badge (Both → beide Pills) - Sub-Zeile mit `PM-{id} · Firma · Datum` - `published_at` als Primärdatum für veröffentlichte PMs - Badge-Mapping erweitert (`muted` für archived/draft statt `hub`) ### Admin-Dashboard (`admin/dashboard.blade.php`) - `recentPRs`-Liste: - Portal-Pills neben Badge (Both → beide Pills) - Sub-Zeile mit `PM-{id} · Firma · User · Datum` - Badge-Mapping mit `muted` für archived/draft - `pendingReviews`-Liste (Review-Queue): - Row-Tinting `is-row-warn` (gelblich) für alle Items - Inline-Action „Prüfen →" rechts oben (Link zur Show-Page, weil Admin-Dashboard Controller+Blade ist und keine direkte Wire-Methode hat) - Portal-Pills unter dem Titel - Container von `` zu `
` umgestellt, weil zwei Links nebeneinander (Titel-Link + Inline-Action) sonst HTML-invalid wären - Hover-Underline auf Titel-Link für klare Klickbarkeit ### Tests/Verifikation - `DashboardTest` (Customer) → 5/5 grün, 21 Assertions - Volle Suite → 230/231 grün, 1 pre-existing Fail (`ApiDocumentationTest`) - `npm run build:portal` → grün (424 KB CSS, +0,2 KB) - `vendor/bin/pint --dirty` → passed ### Dateien - `dev/frontend/hub-flux/16-PHASE-4J-DASHBOARD-LISTS.md` (neu) - `resources/views/livewire/customer/dashboard.blade.php` - `resources/views/admin/dashboard.blade.php` - `dev/frontend/hub-flux/PROGRESS.md` (dieser Eintrag) - `dev/frontend/hub-flux/03-WEITERE-PHASEN.md` (4J + Phase 2/3 retroaktiv auf ✅) --- ## 2026-05-20 · Phase 4I · Admin „Pressemitteilungen" auf Mockup-Patterns - **Anlass**: Direktes Folgepäckchen zu 4H — die in der Customer-Liste entwickelten Mockup-Patterns werden auf die zentrale Admin-Pressemitteilungen-Liste übertragen (Editorial-Workflow). - **Plan-Dokument**: `15-PHASE-4I-ADMIN-PRESS-RELEASES.md`. ### Volt-Komponente erweitert (`admin/press-releases/index.blade.php`) - `pressReleaseStats()` um `rejected` + `archived` Counter erweitert (zwei zusätzliche `selectRaw` im gleichen Statement, weiter über `AdminPerformanceCache`) - Neue Methoden: `setView($view)` (setzt Statusfilter + resetPage), `resetFilters()` (kompletter Reset inkl. Entity-Lookups) ### Markup auf Mockup-Stil - **Saved-Views-Tabs**: 6 Tabs (Alle, In Prüfung, Veröffentlicht, Entwürfe, Abgelehnt, Archiv) mit Counter-Pillen, `is-active`-State spiegelt `$statusFilter` - **Active-Chips** unter Filter-Panel: 8 Filter-Typen (Suche, Status, Portal, Sprache, Kategorie, User, Firma, Kontakt) mit Remove-Button + „Alle zurücksetzen"-Link - **Tabelle umgestellt** (8 → 7 Spalten): - Status + Inline-Action (Freigeben/Ablehnen für Review, Archivieren für Published) — Inline-Actions sind `flux:modal.trigger` für die bestehenden, test-kritischen Modals - Titel mit Sub-Zeile (PM-ID + Firma + Sprache, klickbar) - Portal-Pills (presseecho/businessportal24, bei `Portal::Both` beide nebeneinander) - Kategorie (truncate) - Datum mit Status-Kontext-Sub („veröffentlicht · HH:MM", „eingereicht · HH:MM", …) und `published_at` als Primärdatum wenn vorhanden - Hits monospace - Aktionen-Spalte rechts reduziert auf view + edit (Status-Actions wandern in die Status-Zelle) - **Row-Tinting**: review → `is-row-warn`, rejected → `is-row-err` - **Empty-State 2-stufig**: „mit Filter" → Reset-CTA, „ohne Filter" → Anlegen-CTA ### Was unangetastet bleibt (Test-kritisch) - `publish()`, `reject()`, `archive()`, `sort()`, `with()`, alle `clearXFilter()`, `resetEntityFilters()` - 3 Modals mit Strings „Pressemitteilung veröffentlichen?", „Pressemitteilung ablehnen?", „Pressemitteilung archivieren?" — werden weiter vom Markup gemountet, jetzt zusätzlich via Inline-Action triggerbar - Combobox-Lookups für User/Firma/Kontakt (Schema + Verhalten unverändert) ### Pitfall + Fix - `@php(...)` Inline-Form kompilierte zu ` 0, mit semantisch eingefärbten Zahlen (ok/warn/muted/err) - **Kontext-Hinweis**: „Gefiltert auf :company" als kleine Sub-Zeile, falls Firma-Kontext aktiv (Test-kompatibel!) - **Saved-Views-Tabs**: 6 Tabs (Alle, Veröffentlicht, Entwürfe, In Prüfung, Abgelehnt, Archiv) mit `cnt`-Pille, klicken setzt `statusFilter` via `setView()` - **Filter-Sektion**: Suche + Portal-Select + Firma-Select (letzteres nur wenn `hasGlobalCompanyContext`) - **Active-Chips**: entfernbare Anzeige aller aktiven Filter (Status, Portal, Firma, Suche), mit `Alle zurücksetzen`-Link - **Tabelle**: - Status-Spalte mit Hub-Badge + Inline-Action „Zur Prüfung →" für Drafts (ruft `submitForReview` mit `wire:confirm`) - Titel mit Hub-Style-Link + PM-{id}-Sub - Portal-Pills (eine oder zwei, abhängig vom Portal-Enum-Wert) - Firma als Hub-Link oder „— keine —" italic - Datum: Mono-Font + Sub mit Status-Kontext („veröffentlicht · 09:12", „eingereicht · 17:48" …) - Row-Tint via `is-row-warn` (review) / `is-row-err` (rejected) — dezent beim Hover - **3-fach Empty-State**: - `empty-search` (Suche ohne Treffer) - `empty-filter` (Filter ohne Treffer, warm-Icon-Box) - `empty-none` (gar keine PMs, mit 3-Schritt-Onboarding 01-Firma · 02-Verfassen · 03-Zur Prüfung) - **Status-Aktionen-Legende**: `panel-warm` mit 5-Spalten- Grid (Entwurf, In Prüfung, Veröffentlicht, Abgelehnt, Archiviert) und einer Action-Liste je Status ### Was bewusst NICHT umgesetzt wurde - **Bulk-Selection**: weggelassen (keine Bulk-Actions im Backend → wäre nur Dekoration ohne Funktion) - **Inline-Action „Grund ansehen →"** für Rejected: keine `rejection_reason`-Spalte in `press_releases`-Tabelle (Grund wird nur via E-Mail an Autor versendet) - **Spalten- & Export-Buttons** im Header: keine Backend- Implementierung, weggelassen - **Modals und Bestätigungs-Flows**: bleiben FluxUI - **Volt-Logik außerhalb der Erweiterungen**: unangetastet ### Tests & Hygiene - **Erst-Fail**: `CustomerCompanyContextTest:79` rot, weil „Gefiltert auf Alpha GmbH" im neuen Layout vom Counter-Strip verdrängt wurde - **Fix**: Kontext-Hinweis als zusätzliche kleine Sub-Zeile unter dem Counter-Strip, immer sichtbar wenn Firma-Kontext - **PR + Customer Tests**: 50/50 grün (199 Assertions) - **Volle Suite**: 230 grün (1212 Assertions), 3 skipped, 1 fail = pre-existing `ApiDocumentationTest` (kein Bezug zu UI) - **Build**: `npm run build:portal` clean - **Pint**: `vendor/bin/pint --dirty` → `passed` - **Lints**: keine Errors ### Status - Plan-Status: ✅ Phase 4H **abgeschlossen** - 03-WEITERE-PHASEN.md: 4H ergänzt als ✅ - Match zum Mockup: ≥ 90 % ### Übertragbar auf - `admin/press-releases/index.blade.php` (Phase 4I als natürliche Folgephase) - `admin/users.blade.php`, `admin/contacts/index.blade.php`, `admin/companies/index.blade.php` (Counter-Strip, Saved-Views-Tabs, Filter-Chips wären überall sinnvoll) --- ## 2026-05-20 · Phase 4F · Restliche Admin-Bereiche - **Anlass**: User „4f weiter" nach 4G. Siebtes (und letztes großes) Päckchen aus Phase 4. Ziel: alle übrigen Admin-Pages auf Hub-Stil, damit die komplette interne Strecke visuell abgeschlossen ist. - **Plan-Dokument**: `13-PHASE-4F-ADMIN-REST.md`. Aufgrund des Umfangs (~7.500 Z. Blade) in 4 Sub-Päckchen aufgeteilt. ### 4F-1 · Stammdaten & Switcher - **`admin/presets/{index,create,edit}.blade.php`** + `partials/form-fields.blade.php`: Page-Header („Admin Backend"-Pille, Eyebrow „Administration · Presets"), Filter-Panel, Table-Panel mit Hub-Badges und Hub-Empty-State, Forms in `article.panel`, Required-Marker auf `text-[color:var(--color-err)]`. - **`admin/categories/{index,create,edit}.blade.php`**: Page-Header mit Aktion, KPI-Reihe mit `` (gesamt, aktiv, mit/ohne PMs), Filter & Sort Panel, Category-Cards als `article.panel`, Hub-Badges, Hub-Style innere Items, Danger-Zone mit linkem roten Strip. - **`admin/portal-switcher.blade.php`** (Sidebar): Token-basierte Button-Farben mit `--color-bg-elev`, `--color-bg-rule`, `--color-ink`, `--color-ink-2`, `--color-ink-3`. - **Tests**: `CategoryIndexPerformanceTest`, `AdminCategoryManagementTest`, `AdminPresetManagementTest` — alle grün. ### 4F-2 · Pressekontakte - **`admin/contacts/index.blade.php`** (729 Z.): Page-Header mit Action, KPI-Reihe (``), Filter-Panel, Preset-Panel, Table-Panel mit Hub-Badges und Hub-Empty-State, Hub-Style Flash-Boxen. - **`admin/contacts/create.blade.php`** (275 Z.): Page-Header mit „prefilled company"-Badge, Forms in `article.panel`. - **`admin/contacts/edit.blade.php`** (352 Z.): Page-Header mit ID + Portal-Badge, Forms in `article.panel`, Danger-Zone mit linkem roten Strip. ### 4F-3 · Operations & Finance - **`admin/footer-codes/{index,create,edit}.blade.php`**: Page-Header, KPI-Reihe, Filter-Panel, Table-Panel mit Hub-Badges, Forms in `article.panel`, Category-Checkboxes Hub-styled, Danger-Zone. - **`admin/reports/slow-requests.blade.php`** + `slow-requests-table.blade.php`: Page-Header, Filter-Panel mit Reset-Button, KPI-Reihe, Top Routes/Paths Panels, Slowest-Requests-Table, Frequent Slow Queries Table, EXPLAIN Top Slow Queries Panel — alles Hub-styled. - **`admin/invoices/index.blade.php`** (Legacy-Archiv): Page-Header mit Archive-Badge, Warning-Box für „unmapped", KPI-Reihe, Filter-Panel, Table-Panel mit Hub-Badges + Pagination. - **`admin/coupons/index.blade.php`** (Stub): Page-Header „Vertagt"-Badge, Info-Panel mit Hub-Style List-Items. - **`admin/payments/index.blade.php`** (Stub): Page-Header „In Vorbereitung"-Badge, Info-Panel mit Hub-Style List-Items + Rules. - **Tests**: `AdminFooterCodeManagementTest`, `AdminSlowRequestReportTest`, `AdminSlowRequestLoggingTest`, `AdminLegacyInvoiceArchiveTest` — alle grün. ### 4F-4 · User-Verwaltung - **`admin/roles/{index,create,edit}.blade.php`**: Page-Header (mit ID + System-Role-Badges in edit), Warning-Box für System-Rollen, Forms in `article.panel`, Table-Panel mit Hub-Badges + Empty-State. - **`admin/newsletter/sync.blade.php`** (171 Z.): Page-Header mit Sync-Status-Badge (active/deactivated), Action-Buttons (Dry Run, Test-Sync) im Header, Hub-Style Info/Success-Pillen für Notifications, KPI-Reihe mit `` (gesamt, bestätigt, ausstehend, abgemeldet), Config-Details (Provider, Timeout, Endpoint) als Definition-List in `article.panel`. - **`admin/users.blade.php`** (939 Z.): Page-Header mit Action „Benutzer anlegen", KPI-Reihe (gesamt, aktiv, inaktiv), Filter-Panel mit Reset-Button, Table-Wrapper als `article.panel` mit Hub-Empty-State. **Modal** und alle Tabellen-Inhalte bewusst unangetastet — wegen Test-Strings + Komplexität. - **`admin/users/show.blade.php`** (239 Z.): Hub-Header mit ID + Status-Badges, KPI-Reihe (Portal, Typ, Status, Login), Rollen-Panel, Rechnungsadresse-Panel, „Verknüpfte Firmen & Kontakte"-Panel als verschachtelte Hub-Boxes. - **`admin/users/create.blade.php`** (421 Z.): Hub-Header, Forms in `article.panel` (Basisdaten, Rollenzuweisung, Firmenverknüpfung, Rechnungsadresse), Action-Panel am Ende. - **`admin/users/edit.blade.php`** (1.224 Z.): Hub-Header mit ID/Portal/Status-Badges, Notification als Hub-Style Ok-Pill, Quick-Nav als Token-Pillen mit Hover auf Hub-Blue, KPI-Reihe (Account, Legacy-Profil, Verknüpfungen, Rechnungsadresse). Innere Sektionen (`#account`, `#legacy-profile`, `#roles`, `#company-links`, `#contact-links`, `#billing-address`) bleiben `flux:card` (IDs für Quick-Nav, sehr großes File). Footer-Action-Panel auf Hub umgestellt. - **Tests**: `UserManagementTest`, `UserImpersonationTest`, `UserList*Test` — 29 Tests / 270 Assertions grün. ### Bauplatz-Hygiene - **Build**: `npm run build:portal` — sauber durch. - **Pint**: `vendor/bin/pint --dirty` — `passed`. - **Lints**: keine Linter-Errors in den editierten Blade-Files. - **Pre-existing**: `ApiDocumentationTest` weiterhin rot, kein Bezug zu UI-Styling (siehe frühere Phasen). ### Abgrenzung — was bewusst NICHT angefasst wurde - Volt-Logik in allen Dateien (PHP, `mount`, `save`, `with`, …). - ``, ``, ``, ``, ``, ``, ``, ``, ``. - Innere Sektionen von `users/edit.blade.php` mit ID-Ankern (Quick-Nav-Targets) — diese bleiben `flux:card`, da Footnote-Logik daran hängt und das File 1.224 Z. groß ist. - Modal in `users.blade.php` und alle internen Bestätigungs- Flows (Test-Strings). - Test-relevante Strings überall. ### Status - Plan-Status: ✅ Phase 4F **abgeschlossen**. - 03-WEITERE-PHASEN.md: 4F auf ✅ aktualisiert. --- ## 2026-05-20 · Phase 4G · Customer Portal (Mein Bereich) - **Anlass**: User „weiter 4G" nach 4E. Sechstes Päckchen aus Phase 4: alle übrigen Customer-Pages auf Hub-Stil bringen, damit die komplette User-sichtbare Strecke (Mein Bereich) visuell abgeschlossen ist. - **Plan-Dokument**: `12-PHASE-4G-CUSTOMER-PORTAL.md`. - **Was umgebaut wurde**: - **`customer/company-switcher.blade.php`** (Sidebar-Helper, 91 Z.): „Aktive Firma"-Label als `.badge hub dot`, Portal-Suffix als Token-Eyebrow, „Keine Firma zugeordnet" als `.badge warn`. ``, `Firma öffnen`-Button und `route('me.press-kits.show', …)` bleiben — alles, was der `CustomerCompanyContextTest` assertiert, ist erhalten. - **`customer/bookings.blade.php`** (Coming-Soon, 52 Z.): Hub-Page-Header („User Backend"-Pille, „Mein Bereich · Finanzen"-Eyebrow, Warn-Pille „In Vorbereitung"). Info-Box auf Hub-Soft-Token, 3 Feature-Panels mit Eyebrow + Beschreibung. - **`customer/invoices.blade.php`** (194 Z.): Page-Header mit Aktion „Rechnungsadresse im Profil pflegen". Hinweis in eigenes `.panel`. 4 KPI-Cards (primary/muted/ok/warn). Filter-Panel, Tabellen-Panel mit Hub-Badges (`ok dot` für bezahlt, `warn dot` für offen), Empty-State als Icon-Box. Pagination am Boden im Panel mit Top-Border. Notification als Hub-Warn-Pille mit Icon. - **`customer/tokens.blade.php`** (212 Z.): Hub-Page-Header mit „API-Dokumentation"-Aktion. Flash-Boxen (success/warn) in Token-Style. „Neuer Token"-Anzeige als eigenes Panel mit linkem Warn-Strip (`border-left:3px solid --color-warn`) + `.badge warn`-„Nur jetzt sichtbar". Form in Panel mit Border-Top-getrenntem Submit-Bereich. Tabelle in Panel mit Counter, Berechtigungen als `.badge hub` (statt `flux:badge`), Empty-State als Icon-Box. - **`customer/press-kits/index.blade.php`** (119 Z.): Hub-Page-Header. Filter-Panel. Karten-Grid mit `.panel`-Karten: `.panel-head` mit Status-Badge (`ok dot` / `err dot`), Body mit Slug, Portal/Rolle/ Footer-Badges, KPI-Boxen (Border + bg-elev), Footer mit Border-Top + „Firma öffnen"-Button. Empty-State als full-width Panel mit Hub-Icon-Box. - **`customer/press-kits/show.blade.php`** (734 Z. → Polish): Großes Detail-Cockpit komplett auf Hub: - **Header**: Pillen-Reihe (User-Backend, Mein Bereich · Firma, Aktiv/Inaktiv, Portal, Rolle, ggf. Footer-Code-aus) + Logo-Box (oder Hub-Icon-Box) + H1 + Slug. Aktionen rechts: Zurück, Stammdaten bearbeiten, Neue PM. - **Quick-Nav**: Anker-Links als pillen-artige Hover-Buttons mit Bottom-Border. - **4 KPI-Cards**: PMs (primary), Pressekontakte (ok), Portal (muted), Deine Rolle (muted) — mit `canManageCompany`-abhängiger Trend. - **Stammdaten-Panel**: `.panel-head` mit Bearbeiten- Button. Inline-Form als `bg-elev`-Block mit Border-Top-getrennten Sections (Logo / Aktionen). Daten-Liste als `
` mit Token-Labels + Werten, Website als Hub-Underline-Link. - **Pressekontakte-Panel**: `.panel-head` mit Counter. Inline-Form analog Stammdaten. Kontakt-Items als `bg-elev`-Boxen mit Aktionen rechts. Empty-State als gestrichelte Box. Flash-Messages („Pressekontakt wurde angelegt./aktualisiert./ gelöscht.") in Token-Success-Pille. - **Pressemitteilungen-Panel**: Tabelle mit Hub-Status- Badges (ok/warn/err/hub). Empty-State Hub-Icon-Box. - **Abrechnung & Statistik**: 2 Panels nebeneinander mit „In Vorbereitung"/„Später"-Warn-Pillen. Abrechnung mit gestrichelter Hint-Box. Statistik mit 2 Token-Mini-Stats. - **Was bewusst unverändert blieb**: - Volt-PHP-Logik in allen Dateien (mount, saveCompany, saveContact, deleteContact etc.). - Flash-Strings („Stammdaten wurden gespeichert.", „Pressekontakt wurde angelegt./aktualisiert./gelöscht.", „Token wurde erstellt", „Token wurde widerrufen.", „API-Tokens werden erst freigeschaltet" etc.) — bleiben in Volt-PHP wie zuvor und erscheinen in den neuen Hub-Pillen. - Section-Headings „Abrechnung" und „Statistik" — Tests assertieren genau diese Strings. - Strings im Header („Alpha GmbH", „Firma öffnen", `route('me.press-kits.show', …)`). - FluxUI-Form-Komponenten (`flux:input`, `flux:select`, `flux:textarea`, `flux:checkbox`, `flux:field`, `flux:error`, `flux:table*`) — nur Wrapper-Markup verändert. - **Verifikation**: - `php artisan view:clear` ✓ - `npm run build:portal` ✓ — 418.11 kB CSS / 44.38 kB JS (keine Größenänderung gegenüber 4E). - `php artisan test --compact tests/Feature/{ CustomerPortalTest, CustomerCompanyContextTest, PanelConsolidationTest}.php` → **29 passed, 131 assertions**. - Volle Suite: **230 passed, 3 skipped, 1 pre-existing fail** (`ApiDocumentationTest` — fehlende `docs/api/v1.yml`, war auch vor 4G/4E/4D rot). - `vendor/bin/pint --dirty --format agent` → `passed`. - **Resultat**: - Mein-Bereich des Users ist visuell **vollständig** auf Hub-Sprache. - Sidebar-Nav (presse-kits, invoices, tokens, bookings) läuft jetzt durchgängig im selben Vokabular wie Dashboard + Press-Releases. - Detail-Cockpit `press-kits/show` ist das visuell aufwendigste Customer-Page und stimmt jetzt mit Admin- Detail-Show überein (Pillen-Header, KPI-Reihe, Panels, Quick-Nav, Tabellen). - **Nächste mögliche Päckchen**: - **4F** Restliche Admin-Bereiche (contacts, categories, presets, footer-codes, slow-requests, users). - **press-release-images-manager** Komponente (kleiner Cleanup aus 4C-Resten). - **Phase 5** Dark-Mode-Konsolidierung. --- ## 2026-05-20 · Phase 4E · Profile & Settings - **Anlass**: User „okay weiter" nach 4D. Fünftes Päckchen aus Phase 4: alle Settings-Pages (Admin + Customer) auf Hub-Stil. - **Plan-Dokument**: `11-PHASE-4E-PROFILE-SETTINGS.md`. - **Zentraler Trick — Wrapper umgebaut**: - **`resources/views/partials/settings-heading.blade.php`**: Komplett neu als Hub-Page-Header (Pille „Admin Backend", Eyebrow „Mein Konto · Einstellungen", H1 „Settings", Subtitle). - **`resources/views/components/settings/layout.blade.php`**: Komplett neu als 2-Spalten-Grid mit Sidebar-Nav-`.panel` („Mein Konto"-Eyebrow + FluxUI-Navlist drin) und Content-`.panel` mit `.panel-head` für Page-Heading + Subheading im Body. Heading/Subheading-Slots der einzelnen Settings-Pages werden so automatisch im Hub-Look gerendert. - **Effekt**: `settings/appearance.blade.php` musste **gar nicht** angefasst werden — übernimmt den Hub-Look via Wrapper. Die anderen beiden brauchen nur Save-Bar-Polish. - **Was umgebaut wurde**: - **`settings/profile.blade.php`**: Verification-Hinweis von ``-Link auf Hub-Warn-Box (gelber Linker Strip, Icon, Hub-Link für „re-send the verification email"). Save-Bar mit Border-Top + Token-`Saved.`-Pill. Delete-Button-Section visuell durch Border-Top abgetrennt. - **`settings/password.blade.php`**: Save-Bar gleicher Polish. - **`settings/delete-user-form.blade.php`**: Das `mt-10`-Card durch Hub-Danger-Box mit `border-l-[3px] border-l-[color:var(--color-err)]`, „Danger Zone"-Eyebrow, H3 + Subtitle + Trash-Button. Modal-Markup komplett unverändert (Confirm-Password-Modal kommt aus FluxUI-Std.). - **`customer/profile.blade.php`**: Page-Header („User Backend"-Pille, „Mein Bereich · Profil"-Eyebrow, „Mein Profil"-H1). Form in 3 Panels + Aktions-Panel: Konto (Name/E-Mail/Sprache) · Profil (Anrede, Vor-/Nachname, Telefon, Backlink, Checkboxen) · Rechnungsadresse (mit Warn-Hub-Box, wenn unvollständig). Zugeordnete-Firmen als eigenes `.panel` mit Hub-Badges (`ok` für Eigentümer, `hub` für Portal/Rolle). - **`customer/security.blade.php`**: Page-Header. 4-spaltige KPI-Reihe mit `.panel` + uppercase-Eyebrow + großer Wert + Hub-Badge (E-Mail bestätigt? · 2FA aktiv? · Letzter Login + IP · Sessions-Count). 2-Spalten-Grid für Passwort + E-Mail in `.panel` mit `.panel-head`. 2FA-`.panel` mit ok-Badge im Header wenn aktiv, Recovery-Codes mit Hub-Bg-Boxes. Sessions-`.panel` mit Eintrags-Counter, Empty-State als Hub-Icon-Box. **Alle Test-Strings erhalten**: „Konto-Sicherheit", „Letzter Login", „Aktive Sessions", „Passwort ändern", „E-Mail-Adresse ändern", „Zwei-Faktor-Authentifizierung", „Rechnungsadresse". - **Tests**: - Smoke: `Settings|CustomerProfileSecurity| CustomerCompanyContext` → 33 passed (146 assertions). - Volle Suite: 230 passed / 3 skipped / 1 pre-existing `ApiDocumentationTest`-Fail. - **Build / Pint**: `npm run build:portal` ✓ (418 kB CSS), `pint --dirty` ✓, keine Linter-Errors. - **Bewusst NICHT angefasst**: - Volt-Logik in allen Dateien. - Confirm-User-Deletion-Modal-Markup (`settings/delete-user-form.blade.php`). - Fortify-2FA-Logik (Enable / Disable / Recovery-Codes- Regenerate / QR-Generation). - `` Component-Wrapper — nur Slot-Inhalt. - **Nächster Schritt** — Vorschlag: - **4G** Customer-Bereiche (invoices, tokens, bookings, company-switcher, me/press-kits/*) — Tests fordern Strings, aber alle gut greifbar. - **4F** Restliche Admin-Bereiche (contacts, categories, presets, footer-codes, slow-requests, users) — größeres Päckchen, sollte weiter unterteilt werden. --- ## 2026-05-20 · Phase 4D · Companies (admin) - **Anlass**: User „okay weiter" nach 4C. Viertes Päckchen aus Phase 4: alle vier Admin-Companies-Pages auf Hub-Stil. - **Plan-Dokument**: `10-PHASE-4D-COMPANIES.md`. - **Was umgebaut wurde**: - **`admin/companies/index.blade.php`**: Hub-Page-Header („Admin Backend"-Pille, Eyebrow „Stammdaten · Firmen", H1 „Firmen", Subtitle, CTA „Neue Firma" rechts) → 3 `` (Gesamt/Aktiv/Inaktiv) → Filter-`.panel` mit Combobox-Selects unverändert in der Logik, nur Hülle getauscht → Tabelle in `.panel overflow-hidden` mit `.panel-head` „Alle Firmen" + Eintrags-Counter. Tabellen-Status/Portal/Count-Badges auf Hub-Klassen (`badge ok|err|hub` + Dot) gesetzt. Empty-State als Hub-Icon-Box. - **`admin/companies/show.blade.php`**: Header mit Status-, Portal- und ID-Pille; Logo-Box neben H1. KPI-Reihe (3 stat-cards). Tabs „Überblick" / „Kontakte" als schlankes Bottom-Border-Pattern (kein FluxUI-Tabs-Group nötig). Beide Tab-Inhalte komplett in `.panel`-Sektionen mit `dl`-Layouts bzw. Kontakt-Karten. Existing-Contact-Combobox bleibt FluxUI; Wrapper auf Hub-`bg-elev`. Flash-Boxen (success/error/info) auf Token-Pillen. - **`admin/companies/edit.blade.php`**: Page-Header („ID"-Pille), 5 Form-Panels (Basisinformationen, Adresse, Rechtliche Daten, Logo & Status, Aktionen). Required-Marker auf `text-[color:var(--color-err)]`. Logo-Vorschau-Bild nutzt Hub-Token-Border. „Logo wird beim Speichern entfernt" auf Hub-Warn-Box (statt `flux:callout`). **Delete-Confirm-Modal komplett unverändert.** - **`admin/companies/create.blade.php`**: gleiches Schema wie Edit, ohne Status-Pille und ohne Delete-Button. Forms in 5 Panels strukturiert. - **Tests**: - Companies-Smoke: `UserManagement|PortalAssetManifest` → 25 passed (227 assertions). - Volle Suite: 230 passed / 3 skipped / 1 vorbestehender Fail (`ApiDocumentationTest` — fehlende Datei `docs/api/v1.yml`, nicht durch Phase 4D verursacht). - **Build / Pint**: `npm run build:portal` ✓ (418 kB CSS, 44 kB JS), `vendor/bin/pint --dirty --format agent` ✓. - **Bewusst NICHT angefasst**: - Volt-Logik in allen vier Dateien. - `deleteCompany`-Methode und ihr Confirm-Modal-Markup (Test `UserManagementTest::admin can delete company` prüft Redirect-Verhalten, nicht UI). - `` (Kontakt-/User-Lookup) und alle anderen `flux:field`/`flux:input`-Bausteine — Token-Bridging trägt. - Test-relevante Strings: „Firmen", „Portal", „Alle Portale", „Presseecho", „Businessportal24", „/admin/companies/…". - **Nächster Schritt (Phase 4E oder 4F)**: - **4E** = Profile/Settings (`settings.*`, `me.profile`, `me.security`) — eher klein, viele kleine Panels. - **4F** = Restliche Admin-Bereiche (contacts/, categories/, presets/, footer-codes/, slow-requests, …) — Päckchen-Größe. --- ## 2026-05-19 · Phase 4C · Press-Releases Forms (create / edit) - **Anlass**: User-Freigabe „ja" nach 4B. Drittes Päckchen aus Phase 4 — Forms (create + edit) für Admin und Customer. - **Plan-Dokument**: `09-PHASE-4C-PRESS-RELEASES-FORMS.md`. - **Was umgebaut wurde (alle 4 Forms)**: - **Page-Header** wie auf Listen und Detail: Hub-Badge („Admin Backend" / „User Backend") + Eyebrow + großes H1 + Subtitle. Bei Edit zusätzlich Status-Pille im Header-Meta (`@class([])`-Syntax mit `ok|warn|err|hub`) und „ID"-Pill. Aktions-Buttons rechts (Zurück). - **Form-Sektionen** als `.panel` mit `.panel-head` und `section-eyebrow`: „Inhalt", „SEO & Links", „Metadaten", „Status-Aktionen" (Admin-Edit), „Aktionen". Innenraum immer `p-5 space-y-4`. - **Form-Felder** bleiben FluxUI (``, ``, ``, ``, `` inkl. Combobox-Variant, ``, ``) — das Token-Bridging aus Phase 1 zieht. - **Required-Marker** von `text-red-500` auf Token-Farbe (`text-[color:var(--color-err)]`) umgestellt. - **Save-/Submit-Buttons** in eigenem `.panel` mit Header „Aktionen" statt „nackten" Buttons in der Sidebar. - **Flash-Boxen** (Success/Error) auf Hub-Token-Pillen. - **Admin-Edit-spezifisch**: 5 Sidebar-Panels (Status-Aktionen + Metadaten + Aktionen + Modals). „Status-Aktionen"-Panel zeigt rechts im Header die aktuelle Status-Pille — visuelle Doppelung mit dem Page-Header-Status, aber im Kontext der Sidebar sinnvoll als Vergleichs-Anker für die „Neuer Status"-Auswahl. - **Was explizit NICHT angefasst wurde**: - **Volt-Logik** (alle `save`/`publish`/`reject`/`backToDraft`/ `archive`/`changeStatus`/`deletePressRelease`/`mount`/`with`/ `selectedCompany`-Methoden) — Layout-only. - **Confirm-Modals** der Admin-Edit-Page (`confirm-status-change`, `confirm-delete-press-release`) — Tests in `AdminPressReleaseActionsTest` assertieren Wortlaute. - **Wortlaute**: „Neuer Status", „Status wechseln", „Status wirklich wechseln?", „Pressemitteilung löschen?", „Status wurde auf" — alle erhalten. - **``** — eigene Komponente, kommt im jeweiligen eigenen Päckchen dran. - **Build/Test**: - `npm run build:portal` → ok (`portal-D0cNdOWP.css` 418.42 kB). - Linter clean (alle 4 Dateien). - `php artisan test --compact --filter='PressRelease|CustomerCompanyContext|CustomerProfileSecurity|PanelConsolidation'` → **72 passed (310 assertions)**. - Volle Suite: 230 passed, 3 skipped, 1 pre-existing failure (`ApiDocumentationTest`). - Pint `--dirty --format agent` → passed. - **Offene Fragen**: Keine. - **Nächster Schritt**: Phase 4 für Press-Releases ist mit 4A/4B/4C komplett abgeschlossen. Nächstes Päckchen je nach Sichtbarkeit: **4D = Companies** (`admin.companies.*`) ODER **4E = Settings / Profile** (`me.profile`, `me.security`). --- ## 2026-05-19 · Phase 4B · Press-Releases Detail/Show-Pages - **Anlass**: User-Freigabe „weiter" nach 4A. Zweites Päckchen aus Phase 4 — Detail/Show-Pages für Admin und Customer. - **Plan-Dokument**: `08-PHASE-4B-PRESS-RELEASES-DETAIL.md`. - **Was umgebaut wurde (beide Show-Pages)**: - **Page-Header** mit Hub-Badge + Eyebrow + Status-Pill + Sprache + Portal (Admin nur), darunter großer H1 mit dem PM-Titel und Subtitle mit Firma/Kategorie/Autor bzw. Datum. Aktions-Buttons rechts (Bearbeiten/Vorschau-Link/Zurück). - **Status-Workflow-Aktionsbar** als `.panel` mit `.panel-head` und passend gefärbtem Badge je nach Status (warn=Review, ok=Published, err=Rejected, hub=Draft). - **Content-Hauptbereich** (PM-Text) als `.panel` mit eigenem Header „Inhalt" + `prose` darunter. Keywords/Backlink darunter als Footer mit Hub-Rule. - **Sidebar / Side-Cards** (Admin) als kompakte `.panel` mit `panel-head` „Details" und „Bilder" + Hub-Badges. - **Status-Verlauf-Timeline** als `.panel` mit Hub-Badge je Log-Eintrag (`.badge.ok|warn|err|hub`) statt FluxUI-`color`-Props. - **Status-Badges** in Header und Timelines komplett auf `` mit `@class([])`-Syntax. - **Customer-Show-spezifisch**: - **Rejection-Hinweis** als roter `.panel` mit linker Akzent-Border (`border-left-width:3px`) und Icon-Box — statt ``. - **Review-Pending-Hinweis** als warner `.panel` mit Akzent-Border und Clock-Icon-Box — statt ``. - **Share-Link-Erfolgsblock** als ok-`.panel` mit Gültigkeits-Badge im Header und readonly-Input im Body. - **Contacts-Liste** als Hub-Items (`bg-bg-elev` + `border-bg-rule`), Empty-State mit gestrichelter Border. - **Status-Tile-Grid** (Aktuell/Erstellt/Veröffentlicht/Aufrufe) als 4-er-Grid mit kleinen Hub-Tiles statt zinc-Hintergründen. - **Admin-Show-spezifisch**: - **Modals** (Publish/Reject/Archive) komplett unverändert — Tests in `AdminPressReleaseActionsTest` assertieren Wortlaute. - **Was explizit NICHT angefasst wurde**: - **Volt-Logik** (publish/reject/archive/submitForReview/ generateShareLink/with-Method) — Layout-only. - **Wortlaute** „Erneut einreichen", „Werbliche Sprache wurde markiert." aus `PressReleaseWorkflowTest`. - **Bestehende Modals** und ihre Wortlaute. - **Mini-Stolperer (sofort gefixt)**: - Erst zwei nicht-existente Tokens (`--color-rule`, `--color-panel-soft`) verwendet. Korrigiert zu `--color-bg-rule` und `--color-bg-elev` (16 + 5 Vorkommen). Wäre im Browser stumm gefailed (Tailwind hätte die Klassen einfach nicht ausgegeben). - **Build/Test**: - `npm run build:portal` → ok (`portal-Bq4pkLWt.css` 418.36 kB). - Linter clean. - `php artisan test --compact --filter='PressReleaseWorkflow|AdminPressReleaseActions'` → 16 passed (52 assertions). - Volle Suite: 230 passed, 3 skipped, 1 pre-existing failure (`ApiDocumentationTest`, schon vor 4A bekannt). - Pint `--dirty --format agent` → passed. - **Offene Fragen**: Keine. - **Nächster Schritt**: Päckchen **4C** = Press-Releases Forms (`create.blade.php` + `edit.blade.php`, Admin + Customer). Forms sind tendenziell aufwendiger (mehr FluxUI-Felder, ggf. zusätzliche Logik wie Image-Uploads). --- ## 2026-05-19 · Phase 4A · Press-Releases Listen-Pages - **Anlass**: User-Freigabe „weiter" nach Phase 3 (Admin-Dashboard). Phase 4 ist laut Roadmap „der Marathon" über Listen-/Detail-Pages (~3–5 Tage iterativ). Wegen Umfang in Päckchen geteilt — A = Listen, B = Detail/Show, C = Forms, D = Companies, E = Settings, F = Rest. - **Plan-Dokument**: `07-PHASE-4A-PRESS-RELEASES-LISTEN.md` (knapp, Scope hart auf zwei `index.blade.php`-Files begrenzt, mit explizitem „NICHT in diesem Päckchen"-Block). - **Scope dieses Päckchens**: - `resources/views/livewire/admin/press-releases/index.blade.php` - `resources/views/livewire/customer/press-releases/index.blade.php` - **Was umgebaut wurde (beide Listen)**: - **Page-Header** im Hub-Stil: `
` mit `1fr auto`-Grid, Hub-Badge („Admin Backend" / „User Backend"), Eyebrow, großes H1, Subtitle, rechts der CTA-Primary-Button (FluxUI bleibt für den Button). - **Filter-Bar** als `.panel` + `.panel-head` mit `section-eyebrow` „Filter & Suche". FluxUI-Inputs (Search-Input, Selects, Combobox für User/Company/Contact-Lookup) bleiben unverändert — Hub hat kein eigenes Combobox-Pendant. - **Tabelle** als `.panel` mit `.panel-head` „Alle Pressemitteilungen" + Eintrags-Counter rechts. FluxUI-`` bleibt — das Zinc-→Hub-Mapping aus Phase 1 zieht hier perfekt. - **Status-Badges**: `` ersetzt durch `` für visuelle Konsistenz mit dem Dashboard (`@class([...])`-Syntax). - **Empty-State** mit Icon-Box (`bg-[color:var(--color-hub-soft)]`, Hub-Border, `flux:icon.newspaper`) + Headline + Subtext. - **Flash-Boxen** auf Hub-Tokens (`--color-ok-soft`, `--color-err-soft`, `--color-gain-deep`, `--color-loss`) statt `bg-green-50` etc. - **Admin-spezifisch**: Vier `` (Gesamt/Veröffentlicht/ In Prüfung/Entwürfe) als KPI-Reihe — identisches Layout wie Admin-Dashboard. - **Was explizit NICHT angefasst wurde**: - **Confirm-Modals** (Publish/Reject/Archive) — Tests in `AdminPressReleaseActionsTest` assertieren die Wortlaute („Pressemitteilung veröffentlichen?" etc.) → unverändert. - **Volt-Logik** (Sort, Filter, alle Methods) — Layout-only. - **Bonus-Fix**: `customer/dashboard.blade.php` Subtitle bekommt einen „Übersicht für :company —"-Einschub, wenn `$selectedCompany` gesetzt ist. Korrigiert eine Phase-2-Regression bei `CustomerCompanyContextTest > customer dashboard is filtered by …`. - **Build/Test**: - `npm run build:portal` → ok (`portal-DaL-tXm-.css` 418.75 kB). - Linter clean. - `php artisan test --compact --filter='PressReleaseWorkflow|AdminPressReleaseActions|Dashboard|CustomerCompanyContext'` → 45 passed (163 assertions). - Volle Suite: 230 passed, 3 skipped, 1 failed. Der einzelne Fail (`ApiDocumentationTest`) ist **pre-existing** — via `git stash` verifiziert, war auch vor diesem Päckchen rot (`/docs/api/v1` liefert nicht das erwartete YAML). - Pint `--dirty --format agent` → passed. - **Offene Fragen**: Keine. - **Nächster Schritt**: Päckchen **4B** = Detail/Show-Pages (`admin/press-releases/show.blade.php` + `customer/press-releases/show.blade.php`). Dort ist mit der „Werbliche Sprache wurde markiert."- und „Erneut einreichen"- Assertion aus `PressReleaseWorkflowTest` zu rechnen — Wortlaute bleiben unverändert. --- ## 2026-05-19 · Phase 3 · Admin-Dashboard im Hub-Vokabular - **Anlass**: User-Freigabe „weiter" nach Phase 5 (Dark Mode). Phase 3 laut Roadmap: Admin-Dashboard (`/dashboard`) im selben Vokabular wie Customer-Dashboard. - **Plan-Dokument**: `06-PHASE-3-ADMIN-DASHBOARD.md` (knapper als Phase 2 — niedrig-risiko, keine neuen Konzepte, reine Vokabular-Migration auf die DRY-Schicht aus Phase 2). - **Ausgangslage**: `admin/dashboard.blade.php` war Controller-rendered (kein Volt), nutzte reines Tailwind mit `zinc-*`-Klassen, harten Farb-Pillen (`bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 …`) und keiner FluxUI-Komponente. Visuell aus der Zeit gefallen. - **`admin/dashboard.blade.php` komplett umgeschrieben**: - **Page-Header**: Hub-Badge „Admin Backend" + Eyebrow + großes H1 + Subtitle mit `auth()->user()->name`. Rechts: ok-Pille „Alle Systeme operational". - **KPI-Reihe** (5 Stat-Cards via ``): - Pressemitteilungen → `primary` mit Trend-Slot „X pub · Y prüf · Z entwurf" (Wortlaut für Test-Assertions bewusst beibehalten). - In Prüfung → `warn` als eigene KPI (war vorher nur in der PM-Card versteckt — jetzt klickbar direkt in den Filter). - Firmen / Kontakte / Benutzer → `muted` mit kurzen Sublines. - Alle Cards sind klickbar (link-wrapped) → CRM-/Content-Übersichten. - **2-Spalten-Grid** (`2fr 1fr`): - Links: Panel „Letzte Pressemitteilungen" mit Hub-Liste + `.badge.ok|warn|err|hub`-Pillen (statt Tailwind-Farben). - Rechts: Panel „Zur Prüfung" mit warn-Pille (Count) oder ok-Pille „leer". „+ N weitere"-Link im Footer. - **Newsletter + Quick-Actions** (`1fr 2fr`, NEU): - Links: `panel-warm` Newsletter-Block mit Mono-Zahl + Subline + Sync-Link. - Rechts: Quick-Action-Grid mit 4 Icon-Buttons (Pressemitteilungen, Firmen, Rechnungen, Voreinstellungen) — Icon-Tile wechselt auf Hover ins gefüllte Hub-Blau. - **Footer**: subtle Link-Reihe (Benutzer / Rollen / Performance). - **Was bewusst NICHT geändert**: - Controller-Logik, Datenform, Cache-Strategie (`AdminPerformanceCache`). - Newsletter-Count bleibt, wandert nur in eigenen Block. - **Build/Verifikation**: - `npm run build:portal` → `portal: 417.81 kB` (Δ -0.7 kB, weil viele Zinc-Tailwind-Klassen weggefallen sind). - `php artisan test --filter='DashboardTest|AuthenticationTest|RegistrationTest|CustomerPortalTest'` → **18/18 passed**, 70 Assertions. `DashboardTest` mit seinen Wortlaut-Assertions (`3`, `1 pub`, `1 prüf`, `1 entwurf`, `Review Dashboard PM`) bleibt grün ohne Anpassung. - `vendor/bin/pint --dirty` → `passed`. - Linter: 0 Errors. - **Lessons learned**: - Vokabular-Migrations ohne Logik-Eingriff brauchen ~15 Minuten dank DRY-Schicht aus Phase 2 — der Wert der `shared/hub-components.css` + ``-Investition zahlt sich ab Phase 3 deutlich aus. - Der `` ist auch ohne `:value`-Number-Format flexibel (`number_format()` direkt im Aufruf eingebettet). - Test-Assertions können bei Layout-Refactors überraschend stabil bleiben, wenn man die Original-Strings im neuen Layout an sinnvollen Stellen behält (hier: im Trend-Slot der KPI-Card). --- ## 2026-05-19 · Phase 5 · Dark Mode + Switcher im User-Menü - **Anlass**: User-Feedback nach Phase 2: „Switch in den Dark Mode funktioniert nicht. Zusätzlich hätte ich gerne einen Switcher hell/dunkel direkt im User-Menü." → Phase 5 vorgezogen vor Phase 3/4, weil der Switcher der natürliche UX-Einstiegspunkt für Dark Mode ist und der Notfall-Hack aus Phase 1 (`portal.css: .dark { --color-accent: var(--color-hub) }`) endlich verschwinden soll. - **Plan-Dokument**: `05-PHASE-5-DARK-MODE.md` mit Token-Mapping-Tabelle, drei wichtigen Token-Konstanten-Tricks (`--color-accent-warm`, `--color-panel-dark`, `--color-panel-dark-2`) und Switcher-Snippet. - **`design-tokens.css` — Dark-Block aktiviert**: - Vollständiger `.dark { … }`-Block mit Werten aus `User Dashboard presseportale Dark.html`. - Surfaces: bg/elev/card/rule schalten auf `#0e1218`-Familie. - Hub-Blau wird **heller** im Dark Mode (`#5a78c2` statt `#1a2540`) — notwendig für Lesbarkeit auf dunklem Bg. - Bernstein-Akzent ebenfalls heller (`#d9a560`). - Status-Farben (`ok/warn/err`) auf dunkle, gesättigte Variante. - Schatten-Tokens neutral-schwarz statt hub-blau-warm. - `color-scheme: dark;` als Hint für native Form-Controls. - **3 neue Token-Konstanten** (gleicher Wert in beiden Modi): - `--color-accent-warm` (`#b07a3a`) — für Stellen, die explizit Bernstein bleiben müssen (Hint-Card-Border-Left, Progress-Bar-Fill); löst die Kollision auf, dass `--color-accent` im Portal auf Hub-Blau gemapt ist und im Dark Mode noch heller würde. - `--color-panel-dark` (`#0f1729`) und `--color-panel-dark-2` (`#1a2540`) — für `.panel-dark` und Brand-Bridge-Inner-Boxes. Ohne diese würde der dunkle Bridge-Container im Dark Mode plötzlich hellblau, weil `var(--color-hub)` zum hellen Wert wird. - **`portal.css` — Notfall-Hack aus Phase 1 entfernt**: - Der `.dark { --color-accent: var(--color-hub); … }`-Block ist überflüssig, weil das echte Dark-Token-Mapping in `design-tokens.css` `--color-hub` automatisch auf `#5a78c2` schaltet — und `--color-accent` darüber per `var(--color-hub)`-Verweis dynamisch mitkommt. - Primary-Button-Shadows zusätzlich für `.dark` überschrieben: statt `rgba(26, 37, 64, …)` (warmer Hub-Blau-Alpha) jetzt `rgba(0, 0, 0, …)` (neutral-schwarz), weil der hub-blaue Schatten auf dunklem Card-BG zu sichtbar wirkt. - **`hub-components.css` — Dark-tauglich gemacht**: - `.panel-dark` nutzt jetzt `var(--color-panel-dark-2)` und `var(--color-panel-dark)` (vorher `var(--color-hub)` und `var(--color-topbar-deep)`) → bleibt im Dark Mode dunkel. - `.hint-card border-left` und `.hint-bar > span` nutzen `var(--color-accent-warm)` (konstant Bernstein) statt `var(--color-accent)` (im Portal Hub-Blau-Mapping). - `.hint-card background` schaltet auf `var(--color-bg-card-warm)` — ist im Light Mode `#efeadc` (warmes Buchpapier), im Dark Mode `#1f1a12` (warm-dunkles Bernstein-Substrat). - Restliche Klassen funktionieren **automatisch**, weil alle Werte über Tokens laufen — DRY-Architektur zahlt sich aus. - **`customer/dashboard.blade.php`**: - Brand-Bridge-Inner-Boxes auf `bg-[color:var(--color-panel-dark-2)]` umgestellt (statt `--color-hub-2`, das im Dark Mode hell würde). - **Switcher im User-Menü** (`sidebar.blade.php` — Desktop + Mobile): - Vor dem Logout-Button: kleiner Block mit Eyebrow „Erscheinung" und FluxUI-Segmented-Radio-Group mit Icons-only. ```blade ``` - `$flux.appearance` ist FluxUIs Magic-Object — LocalStorage-persistent, `@fluxAppearance`-Script setzt `class="dark"` auf ``. - In BEIDEN Dropdowns (Desktop in der Sidebar, Mobile im Header). - **Hub-Frontend bleibt Light-Only** (Plan-Vorgabe): - `auth/pressekonto.blade.php` lädt KEIN `@fluxAppearance` und KEIN `partials/head`. Damit wird `class="dark"` dort nie gesetzt — auch nicht, wenn LocalStorage `dark` enthält. Kein Eingriff nötig. - Hub-Landing analog (eigene ``-Pipeline). - **Build/Verifikation**: - `npm run build:portal` → `portal: 418.55 kB` (Δ +1.5 kB für Dark-Tokens + Switcher). - `php artisan test --filter='DashboardTest|AuthenticationTest|RegistrationTest|CustomerPortalTest'` → **18/18 passed**, 70 Assertions. Keine Regressions. - `vendor/bin/pint --dirty` → `passed`. - Linter: 0 Errors. - **Lessons learned**: - Tailwind v4's `@theme`-Variables sind **dynamisch** zur Laufzeit — `--color-accent: var(--color-hub)` schaltet beim `.dark`-Switch automatisch mit, ohne dass man `.dark { --color-accent: … }` extra setzen muss. - Wenn ein Wert in beiden Modes IDENTISCH bleiben soll (`--color-panel-dark-2`, `--color-accent-warm`), gehört er in den Light-Block und wird im `.dark`-Block bewusst NICHT überschrieben. Vererbung tut den Rest. - FluxUI's `flux:radio.group variant="segmented"` mit Icons-only ist perfekt für Dropdown-Switcher; `:title="…"` liefert Tooltips ohne sichtbares Label. - **Bonus**: Der ursprüngliche Symptom-Fix aus Phase 1 (Multi-Alpine + Dark-Mode-Bug) ist jetzt strukturell aufgelöst: - Alpine kommt ausschließlich aus `@fluxScripts`. - Dark Mode ist echt umgesetzt, nicht maskiert. --- ## 2026-05-19 · Phase 2 · Customer-Dashboard auf Mockup-Stil - **Anlass**: User-Freigabe „weiter“ nach Behebung des Dark-Mode/Alpine-Bugs. Phase 2 laut Plan: Customer-Dashboard (`/admin/me`) an `User Dashboard presseportale.html` angleichen. - **Vorab-Doku**: `04-PHASE-2-CUSTOMER-DASHBOARD.md` mit Mockup-Diff-Tabelle, CSS-Strategie, Component-Skizzen, Akzeptanzkriterien. - **CSS-Architektur — DRY-Schritt**: - Neue Datei `resources/css/shared/hub-components.css` als **Single Source of Truth** für Hub-Layout-Bausteine: `.panel` (warm/dark/head), `.stat-card` (+ Varianten `is-primary|ok|warn|muted`, `.stat-strip`, `.stat-label`, `.stat-num`, `.stat-meta`, `.stat-trend`), `.hint-card` (+ `.hint-ico`, `.hint-bar`, `.hint-action`), `.badge` (+ `.warn|ok|hub|err|dot`), `.bridge-row`/`.dot-pe`/`.dot-bp`, portable `.eyebrow` und `.section-eyebrow` (für Portal — Web-Build überschreibt idempotent). - Importiert in **beiden** Builds: - `resources/css/portal.css` (nach `flux.css`) - `resources/css/web/shared-styles.css` (nach `design-tokens.css`) - Alle Werte nutzen `var(--color-*)`-Tokens — keine Hex-Literale. - Fehlende Tokens nachgezogen in `shared/design-tokens.css`: `--color-bg-rule-2` (hellere Progress-Track-Variante) und `--color-gain-deep` (dunkles Grün für `is-ok`-Stat-Num). - **Neue Blade-Components** in `resources/views/components/portal/`: - `` mit Slots `meta` (oben rechts) und `trend` (unten). - `` mit Progress-Bar bei gesetztem `$percent` und `wire:navigate`-Link. - **`livewire/customer/dashboard.blade.php` komplett neu**: - **Page-Header**: Hub-Badge „User Backend“ + Eyebrow + großes H1 + Subtitle. Rechts entweder Aktive-Firma-Pille **oder** Warn-Pille „Keine Firma zugeordnet → zuordnen“. - **KPI-Reihe** (4 `stat-card`s): Gesamt (primary, mit Δ-zu-Vormonat), Veröffentlicht (ok), In Prüfung (warn), Entwürfe (muted). - **Zweispalten-Grid** (`lg:grid-cols-[2fr_1fr]`): - Links: Panel mit Pressemitteilungen-Liste oder Empty-State (Icon-Box mit Notification-Dot, Headline, Action-Button, Schritt-Karten 01/02/03, Footer-Tipp „4 Stunden“). - Rechts: Panel „Datenqualität“ mit ``s oder Success-State („Alles im grünen Bereich“). - **Unteres Grid**: Firmen-Panel (Liste **oder** dashed Empty-Slot + Hinweis-Box) + Brand-Bridge-Dark-Panel (presseecho + businessportal24 mit Status, API-Status-Indikator, Tarif). - **Footer**: Subtle-Link-Reihe (Sicherheit, API & Tokens, Profil). - **Volt-`with()` erweitert**: - `stats.deltaMonth` — Monatsvergleich via zweiter `whereBetween`-Query. - `profileCompleteness` (Heuristik auf 6 Profile-Kernfelder). - `billingCompleteness` (5 Address-Pflichtfelder). - `bridgeStatus` (vorerst hardcoded `connected` — Phase 3+). - `qualityHints[]` optional um `percent`-Feld erweitert — wenn gesetzt, rendert Progress-Bar in der Hint-Card. - **Edge-Case beim Bauen**: Blade interpretierte `` im PHPDoc- Kommentar von `hint-card.blade.php` als echten Component-Tag und produzierte `Undefined variable $component`. Fix: Doc-Block entkontaminiert („flux:icon“ ohne Spitzklammern). - **Tests neu** in `tests/Feature/Customer/DashboardTest.php` (5 Cases): 1. Customer-Dashboard rendert Core-Sections ohne Fehler 2. Empty-State mit 3-Schritt-Intro wird gezeigt 3. PR-Liste + KPI-Zahlen rendern bei vorhandenen Daten 4. Profile-Completeness-Hint mit Prozentwert erscheint bei Teildaten 5. Vollständiges Profil + Billing → Hints werden ausgeblendet, „Alles im grünen Bereich“ wird gezeigt - **Ergebnis**: 5/5 passed, 21 Assertions. - Cross-Check: Verwandte Tests (`Dashboard|Authentication|Registration|CustomerPortal`) → **18/18 grün**, 70 Assertions. Keine Regressions. - **Build/Verifikation**: - `npm run build:portal` → `portal: 417.02 kB` (Δ +8 kB für neue Hub-Components-Klassen + Dashboard-Klassen). Web-Build unverändert. - `vendor/bin/pint --dirty` → `passed`. - **Was bewusst weggelassen**: - Sparklines auf den Stat-Cards (kommen mit echten Trend-Daten in Phase 4). - Topbar-Anpassung (eigene Phase, später). - ``-Extraktion (Brand-Bridge bleibt inline, bis sie an mehr Stellen gebraucht wird). - **Lessons learned**: - PHPDoc in Blade-Components darf KEINE ``-Tags enthalten — der Blade-Pre-Compiler scannt das gesamte File vor dem PHP-Parsing. - `shared/hub-components.css` ist der richtige Hebel für Phase 3 (Admin-Dashboard) und Phase 4 (Listen/Detail) — Components-CSS muss nicht mehr pro Phase neu definiert werden. --- ## 2026-05-19 · Phase 1 Refinement · Dark-Mode-Bug + Multi-Alpine - **Anlass**: User-Review nach Kontrast-Refinement: zwei konkrete Befunde aus der Browser-Konsole. 1. `[Warning] Detected multiple instances of Alpine running (me, line 114)` 2. Button-Default-Farbe ist `rgb(109, 138, 211)` (helles Blau), nicht das gewünschte Hub-Blau `rgb(26, 37, 64)`. Computed Style zeigt: `.bg-[var(--color-accent)] { background-color: var(--color-accent); }` → `var(--color-accent)` löst zu `#6d8ad3` auf. - **Diagnose Bug 1 (Alpine)**: - `partials/head.blade.php` lädt `resources/js/app.js` über `@vite`. `app.js` ruft `Alpine.start()` auf. - Am Ende des `` injiziert `@fluxScripts` (sidebar.blade.php Zeile nahe ``) **eine eigene Alpine-Instanz** (FluxUI bringt Alpine selbst mit). - Ergebnis: zwei `window.Alpine`-Objekte, Bindings teils tot, Warning. - Auf der Login-Seite hatten wir das **bereits** gefixt (app.js dort rausgenommen), aber im Portal-Layout war der Fix noch offen. - **Diagnose Bug 2 (Button-Farbe)**: - Der Wert `rgb(109, 138, 211)` = `#6d8ad3` ist **exakt** der Dark-Mode-Fallback, den ich in `portal.css` unter `.dark { --color-accent: #6d8ad3 }` definiert hatte. - FluxUI's `@fluxAppearance`-Helper schreibt `class="dark"` auf ``, sobald der User den Appearance-Switcher mal auf "dark" gestellt hatte (gespeichert in `localStorage`). - Damit überschreibt der Dark-Block den Light-Mode-Akzent `var(--color-hub)` (`#1A2540`) — Buttons sehen plötzlich blassblau aus. - Dark Mode ist laut Plan erst Phase 5, war aber durch den Fallback-Block bereits halb aktiv. - **Fixes**: - `resources/views/partials/head.blade.php`: `resources/js/app.js` aus dem `@vite`-Aufruf **entfernt**. Alpine kommt im Portal ausschließlich über `@fluxScripts`. (Hub-Landing nutzt `partials/head` nicht — eigene ``-Pipeline.) - `resources/css/portal.css`: Der `.dark { --color-accent: ... }`- Block setzt jetzt **bewusst** alle Akzent-Tokens auf `var(--color-hub)` (also den Light-Mode-Wert). Damit bleibt das Hub-Blau auch bei eingeschaltetem `class="dark"` konsistent. Kommentar dokumentiert, dass Phase 5 diesen Block durch das echte Dark-Token-Mapping aus `shared/design-tokens.css` ersetzt. - **Build/Verifikation**: - `npm run build:portal` → `portal: 408.97 kB` (Δ +0.02 kB). - `php artisan test --compact --filter='AuthenticationTest|RegistrationTest'` → **6 passed, 19 assertions**, 1.93s. - `vendor/bin/pint --dirty --format agent` → `passed`. - **User-Action zur Verifikation**: - Im Portal Hard-Reload (Cmd/Strg+Shift+R) — die alte `app.js` hängt sonst im HTTP-Cache. - Konsole sollte **keine** Multi-Alpine-Warning mehr werfen. - Primary-Buttons sind im Default-State sattes Hub-Blau (`#1A2540`), Hover noch dunkler (`#243152`). --- ## 2026-05-19 · Phase 1 Refinement · Kontraste & Button-Hover - **Anlass**: User-Review nach Phase 1: „Buttons als Primary auf `rgb(26, 37, 64)` einsetzen, die andere Farbe ist deutlich zu hell. Es fehlen noch deutliche Kontraste." - **Diagnose**: - FluxUI-Primary-Buttons sind eigentlich **schon** auf `var(--color-accent)` = `var(--color-hub)` = `#1A2540` = `rgb(26, 37, 64)` gesetzt (im Build verifiziert). Sollte stimmen. - **ABER**: Der FluxUI-Default-Hover ist `hover:bg-[color-mix(in_oklab,_var(--color-accent),_transparent_10%)]` — auf hellem Buchpapier wirkt das hell-blau statt der gewünschten Hub-Konvention `hover:bg-hub-2` (dunkler als Default). - Außerdem zu wenig Schatten/Border-Kontrast für klare Button-Kanten. - **Erkenntnis**: Frühere `[data-flux-button][data-variant="primary"]`- Overrides griffen **nie**, weil FluxUI kein `data-variant`-Attribut rendert. Variant-Styling kommt komplett über Tailwind-Klassen (z.B. `bg-[var(--color-accent)]`). Neue Selektor-Strategie nutzt jetzt diese Klassen direkt (mit escapeten Brackets). - **Fixes in `portal.css`**: - **Primary-Button-Hover** auf `var(--color-hub-2)` (#243152) statt FluxUI's color-mix-Hellung. Selektor: `[data-flux-button].hover\:bg-\[color-mix\(in_oklab\,_var\(--color-accent\)\,_transparent_10\%\)\]:hover` - **Primary-Button-Shadow**: kräftigeres Inset-Highlight + warmer Drop-Shadow in Hub-Blau-Alpha für klare Kanten auf Buchpapier. Border zusätzlich auf `var(--color-hub-2)` für definierten Rand. - **Hover-State**: noch stärkerer Shadow + Drop für Tiefenwirkung. - **Input-Focus** auf Hub-Blau-Ring (statt blassem Default-Akzent), mit Buchpapier-Offset für saubere Trennung. - Alter (defunkter) `[data-flux-button][data-variant="primary"]`- Block entfernt, weil Selektor nicht existiert. - **Build/Verifikation**: - `npm run build:portal` → `portal: 408.95 kB` (von 409.03 kB, -0.08 kB). - Im finalen CSS verifiziert: * Hub-2-Hover-Override: `[data-flux-button].hover\:bg-…:hover{background-color:var(--color-hub-2)!important}` * Shadow-Override: `[data-flux-button].bg-\[var\(--color-accent\)\]{border-color:var(--color-hub-2);box-shadow:inset 0 1px #ffffff2e,0 1px 2px #1a254040,…}` * Input-Focus-Ring auf Hub-Blau. - `vendor/bin/pint --dirty` → passed. - **Wirkung**: - Primary-Button **Default**: `#1A2540` (Hub-Blau), klarer warmer Schatten, definierter Rand → fühlt sich „solid" an wie auf der Hub-Landing. - Primary-Button **Hover**: `#243152` (Hub-2, dunkler statt heller) — Hub-Konvention, signalisiert Interaktion ohne den Eindruck einer schwächeren Farbe zu erzeugen. - Inputs zeigen jetzt einen **erkennbaren Hub-Blau-Ring** beim Fokus statt eines blassen Bernstein-Schimmers. - **Hinweis an User**: **Hard-Reload** (Cmd+Shift+R) ist nötig — das CSS-Bundle hat einen neuen Hash (`portal-kuU-opFv.css`). - **Verfeinerungen noch offen für Phase 2 / Iteration**: - Subtle/Filled/Ghost-Buttons (``) sind weiterhin transparent-zinc → bei Bedarf in Phase 2 angleichen. - Sidebar-Active-Item hat schon Hub-Soft + 2 px-Strip; ggf. den Strip noch sichtbarer machen wenn weiter zu unauffällig. --- ## 2026-05-19 · Phase 1 abgeschlossen · Portal-Shell auf Hub-Design - **Was**: Portal-Shell (Sidebar, Topbar, Layout-Container, Customer-Banner) visuell ans Hub-Design angeglichen. FluxUI bleibt komplett erhalten — Anpassung erfolgt über CSS-Token-Bridging und `[data-flux-*]`-Overrides. Brand-Mark ersetzt das Starter-Kit-Logo. Light Mode ist Default; Dark Mode für Phase 5 vorbereitet (heller Hub-Blau). - **Dateien (geändert)**: - `resources/css/portal.css` — komplett refactored: * `--font-sans: "Inter Tight"` statt `Instrument Sans` * `--color-accent: var(--color-hub)` (#1A2540) statt `#3ea3dc` * `--color-zinc-50..950` auf Hub-Buchpapier-Familie gemappt (Zinc-100 = #F6F4EF, Zinc-700 = #1A1F1C, Zinc-900 = Hub-Blau) * Hub-Stil-Overrides: `[data-flux-sidebar]`, `[data-flux-navlist]`, `[data-flux-navlist-item]` mit Active-Strip links, `[data-flux-button]` Primary/Filled auf Hub-Blau, `[data-flux-card]` Buchpapier * Dark-Mode-Block bleibt vorerst minimal (nur Accent), Vollumstellung in Phase 5 - `resources/views/partials/head.blade.php` — Bunny-Font auf `inter-tight + jetbrains-mono + source-serif-4` (für Brand-Mark) - `resources/views/components/layouts/app/sidebar.blade.php`: * `class="dark"` aus `` entfernt * `` jetzt `bg-bg text-ink antialiased` (Hub-Buchpapier) * `` ersetzt durch `` + Eyebrow "Publisher · Hub" — sowohl im Desktop- Brand-Block als auch im Mobile-Header * Testmodus-Block (Impersonation) komplett im Hub-Stil: dunkles Hub-Blau-Panel mit Bernstein-Eyebrow „Testmodus aktiv", weiße CTA „Zurück zum Admin" (statt Amber-Warnfarbe wie zuvor) * Resources-Block (Tailwind/HeroIcons/Flux/Repository) entfernt — gehört nicht ins Live-Portal - `resources/views/components/layouts/app.blade.php`: * Customer-Banner („User Backend") visuell auf Hub-Stil: Hub-Soft- Hintergrund + Hub-Soft-2-Border, Hub-Blau-Pille mit Dot, Eyebrow „Firmenkontext" in Sperrschrift, Heading in Hub-Ink - **Dateien (Test-Anpassungen, weil rollen-basierter Redirect aus Login-Fix weitere Tests berührt hat)**: - `tests/Feature/Auth/AuthenticationTest.php` — Test-User auf `superAdmin()` umgestellt (sonst `canAccessAdmin() === false` → Redirect auf `/` statt `/dashboard`) - `tests/Feature/Auth/RegistrationTest.php` — `terms_accepted` im Formular gesetzt + Redirect-Erwartung auf `/` angepasst (frisch registrierte User haben keine Rolle → Fallback `/`) - **Build/Test**: - `npm run build:portal` → `portal: 409.03 kB` (vorher 408.89 kB, +0.14 kB). Unter dem 10 %-Akzeptanzkriterium (≤ 450 kB). - CSS-Inspektion bestätigt Phase-1-Tokens: * `--color-accent: var(--color-hub)` und `.dark { --color-accent: #6d8ad3 }` im `:root` * `--color-zinc-700: #1a1f1c` (Hub-Ink statt Zinc-Grau) * `--font-sans: "Inter Tight"` ohne Instrument-Sans-Vorlauf * Hub-Overrides im Output: `[data-flux-sidebar]{background:var(...)}`, `[data-flux-navlist-item][aria-current=page]{background:var( --color-hub-soft);...}` - Auth-Test-Suite: **0 zusätzliche Regressionen** verifiziert (8 fail / 15 passed vor & nach Phase 1; verbleibende Failures sind pre-existing Domain-Mismatch- und CSRF-Issues im Test-Setup). - Smoke-Test: `/dashboard`, `/admin/me`, `/settings/profile`, `/admin/companies`, `/admin/press-releases` antworten alle HTTP 302 → `/login` (erwartetes Verhalten ohne Auth-Session via curl). - `vendor/bin/pint --dirty` → passed. - **Bewusst NICHT in Phase 1**: - **Topbar** mit Breadcrumb + Bridge-Row + Search + „Neue Mitteilung"-CTA aus dem Mockup → ist für Phase 2 (Customer-Dashboard) sinnvoller, weil die Topbar dort Page-Kontext (Breadcrumb-Titel) braucht. Aktuell nutzen wir FluxUI's Default-Layout ohne dedizierte Topbar. - Konto-Switcher als Sidebar-Header (Avatar + Name + Firma als Dropdown-Trigger oben statt unten) → das User-Menü unten bleibt vorerst FluxUI-Standard. Iterativ in Phase 2. - Dashboard-Inhalte (Stat-Cards, Hint-Cards) → Phase 2 & 3. - Listen-Pages → Phase 4 (automatisch durch Token-Bridge schon „okay"). - **Beobachtungen**: - FluxUI-Navlist-Item-Selektor: `[aria-current="page"]` greift. Falls künftige FluxUI-Versionen `[data-current="true"]` statt `[aria-current="page"]` setzen, deckt der Override beides ab. - Vendor-Pfad in `partials/head.blade.php` bewusst nicht geändert — Vite-Setup mit `build/portal` bleibt wie gehabt. - **Nächster Schritt**: **Review-Stopp**. Bei Freigabe → entweder Phase 2 (Customer-Dashboard-Stat-Cards & Hint-Cards) oder Topbar- Iteration. Entscheidung bei dir/Frank nach Anschauen. --- ## 2026-05-19 · Login-Funktionsfix · Doppelte Alpine-Instanz - **Was**: Nach Phase 0 trat ein funktionaler Bug auf: Login-Form ging nicht durch, Browser-Logs zeigten „Detected multiple instances of Alpine running" und „Livewire: published assets out of date". - **Ursache**: Das Hub-Auth-Layout (`pressekonto.blade.php`) lud sowohl `resources/js/app.js` (das `Alpine.start()` aufruft) als auch `@livewireScripts` (das Alpine intern mitbringt). Zwei Alpine-Instanzen → `wire:submit`, `x-data`, `wire:model` brachen. - **Fixes**: - `app.js` aus `@vite([…])` im Auth-Layout entfernt — Alpine kommt nun nur noch über Livewire. - `php artisan livewire:publish --assets` → `livewire.js` von 450 kB auf 552 kB aktualisiert (Versions-Mismatch behoben). - Login/Register-Redirect auf rollen-basierte Logik umgestellt (Admin → `/dashboard`, Customer → `/admin/me`, sonst `/`) und `navigate: true` entfernt — SPA-Navigation kann den Wechsel zwischen Web-Build (Hub-Auth) und Portal-Build (FluxUI-Dashboard) nicht handhaben. - **Dateien**: - `resources/views/components/layouts/auth/pressekonto.blade.php` - `resources/views/livewire/auth/login.blade.php` - `resources/views/livewire/auth/register.blade.php` - **Verifikation**: Login funktioniert (User bestätigt), Redirect zum Dashboard läuft, keine Browser-Warnings mehr. --- ## 2026-05-19 · Phase 0 abgeschlossen · Token-Unifizierung - **Was**: Single Source of Truth für Design-Tokens etabliert. Web- und Portal-Build importieren beide `resources/css/shared/design-tokens.css`. Visuelle Unverändertheit verifiziert — FluxUI-Defaults gewinnen im Portal weiterhin (Instrument Sans, Zinc, `#3ea3dc`), wird in Phase 1 abgelöst. - **Dateien**: - `resources/css/shared/design-tokens.css` **neu** — alle Hub-Tokens plus Status-Reihe (`--color-warn`, `--color-warn-soft`, `--color-err`, `--color-err-soft`, `--color-ok-soft`), Bridge-Dots (`--color-bridge-presseecho`, `--color-bridge-businessportal`), Radii (`--radius-xs/sm/md/lg`) und Schatten (`--shadow-soft`, `--shadow-auth`). Dark-Mode-Block als auskommentierter Vorgriff. - `resources/css/web/shared-styles.css` — Token-Import nach `tailwindcss` ergänzt. Wirkt damit für alle Web-Themes (pressekonto, presseecho, businessportal24). - `resources/css/web/theme-pressekonto.css` — kompletter `@theme {}`- Block entfernt (lebt jetzt in der Token-Datei). HSL-Legacy-Variablen und `@layer components { … }` blieben unverändert. - `resources/css/portal.css` — Token-Datei importiert, FluxUI-Setup mit Zinc + Instrument Sans bleibt vorerst dominant. Phase 1 löst es ab. - **Build/Test**: - `npm run build:web` → `theme-pressekonto: 193 kB` (vorher 189 kB, +4 kB für neu gebridgde Status-Tokens), `theme-presseecho: 189 kB`, `theme-businessportal24: 189 kB`. - `npm run build:portal` → `portal: 408 kB` (vorher 397 kB, +12 kB für die zusätzlich verfügbaren Hub-Tokens als CSS-Vars im `:root`). Wird in Phase 1 wieder egalisiert, wenn das Zinc-Setup wegfällt. - Smoke-Test: `pressekonto.test/`, `/login`, `/register`, `/dashboard` (302→login), `presseecho.test/`, `businessportal24.test/` — alle HTTP 200/302 wie erwartet. - CSS-Inspektion: `--color-hub: #1a2540` und `--color-warn: #a87a1f` sind in `theme-pressekonto-*.css` enthalten. Im Portal-Build bleibt `--font-sans: "Instrument Sans"` — bestätigt visuelle Unverändertheit. - `vendor/bin/pint --dirty` → passed. - **Offene Fragen / Beobachtungen**: - Weil Tailwind v4 alle in `@theme {}` definierten Tokens als CSS- Variablen im `:root` ausgibt (auch ohne Utility-Verwendung), wird der Portal-CSS-Wuchs in Phase 1 nicht komplett zurückgehen — die Hub-Tokens bleiben als Variablen verfügbar. Der Nettoeffekt nach Phase 1 sollte ähnlich bleiben (+/- 5–10 kB). - Dark-Mode-Block in `design-tokens.css` ist auskommentiert vorbereitet, aber bewusst noch nicht aktiv (Phase 5). - **Nächster Schritt**: **Review-Stopp**. Bei Freigabe → Phase 1 starten: Portal-Shell (Sidebar, Topbar, Layout-Container) auf Hub-Style mappen, Zinc-Palette ablösen, FluxUI-Overrides via `[data-flux-*]`-Selektoren in `portal.css`. Details: `02-PHASE-1-PORTAL-SHELL.md`. --- ## 2026-05-19 · Setup - **Was**: Architektur-Entscheidung getroffen → **Plan B** (Tokens teilen, Komponenten getrennt lassen). Dokumentation in `dev/frontend/hub-flux/` angelegt: - `README.md` — Übersicht & Entscheidung - `01-PHASE-0-TOKENS.md` — Detail-Plan für Tokens-Vereinheitlichung - `02-PHASE-1-PORTAL-SHELL.md` — Detail-Plan für Portal-Shell-Refresh - `03-WEITERE-PHASEN.md` — Outline Phase 2–6 - `PROGRESS.md` — dieses Log - **Bestätigt**: - Hub-Blau als **Primärer Akzent** im Portal (klarer Kontrast zum hellen Buchpapier-Hintergrund) - Bernstein als **Sekundärer Akzent** (Notifications, Datenqualität, Akzent-Ribbons) - Brand bleibt **pressekonto** (auch im Portal-Sidebar via ``) - Reihenfolge: erst Phase 0 + 1, dann Review - **Nächster Schritt**: Phase 0 umsetzen → `resources/css/shared/design-tokens.css` erstellen, `theme-pressekonto.css` refactoren, `portal.css` minimal vorbereiten. --- ## Vorlauf (zur Erinnerung, was schon steht) - **Hub-Landing** (`web/pressekonto.blade.php`) — fertig, lebt im Web-Build mit `theme-pressekonto.css` - **Hub-Auth** (Login, Register, Forgot, Reset, Verify, Confirm) — fertig im Hub-Design, neues Layout `auth/pressekonto.blade.php` im Web-Build - **Register-AGB-Checkbox** mit Server-Side-Validierung ergänzt - **Portal-Backend** mit FluxUI funktional vorhanden (Admin-Dashboard, Customer-Dashboard, 77 Blade-Dateien mit FluxUI), aber **visuell noch Starter-Kit-Look** (Zinc + `#3ea3dc` + Instrument Sans) --- ## Template für neue Einträge ```markdown ## YYYY-MM-DD · Phase N · Kurztitel - **Was**: [Was wurde konkret gemacht?] - **Dateien**: [Pfade] - **Build/Test**: [Wie verifiziert?] - **Offene Fragen**: [Falls etwas unklar geblieben ist] - **Nächster Schritt**: [Was als Nächstes ansteht] ```