presseportale/docs/PHASE-8-USER-PANEL-PLAN.md
Kevin Adametz 8d8d957884 Doku: Status-Sync 11./12.06., Decision-Update Preisstruktur und Phase-9-Plan
- Decision-Update Preisstruktur & Veroeffentlichungs-Flow aufgenommen
  (Launch-Tarife, Slot-Verbrauch bei Veroeffentlichung, Submit-Gate,
  Launch-Credits) inkl. Klarstellung 12.06.: Gelb geht direkt live,
  keine manuelle Pruef-Queue, nur Rot wird abgelehnt
- Alle Status-Dokumente auf den Code-Stand gezogen: README-Index,
  STATUS-ABGLEICH (KI-Pipeline, Bilder/Lizenzen, Pricing), Checkliste
  (KI- und Titelbild-Bloecke, Launch-To-dos), Admin-User,
  user-zusammenhaenge (Datenmodell-Delta), Entwicklungsplan KI-Pruefung
  (Phase 0 abgehakt, Decision-Abgleich)
- Ueberschriebene Tarif-Abschnitte in Konzept-Update 1/2 und
  Relaunch-Konzept mit Superseded-/IST-Hinweisen markiert
- Neues Plan-Dokument PHASE-9-FLOW-UND-TARIFE-PLAN.md (9A-9J)
- Phase-8-Roadmap-Doku (20-PHASE-8-USER-PANEL.md) + PROGRESS-Eintraege

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 09:20:22 +00:00

