presseportale/dev/frontend/hub-flux/PROGRESS.md
2026-06-12 14:36:18 +00:00

98 KiB
Raw Blame History

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 · 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 6401024px). (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 (9A9C)

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 (9D9J: Tarif-Datenmodell, Stripe/Cashier — Dependency-Freigabe nötig, Tarif-UI, Tageslimit, Einzel-PM, Launch-Credits).


2026-05-29 · Phase 8 · User-Panel-Konsolidierung abgeschlossen (8F8K)

Abschluss von Phase 8. Die erste Hälfte (8A8E: 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 (8A8E 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).
  • <x-portal.press-release-placeholder> + 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/profileassertOk() 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 07 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 (06). 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)
  • <flux:textarea><flux:editor> 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 06) 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: <html>-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 04 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. 182248) 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 4A4J 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/ (0016 + 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, <x-portal.stat-card>, <x-portal.hint-card>, 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 <a> zu <div> 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 <?php($var = ...) ohne Whitespace → PHP-Syntax-Fehler trotz balancierter Klammern. Workaround: alle drei Inline-@php(...) zu Block-@php ... @endphp umgewandelt.

Verifikation

  • npm run build:portal → grün (424 KB CSS, +5 KB ggü. 4H)
  • vendor/bin/pint --dirty → passed
  • Gezielt: AdminPressReleaseActionsTest + PressReleaseWorkflowTest → 16/16 grün, 52 Assertions
  • Volle Suite: 230/231 grün, 1 pre-existing Fail (ApiDocumentationTest — fehlende docs/api/v1.yml, unverändert seit 4D), 3 skipped, 1212 Assertions

Dateien

  • dev/frontend/hub-flux/15-PHASE-4I-ADMIN-PRESS-RELEASES.md (neu)
  • resources/views/livewire/admin/press-releases/index.blade.php
  • dev/frontend/hub-flux/PROGRESS.md (dieser Eintrag)
  • dev/frontend/hub-flux/03-WEITERE-PHASEN.md (4I-Eintrag)

2026-05-20 · Phase 4H · Customer „Meine Pressemitteilungen" auf Mockup

  • Anlass: Nach Abschluss von 4F entschied sich der User bewusst gegen die nächste Roadmap-Phase und für detail-tief in das wichtigste Customer-Arbeitstool zu gehen. Vorlage: dev/frontend/tailwind_v3/User Pressemitteilungen presseportale.html.

  • Plan-Dokument: 14-PHASE-4H-PRESS-RELEASES-MOCKUP.md.

Hub-CSS erweitert (resources/css/shared/hub-components.css)

Neue Komponenten — alle aus Tokens, KEINE Hex-Literale:

  • .counter-strip + .seg (mit is-ok, is-warn, is-err, is-muted) + .sep
  • .view-tabs + .view-tab (mit is-active, cnt-Pille)
  • .filter-chip (mit is-active, caret)
  • .active-chip (mit x-Remove-Button)
  • .portal-pill (mit pe + bp Dot-Varianten, nutzt --color-bridge-presseecho / --color-bridge-businessportal)
  • .inline-action (mit warn + err Varianten)
  • .is-row-warn + .is-row-err (Row-Tinting via color-mix aus Status-Soft-Token gegen Card-Background)
  • .empty-stage + .empty-ico (mit warm + err Varianten)
    • .empty-title + .empty-sub
  • .badge.muted (neue Variante neben ok, warn, err, hub)

Volt-Komponente erweitert (customer/press-releases/index.blade.php)

  • Aggregierte statusCounts (assoc) per groupBy('status') über dieselbe $base-Query — eine zusätzliche SQL-Query
  • Neue Properties: $portalFilter
  • Neue Methoden: setView($status), resetFilters(), updatedPortalFilter()
  • Daten an View: statusCounts, portalOptions
  • Paginierung von 100 → 25 (sinnvoller für Mockup-Layout)

Markup auf Mockup-Stil (~50 % → ~90 %)

  • Counter-Strip im Header: dynamische Segmente werden nur angezeigt, wenn Count > 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 --dirtypassed
  • 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 <x-portal.stat-card> (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 (<x-portal.stat-card>), 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 <x-portal.stat-card> (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 --dirtypassed.
  • 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, …).
  • <flux:input>, <flux:select>, <flux:checkbox>, <flux:textarea>, <flux:field>, <flux:error>, <flux:table.*>, <flux:button>, <flux:modal>.
  • 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. <flux:select>, 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 <dl> 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}.php29 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 agentpassed.
  • 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 <flux:text>-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).
    • <x-action-message> 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 <x-portal.stat-card> (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).
    • <flux:select variant="combobox"> (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 (<flux:field>, <flux:label>, <flux:input>, <flux:textarea>, <flux:select> inkl. Combobox-Variant, <flux:checkbox>, <flux:error>) — 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.
    • <livewire:components.press-release-images-manager> — 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 <span class="badge …"> mit @class([])-Syntax.
  • Customer-Show-spezifisch:

    • Rejection-Hinweis als roter .panel mit linker Akzent-Border (border-left-width:3px) und Icon-Box — statt <flux:callout color="red">.
    • Review-Pending-Hinweis als warner .panel mit Akzent-Border und Clock-Icon-Box — statt <flux:callout color="yellow">.
    • 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 (~35 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: <header> 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-<flux:table> bleibt — das Zinc-→Hub-Mapping aus Phase 1 zieht hier perfekt.
    • Status-Badges: <flux:badge color="green|yellow|red|zinc|blue"> ersetzt durch <span class="badge ok|warn|err|hub"> 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 <x-portal.stat-card> (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 <x-portal.stat-card>):
      • 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:portalportal: 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 --dirtypassed.
    • 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
      • <x-portal.stat-card>-Investition zahlt sich ab Phase 3 deutlich aus.
    • Der <x-portal.stat-card> 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.
      <flux:radio.group x-data variant="segmented" size="sm" x-model="$flux.appearance">
          <flux:radio value="light" icon="sun" :title="__('Hell')" />
          <flux:radio value="dark" icon="moon" :title="__('Dunkel')" />
          <flux:radio value="system" icon="computer-desktop" :title="__('System')" />
      </flux:radio.group>
      
    • $flux.appearance ist FluxUIs Magic-Object — LocalStorage-persistent, @fluxAppearance-Script setzt class="dark" auf <html>.
    • 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 <head>-Pipeline).
  • Build/Verifikation:

    • npm run build:portalportal: 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 --dirtypassed.
    • 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/:

    • <x-portal.stat-card variant="…" label="…" :value="…"> mit Slots meta (oben rechts) und trend (unten).
    • <x-portal.hint-card :icon :title :percent :href :action> 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-cards): 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 <x-portal.hint-card>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 <flux:icon> 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:portalportal: 417.02 kB (Δ +8 kB für neue Hub-Components-Klassen + Dashboard-Klassen). Web-Build unverändert.
    • vendor/bin/pint --dirtypassed.
  • Was bewusst weggelassen:

    • Sparklines auf den Stat-Cards (kommen mit echten Trend-Daten in Phase 4).
    • Topbar-Anpassung (eigene Phase, später).
    • <x-portal.bridge-card>-Extraktion (Brand-Bridge bleibt inline, bis sie an mehr Stellen gebraucht wird).
  • Lessons learned:

    • PHPDoc in Blade-Components darf KEINE <flux:*>-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 <body> injiziert @fluxScripts (sidebar.blade.php Zeile nahe </body>) 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 <html>, 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 <head>-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:portalportal: 408.97 kB (Δ +0.02 kB).
    • php artisan test --compact --filter='AuthenticationTest|RegistrationTest'6 passed, 19 assertions, 1.93s.
    • vendor/bin/pint --dirty --format agentpassed.
  • 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:portalportal: 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 (<flux:button variant="filled">) 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 <html> entfernt
      • <body> jetzt bg-bg text-ink antialiased (Hub-Buchpapier)
      • <x-app-logo> ersetzt durch <x-web.brand-mark brand="pressekonto" :serif="false" /> + 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.phpterms_accepted im Formular gesetzt + Redirect-Erwartung auf / angepasst (frisch registrierte User haben keine Rolle → Fallback /)
  • Build/Test:
    • npm run build:portalportal: 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 --assetslivewire.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:webtheme-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:portalportal: 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 (+/- 510 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 26
    • 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 <x-web.brand-mark>)
    • 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

## 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]