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

23 KiB
Raw Blame History

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 Roadmap-Abschluss: 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:
      @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.
  • 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:

<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

$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

$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:

$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:

$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):

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

  • Customer-Show + Admin-Show stellen alle Phase-7-Felder dar
  • PM-Listen markieren Scheduling und Embargo
  • Pressekontakt-Sidebar warnt bei leerer Auswahl
  • docs/user-admin/* ist mit dem Code synchron
  • Firmen-Liste entspricht dem Mockup zu ≥ 90 %
  • Jede PM hat ein sichtbares Hero-Bild (echtes oder Platzhalter)
  • Image-Upload erfasst Urheber + Lizenz-Typ + Rechte-Bestätigung
  • „Zur Prüfung einreichen" erfordert eine bewusste Modal-Bestätigung
  • Quota-Counter inkrementiert pro Einreichung, resettet monatlich
  • Tests grün (375 passed, 4 skipped — inkl. pre-existing ApiDocumentationTest)
  • Pint clean, Build clean
  • 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.