612 lines
23 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 8 · User-Panel-Konsolidierung & Pressemitteilungs-Lifecycle
Stand: 2026-05-29 — **abgeschlossen** (alle Päckchen 8A8K umgesetzt).
Vorgänger: Phase 7 (Press-Release-Form-Refactor — abgeschlossen)
Abgleich-Doku: [`docs/STATUS-ABGLEICH-USER-PANEL.md`](./STATUS-ABGLEICH-USER-PANEL.md)
Roadmap-Abschluss: [`dev/frontend/hub-flux/20-PHASE-8-USER-PANEL.md`](../dev/frontend/hub-flux/20-PHASE-8-USER-PANEL.md)
> **Status 29.05.2026**: Alle Sub-Päckchen abgeschlossen. Bewusste
> Abweichung in 8H: Upload-Control bleibt `flux:input type=file` statt
> `flux:file-upload` (Stabilität); die Lizenz-Pflichtfelder sind vollständig
> umgesetzt. Der Rechtstext im Veröffentlichungs-Modal (8I) ist ein
> Platzhalter und vor Go-Live anwaltlich zu prüfen.
---
## 0. Worum es geht
Phase 8 bündelt drei thematisch zusammenhängende Bündel:
1. **User-Panel-Konsolidierung** — Lücken aus Phase 7 schließen, Firmen-Liste
auf das Mockup-Niveau heben (`dev/frontend/tailwind_v3/User Firmen presseportale.html`).
2. **Pressemitteilungs-Titelbild & SVG-Platzhalter** — jede PM bekommt ein
sichtbares Hero-Bild, entweder eigener Upload oder ein farbiger
SVG-Platzhalter aus einem definierten Set.
3. **Veröffentlichungs-Modal mit rechtlichem Hinweis + Quota-Vorbereitung**
— Pressemitteilungen werden nur über ein bewusstes Modal eingereicht;
in dem Modal steht ein rechtlicher Hinweis und die Information, dass
ein PM-Kontingent verbraucht wird.
Alle Änderungen, die das User-Panel betreffen, werden **konsistent ins
Admin-Panel übertragen**, sobald sie inhaltlich passen (z. B.
Show-Pages, Listen-Indikatoren, Bild-Manager-Wrapper).
---
## 1. Sub-Päckchen-Übersicht
| ID | Thema | Größe | Risiko |
|---|---|---|---|
| **8A** | Show-Page-Lücken schließen (subtitle, scheduling, embargo, boilerplate_override) — Customer + Admin | S | gering |
| **8B** | Listen-Indikatoren für Scheduling/Embargo — Customer + Admin | S | gering |
| **8C** | Pressekontakt-Warnung in Sidebar-Card (Customer + Admin) + Tests | XS | gering |
| **8D** | Doku-Pflege: Phase-7-Schlussfeinheiten in `19-PHASE-7-…md` + Konzept-Anpassungen aus Abgleich | S | keine |
| **8E** | Firmen-Liste auf Mockup-Niveau (Counter-Strip, Saved-Views, Filter-Chips, Card/List-Toggle, Rollen-Legende) | L | mittel |
| **8F** | SVG-Platzhalter-Set extrahieren + auswählbar machen (Customer-Modal) | M | mittel |
| **8G** | PressRelease-Titelbild — Schema, Default-Platzhalter, Vorschau im Form | M | mittel |
| **8H** | FluxUI `flux:file-upload` im Image-Manager + Pflichtfelder Urheber/Lizenz/Rechte | M | mittel |
| **8I** | Veröffentlichungs-Modal mit Rechts-Hinweisen + Quota-Anzeige (Customer) + Hook | M | mittel |
| **8J** | Quota-Stub: Demo-Counter im Datenmodell + Decrement-Hook im PressReleaseService | M | hoch (Datenmodell) |
| **8K** | Tests + Pint + Build + Roadmap-Update + Abschluss-Eintrag in `PROGRESS.md` | S | gering |
**Abkürzungen**: XS = < 1 h, S = 13 h, M = 38 h, L = 816 h.
Reihenfolge entspricht dem geplanten Ablauf. Nach jedem Päckchen ist ein
Review-Stopp mit dem User vorgesehen, bevor das nächste startet.
---
## 2. Päckchen im Detail
### 8A · Show-Page-Lücken schließen
**Ziel**: Customer-Show und Admin-Show zeigen die Phase-7-Felder.
**Anpassungen**:
- `resources/views/livewire/customer/press-releases/show.blade.php`
- Untertitel direkt unter H1 als kleinere Headline anzeigen
- Geplante Veröffentlichung"-Card mit `scheduled_at`, falls gesetzt
- Embargo bis"-Card mit `embargo_at`, falls gesetzt
- Boilerplate (Override)"-Card, falls `boilerplate_override` befüllt
- `resources/views/livewire/admin/press-releases/show.blade.php`
- Untertitel
- Boilerplate-Override (Scheduling/Embargo sind bereits da)
**Tests**: bestehende Show-Tests erweitern um Assertions für die neuen
Sichtbarkeiten.
**Akzeptanz**: Customer- und Admin-Show stellen exakt dieselben PM-Felder
dar, die in den Forms gepflegt werden können (außer Anhänge deaktiviert).
---
### 8B · Listen-Indikatoren für Scheduling/Embargo
**Ziel**: In beiden PM-Listen sieht man auf einen Blick, welche PMs
geplant sind oder unter Embargo stehen.
**Anpassungen**:
- `customer/press-releases/index.blade.php`:
- In der Datums-Spalte: zusätzliches Sub-Label geplant · 21.05. 14:00"
bzw. Embargo bis 21.05."
- `admin/press-releases/index.blade.php`:
- Analog
**UI-Pattern**: Mono-Sub-Zeile unter dem Hauptdatum, wenn `status =
review` UND `scheduled_at`/`embargo_at` gesetzt.
**Tests**: ein neuer Pest-Test pro Liste, der ein PM mit
`scheduled_at`/`embargo_at` erstellt und die Sub-Zeile assertet.
---
### 8C · Pressekontakt-Warnung im Form-Sidebar
**Ziel**: Wenn keine Pressekontakt in einer PM gewählt ist, zeigt die
Sidebar-Card eine dezente Warn-Box (analog zur Telefonnummer fehlt"-
Warnung), damit klar ist, dass die PM zwar speicherbar ist, aber
einen Kontakt empfehlen sollte.
**Anpassungen**:
- `customer/press-releases/create.blade.php` + `edit.blade.php`:
- Im Pressekontakt-Sidebar-Card:
```blade
@if (! $contactId)
<div class="inline-callout warn">
<flux:icon.exclamation-triangle class="size-4" />
Es wurde noch kein Pressekontakt ausgewählt. Empfohlen, aber nicht zwingend.
</div>
@endif
```
- `admin/press-releases/create.blade.php` + `edit.blade.php`: identisch
**Tests**: Form-Render-Test mit ohne-Kontakt-Setup, das den Warn-String
assertet.
---
### 8D · Doku-Pflege
**Ziel**: `19-PHASE-7-PRESS-RELEASE-FORM.md` und Konzept-Dokumente an
den IST-Stand anpassen, damit zukünftige Phasen auf einer sauberen
Basis aufsetzen.
**Konkret**:
- `dev/frontend/hub-flux/19-PHASE-7-PRESS-RELEASE-FORM.md`:
- Block ergänzen: „Anhänge deaktiviert; Tests skipped; siehe Security-Review"
- Block ergänzen: „Pressekontakt nullable; Warnung im Sidebar"
- Block ergänzen: „Sidebar-Reihenfolge: Status → Kategorie → Portal
(Pill) → Pressekontakt → Themen-Tags → Veröffentlichung → Weitere
Felder → Phase-2-Footer"
- `docs/user-admin/Admin-User.md`: Aktualisierungen aus
[`STATUS-ABGLEICH-USER-PANEL.md` Abschnitt 6.1](./STATUS-ABGLEICH-USER-PANEL.md#61-sofort-ohne-risiko-machbar).
- `docs/user-admin/checkliste-user-backend.md`: neuen Phase-7-Block
hinzufügen.
**Akzeptanz**: Wer die `docs/`-Hauptdokumente liest, bekommt einen
zutreffenden Eindruck vom IST-Stand.
---
### 8E · Firmen-Liste auf Mockup-Niveau
**Status**: ✅ abgeschlossen (21.05.2026)
**Was umgesetzt wurde**
- `resources/views/livewire/customer/press-kits/index.blade.php` komplett
überarbeitet (Volt + Hub-Tokens).
- Neue Hub-Tokens in `resources/css/shared/hub-components.css`: `.firm-card`,
`.add-tile`, `.seg-toggle`, `.role-pill`, `.mini-logo`, `.lg-*`-Logo-Varianten,
`.menu-trigger`, `.card-action`, `.page-btn`.
- Counter-Strip mit `Firmen · aktiv · PMs gesamt · Pressekontakte hinterlegt`.
- Saved-View-Tabs `Alle/Aktiv/In Anlage/Inaktiv/Mit mir geteilt`, mit
Live-Counts. „In Anlage" ist bewusst noch leer (Phase-2-Heuristik).
- Filter-Chips (Portal, Rolle) via FluxUI-Dropdown + URL-Sync (`?view=…&portal=…&role=…&mode=…`).
- View-Toggle Karten / Liste, persistiert in der URL als `?mode=list`.
- Karten- und Listen-Ansicht mit Hub-Look, deterministische Logo-Varianten,
Status-Badge, Portal-Pills, Rolle-Pill, KPIs (PMs / Kontakte / letzte PM),
Aktionen „Firma öffnen" und „Neue PM".
- Add-Tile auf der letzten Seite (CTA: `Firma anlegen anfragen` → Profil).
- Empty-States: 3-Schritt-Onboarding (keine Firmen) und Reset-CTA (Filter
ohne Treffer).
- Rollen-Legende als `panel-warm` mit Owner / Verantwortlich / Mitglied.
- `tests/Feature/CustomerPressKitIndexPhase8eTest.php` mit 14 Tests
(Counter, Saved-Views, Filter, View-Mode, Empty-States, Add-Tile,
Rollen-Legende).
**Ziel**: `customer/press-kits/index.blade.php` entspricht dem Mockup
`dev/frontend/tailwind_v3/User Firmen presseportale.html`.
**Mockup-Komponenten** (alle CSS-Klassen aus
`resources/css/shared/hub-components.css` bereits vorhanden oder leicht
ergänzbar):
- **Counter-Strip** (`.counter-strip`): `X Firmen · X aktiv · X PMs gesamt · X Pressekontakte`
- **Saved-View-Tabs** (`.view-tabs`): `Alle / Aktiv / In Anlage / Inaktiv / Mit mir geteilt`
- „In Anlage" = neue Firma, die noch keine Stammdaten hat (heuristisch: kein Logo + keine PMs)
- „Mit mir geteilt" = `company_user.role IN (member, responsible)` ohne `owner_user_id = me`
- **Filter-Chips** (`.filter-chip`):
- Status (Aktiv/Inaktiv/Alle)
- Portal (presseecho/businessportal24/Alle)
- Rolle (Admin/Redakteur/Beobachter/Alle) — „Admin"-Begriff für Owner,
„Redakteur" für `responsible`, „Beobachter" für `member`
- Branche (Auswahl aus vorhandenen `Industry`-Werten)
- **Seg-Toggle** (`.seg-toggle`): Karten- vs. Listen-Ansicht (Default Karten)
- **Karten** (`.firm-card`):
- Logo (Hub-Token-Box oder echtes Logo)
- Status-Badge
- Portal-Pills (eine oder zwei, je nach Firma-Portal)
- Rolle-Pill (`.role-pill.admin` für Owner)
- KPIs: PMs, Pressekontakte, Datum letzte PM
- Aktionen: „Bearbeiten" (zur Firma) + „Neue PM" (Editor mit Firma vorgewählt)
- `is-self`-Highlight für die aktive Firma aus dem Context-Switcher
- **Add-Tile**: „Neue Firma anlegen" — derzeit nur Anfrage-Link auf Profil
(Self-Service-Anlage ist Phase-2-Thema)
- **Empty-States**:
- Keine Firma: 3-Schritt-Onboarding (Stammdaten → Boilerplate → Pressekontakte) — derzeit nur Anfrage-CTA
- Filter ohne Treffer: Reset-CTA
- **Rollen-Legende** am Ende als `panel-warm`
**Volt-Anpassungen**:
- Computed Properties: `statusCounts`, `portalOptions`, `roleOptions`,
`industryOptions`
- Neue Properties: `$statusFilter`, `$portalFilter`, `$roleFilter`,
`$industryFilter`, `$viewMode` (cards|list)
- Methoden: `setView($status)`, `resetFilters()`, `toggleViewMode()`
**Was bewusst NICHT in 8E kommt**:
- Echte Self-Service-Firma-Anlage (Phase 2)
- Statistik-Tab in Firmen-Detail (Phase 2)
- Abrechnung pro Firma (Phase 2)
**Tests**:
- Komponente rendert mit 0/1/N Firmen
- Filter-Kombinationen
- View-Mode-Toggle
- Counter-Strip-Zahlen stimmen mit den Filtern
**Admin-Spillover**: Die `admin/companies/index.blade.php` hat einen
ähnlichen, älteren Mockup-Stand. Wenn Zeit übrig: Counter-Strip und
Saved-Views auch dort einbauen (separater Patch im selben Päckchen,
nur wenn ohne Mehraufwand machbar — sonst Phase 9).
---
### 8F · SVG-Platzhalter-Set
**Ziel**: Ein wiederverwendbares Set von Hero-Platzhaltern für PMs ohne
echtes Titelbild.
**Quelle**: Bestehende inline SVGs in den Landing-Page-Komponenten:
- `resources/views/components/web/focus-hero.blade.php` (760×500, Punkt-Pattern + Kreise)
- `resources/views/components/web/feed-top-item.blade.php` (240×160, Linien + Punkte)
- ggf. weitere aus `industry-spotlight`, `quality-summary`, `live-ticker`
**Neue Struktur**:
```
resources/views/components/portal/press-release-placeholder.blade.php
public/images/press-release-placeholders/
01-grid-blue.svg
02-grid-green.svg
03-grid-amber.svg
04-lines-blue.svg
05-lines-green.svg
06-lines-amber.svg
07-dots-blue.svg
08-dots-green.svg
09-dots-amber.svg
```
Jede SVG ist 1600×900 (Hero-Aspect-Ratio 16:9), entspricht dem Bildformat
des `large`-Variant aus `ImageService`.
**Komponente**:
```blade
<x-portal.press-release-placeholder
:variant="$pressRelease->placeholder_variant ?? '01-grid-blue'"
:title="$pressRelease->title"
class="aspect-[16/9] w-full" />
```
**Modal-Auswahl** (Volt-Sub-Komponente):
```
resources/views/livewire/components/press-release-placeholder-picker.blade.php
```
- Grid 3×3 mit Vorschau aller Varianten
- Wire-Event `placeholderSelected($variant)` → Parent setzt
`placeholder_variant` und schließt das Modal
**Tests**: Komponente rendert mit jeder Variante; Picker emittiert
korrektes Event.
---
### 8G · Titelbild-Schema & Default-Logik
**Ziel**: Jede PM hat **immer** ein Hero-Bild — entweder echtes Bild
oder Platzhalter.
**Schema-Änderung**:
Migration: `add_placeholder_variant_to_press_releases.php`
```php
$table->string('placeholder_variant', 32)->nullable()->after('boilerplate_override');
```
**Default-Logik**:
- `PressRelease::booted` → bei `creating`: wenn `placeholder_variant`
leer, würfle eine Variante aus dem Set
- alternativ in `customer/press-releases/create.blade.php`: nach
`mount()` Default setzen
**Cover-Image-Resolver** (Service):
```
app/Services/PressRelease/PressReleaseCoverImage.php
```
Public-Methoden:
- `coverUrl(PressRelease $pr, string $variant = 'large'): string`
- wenn echtes Preview-Bild da → `variantUrl($variant)`
- sonst → `asset('images/press-release-placeholders/'.$pr->placeholder_variant.'.svg')`
- `coverIsPlaceholder(PressRelease $pr): bool`
**Verwendung**:
- Hero in Customer-Show, Admin-Show, Public-Detail-Page
- Thumb in beiden Listen
- Vorschau im Form
**Tests**: Resolver-Unit-Test für beide Fälle.
---
### 8H · FluxUI File-Upload + Lizenzfelder
**Ziel**: Image-Manager nutzt `flux:file-upload`, erfasst die rechtlich
nötigen Felder, eckende UI passt zum Mockup.
**Anpassungen** in `resources/views/livewire/components/press-release-images-manager.blade.php`:
- `<flux:file-upload accept="image/jpeg,image/png,image/webp" wire:model="newImage" />`
(Dropzone-Style aus FluxUI)
- Zusätzliche Felder als FluxUI-Inputs:
- **Urheber/Fotograf** (`flux:input` required)
- **Lizenztyp** (`flux:select` mit Enum-Werten):
- Eigene Aufnahme
- CC-Lizenz (Lizenz-URL Pflicht)
- Kommerzielle Lizenz erworben (Lizenz-URL Pflicht)
- Einwilligung des Urhebers
- Sonstiges
- **Lizenz-URL** (`flux:input` conditional required)
- **Personen-Einwilligung** (`flux:checkbox` optional)
- **Rechte-Bestätigung** (`flux:checkbox` required, mit AGB-Text aus
`Presseportal Konzept für Relaunch.md` Abschnitt 2)
**Schema**:
Migration: `add_license_fields_to_press_release_images.php`
```php
$table->string('author')->nullable();
$table->string('license_type', 32)->nullable();
$table->string('license_url')->nullable();
$table->boolean('persons_consent')->default(false);
$table->timestamp('rights_confirmed_at')->nullable();
```
Enum: `App\Enums\ImageLicenseType` (PHP-Enum mit Labels).
**Tests**:
- Upload ohne Urheber → Validierungs-Fehler
- Upload mit `license_type = cc` ohne `license_url` → Fehler
- Upload mit allen Pflichtfeldern → erfolgreich, `rights_confirmed_at` gesetzt
**UI-Skizze** (Form-Reihenfolge):
```
┌─────────────────────────────────────┐
│ [flux:file-upload Dropzone] │
├─────────────────────────────────────┤
│ Urheber: [input] * │
│ Lizenztyp: [select] * │
│ Lizenz-URL: [input] (*) │
│ [ ] Personen-Einwilligung │
│ [ ] Ich bestätige die Rechte * │
│ │
│ [Hochladen] │
└─────────────────────────────────────┘
```
---
### 8I · Veröffentlichungs-Modal
**Ziel**: Customer kann eine PM nur über ein explizites Modal mit
rechtlichem Hinweis und Quota-Information einreichen.
**Anpassungen** in `customer/press-releases/edit.blade.php`:
- „Zur Prüfung einreichen"-Button löst Modal-Open aus statt direkt
`submitForReview` zu rufen
- Modal-Inhalt:
- Eyebrow „Veröffentlichung"
- H3 „Pressemitteilung zur Prüfung einreichen"
- Block „Rechtliche Hinweise" (aus Konzept-Abschnitt 5 zur
DSGVO-Position + Abschnitt 2 zu Bildrechten, plus AGB-Verweis)
- Block „Kontingent" — Anzeige `Ihre verbrauchten PMs in diesem Monat: X / Y`
- Block „Bestätigungen":
- [ ] Inhalt entspricht den AGB
- [ ] Bildrechte sind geklärt
- [ ] Pressekontakt-Daten korrekt
- Footer: „Abbrechen" (sekundär) + „Veröffentlichung anfordern" (primär,
disabled bis alle 3 Checkboxen gesetzt)
- Confirm-Button ruft `submitForReview()` (existiert bereits)
**Schreibweise** (juristisch sicherer Ankertext):
```
Mit dem Einreichen dieser Pressemitteilung versichern Sie:
- Sie sind befugt, den Inhalt zu veröffentlichen.
- Alle verwendeten Bilder, Logos und Zitate liegen in Ihrer Nutzungsbefugnis.
- Personenbezogene Daten sind nur in dem für die Berichterstattung
zwingend erforderlichen Umfang enthalten.
- Aussagen entsprechen Ihrem Wissensstand und sind sachlich richtig.
Sie stellen [Plattform] von Ansprüchen Dritter frei, die aus einer
unberechtigten Nutzung von Inhalten resultieren. Die endgültige
Veröffentlichung erfolgt nach redaktioneller Prüfung.
```
(Exakte Formulierung muss vor Go-Live durch einen Anwalt geprüft werden —
für die Bauphase reicht der Platzhalter.)
**Quota-Anzeige**:
In Phase 8I noch ohne echte Tarif-Logik: Anzeige eines **Demo-Counters**,
der aus einer einfachen Aggregation (`User::pressReleasesPublishedThisMonth()`)
kommt — und einer hartcodierten Obergrenze (z. B. 3 als Starter-Default).
Das echte Tarif-System kommt in einer späteren Phase.
**Tests**:
- Modal öffnet sich bei Klick
- Submit-Button bleibt disabled bis alle 3 Checkboxen gesetzt
- Nach Bestätigung: PM-Status ist `review`
- Toast-Bestätigung wird angezeigt
**Admin-Spillover**: Im Admin-Editor reicht der Admin direkt ein
(`publish`), kein Modal nötig — der Hinweis ist auf den Customer-Flow
zugeschnitten.
---
### 8J · Quota-Stub
**Ziel**: Die UI-Anzeige in 8I hängt am echten Datenmodell, auch wenn
das vollständige Tarif-/Credit-System erst später kommt.
**Datenmodell** (minimal):
Migration: `create_user_quota_table.php` oder als JSON in `profiles`?
> **Entscheidung**: Wir machen es als Migration-light auf `users`:
>
> ```php
> $table->unsignedInteger('press_release_quota')->default(3)->after('settings');
> $table->unsignedInteger('press_release_quota_used_this_month')->default(0)->after('press_release_quota');
> ```
>
> Diese Spalten sind temporär, das echte Tarif-Modell überschreibt sie
> oder ersetzt sie durch eigene Tabellen.
**Service** in `PressReleaseService::submitForReview`:
```php
$user->increment('press_release_quota_used_this_month');
```
(Reset des Counters per Scheduled-Command monatlich → eigener kleiner
Befehl `ResetMonthlyPressReleaseQuota`.)
**API für die View** (z. B. via `User`-Method):
```php
public function pressReleaseQuotaRemaining(): int
{
return max(0, $this->press_release_quota - $this->press_release_quota_used_this_month);
}
```
**Akzeptanz**: Veröffentlichungs-Modal zeigt sinnvolle Zahlen, der
Counter erhöht sich nach Submit, der Scheduler-Command resettet ihn
zum 1. des Monats. Tarif-Anbindung folgt später.
**Tests**:
- Counter inkrementiert bei `submitForReview`
- Counter resettet via Scheduled-Command (Unit-Test)
---
### 8K · Tests, Pint, Build, Roadmap
**Ziel**: Saubere Übergabe.
- `vendor/bin/sail artisan test --compact` muss durchlaufen (außer
pre-existing `ApiDocumentationTest`).
- `vendor/bin/sail bin pint --dirty --format agent` clean.
- `vendor/bin/sail npm run build:portal` clean.
- `dev/frontend/hub-flux/20-PHASE-8-USER-PANEL.md` als neue
Roadmap-Doku (analog zu `19-…`).
- Eintrag in `dev/frontend/hub-flux/PROGRESS.md` mit allen Sub-Päckchen.
- `docs/user-admin/checkliste-user-backend.md` um Phase-8-Block ergänzen.
---
## 3. Was außerhalb von Phase 8 bleibt
Bewusst nicht in Phase 8:
- **Magic-Link-Flow für Pressekontakte** → Phase 9 oder Phase 2 lt. Konzept
- **Statistik-Tab in Firmen-Detail** → Phase 9
- **Self-Service-Firmen-Anlage** → Phase 9
- **Notice-and-Action für externe Meldungen** → Phase 2/3
- **KI-Vorprüfung** → Phase 2/3
- **Korrektur-/Update-Hinweis-System** → Phase 2/3
- **Echtes Tarif-/Credit-System mit Stripe** → eigene Phase
- **Trust-Score / Score-System** → Phase 3
- **Anhänge-Reaktivierung** → eigener Sicherheits-Audit-Track
---
## 4. Reihenfolge & Review-Punkte
Vorgeschlagene Reihenfolge der Päckchen:
```
8D (Doku zuerst — bewegt sich nichts am Code)
→ 8A (Show-Page-Lücken)
→ 8B (Listen-Indikatoren)
→ 8C (Pressekontakt-Warnung)
→ Review-Stopp mit User
→ 8E (Firmen-Liste auf Mockup) — größtes Päckchen, Review davor
→ Review-Stopp
→ 8F (SVG-Platzhalter extrahieren)
→ 8G (Titelbild-Schema + Default)
→ 8H (FluxUI File-Upload + Lizenzfelder)
→ Review-Stopp
→ 8J (Quota-Stub im Datenmodell zuerst, damit 8I darauf aufsetzen kann)
→ 8I (Veröffentlichungs-Modal)
→ 8K (Abschluss)
```
**Begründung**: Doku zuerst, weil der Abgleich sonst veraltet. Dann
kleine UX-Lücken (8A8C), die schnelle Wins sind. Anschließend die
größere Firmen-Liste (8E), die ein eigenes Päckchen ist. Bild- und
Veröffentlichungs-Block am Ende, weil sie thematisch zusammengehören
und Schema-Änderungen mitbringen.
---
## 5. Risiken & Annahmen
- **Annahme**: FluxUI `flux:file-upload` ist in der aktuellen Version
voll funktional und kompatibel mit `WithFileUploads` von Livewire.
Fallback: bestehender Standard-Upload bleibt erhalten.
- **Annahme**: SVG-Platzhalter (1600×900) sind klein genug, dass wir
sie direkt aus `public/images/...` ausliefern — kein CDN-Setup nötig.
- **Risiko**: Schema-Änderungen in 8G + 8H + 8J berühren produktive
Tabellen (`press_releases`, `press_release_images`, `users`). Alle
Migrations sind additive (nullable + default), Rollback-fähig.
- **Risiko**: Quota-Stub in 8J wird vom echten Tarif-System abgelöst
— Code-Schnittstelle (`pressReleaseQuotaRemaining()`) muss stabil bleiben,
damit das Veröffentlichungs-Modal nicht neu gebaut werden muss.
- **Risiko**: Rechtstext im Veröffentlichungs-Modal ist Platzhalter.
Vor Go-Live durch Anwalt zu prüfen.
---
## 6. Akzeptanzkriterien Phase 8 gesamt
- [x] Customer-Show + Admin-Show stellen alle Phase-7-Felder dar
- [x] PM-Listen markieren Scheduling und Embargo
- [x] Pressekontakt-Sidebar warnt bei leerer Auswahl
- [x] `docs/user-admin/*` ist mit dem Code synchron
- [x] Firmen-Liste entspricht dem Mockup zu ≥ 90 %
- [x] Jede PM hat ein sichtbares Hero-Bild (echtes oder Platzhalter)
- [x] Image-Upload erfasst Urheber + Lizenz-Typ + Rechte-Bestätigung
- [x] „Zur Prüfung einreichen" erfordert eine bewusste Modal-Bestätigung
- [x] Quota-Counter inkrementiert pro Einreichung, resettet monatlich
- [x] Tests grün (375 passed, 4 skipped — inkl. pre-existing `ApiDocumentationTest`)
- [x] Pint clean, Build clean
- [x] Roadmap-Eintrag und `PROGRESS.md`-Block geschrieben
---
## 7. Nächster Schritt
Mit **8D (Doku)** starten, weil das ohne Code-Änderungen funktioniert
und den Boden für die folgenden Päckchen ebnet. Direkt im Anschluss
**8A8C** als Block, weil sie zusammen die Phase-7-Lücken schließen.
Danach Review-Stopp für Phase 8E (Firmen-Liste) — das ist das
sichtbarste Päckchen für den User und sollte mit klarem Mockup-Vergleich
abgenommen werden.