- 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>
612 lines
23 KiB
Markdown
612 lines
23 KiB
Markdown
# Phase 8 · User-Panel-Konsolidierung & Pressemitteilungs-Lifecycle
|
||
|
||
Stand: 2026-05-29 — **abgeschlossen** (alle Päckchen 8A–8K 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 = 1–3 h, M = 3–8 h, L = 8–16 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 (8A–8C), 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
|
||
**8A–8C** 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.
|