29-05-2026 Optimierungen Fixes am Code

This commit is contained in:
Kevin Adametz 2026-05-29 12:42:05 +00:00
parent e8c47b7553
commit 4bb9094207
31 changed files with 5141 additions and 76 deletions

View file

@ -2,6 +2,7 @@
namespace App\Actions\Fortify;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Validation\Rules\Password;
trait PasswordValidationRules
@ -9,7 +10,7 @@ trait PasswordValidationRules
/**
* Get the validation rules used to validate passwords.
*
* @return array<int, \Illuminate\Contracts\Validation\Rule|array<mixed>|string>
* @return array<int, Rule|array<mixed>|string>
*/
protected function passwordRules(): array
{

View file

@ -55,6 +55,7 @@ class GenerateDomainFavicons extends Command
// Wenn die Datei bereits existiert, frage, ob sie überschrieben werden soll
if (File::exists($faviconPath) && ! $this->confirm("Favicon für '{$theme}' existiert bereits. Überschreiben?")) {
$this->info("Favicon für '{$theme}' übersprungen.");
continue;
}

View file

@ -1,36 +0,0 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
\App\Console\Commands\GenerateDomainFavicons::class,
];
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*/
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}

View file

@ -1,5 +1,7 @@
<?php
use App\Models\User;
return [
/*
@ -62,7 +64,7 @@ return [
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => env('AUTH_MODEL', App\Models\User::class),
'model' => env('AUTH_MODEL', User::class),
],
// 'users' => [

View file

@ -1,5 +1,7 @@
<?php
use Illuminate\Database\Eloquent\Model;
return [
/*
@ -52,7 +54,7 @@ return [
|
*/
'parent' => Illuminate\Database\Eloquent\Model::class,
'parent' => Model::class,
/*
|--------------------------------------------------------------------------
@ -522,7 +524,7 @@ return [
'path' => app_path('Models/Legacy/Presseecho'),
'namespace' => 'App\\Models\\Legacy\\Presseecho',
'connection' => true,
'parent' => Illuminate\Database\Eloquent\Model::class,
'parent' => Model::class,
'timestamps' => true,
'soft_deletes' => true,
'snake_attributes' => true,
@ -549,7 +551,7 @@ return [
'path' => app_path('Models/Legacy/Presseecho'),
'namespace' => 'App\\Models\\Legacy\\Presseecho',
'connection' => true,
'parent' => Illuminate\Database\Eloquent\Model::class,
'parent' => Model::class,
'timestamps' => true,
'soft_deletes' => true,
'snake_attributes' => true,
@ -575,7 +577,7 @@ return [
'path' => app_path('Models/Legacy/Businessportal'),
'namespace' => 'App\\Models\\Legacy\\Businessportal',
'connection' => true,
'parent' => Illuminate\Database\Eloquent\Model::class,
'parent' => Model::class,
'timestamps' => true,
'soft_deletes' => true,
'snake_attributes' => true,

View file

@ -1,5 +1,9 @@
<?php
use Spatie\Permission\DefaultTeamResolver;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
return [
'models' => [
@ -13,7 +17,7 @@ return [
* `Spatie\Permission\Contracts\Permission` contract.
*/
'permission' => Spatie\Permission\Models\Permission::class,
'permission' => Permission::class,
/*
* When using the "HasRoles" trait from this package, we need to know which
@ -24,7 +28,7 @@ return [
* `Spatie\Permission\Contracts\Role` contract.
*/
'role' => Spatie\Permission\Models\Role::class,
'role' => Role::class,
],
@ -136,7 +140,7 @@ return [
/*
* The class to use to resolve the permissions team id
*/
'team_resolver' => \Spatie\Permission\DefaultTeamResolver::class,
'team_resolver' => DefaultTeamResolver::class,
/*
* Passport Client Credentials Grant
@ -183,7 +187,7 @@ return [
* When permissions or roles are updated the cache is flushed automatically.
*/
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
'expiration_time' => DateInterval::createFromDateString('24 hours'),
/*
* The cache key used to store all permissions.

View file

@ -1,5 +1,8 @@
<?php
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
use Laravel\Sanctum\Http\Middleware\AuthenticateSession;
use Laravel\Sanctum\Sanctum;
return [
@ -76,9 +79,9 @@ return [
*/
'middleware' => [
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
'authenticate_session' => AuthenticateSession::class,
'encrypt_cookies' => EncryptCookies::class,
'validate_csrf_token' => ValidateCsrfToken::class,
],
];

View file

@ -187,7 +187,7 @@ aber:
---
## Gesamt-Status (Stand 2026-05-20)
## Gesamt-Status (Stand 2026-05-29)
| Phase | Inhalt | Status |
|---|---|---|
@ -198,6 +198,7 @@ aber:
| 4 | Listen/Detail durchgehen (4A4J) | ✅ **komplett** |
| 5 | Dark Mode konsistent | ✅ **abgeschlossen** |
| 6 | Auth-Cleanup | ✅ **abgeschlossen** |
| 7 | Press-Release-Form-Refactor (7A7F) | ✅ **abgeschlossen** (`19-PHASE-7-…`) |
### Phase 4 — Sub-Päckchen im Detail
@ -219,13 +220,21 @@ aber:
> Die hub-flux-Roadmap ist mit Phase 6 **vollständig** abgeschlossen.
> Alle weiteren Themen sind eigene Initiativen.
**🟡 In Planung — Phase 7 (Press-Release-Form-Refactor):**
Mockup `User Neue Mitteilung presseportale.html` wird auf den
**✅ Abgeschlossen — Phase 7 (Press-Release-Form-Refactor):**
Mockup `User Neue Mitteilung presseportale.html` auf den
Customer-Create/Edit-Flow übertragen. Plan-Doc:
`19-PHASE-7-PRESS-RELEASE-FORM.md`.
Päckchen 7A (Migrations) → 7B (flux:editor + Sanitizer) →
7C (Customer-Create-UI) → 7D (Customer-Edit-UI) →
7E (Anhänge-Manager) → 7F (Scheduling/Embargo, optional).
`19-PHASE-7-PRESS-RELEASE-FORM.md`. Alle Päckchen umgesetzt:
7A (Migrations: subtitle/boilerplate_override/scheduled_at/
embargo_at + attachments-Tabelle + companies.boilerplate) →
7B (`flux:editor` + `PressReleaseHtmlSanitizer` via mews/purifier) →
7C (Customer-Create-UI, 2-Spalter mit sticky Settings-Sidebar) →
7D (Customer-Edit-UI) → 7E (Anhänge-Manager) →
7F (Scheduling/Embargo + `press-releases:publish-scheduled`
Command + 5-Min-Scheduler). Admin-Create/Edit ziehen optisch mit;
Scheduling-UI bleibt Customer-seitig. Detail siehe `PROGRESS.md`
(Eintrag 2026-05-22).
### Offene Folge-Initiativen
1. **Manueller Dark-Mode Smoke-Test**: Im Browser User-Menü →
Erscheinung → „Dunkel" und durch die Hauptseiten klicken
@ -233,10 +242,10 @@ Päckchen 7A (Migrations) → 7B (flux:editor + Sanitizer) →
Lesbar + konsistent. Kleine Polish-Runde, falls visuelle
Auffälligkeiten.
2. **PM-Form-Wizard-Refactor**: Mockup
`User Neue Mitteilung presseportale.html` auf den bestehenden
Press-Release-Create/Edit-Flow übertragen. Größere Aktion mit
eigener Phase.
2. **Admin-Create/Edit nachziehen**: Die Admin-PM-Forms haben das
Phase-7-Layout optisch übernommen, aber Scheduling/Embargo-UI
ist bewusst Customer-seitig geblieben. Bei Bedarf als kleines
Folge-Päckchen für Admins ergänzen.
3. **Web-Frontend-Block** (eigenständig, NICHT Teil von Phase 16):
Die noch ungenutzten Mockups

View file

@ -5,6 +5,118 @@
---
## 2026-05-29 · Wartung · Test-Regression-Fix + Phase-7-Doku nachgezogen
Review der Gesamt-Umsetzung. Zwei Befunde behoben:
### Fix — `ProfileUpdateTest > profile page is displayed`
Seit dem Customer-Portal-Umbau ist `/settings/profile` ein
**Redirect** auf `/admin/me/profile` (route `me.profile`), die
Profil-Pflege liegt jetzt in der Volt-Komponente
`customer.profile`. Der Starter-Kit-Test machte aber weiterhin
`GET /settings/profile``assertOk()` und lief deshalb auf 302
statt 200 (undokumentierte Regression aus dem Commit
„Optimierung der User und Admin Panels", 2026-05-22).
Test umgestellt auf `assertRedirect('/admin/me/profile')`. Das
Rendern der Zielseite ist bereits durch
`CustomerProfileSecurityTest` (Volt-Komponententest) abgedeckt,
also keine Doppelung. Die übrigen 4 Tests der Datei nutzen
weiterhin `Volt::test('settings.profile')` / `delete-user-form`
(Komponenten existieren und sind funktional).
### Doku-Sync
Phase 7 war im Code vollständig umgesetzt (siehe Eintrag unten),
aber im Log nicht erfasst und in den Status-Tabellen
widersprüchlich (`19-PHASE-7` „✅", `03-WEITERE-PHASEN` „🟡 in
Planung", `README` noch auf Stand Phase 2). Nachgezogen:
- Phase-7-Eintrag in diesem Log ergänzt.
- `README.md` Status-Tabelle auf Phase 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

View file

@ -11,11 +11,16 @@
|-------|--------------|--------|
| 0 | Design-Tokens vereinheitlichen | **✅ abgeschlossen** (2026-05-19) |
| 1 | Portal-Shell (Sidebar, Layout, Brand-Mark) | **✅ abgeschlossen** (2026-05-19) |
| 2 | Customer-Dashboard auf Mockup-Stil (inkl. Topbar) | 🟡 wartet auf Freigabe |
| 3 | Admin-Dashboard konsistent | ⚪ später |
| 4 | Listen-/Detail-Pages | ⚪ iterativ |
| 5 | Dark Mode konsistent | ⚪ später |
| 6 | Auth-Konsolidierung (optional) | ⚪ optional |
| 2 | Customer-Dashboard auf Mockup-Stil | **✅ abgeschlossen** (in P1, verfeinert in 4J) |
| 3 | Admin-Dashboard konsistent | **✅ abgeschlossen** (in P1, verfeinert in 4J) |
| 4 | Listen-/Detail-Pages (4A4J) | **✅ abgeschlossen** (2026-05-20) |
| 5 | Dark Mode konsistent | **✅ abgeschlossen** (2026-05-20) |
| 6 | Auth-Cleanup | **✅ abgeschlossen** (2026-05-20) |
| 7 | Press-Release-Form-Refactor (7A7F) | **✅ abgeschlossen** (2026-05-22) |
Die hub-flux-Roadmap (Phase 06) ist vollständig abgeschlossen; Phase 7
(PM-Form-Refactor) als Folge-Initiative ebenfalls. Detail-Plan für Phase 7:
[`19-PHASE-7-PRESS-RELEASE-FORM.md`](./19-PHASE-7-PRESS-RELEASE-FORM.md).
→ Tagesaktueller Fortschritt: [`PROGRESS.md`](./PROGRESS.md)

View file

@ -0,0 +1,170 @@
> **Stand der Doku**: 21.05.2026 — diese Liste beschreibt den Zielzustand
> der oeffentlichen Strecke. Welcher Punkt bereits umgesetzt ist, ist
> jeweils mit einer kurzen IST-Notiz markiert.
Das sind die Seiten, die eigene URLs brauchen, weil sie verlinkbar sein müssen, SEO-Wert haben oder direkt von extern angesteuert werden.
#### Inhalts-Seiten (Lese-Erfahrung)
**1. Pressemitteilungs-Detailseite** `/p/[slug]` oder `/pressemitteilung/[id]` Die wichtigste Seite überhaupt. Jede einzelne PM bekommt eine eigene Seite. Hier landen 90% des Traffics aus Google, Newsletter und Social Shares.
_IST 21.05.2026_: umgesetzt als `resources/views/web/release-detail.blade.php` (Route `release.detail`, URL `/release/{slug}`). Das URL-Schema weicht vom Plan ab, ist aber konsistent über alle Themen.
**2. Branchen-Übersichten** `/branche/[slug]` Zum Beispiel `/branche/energie-klima`, `/branche/finanzen`. Aggregierte Sicht auf alle PMs einer Branche, mit Sub-Filtern. Das sind deine SEO-Goldgruben (jede Branche eine ranking-fähige Landing Page).
_IST 21.05.2026_: umgesetzt (`web/kategorie.blade.php`, `web/kategorien.blade.php`).
**3. Regionen-Übersichten** `/region/[slug]` `/region/deutschland`, `/region/bayern`, `/region/oesterreich`. Analog zu Branchen, regional gefiltert.
_IST 21.05.2026_: noch nicht umgesetzt.
**4. Newsroom-Seite eines Unternehmens** `/newsroom/[slug]` Markenseite eines Premium-Publishers mit eigener URL, Logo, allen PMs des Unternehmens. Ist gleichzeitig Verkaufsargument für Pro-/Agency-Tarif und SEO-Vorteil für die Unternehmen.
_IST 21.05.2026_: Layout vorhanden (`web/newsrooms.blade.php`), Daten-Anbindung pro Firma noch offen.
**5. Such-Ergebnisseite** `/suche?q=...` Volltextsuche mit Filtern (Erweiterte Suche schreibt in URL-Parameter, dadurch teilbar/bookmarkbar).
_IST 21.05.2026_: Layout vorhanden (`web/suche.blade.php`), Volltextsuche noch nicht aktiv.
**6. Tag-/Themen-Seite** `/thema/[slug]` _(optional, später)_ Nicht im ersten Release zwingend, aber sehr SEO-wirksam für aktuelle Themen ("Künstliche Intelligenz", "Lieferkettengesetz", "Energiekrise"). Würde ich datengetrieben aus den meistverwendeten Tags generieren lassen.
_IST 21.05.2026_: nicht umgesetzt (bewusst spaeter).
#### Service-/Vertriebs-Seiten
**7. Pressemitteilung einreichen / Veröffentlichen** `/veroeffentlichen` Die Conversion-Landingpage für neue Publisher. Erklärt Mehrwert, zeigt Tarife, Editor-Vorschau. Dahinter der eigentliche Editor (im User-Bereich).
_IST 21.05.2026_: Landing-Seite vorhanden (`web/veroeffentlichen.blade.php`). Editor-Strecke im User-Bereich ist umgesetzt (siehe Phase 7).
**8. Tarife & Preise** `/preise` _(oder als Modal aus mehreren Stellen aufrufbar)_ Da Tarife auch im Modal aus dem CTA aufgerufen werden, ist die Frage: brauchen wir die Seite? Antwort ja, weil SEO ("Pressemitteilung veröffentlichen Preise" ist eine wichtige Suche) und weil sie verlinkbar sein muss aus AGB, Footer, Mediadaten.
_IST 21.05.2026_: Layout vorhanden (`web/preise.blade.php`), echte Tarife noch nicht hinterlegt (Tarif-Modul siehe `Presseportal Konzept für Relaunch.md` Abschnitt 8).
**9. Mediadaten / Werbung** `/mediadaten` oder `/werben` Für Mediaplaner und potentielle Werbekunden: alle buchbaren Slot-Typen (Top-Slot, Highlights, Newsletter, Branchen-Sponsoring), Reichweiten-Daten, Preise, Booking-Kontakt. Pflicht-Seite für jede Plattform mit Anzeigeninventar.
_IST 21.05.2026_: nicht umgesetzt.
**10. Newsletter-Anmeldung als eigene Seite** `/newsletter` Auch wenn Newsletter im Footer und in einer Sektion auf der Startseite eingebettet ist, brauchst du eine eigene Seite für direkte Anmelde-Links (aus E-Mails, Social, Werbekampagnen).
_IST 21.05.2026_: Layout in den Themes vorhanden, eigene Anmelde-Seite noch nicht.
#### Vertrauens- / Editorial-Seiten
**11. Über uns** `/ueber-uns` Plattform-Geschichte, Team, redaktionelle Haltung. Kurz und persönlich, kein Marketing-Geschwurbel.
_IST 21.05.2026_: Layout vorhanden (`web/ueber-uns.blade.php`, `web/team.blade.php`).
**12. Redaktion / Redaktionsrichtlinien** `/redaktion` Wichtige Vertrauensseite: Wer prüft die Inhalte? Wie funktioniert der Content-Score? Was ist der Unterschied zu redaktionell geprüften Anzeigen? Diese Seite differenziert dich von Spam-Portalen.
_IST 21.05.2026_: noch nicht umgesetzt.
**13. Kontakt** `/kontakt` Klassisch, mit Funktions-E-Mails (presse@, redaktion@, werbung@, support@) und Kontaktformular.
_IST 21.05.2026_: Layout vorhanden (`web/kontakt.blade.php`).
#### Rechtliches
**14. Impressum** `/impressum` **15. Datenschutz** `/datenschutz` **16. AGB** `/agb` **17. Cookie-Einstellungen** `/cookies` _(oder Modal)_
Diese vier sind Pflicht und nicht zusammenfassbar.
_IST 21.05.2026_: alle vier als Layout vorhanden (`web/impressum.blade.php`, `web/datenschutz.blade.php`, `web/agb.blade.php`, `web/cookies.blade.php`). Inhalte sind teilweise Platzhalter — vor Go-Live durch Anwalt zu pruefen.
#### Technik / Distribution
**18. RSS-Feeds-Übersicht** `/feeds` Liste aller verfügbaren RSS-Feeds (alle, pro Branche, pro Region). Eine Seite, listet alle Feed-URLs auf.
_IST 21.05.2026_: nicht umgesetzt.
**19. API-Dokumentation** `/api` Für Distribution-Partner und Pro-/Agency-Kunden mit API-Zugang.
_IST 21.05.2026_: Seite vorhanden (`web/api.blade.php`). Pre-existing `ApiDocumentationTest` ist rot, weil `docs/api/v1.yml` noch fehlt — eigener Track.
#### DSA-/Rechts-Pflichten
**20. PM melden** `/melden/[id]` Öffentlicher Notice-and-Action-Endpoint, eigener Pfad pro PM (kann auch als Modal von der PM-Detailseite kommen, aber direkter Link für rechtssichere Beschwerden besser).
_IST 21.05.2026_: nicht umgesetzt (Phase 2/3, DSA-Pflicht).
**21. Pressemitteilung verwalten (Magic-Link)** `/verwalten` Einstiegspunkt für den Pressekontakt-Flow (E-Mail eingeben → Magic Link). Dahinter dann der eingeloggte Verwaltungs-Bereich.
_IST 21.05.2026_: nicht umgesetzt (Phase 2, siehe `Presseportal Konzept für Relaunch.md` Abschnitt 6).
---
### Was als Modal/Overlay läuft (keine eigene Seite)
Das sind die Sachen, die man oft in einer separaten Seite versteckt sieht, aber besser inline gelöst werden kein Kontext-Verlust für den User.
- **Erweiterte Suche** → Modal mit Filtern (URL-Parameter werden trotzdem gesetzt für Teilbarkeit)
- **Tarife-Übersicht aus CTAs** → Modal (neben der eigenen `/preise`-Seite)
- **Whitepaper-Download mit Lead-Capture** → Modal mit Name/E-Mail-Feldern
- **Newsletter-Anmeldung aus Sektion** → inline ohne Seitenwechsel
- **PM melden aus Detailseite** → Modal (mit Fallback auf eigene URL)
- **Cookie-Einstellungen** → Modal (mit Fallback auf eigene URL für Rechtssicherheit)
- **Login** → Modal (Anmelden-Button öffnet Modal, kein Seitenwechsel; eigene Seite nur als Fallback `/login`)
- **Bild-Lightbox** auf PM-Detailseite → Overlay
- **Teilen-Funktionen** auf PM-Detailseite → Modal mit Plattform-Auswahl und vorgenerierten Texten
- **Tarif-Wechsel im User-Bereich** → Modal
- **Credit-Aufladung** → Mini-Checkout-Modal (war im Konzept schon so geplant)
---
### Was im eingeloggten User-Bereich liegt
Hier ist wichtig: **alles unter einer einzigen Dashboard-URL**, nicht 15 Untermenüs. Ein Bereich, mehrere Tabs/Sektionen.
**Publisher-Dashboard** `/dashboard`
> **IST-Stand 21.05.2026**: Im Code heisst der Customer-Bereich `/admin/me`
> (Routen-Namen `me.*`); das Admin-Backend liegt unter `/dashboard` und ist
> Editoren/Admins vorbehalten. Die Bereiche im User-Backend sind als
> eigene Pages mit `wire:navigate` (kein vollst. Seitenwechsel)
> umgesetzt und ueber die Sidebar navigierbar. Eine echte
> Tab-Komponente innerhalb einer einzigen URL gibt es nicht — der
> Mehrwert ist gleich.
Mit folgenden Bereichen als Tabs oder Sidebar-Navigation (kein Seitenwechsel zwischen den Tabs, oder URL-Tabs wie `/dashboard/meldungen`):
- **Übersicht** Stats, Credit-Stand, letzte Aktivitäten _(umgesetzt als `customer/dashboard`)_
- **Meine Pressemitteilungen** Liste mit Status, Bearbeiten, Korrektur, Update _(umgesetzt als `customer/press-releases/{index,show,create,edit}`)_
- **Editor** Neue PM erstellen / bestehende bearbeiten (eigene Unter-URL `/editor` oder `/editor/[id]`) _(umgesetzt als Teil von `press-releases.{create,edit}`)_
- **Newsroom** Markenseite konfigurieren (für Pro/Agency) _(nicht umgesetzt — Phase 2)_
- **Statistiken** Detail-Auswertungen pro PM _(nicht umgesetzt — Phase 2)_
- **Credits & Rechnungen** Stand, Verlauf, Pakete kaufen, Rechnungen herunterladen _(nur Rechnungen umgesetzt; Credits sind Phase 2)_
- **Tarif & Account** Tarif-Verwaltung, Rechnungsdaten, Team-Mitglieder (für Agency) _(Profil + Rechnungsadresse umgesetzt; Tarif/Team Phase 2)_
- **Boost & Platzierungen** Slot-Buchungen, Verlauf, neue buchen _(als Stub vorhanden `customer/bookings`)_
**Pressekontakt-Bereich** (Magic-Link) `/verwalten/[token]`
Vereinfachte Version des Dashboards für nicht-registrierte Pressekontakte:
- Liste der PMs mit dieser E-Mail
- Änderungs-Wizard (Pfade AG)
- Optional: Account-Anlage für späteren direkten Zugriff
**Admin-Bereich** `/admin` _(intern, nicht öffentlich)_
Eigene Anwendung im Grunde, aber URL-mäßig unter Hauptdomain:
- Review-Queue (Gelb-PMs, Beschwerden, Persönlichkeitsrecht-Pfad F)
- User-Verwaltung
- Inventar-Management (welche Slots sind gebucht)
- Editorial-Picks setzen
- Reports / Statistiken
---
### Strukturelle Faustregeln, die ich anwenden würde
**1. Maximal zwei Klicks ab Startseite zu jeder Funktion.** Aus Startseite → Branchenseite → PM-Detail. Aus Startseite → Veröffentlichen → Tarif-Auswahl. Wenn etwas drei Klicks braucht, ist es falsch verortet.
**2. Footer ist die Sitemap.** Alle Service- und Rechts-Seiten leben _nur_ im Footer. Keine Mega-Menüs im Header. Die Hauptnavigation oben ist ausschließlich Branchen-Navigation plus Veröffentlichen-CTA.
**3. URL-Schemata konsistent.** Singular für Detailseiten (`/branche/...`, `/newsroom/...`), Verben für Aktionen (`/veroeffentlichen`, `/melden`, `/verwalten`). Keine kryptischen IDs in URLs, wenn vermeidbar Slugs für SEO.
**4. Modals statt Seiten, wenn möglich.** Aber: jeder Modal hat einen Fallback-URL-Endpoint, falls jemand direkt verlinkt oder einen Bookmark setzt. Beispiel: Tarife-Modal → `/preise` als eigene Seite existiert weiterhin.
**5. Dashboard ist EIN Bereich.** Nicht "Meine PMs" als eigene Seite, "Stats" als andere, "Credits" als dritte alles unter `/dashboard` mit Tabs. Reduziert kognitive Last und Navigation.
---
### Zusammenfassung als Liste zum Abhaken
**Öffentliche Inhalts-Seiten (6):** Detailseite, Branche, Region, Newsroom, Suche, Thema
**Service-/Vertriebs-Seiten (4):** Veröffentlichen, Preise, Mediadaten, Newsletter
**Vertrauen/Editorial (3):** Über uns, Redaktion, Kontakt
**Rechtliches (4):** Impressum, Datenschutz, AGB, Cookies
**Technik/Distribution (2):** Feeds, API-Doku
**DSA-Pflicht (2):** Melden, Verwalten (Magic-Link-Einstieg)
**Eingeloggte Bereiche (3):** Dashboard, Pressekontakt-Bereich, Admin
**Macht insgesamt 24 echte Seiten/Bereiche** das ist für eine Plattform dieser Tiefe sehr schlank. Vergleichswert: presseportal.de hat über 80 Seiten in der Sitemap.

View file

@ -0,0 +1,51 @@
# KI- und Entwickler-Workflow (Pressekonto)
Kurzanleitung für Arbeit im Dev-Container: Dokumentation (Obsidian), Issues (Forgejo/`tea`), Git.
## 1. Dokumentation und Kontext (Obsidian)
- **Speicherort:** Markdown-Dateien liegen im Projekt unter **`docs/`** (im Container: `/var/www/html/docs/`). Dieser Ordner ist per Dev-Container mit dem Obsidian-Vault verbunden; Änderungen synchronisieren mit dem lokalen Vault.
- **Bei Fragen nach „Kontext“, „Briefing“ oder „Plan“:** Zuerst **`docs/`** nach passenden `.md`-Dateien durchsuchen (Dateinamen und Überschriften).
- **Neue Inhalte:** Konzepte, Pläne, Briefings und Spezifikationen **immer als `.md` in `docs/`** ablegen. Sinnvolle, stabile Dateinamen wählen (z.B. `feature-stripe-briefing.md`).
## 2. Issue-Management (Forgejo mit `tea`)
**Nur das `tea`-CLI im Terminal verwenden** — keine direkten HTTP/cURL-Aufrufe auf die Forgejo-API für Issue-Operationen.
| Aktion | Befehl |
|--------|--------|
| Issues auflisten | `tea issue list` |
| Issue anzeigen | `tea issue view <id>` |
| Neues Issue (Beschreibung aus Datei) | `tea issue create --title "Kurztitel" --description "$(cat docs/beispiel.md)"` |
| Issue aktualisieren | `tea issue edit <id> --description "$(cat docs/update.md)"` |
**Hinweise:**
- Nach `postCreateCommand` liegt `tea` typischerweise unter `~/.local/bin`; Shell neu starten oder `export PATH="$HOME/.local/bin:$PATH"` setzen, falls `tea` nicht gefunden wird.
- Remote/Ziel prüfen bei Bedarf mit `tea repos` bzw. der in `postCreateCommand` konfigurierten Login-Instanz **`gitmedia`** (`https://git.adametz.media`).
- **`tea` ist im Setup bereits authentifiziert** — keine Zugangsdaten oder Tokens im Chat abfragen oder in Repos committen. Token-Datei nur read-only gemountet (`/tmp/.forgejo_token`).
**Weitere Unterbefehle:** `tea issue --help`, `tea issue create --help`.
## 3. Git und Commits (Issue-Schließen)
1. **Conventional Commits:** Präfixe wie `feat:`, `fix:`, `refactor:`, `docs:`, `chore:` verwenden.
2. **Issue-Verknüpfung zum automatischen Schließen:** Im Commit-Text die Issue-Nummer mit **`closes`** (alternativ in vielen Gitea/Forgejo-Setups auch `fix(es)`, `resolve(s)`) angeben.
**Beispiel:**
```text
feat: Stripe-Zahlung implementiert, closes #12
```
3. **Branching:** Projekt-Branches und MR/PR-Regeln wie im Team vereinbart beibehalten.
## 4. Arbeitsweise (KI / Agent)
- Kontext zuerst aus **`docs/`**, dann Code und bestehende Projektregeln (z.B. `AGENTS.md`, `.cursorrules`).
- Terminal-Befehle direkt ausführen, wo sinnvoll; Antworten **prägnant**, Entscheidungen und Änderungen **nachvollziehbar** dokumentieren (bei Bedarf kurz in `docs/` oder im Issue).
- Keine manuelle Nachfrage nach Forgejo-Zugang **sofern `tea login` für `gitmedia` gesetzt ist**; bei `tea`-Fehlern Umgebung/Remote prüfen, nicht nach Passwörtern fragen.
---
*Letzte inhaltliche Ausrichtung: Dev-Container `workspaceFolder` `/var/www/html`, Vault-Bind nach `docs/`.*

View file

@ -0,0 +1,605 @@
# Phase 8 · User-Panel-Konsolidierung & Pressemitteilungs-Lifecycle
Stand: 2026-05-21
Vorgänger: Phase 7 (Press-Release-Form-Refactor — abgeschlossen)
Abgleich-Doku: [`docs/STATUS-ABGLEICH-USER-PANEL.md`](./STATUS-ABGLEICH-USER-PANEL.md)
---
## 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
- [ ] 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 (außer 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.

64
docs/README.md Normal file
View file

@ -0,0 +1,64 @@
# `docs/` — Konzept- und Status-Dokumente
Stand: 21.05.2026 — nach Phase 7 (PM-Form-Refactor) und vor Phase 8 (User-Panel-Konsolidierung).
Diese README ist der schnellste Einstieg in den `docs/`-Ordner.
Sie verlinkt die zentralen Dokumente und sortiert sie nach „Was ist der aktuelle Stand?" vs. „Was ist konzeptueller Zielzustand?".
## Schneller Einstieg
| Frage | Doku |
|---|---|
| Was ist im Code, was ist Konzept, was fehlt? | [`STATUS-ABGLEICH-USER-PANEL.md`](./STATUS-ABGLEICH-USER-PANEL.md) |
| Wie geht es als Naechstes weiter? | [`PHASE-8-USER-PANEL-PLAN.md`](./PHASE-8-USER-PANEL-PLAN.md) |
| Was ist Phase 1 + Phase 7 (User Backend, Pressemitteilungs-Form)? | [`user-admin/checkliste-user-backend.md`](./user-admin/checkliste-user-backend.md) |
| Welche Hub-Flux-Phasen sind durch? | [`../dev/frontend/hub-flux/PROGRESS.md`](../dev/frontend/hub-flux/PROGRESS.md) |
| Was ist die Phase-7-Detail-Doku? | [`../dev/frontend/hub-flux/19-PHASE-7-PRESS-RELEASE-FORM.md`](../dev/frontend/hub-flux/19-PHASE-7-PRESS-RELEASE-FORM.md) |
## Aufbau
### `user-admin/`
Konzept und Status-Dokumentation fuer das User- und Admin-Backend.
Jede Seite hat oben einen Verweis auf den aktuellen Code-Stand und referenziert den Abgleich.
- [`Admin-User.md`](./user-admin/Admin-User.md) — Hauptdokument zum User-/Admin-Backend, Phase 1 + Phase 7 zusammengefasst, Phase 8 verlinkt.
- [`checkliste-user-backend.md`](./user-admin/checkliste-user-backend.md) — Erledigt/Offen-Liste pro Phase.
- [`user-zusammenhaenge.md`](./user-admin/user-zusammenhaenge.md) — Datenmodell-Mapping, Models, Services und Commands.
- [`Presseportal Konzept für Relaunch.md`](./user-admin/Presseportal%20%E2%80%93%20Konzept%20f%C3%BCr%20Relaunch.md) — Zielzustand der Plattform (KI-Workflow, Bilder, Notice-and-Action, DSGVO, Magic-Link, Tarife, Korrektur-Modell). Jeder Abschnitt hat eine **IST-Stand-Box**.
### `konzept/`
Strategische Konzepte und Updates. Sie beschreiben Themen, die teilweise oder noch gar nicht gebaut sind. Jedes Update hat oben einen IST-Stand-Hinweis.
- [`Entwicklungs-Konzept - Frontend-Komponenten Multi-Brand.md`](./konzept/Entwicklungs-Konzept%20-%20Frontend-Komponenten%20Multi-Brand.md) — Multi-Brand-Architektur (umgesetzt).
- [`Konzept-Update 1 Überarbeitete Abschnitte.md`](./konzept/Konzept-Update%201%20%E2%80%93%20%C3%9Cberarbeitete%20Abschnitte.md) — Tarife, Credits, Score (noch nicht umgesetzt).
- [`Konzept-Update 2 Score-Stufen-System.md`](./konzept/Konzept-Update%202%20%E2%80%93%20Score-Stufen-System.md) — Drei-Stufen-Score (noch nicht umgesetzt).
### Top-Level
- [`STATUS-ABGLEICH-USER-PANEL.md`](./STATUS-ABGLEICH-USER-PANEL.md) — Konzept-vs-Code-Vergleich pro Page.
- [`PHASE-8-USER-PANEL-PLAN.md`](./PHASE-8-USER-PANEL-PLAN.md) — Detail-Plan der naechsten Sub-Paeckchen.
- [`Echte öffentliche Unterseiten.md`](./Echte%20%C3%B6ffentliche%20Unterseiten.md) — Sitemap-Konzept, jede Seite mit IST-Notiz.
- [`KI-UND-ENTWICKLER-WORKFLOW.md`](./KI-UND-ENTWICKLER-WORKFLOW.md) — Workflow fuer KI-/Entwickler-Sessions.
## Lesehilfe
In den ueberarbeiteten Dokumenten finden sich folgende Markierungen:
| Marker | Bedeutung |
|---|---|
| **IST-Stand JJJJ-MM-TT** | Kompakte Notiz oben am Abschnitt, was im Code tatsaechlich umgesetzt ist. |
| Phase 1 / Phase 7 / Phase 8 | Verweis auf die aktuelle Roadmap. Phase 1 = Grund-User-Backend; Phase 7 = PM-Form-Refactor; Phase 8 = User-Panel-Konsolidierung. |
| Hub-Flux | Visuelle Migrationsphase des User Backends, gepflegt in `dev/frontend/hub-flux/`. |
| Phase 2 / Phase 3 | Spaeter — Magic-Link-Flow, KI-Vorpruefung, Tarif-Modul, Score, Notice-and-Action. |
## Wie pflegen wir die Doku?
- Wenn sich der Code so weit aendert, dass ein Konzept-Abschnitt nicht mehr stimmt, kommt eine **IST-Stand-Box** an den Abschnitt, statt den Konzept-Text zu loeschen. So bleibt die urspruengliche Zielvorstellung lesbar.
- Jeder Phasen-Abschluss aktualisiert
- `user-admin/checkliste-user-backend.md` (Erledigt-Block),
- `STATUS-ABGLEICH-USER-PANEL.md` (Abgleich),
- `dev/frontend/hub-flux/PROGRESS.md` (Tagebuch),
- und ggf. die Detail-Doku in `dev/frontend/hub-flux/`.
- Neue grosse Themen bekommen ein eigenes Plan-Dokument auf `docs/`-Top-Level (z. B. `PHASE-8-USER-PANEL-PLAN.md`).

View file

@ -0,0 +1,311 @@
# Status-Abgleich · User Panel
Stand: 2026-05-21
> Dieses Dokument vergleicht die Konzept-Dokumente im Ordner `docs/` mit dem
> tatsächlichen Code-Stand. Es dient als Single Source of Truth für die
> Entscheidung, welche Konzept-Abschnitte aktualisiert werden müssen und wo
> weiterhin Lücken bestehen.
>
> **Methode**: Code-Inspektion aller Customer-Komponenten in
> `resources/views/livewire/customer/`, Models, Migrationen und Services
> gegen die Inhalte der Doku in `docs/user-admin/` und `docs/konzept/`.
>
> **Lese-Hilfe**:
>
> - ✅ **Doku stimmt mit Code überein** — kein Handlungsbedarf
> - 🔄 **Doku ist überholt** — Code ist schon weiter, Doku muss nachgezogen werden
> - 📝 **Code ist hinter Doku** — Konzept beschreibt etwas, das noch nicht
> gebaut ist
> - ⚠️ **Inkonsistent** — Doku und Code widersprechen sich
> - ❓ **Nicht im Konzept** — im Code da, aber nirgends dokumentiert
---
## 1. Globale Architektur des User Backends
| Konzept-Aussage | Quelle | IST im Code | Status |
|---|---|---|---|
| User/Admin getrennt, technisch gemeinsames Backend, Trennung über Rollen/Policies | `Admin-User.md` | Umgesetzt: `PressReleasePolicy`, Spatie-Rollen, Customer-/Admin-Routen | ✅ |
| Navigation in „Mein Bereich · Finanzen · Konto" | `Admin-User.md`, `checkliste-user-backend.md` | `components/layouts/app/sidebar.blade.php` setzt die drei Gruppen | ✅ |
| Topbar oben rechts mit Firmen-Kontext-Switcher | `Admin-User.md` | `customer/company-switcher.blade.php` + Layout-Integration | ✅ |
| „Pressemappen" terminologisch auf „Firmen" umbenannt | `checkliste-user-backend.md` | Im UI durchgehend „Firmen" / „Meine Firmen"; Routen heißen aus Legacy-Gründen weiterhin `me.press-kits.*` | 🔄 (Doku-Nacharbeit: Hinweis ergänzen, dass die internen Route-Namen weiterhin `press-kits` heißen) |
| Phase 1 funktional abgeschlossen | `checkliste-user-backend.md` Z. 71 | Trifft zu — alle in „Erledigt" markierten Punkte sind im Code verifizierbar | ✅ |
---
## 2. Customer-Pages — IST-Stand pro Page
### Dashboard (`customer/dashboard.blade.php`)
| Konzept-Aussage | IST | Status |
|---|---|---|
| Datenqualitäts-Hinweise (Profil, Rechnungsadresse, Pressekontakte, PMs ohne Firma) | umgesetzt mit `<x-portal.hint-card>` | ✅ |
| KPI-Reihe Pressemitteilungen | umgesetzt mit `<x-portal.stat-card>`, Trend-Slot mit `pub/prüf/entwurf` | ✅ |
| Filter-Reaktion auf Firmen-Kontext | `recent` und `companies` queries respektieren `selectedCompany` | ✅ |
### Pressemitteilungen-Liste (`customer/press-releases/index.blade.php`)
| Konzept-Aussage | IST | Status |
|---|---|---|
| Status-Tabs (Alle/Veröffentlicht/Entwürfe/Prüfung/Abgelehnt/Archiv) | umgesetzt als `view-tabs` mit Counter-Pillen | ✅ |
| Filter ohne Firma, Status, Portal | `statusFilter`, `portalFilter`, `companyFilter` aktiv | ✅ |
| Filter-Presets (`user_filter_presets`) | **fehlt** | 📝 (in Phase 2 lt. Doku — bleibt pending) |
| PM-Detail Tab „Verlauf" aus `press_release_status_logs` | als „Status & Verlauf"-Card eingebaut, nicht als eigener Tab | 🔄 (Doku-Anpassung: Card statt Tab; funktional gleichwertig) |
| Hinweis Scheduling/Embargo in der Liste | **fehlt** | 📝 (s. eigener Gap-Block unten) |
### Pressemitteilungs-Forms
| Konzept-Aussage | IST | Status |
|---|---|---|
| Einfacher Editor mit Absätzen, fett + kursiv | Flux-Editor mit `heading | bold italic | bullet ordered blockquote | link` — mehr als minimal | 🔄 (Konzept-Update: Aktuelle Toolbar ist bewusst etwas größer als „nur fett + kursiv") |
| Pflichtfeld `company_id` für Customer | Validation `required` | ✅ |
| Portal aus Firma abgeleitet (Customer) | `updatedCompanyId()` setzt `portal` aus `company->portal` | ✅ |
| `subtitle`-Feld | seit Phase 7 da | ❓ (im Konzept nicht erwähnt, aber sinnvoll) |
| `scheduled_at`, `embargo_at`-Felder im Form | seit Phase 7F da | ❓ (im Konzept nicht beschrieben) |
| HTML-Sanitizer auf Save | `PressReleaseHtmlSanitizer` (mews/purifier) | ❓ (Konzept-Punkt 2/Bilder nennt KI-Check, aber keinen HTML-Sanitizer — sollte dokumentiert werden) |
| Boilerplate-Override pro PM | seit Phase 7 als optionaler Override-Text | ❓ (im Konzept nicht erwähnt) |
| Pressekontakt-Zuordnung Single-Select (1 pro PM, n:m beibehalten) | seit Phase 7, jetzt optional/Warnung | 🔄 (Konzept-Punkt: Pressekontakt war ursprünglich „mehrere möglich", jetzt 1 pro PM optional) |
| Attachment-Manager | **temporär deaktiviert wegen Security-Review** | ⚠️ (Konzept beschreibt Anhänge, Code hat es auskommentiert) |
### Pressemitteilungs-Detail (Customer Show)
| Konzept-Aussage | IST | Status |
|---|---|---|
| Status & Verlauf inkl. Logs | umgesetzt | ✅ |
| Zugeordnete Pressekontakte | umgesetzt | ✅ |
| Rejection-Begründung sichtbar | umgesetzt | ✅ |
| Vorschau-Link für externe Reviewer | `generateShareLink` via Magic-Link-Token | ✅ |
| Anzeige Subtitle / `scheduled_at` / `embargo_at` / `boilerplate_override` | **fehlt** (nur Admin-Show hat scheduled/embargo) | 📝 (offener Punkt aus letzter Diskussion) |
### Firmen-Liste (`customer/press-kits/index.blade.php`)
| Konzept-Aussage / Mockup | IST | Status |
|---|---|---|
| Karten-Grid pro Firma mit Logo, Status, Portal, Rolle, KPIs | Karten vorhanden, aber **deutlich schlichter** als Mockup | 📝 (Hauptthema Phase 8) |
| Counter-Strip (X Firmen, X aktiv, X PMs total, X Kontakte) | **fehlt** | 📝 |
| Saved-View-Tabs (Alle / Aktiv / In Anlage / Inaktiv / Mit mir geteilt) | **fehlt** | 📝 |
| Filter-Chips (Status / Portal / Rolle / Branche) | **nur Volltext-Suche** | 📝 |
| Seg-Toggle Karten/Liste | **fehlt** | 📝 |
| Empty-States (keine Firma / Filter ohne Treffer) | nur generischer Empty-State | 📝 |
| Rollen-Legende (Admin / Redakteur / Beobachter) | **fehlt** | 📝 |
| „Firma anlegen"-CTA | derzeit zeigt nur „Firma anlegen anfragen" → Profil; Mockup hat direkten Anlage-Flow | 🔄 (Firma-Self-Service ist Phase-2-Thema laut Doku, im Mockup aber wie Phase-1 dargestellt) |
### Firmen-Detail (`customer/press-kits/show.blade.php`)
| Konzept-Aussage | IST | Status |
|---|---|---|
| Tabs Übersicht / Stammdaten / Pressekontakte / PMs / Statistik / Abrechnung | **eine lange Seite mit Quick-Nav-Anker, keine echten Tabs** | 🔄 (Doku-Update: Quick-Nav statt Tabs; visuell gleichwertig) |
| Stammdaten inkl. Logo bearbeitbar | umgesetzt | ✅ |
| Pressekontakte verwalten | umgesetzt | ✅ |
| Eigentümer-Anzeige konsolidiert aus `owner_user_id` + `company_user.role` | umgesetzt | ✅ |
| Statistik-Tab | nur Stub („In Vorbereitung") | 📝 (Phase 2) |
| Abrechnung-Tab | nur Stub | 📝 (Phase 2) |
| Magic-Link aktiv/inaktiv-Badge pro Kontakt | **fehlt** | 📝 |
| Anzahl PMs pro Kontakt aus `press_release_contact` | **fehlt** | 📝 |
| „+ Neuer Pressekontakt" mit Magic-Link-Berechtigung-Toggle | nur Basis-Form, kein Magic-Link-Toggle | 📝 (gehört zu Magic-Link-Flow → Phase 2) |
### Settings (`settings/*`, `customer/profile`, `customer/security`)
| Konzept-Aussage | IST | Status |
|---|---|---|
| Profil + Rechnungsadresse + Sicherheit + Newsletter + API-Tokens | alles vorhanden | ✅ |
| Magic-Link-Verlauf in Sicherheit | **fehlt** | 📝 (Phase 2) |
| API-Nutzungs-Log | **fehlt** | 📝 (Phase 2) |
| Team-Tab (Agency-Tarif) | **fehlt** | 📝 (Phase 2) |
### Finanzen
| Konzept-Aussage | IST | Status |
|---|---|---|
| Rechnungen mit Legacy-Archiv | umgesetzt | ✅ |
| Buchungen & Add-ons | nur Stub | 📝 (Phase 2) |
| Credits & Tarif | nur „bald"-Eintrag in Sidebar | 📝 (Phase 2) |
| Zahlungsmethoden firmenscharf | **fehlt** | 📝 (Phase 2) |
---
## 3. Großthemen aus dem Konzept — Status
### 3.1 KI-Freigabe-Workflow
**Konzept-Stand**: `Presseportal Konzept für Relaunch.md` Abschnitt 1.
| Punkt | Code-Stand |
|---|---|
| KI-Vorprüfung mit JSON-Antwort | **nicht implementiert** |
| Drei-Stufen-Ergebnis grün/gelb/rot | nur „review"/„published"/„rejected" via Admin-Flow |
| Logging der KI-Antworten | **fehlt** |
| Trust-Score | **fehlt** |
| Blacklist-Wort-Check | **vorhanden** über `PressReleaseService::submitForReview` mit `BlacklistViolationException` |
**Bewertung**: 📝 — Konzept-Vision für Phase 2/3, im Code nur die rudimentäre Blacklist-Variante.
### 3.2 Bilder & Lizenzen
**Konzept-Stand**: `Presseportal Konzept für Relaunch.md` Abschnitt 2 + `Admin-User.md` Punkt 4.
| Punkt | Code-Stand |
|---|---|
| Upload-Workflow (Eigenes / Stock / KI) | Nur „Eigenes Bild" via `press-release-images-manager` |
| Pflichtfelder (Urheber, Lizenztyp, Lizenz-URL, Personen-Einwilligung, Rechte-Bestätigung) | Nur `title` + `copyright` (Freitext) — **deutlich unter Konzept** |
| KI-Wasserzeichen-Check | **fehlt** |
| Unsplash/Pexels-API | **fehlt** |
| KI-Bildgenerierung | **fehlt** |
| `is_preview`-Flag für Titelbild | im Modell vorhanden, im Manager toggelbar |
| Bild-Varianten (thumb/medium/large) | `ImageService::PRESS_RELEASE_IMAGE_VARIANTS` generiert sie automatisch |
| SVG-Platzhalter, falls keine Bilder | **inline in Landing-Page-Komponenten (z. B. `focus-hero`, `feed-top-item`), kein zentrales Set** |
**Bewertung**: 📝 — Großthema für Phase 8 (siehe Plan). Lizenzfelder + SVG-Platzhalter sind Pflicht, bevor Bild-Upload produktiv geht.
### 3.3 Notice-and-Action (Meldung durch Dritte)
**Konzept-Stand**: `Presseportal Konzept für Relaunch.md` Abschnitt 3.
| Punkt | Code-Stand |
|---|---|
| Öffentliches Melden-Formular | **fehlt** |
| Ticketsystem mit Kategorien (Urheberrecht, Persönlichkeitsrecht, …) | **fehlt** |
| KI-Triage | **fehlt** |
| Quarantäne-Flow | **fehlt** |
**Bewertung**: 📝 — Phase 2/3-Thema, im Konzept gut beschrieben.
### 3.4 Magic-Link-Flow für Pressekontakte
**Konzept-Stand**: `Presseportal Konzept für Relaunch.md` Abschnitt 6.
| Punkt | Code-Stand |
|---|---|
| `magic_links`-Tabelle | **vorhanden** |
| Magic-Link-Generator | `MagicLinkGenerator` existiert (wird für PM-Vorschau-Links genutzt) |
| Magic-Link für Pressekontakt-Zugang | **fehlt** als eigener Flow |
| Token-Tabelle `press_release_access_requests` o. ä. | **fehlt** |
| Änderungs-Wizard (Tippfehler/Daten/Korrektur/Update/DSGVO) | **fehlt** |
**Bewertung**: 📝 — Phase 2-Thema, vollständig im Konzept beschrieben.
### 3.5 Pricing / Tarife / Credits
**Konzept-Stand**: `Presseportal Konzept für Relaunch.md` Abschnitt 8 + 9, `Konzept-Update 1.md`.
| Punkt | Code-Stand |
|---|---|
| Tarif-Tabellen (Einzel/Starter/Business/Pro/Agency) | **nicht im Datenmodell** |
| PM-Kontingent pro Tarif | **fehlt** |
| Bonus-Credits monatlich | **fehlt** |
| Credit-Pakete | **fehlt** |
| Auto-Refill | **fehlt** |
| Stripe-Integration | **fehlt** |
| `user_payment_options`-Tabelle | **vorhanden** (Pivot zu Companies da, aber kein aktiver Flow) |
**Bewertung**: 📝 — Phase 2/3, größtes ungebautes Feature. Für Phase 8 ist relevant: bei PM-Einreichung wird **konzeptuell** Quota dekrementiert; die UI-Anzeige im Veröffentlichungs-Modal kann darauf vorbereitet werden, das echte Decrement-Verhalten kommt aber erst mit dem Tarif-Modul.
### 3.6 Korrektur-Modell & Tombstones
**Konzept-Stand**: `Presseportal Konzept für Relaunch.md` Abschnitt 4.
| Punkt | Code-Stand |
|---|---|
| Korrektur-Hinweis | **fehlt** |
| Update-Hinweis (am Ende anhängen) | **fehlt** |
| Anonymisierung (DSGVO) | **fehlt** |
| Tombstone statt Hard-Delete | `PressReleaseService::deleteFromAdmin()` setzt veröffentlichte PMs auf „archiviert" mit Ersatztext — **rudimentär da** |
| Textvorlagen admin-pflegbar | **fehlt** |
**Bewertung**: 🔄 — Tombstone-Variante existiert minimal; Konzept-Doku sollte den Ist-Stand notieren, der Rest ist Phase 2.
### 3.7 Score / Trust-Score (Konzept-Update 2)
**Konzept-Stand**: `Konzept-Update 2 Score-Stufen-System.md`.
| Punkt | Code-Stand |
|---|---|
| User-Score-Tabelle | **fehlt** |
| Firmen-Score | **fehlt** |
| Auto-Publishing in Abhängigkeit vom Score | **fehlt** |
**Bewertung**: 📝 — Phase 3, vollständig im Konzept.
---
## 4. Was im Code da ist, aber im Konzept nicht / nur am Rande steht
| Feature | Wo im Code | Doku-Nacharbeit |
|---|---|---|
| Phase-7-Schema-Erweiterungen (`press_releases.subtitle`, `scheduled_at`, `embargo_at`, `boilerplate_override`, `no_export`) | Migrationen `2026_05_20_*` | Im Konzept ergänzen, dass PMs Untertitel + Scheduling/Embargo unterstützen |
| `mews/purifier` für HTML-Sanitization | `PressReleaseHtmlSanitizer` | Im Konzept-Abschnitt zu Editor erwähnen |
| `press_release_attachments`-Tabelle + Model | Migration `2026_05_20_143424_*` | UI auskommentiert, Tabelle bleibt → Doku-Anker für spätere Reaktivierung |
| Background-Job für scheduled publishing | `app/Console/Commands/PublishScheduledPressReleases.php`, alle 5 Min via Scheduler | Im Konzept als „automatische Veröffentlichung zum geplanten Termin" hinzufügen |
| FluxUI Toast für UX-Feedback | `Flux::toast()` durchgehend in Customer-Forms | Konzept-übergreifend, kein Konzept-Update nötig |
| Smooth-Scroll zu Validation-Errors | `resources/js/portal-form-hooks.js` | UX-Detail, keine Konzept-Doku |
| Pre-Submit-Check-Liste in PM-Forms | computed `presubmitChecks` | Im Konzept als „Pre-Submit-Check senkt Support-Aufwand" ergänzen |
| Hub-Design-System (Tokens + Komponenten) | `dev/frontend/hub-flux/` (Phase 07) | Eigene Roadmap-Doku, nicht teil von `docs/` |
| Theme-Override pro Domain | `ThemeServiceProvider` + `config/domains.php` | In `Echte öffentliche Unterseiten.md` ergänzen |
| Public-Detail-Page (`web/release-detail.blade.php`) | umgesetzt | In `Echte öffentliche Unterseiten.md` als „existiert" markieren |
---
## 5. Offene Punkte aus dem letzten Code-Review
Diese Punkte habe ich beim Review der Phase-7-Forms gefunden, sie sind weder
in den Konzept-Dokumenten erfasst noch in einem Plan:
| Lücke | Betroffene Dateien | Empfehlung |
|---|---|---|
| Customer-Show zeigt weder `subtitle` noch `scheduled_at`/`embargo_at`/`boilerplate_override` | `customer/press-releases/show.blade.php` | Phase 8 |
| Admin-Show zeigt weder `subtitle` noch `boilerplate_override` | `admin/press-releases/show.blade.php` | Phase 8 |
| Liste-Indikator für Scheduling/Embargo | `customer/press-releases/index.blade.php`, `admin/press-releases/index.blade.php` | Phase 8 |
| Pressekontakt-Sidebar zeigt keine Warn-Box, wenn kein Kontakt gewählt | `customer/press-releases/create.blade.php`, `edit.blade.php` | Phase 8 |
| Anhang-Tests laufen ins Leere | `tests/Feature/PressReleaseAttachmentsManagerTest.php`, Teile von `PressReleasePhase7SchemaTest.php` | Phase 8 → `->skip(...)` mit Verweis auf Security-Review |
| Roadmap-Doku `19-PHASE-7-PRESS-RELEASE-FORM.md` ist nicht mehr aktuell | Letzte 3 große Änderungen fehlen | Phase 8-Doku-Block |
---
## 6. Empfehlungen zur Pflege der Doku
### 6.1 Sofort ohne Risiko machbar
1. In `Admin-User.md` ergänzen: „PMs unterstützen Untertitel, Scheduling und
Embargo seit Phase 7".
2. In `Presseportal Konzept für Relaunch.md` Abschnitt 1: aktuellen
Blacklist-Stand notieren („KI-Vorprüfung folgt; aktuell wird per
Blacklist gegen offensichtliche Verstöße geprüft").
3. In `Presseportal Konzept für Relaunch.md` Abschnitt 2: hinzufügen,
dass Bilder aktuell nur als „Eigenes Bild" hochgeladen werden können,
Stock- und KI-Quellen folgen.
4. In `Presseportal Konzept für Relaunch.md` Abschnitt 4: notieren, dass
Tombstone-Variante rudimentär da ist (`deleteFromAdmin`-Ersatztext),
die Korrektur-/Update-Hinweise aber noch fehlen.
5. In `checkliste-user-backend.md` neuen Block „Phase 7" hinzufügen mit
Verweis auf `dev/frontend/hub-flux/19-PHASE-7-PRESS-RELEASE-FORM.md`.
### 6.2 Mit Phase 8 ergänzen
6. Neuer Abschnitt im `Admin-User.md`: „Titelbild & SVG-Platzhalter".
7. Neuer Abschnitt im `Presseportal Konzept für Relaunch.md`: „Veröffentlichungs-Modal & Quota-Kommunikation".
8. Aktualisierung der Firmen-Liste-Doku im `Admin-User.md` mit den
neuen UI-Bausteinen (Counter-Strip, Saved-Views, Filter-Chips,
Card/List-Toggle, Rollen-Legende).
### 6.3 Längerfristig (Phase 2/3)
9. Magic-Link-Flow für Pressekontakte → eigenes Doku-Kapitel, sobald
gebaut.
10. Tarif-/Credit-System → eigener Architektur-Block (Datenmodell,
Stripe-Integration, Quota-Counter-Implementierung).
---
## 7. Quellen-Übersicht für die nächsten Schritte
| Frage | Quelle |
|---|---|
| Was ist konzeptuell der User-Backend-Aufbau? | `docs/user-admin/Admin-User.md` |
| Was ist bereits umgesetzt, was offen? | `docs/user-admin/checkliste-user-backend.md` (Phase 1 ✅) |
| Datenmodell-Übersicht? | `docs/user-admin/user-zusammenhaenge.md` |
| Großthemen-Konzept (KI, Bilder, Tombstones, Magic-Link, Pricing)? | `docs/user-admin/Presseportal Konzept für Relaunch.md` |
| Brand- & Design-System? | `docs/konzept/Entwicklungs-Konzept - Frontend-Komponenten Multi-Brand.md`, `dev/frontend/hub-flux/*` |
| Score-System? | `docs/konzept/Konzept-Update 2 Score-Stufen-System.md` |
| Aktuelle Phase 7 (PM-Form-Refactor)? | `dev/frontend/hub-flux/19-PHASE-7-PRESS-RELEASE-FORM.md` |
| Nächste Phase 8? | `docs/PHASE-8-USER-PANEL-PLAN.md` (neu, neben diesem Dokument) |

View file

@ -0,0 +1,509 @@
**Datum:** 12. Mai 2026 **Status:** Technisches Implementierungs-Konzept **Tech-Stack:** Laravel 12+, Livewire 4 / Volt, Tailwind CSS (v4), Alpine.js (über Livewire) **Bezug:** Konzept-Update 3 (Multi-Brand-Architektur), Konzept-Update 4 (Positionierung), Brand-Landing-Konzept businessportal24
> **IST-Stand 21.05.2026**: Multi-Brand-Architektur ist umgesetzt
> (`config/domains.php`, `ThemeServiceProvider`, getrennte Vite-Builds
> `portal` + `web`). Die Hub-Migration des User Backends ist als
> eigene Roadmap in `dev/frontend/hub-flux/` dokumentiert (Phasen 07
> abgeschlossen, Phase 8 in Planung). Der hier beschriebene Brand-Context
> wird ueber `View::share()` global aufgeloest.
---
## 1. Leitprinzipien
Vier Regeln, an denen sich jede technische Entscheidung in diesem Dokument messen muss:
1. **Ein Codebase, viele Brands.** Kein Branch pro Portal, keine duplizierten Views. Differenzierung über Konfiguration, CSS-Variablen und gezielte View-Overrides.
2. **Brand-Awareness zentral aufgelöst, nicht in Komponenten verteilt.** Eine Komponente fragt nicht „bin ich auf businessportal24?". Sie konsumiert eine `$brand`-Context-Variable und rendert entsprechend.
3. **Livewire/Volt nur wo nötig.** Statische Komponenten bleiben pures Blade. Reaktivität ist ein Kostenfaktor (Server-Roundtrips, Hydration, State-Management) sie muss verdient werden.
4. **Solo-tauglich heißt: jede Entscheidung muss in 6 Monaten noch verständlich sein.** Lieber explizit als clever.
## 2. Brand-Auflösung (Multi-Tenant-Pattern)
### Brand-Resolution-Pipeline
```
Request → Middleware → BrandResolver → Brand-Context im Container
→ View-Pfad-Override
→ Config-Override
→ Layout-Auswahl
```
**Schritt 1: Domain-Mapping**
Die `brands`-Tabelle aus Update 3 enthält pro Brand mindestens:
- `slug` (z.B. `businessportal24`, `presseecho`, `hub`)
- `primary_domain` (z.B. `businessportal24.com`)
- `theme_key` (z.B. `bp24`, `pe`) Verweis auf CSS-Token-Set
- `config_path` (z.B. `brands/businessportal24.php`)
- `is_publisher_hub` (boolean)
-ist zu prüfen, teils schon im System angelegt!
**Schritt 2: Middleware**
```php
// app/Http/Middleware/ResolveBrand.php
public function handle(Request $request, Closure $next): Response
{
$brand = Cache::rememberForever(
"brand.domain.{$request->getHost()}",
fn() => Brand::query()
->where('primary_domain', $request->getHost())
->orWhereJsonContains('aliases', $request->getHost())
->firstOrFail()
);
app()->instance(Brand::class, $brand);
View::share('brand', $brand);
Config::set('brand', $brand->config());
return $next($request);
}
```
Cache ist hier wichtig die Domain-zu-Brand-Auflösung passiert bei jedem Request. `rememberForever` mit explizitem Cache-Bust beim Brand-Update.
-ist zu prüfen, teils schon im System angelegt!
**Schritt 3: Brand im Container**
Jede Klasse kann via Dependency Injection auf die aktuelle Brand zugreifen:
```php
public function __construct(private Brand $brand) {}
```
In Blade-Templates ist `$brand` durch `View::share()` direkt verfügbar.
### Lokale Entwicklung
Lokal arbeiten mit `.test`-Domains in Docker (devserver) auf dem Server via Treafik:
- `businessportal24.test`
- `presseecho.test`
- `pressekonto.test`
Alle zeigen auf dieselbe Codebase, die Middleware löst per Hostname auf. Keine Subdomains, keine Port-Tricks schmerzfreies lokales Multi-Brand-Setup.
## 3. Theming-System (Tailwind v4 + CSS Custom Properties)
### Empfehlung: Tailwind v4
Falls die Migration auf v4 noch offen ist: **jetzt machen**. Die `@theme`-Direktive in v4 macht Multi-Brand-Theming dramatisch einfacher als das v3-Config-Konstrukt. Native CSS-Variablen, keine PostCSS-Akrobatik mehr.
### Token-Architektur
Drei Ebenen (Beispiel ):
```css
/* Ebene 1: Globale Design-Tokens (markenneutral) */
@theme {
--font-serif: 'Source Serif 4', Georgia, serif;
--font-sans: 'Inter', system-ui, sans-serif;
--spacing-section: 5rem;
--spacing-section-tight: 3rem;
--radius-card: 2px; /* fast keine Rundungen, editorial */
}
/* Ebene 2: Semantische Tokens (markenneutral, aber rollenbasiert) */
:root {
--color-text-primary: var(--brand-text);
--color-text-muted: var(--brand-text-muted);
--color-surface: var(--brand-surface);
--color-accent: var(--brand-accent);
--color-cta-bg: var(--brand-cta-bg);
--color-cta-fg: var(--brand-cta-fg);
--color-hub-transition: var(--brand-hub-bg);
}
/* Ebene 3: Brand-spezifische Werte */
[data-brand="businessportal24"] {
--brand-text: #1a1a1a;
--brand-text-muted: #6b6b6b;
--brand-surface: #fafaf7; /* warmer off-white */
--brand-accent: #d94e1f; /* gedämpftes Orange */
--brand-cta-bg: #d94e1f;
--brand-cta-fg: #ffffff;
--brand-hub-bg: #1a2540; /* dunkelblau, Störer */
}
[data-brand="presseecho"] {
--brand-text: #f0f0e8;
--brand-text-muted: #a0a098;
--brand-surface: #1f2620; /* dunkelgrün-anthrazit */
--brand-accent: #5a8a6b; /* gedämpftes Grün */
--brand-cta-bg: #5a8a6b;
--brand-cta-fg: #ffffff;
--brand-hub-bg: #1a2540; /* Hub-Farbe bleibt konstant! */
}
```
### Brand-Aktivierung im Layout
```blade
{{-- resources/views/layouts/brand.blade.php --}}
<!DOCTYPE html>
<html lang="de" data-brand="{{ $brand->slug }}">
<head>
@vite(['resources/css/app.css', 'resources/js/app.js'])
{{-- Brand-CSS wird im app.css via @import geladen, oder optional separat: --}}
@if($brand->has_custom_css)
<link rel="stylesheet" href="{{ asset("themes/{$brand->slug}.css") }}">
@endif
</head>
<body class="bg-[var(--color-surface)] text-[var(--color-text-primary)]">
{{ $slot }}
</body>
</html>
```
**Wichtig:** Der `data-brand`-Attribut auf `<html>` ist der einzige Hebel, der den gesamten Look umschaltet. Alle Tailwind-Utilities, die brand-spezifische Werte nutzen, greifen über CSS-Variablen darauf zu.
### Pragmatische Tailwind-Nutzung
Die Komponenten schreiben **nicht** `bg-orange-600` (das wäre brand-spezifisch im Markup festgenagelt). Stattdessen:
```blade
<button class="bg-[var(--color-cta-bg)] text-[var(--color-cta-fg)] hover:opacity-90">
Mitteilung einreichen
</button>
```
Oder noch sauberer mit eigenen Tailwind-Utility-Klassen, die in `app.css` definiert werden:
```css
@layer components {
.btn-cta {
@apply bg-[var(--color-cta-bg)] text-[var(--color-cta-fg)]
px-6 py-3 rounded-sm font-medium hover:opacity-90 transition;
}
.btn-hub {
@apply bg-[var(--color-hub-transition)] text-white
px-8 py-6 block;
}
}
```
So bleibt das Markup brand-agnostisch und die Stilfragen zentralisiert.
## 4. Komponenten-Hierarchie und Engine-Wahl
### Drei Render-Modi, drei Anwendungsbereiche
|Modus|Wann verwenden|Performance|Beispiele|
|---|---|---|---|
|**Blade Component**|Statisches Markup, keine Interaktion|⚡⚡⚡|TopBar, Footer, PressItem, StatsRow|
|**Volt (Single-File)**|Lokaler State, einfache Reaktivität, Lifecycle einfach|⚡⚡|AdHocTicker, HeroSlider, Search|
|**Klassisches Livewire**|Komplexe Komponenten mit Services, Events, mehrere Methoden|⚡|PressEditor, NewsroomDashboard|
### Volt: konkrete Empfehlung
Volt ist für dieses Projekt **die richtige Wahl als Default für reaktive Komponenten** aber nicht für statische. Die Gründe:
**Pro Volt:**
- Single-File-Komponenten: PHP-Logik + Blade-Template + Tailwind-Klassen in einer Datei. Solo-Entwickler-Freundlichkeit ist hoch.
- Funktionale API ist deutlich weniger Boilerplate als klassische Livewire-Klassen.
- Volt-Komponenten lassen sich genau wie Livewire-Komponenten lazy-laden (`<livewire:lazy ...>`), was für Above-the-fold-Performance wichtig ist.
**Kontra Volt:**
- Für reine Display-Komponenten ist Volt overkill. Eine `<x-press-item>` ohne State soll keine Livewire-Komponente sein Hydration und Wire-Tracking sind unnötige Kosten.
- Wenn eine Komponente Services injiziert, ein eigenes Test-Setup braucht oder mehr als ~150 Zeilen wächst, ist eine klassische Livewire-Klasse besser strukturierbar.
**Faustregel:**
> Renderst du HTML ohne Server-Interaktion? → Blade Component. Brauchst du `wire:model`, `wire:click`, Polling oder reaktiven State? → Volt. Wird die Komponente komplex, hat Services, eigene Tests? → Klassisches Livewire.
### Konkrete Komponenten-Inventur aus den Screens
Aufgeschlüsselt nach Engine. Das ist die direkte Übersetzung der Screens in technische Bausteine:
#### Blade-Komponenten (statisch, hochfrequent wiederverwendet)
```
<x-brand.top-bar> -- Wirtschafts-Ticker, Sprachen, Newsletter/RSS
<x-brand.header> -- Logo, Suche, CTAs
<x-brand.rubriken-nav> -- Hauptnavigation (Wirtschaft, Tech, Finanzen...)
<x-brand.footer> -- Footer mit Cross-Brand-Hinweis
<x-press.item> -- Standard-Listen-Eintrag mit Slots für Varianten
<x-press.item-hero> -- Große Hero-Variante
<x-press.item-sidebar> -- Kompakte Sidebar-Variante (mit Nummerierung)
<x-press.byline> -- Quelle · Zeit · Lesezeit (wiederverwendbar)
<x-ui.section-header> -- "§ 01" + Label + H2 (das Editorial-Pattern)
<x-ui.stats-row> -- Drei-/Vier-Spalten-Statistik
<x-ui.button> -- CTA-Button mit Varianten (primary, secondary, hub)
<x-ui.badge> -- Branchen-Marker, "Geprüft"-Label
<x-hub.transition-block> -- DER dunkelblaue Störer (siehe Briefing)
<x-hub.cta> -- Inline Hub-CTA (Variante des Störers)
<x-quality.standard-footer> -- "Alle Pressemitteilungen werden geprüft..."
```
#### Volt-Komponenten (reaktiv, isolierter State)
```
<livewire:ad-hoc-ticker /> -- Auto-refresh alle 30s, Polling
<livewire:hero-slider /> -- 3 Top-Meldungen, auto-rotate mit Alpine
<livewire:press-search /> -- Suche im Header
<livewire:press-list-filter /> -- "Alle · Heute · Diese Woche" Tabs
<livewire:newsroom-list /> -- Newsroom-Sidebar mit "heute aktiv" Polling
<livewire:branchen-index /> -- Live-Werte mit ± Indikatoren
<livewire:termine-week /> -- Termine-Karussell mit Wochen-Navigation
```
#### Klassisches Livewire (komplex, services)
```
PressSubmissionForm -- Mehrstufige Einreichung (auf Hub)
NewsroomManager -- Profil-Verwaltung (auf Hub)
AdminReviewQueue -- Redaktions-Tool (auf Hub)
```
Auffällig: die **Brand-Portale brauchen kaum klassisches Livewire**. Das ist konsistent zur Architektur aus Update 3 Brand-Portale sind primär Lese-Oberflächen, der State liegt im Hub.
## 5. Brand-Differenzierung in Komponenten
Drei Mechanismen, in aufsteigender Eingriffstiefe:
### 5a. Konfiguration über Brand-Config
Der einfachste Fall: eine Komponente verhält sich anders je nach Brand-Konfiguration. (Beispiel)
siehe: config/domains.php
```php
return [
'name' => 'businessportal24',
'tagline' => 'Pressemitteilungen · DACH',
'press_item_layout' => 'timeline', // vs. 'topic'
'show_market_ticker' => true,
'show_branchen_index' => true,
'hero_variant' => 'top-meldung', // vs. 'topic-cluster'
'rubriken' => ['Wirtschaft', 'Technologie', /* ... */],
];
return [
'name' => 'presseecho',
'tagline' => 'Branchen-Pressearchiv',
'press_item_layout' => 'topic',
'show_market_ticker' => false,
'show_branchen_index' => false,
'hero_variant' => 'topic-cluster',
'rubriken' => [/* andere Reihenfolge, andere Schwerpunkte */],
];
```
Komponenten lesen daraus:
```blade
@if($brand->config('show_market_ticker'))
<x-brand.market-ticker />
@endif
<x-press.item :layout="$brand->config('press_item_layout')" :item="$item" />
```
**Das ist die häufigste Form der Differenzierung** und sie reicht für ~80 % aller Fälle.
### 5b. Slots und Defaults in Komponenten
Wenn eine Komponente strukturell gleich ist, aber Inhalte/Sprache abweichen:
```blade
{{-- resources/views/components/hub/transition-block.blade.php --}}
@props([
'title' => $brand->config('hub_cta.title') ?? 'Pressemitteilung einreichen',
'description' => $brand->config('hub_cta.description'),
'buttonText' => $brand->config('hub_cta.button') ?? 'Zum Publisher-Bereich',
])
<aside class="btn-hub flex flex-col gap-4">
<h3 class="text-xl font-medium">{{ $title }}</h3>
<p class="text-white/80">
{{ $description ?? $slot }}
</p>
<a href="{{ route('hub.submit', ['brand' => $brand->slug]) }}"
class="btn-cta inline-flex items-center gap-2 w-fit">
{{ $buttonText }}
<span aria-hidden="true"></span>
</a>
</aside>
```
Brand-Texte stehen in Config, Komponente bleibt eine.
### 5c. View-Override pro Brand (Eskalations-Pfad)
Für die seltenen Fälle, in denen eine Brand wirklich ein anderes Markup braucht: Laravel kann View-Pfade brand-spezifisch erweitern.
```php
// app/Providers/BrandServiceProvider.php
public function boot(): void
{
$this->app['view']->prependLocation(
resource_path("views/themes/{$brand->slug}")
);
}
```
Dann sucht Laravel View-Dateien zuerst unter `resources/views/themes/presseecho/components/press/item.blade.php`, dann unter dem Standard-Pfad. **Nur** für die Komponenten, die wirklich anders sein müssen, wird eine Override-Datei angelegt.
> **Disziplin-Regel:** View-Overrides sind die letzte Eskalationsstufe. Erst versuchen, mit Config + Slots auszukommen. Override-Dateien verdoppeln Wartungsaufwand jeder Bugfix muss mehrfach gemacht werden.
## 6. Datei-Struktur (Beispiel, siehe akutelle Struktur und optimiere falls nötig )
```
app/
├── Brand/
│ ├── Brand.php # Eloquent Model
│ ├── BrandManager.php # Service, im Container
│ └── BrandResolver.php # Domain → Brand
├── Http/
│ └── Middleware/
│ └── ResolveBrand.php
├── Livewire/
│ ├── Brand/ # Brand-Portal-spezifisch
│ │ ├── AdHocTicker.php
│ │ ├── HeroSlider.php
│ │ └── PressSearch.php
│ └── Hub/ # Hub-spezifisch
│ ├── PressSubmissionForm.php
│ └── NewsroomManager.php
├── View/
│ └── Components/
│ ├── Brand/
│ ├── Press/
│ ├── Hub/
│ ├── Ui/
│ └── Quality/
└── Providers/
└── BrandServiceProvider.php
config/
└── brands/
├── businessportal24.php
├── presseecho.php
└── hub.php
resources/
├── css/
│ ├── app.css # Tailwind base + semantische Tokens
│ └── themes/
│ ├── businessportal24.css # Brand-Tokens (optional separat)
│ └── presseecho.css
├── js/
│ └── app.js
└── views/
├── layouts/
│ ├── brand.blade.php # Brand-Portal-Layout
│ └── hub.blade.php # Hub-Layout
├── components/ # Standard-Komponenten
│ ├── brand/
│ ├── press/
│ ├── hub/
│ ├── ui/
│ └── quality/
├── livewire/ # Volt-Komponenten
│ ├── ad-hoc-ticker.blade.php
│ ├── hero-slider.blade.php
│ └── press-search.blade.php
├── pages/ # Konkrete Seiten-Templates
│ ├── home.blade.php
│ └── veroeffentlichen.blade.php
└── themes/ # NUR Brand-Overrides
└── presseecho/
└── components/
└── ... # nur was wirklich anders ist
```
## 7. Performance-Strategie
Vier konkrete Hebel, die in dieser Reihenfolge ausgeschöpft werden:
**1. Aggressive View-Caching für statisches Markup.** Press-Listen, Newsroom-Sidebars, Statistik-Zeilen können mit Tag-basiertem Cache gepuffert werden. Neue Mitteilung → relevante Tags invalidieren.
```php
Cache::tags(['press_list', "brand.{$brand->slug}"])
->remember('home.aktuelle-meldungen', now()->addMinutes(5), fn() => /* ... */);
```
**2. Volt-Komponenten lazy laden, wo sinnvoll.** Below-the-fold-Komponenten (Branchen-Index, Termine, Newsroom-Liste) als `lazy`:
```blade
<livewire:branchen-index lazy />
```
Sie laden erst beim Scroll, blockieren nicht das initiale Render.
**3. Asset-Pipeline: ein Bundle, alle Brands.** Über die CSS-Variablen-Strategie ist kein Per-Brand-Build nötig. Ein Vite-Build, der für alle Brands gilt. Spart Komplexität und Cache-Invalidierung.
**4. Brand-Resolution cachen.** Die Domain-zu-Brand-Auflösung ist `rememberForever` (siehe Middleware). Cache-Bust nur beim Brand-Update über Model-Observer.
## 8. Migration der Bestands-Inhalte
Quer zu allem oben: die ~100.000 Bestandsmitteilungen sind im neuem System migriert! Drei Punkte, die das Komponenten-Design beeinflussen:
- **`<x-press.item>` muss tolerant gegenüber unvollständigen Daten sein.** Alte Mitteilungen haben evtl. keine Lesezeit-Schätzung, keine Branchen-Zuordnung, keine sauberen Bilder. Komponente rendert auch dann sauber.
- **Permalink-Stabilität.** Die alte URL-Struktur muss erhalten bleiben (Strategie-Dokument: Tombstone-Modell). Das ist ein Routing-Thema, kein Komponenten-Thema aber die Komponenten dürfen keine URLs hardcoden, sondern nur `route()`-Helpers nutzen.
- **Brand-Zuordnung der Bestände.** Wie in Update 3 festgelegt: am Start ist jede Mitteilung beiden Brands zugewiesen. Komponenten brauchen dafür keine Sonderlogik sie filtern nach Brand-Kontext, und der Pool ist eben (am Anfang) für beide Brands derselbe.
## 9. Entwicklungs-Reihenfolge (Empfehlung)
Konkrete Bauplan-Sequenz, die früh nutzbare Ergebnisse liefert:
**Sprint 1 Fundament**
- Brand-Model, Middleware, Resolver
- Theming-Setup (Tailwind v4, CSS-Variablen, zwei Brand-Themes)
- Layout `brand.blade.php`
- Grundlegende UI-Komponenten (`button`, `badge`, `section-header`)
**Sprint 2 Statisches Markup für businessportal24**
- TopBar, Header, RubrikenNav, Footer
- `<x-press.item>` und seine Varianten
- StatsRow, QualityStandardFooter
- Statische Version der Veröffentlichen-Landing (ohne Reaktivität)
**Sprint 3 Reaktive Komponenten**
- AdHocTicker (Volt + Polling)
- HeroSlider (Volt + Alpine)
- PressList mit Filter-Tabs
- Hub-Transition-Block mit Cross-Domain-Auth-Übergabe
**Sprint 4 Hub-Anbindung**
- Sanctum-Setup für Cross-Domain
- Hub-Routing für `?brand=businessportal24`-Parameter
- Einreichungs-Flow im Hub (klassisches Livewire)
**Sprint 5 Zweite Brand aufschalten**
- `presseecho.test` lokal
- Brand-Config für presseecho
- Erste Override-Komponente: `topic-cluster`-Hero
- Testen: was funktioniert ohne Override, was braucht eines?
Ab Sprint 5 wird die eigentliche Stresstest-Frage beantwortet: **Hält die Architektur, wenn die zweite Brand wirklich anders aussehen soll?** Wenn an Sprint 5 viele Overrides nötig werden, ist die Config-Schicht zu dünn dann iterieren.
## 10. technische Punkte
- **Tailwind v4:** wenn das Projekt noch nicht migriert ist, sollte das _vor_ Sprint 1 entschieden werden. v4 macht das CSS-Variablen-Setup deutlich eleganter.
- **Sanctum-Cookie-Domain für Cross-Domain-Auth:** Detail aus Update 3, muss vor Sprint 4 final geklärt sein. Same-Site-Strategie, SPA-Mode oder klassischer Token-Flow?
- **CDN/Asset-Hosting:** Brand-Bilder, Press-Item-Fotos kommen vom Hub
- **Translations:** DACH-Sprachschalter auf der Startseite (de (ohne parameter) / de-at / de-ch / en) ist eine reine Inhalts-Filterung. Bei Mehrsprachigkeit de/en: i18n-Setup
- **Polling-Frequenzen:** AdHocTicker, Newsroom-Liste wie oft refreshen, ohne dass die Server-Last bei wachsendem Traffic problematisch wird? Anfangswerte: Ticker 30s, Newsroom 60s, Branchen-Index 5 min.
---
_Dieses Konzept ist die technische Brücke zwischen Architektur (Update 3), Positionierung (Update 4) und Implementation. Es legt fest, wie Komponenten strukturiert werden, damit die Brand-Differenzierung skaliert ohne in eine Codebase-Duplikation zu kippen. Anpassungen sollten dokumentiert und mit den Update-Dokumenten abgeglichen werden._

View file

@ -0,0 +1,290 @@
**Stand:** Mai 2026 **Portale:** presseecho.de, businessportal24.de **Zweck:** Strategisches Leitkonzept zur Differenzierung gegenüber Wettbewerbern und zur schrittweisen Reaktivierung der beiden übernommenen Portale.
---
## 1. Ausgangslage
Zwei übernommene Pressekonto, über 10 Jahre alt, mit zusammen rund 100.000 archivierten Pressemitteilungen. Frontend nicht responsive, Backend technisch veraltet. Aktive Bestandskunden vorhanden, größerer inaktiver Stamm. Aktuell ca. 50 % des Traffics über connektar.de als Distribution-Partner.
Relaunch in Umsetzung: Laravel-Backend mit 1:1-Datenmigration, neues Tailwind-basiertes Frontend, schrittweise Markendifferenzierung der beiden Portale.
**Entwicklung:** Solo, neben Hauptberuf, mit KI-gestützter Entwicklung. **Laufende Kosten:** Minimal (Hetzner-Server ~10 €/Monat). **Existenzdruck:** Keiner. Projekt ist optionales Nebeneinkommen.
---
## 2. Marktposition: bewusste Nische statt Konkurrenz
### Ehrliche Einschätzung des Marktes
Klassische Pressekonto verlieren redaktionelle Relevanz, behalten aber SEO-Funktion. Käufer buchen Pressemitteilungen heute nicht mehr für Journalistenanrufe, sondern für:
1. Backlinks und SEO-Substanz
2. Google-Sichtbarkeit zu konkreten Suchbegriffen
3. Digitale Sichtbarkeit (Investoren, Partner, Bewerber finden aktuelle PMs)
4. Inhalte für eigene Newsroom-Seiten
Wettbewerber wie openPR und Pressebox kommunizieren weiterhin das alte Versprechen („Reichweite zu Journalisten") und sind in Lock-in-Modellen, Vertriebsstrukturen und Distributions-Verträgen gefangen.
### Eigene Positionierung
**„Pressemitteilungen für digitale Sichtbarkeit ehrlich, fair, ohne Lock-in."**
Keine Massenabdeckung. Keine aggressive Skalierung. Eine ruhige, durchdachte Alternative, die genau die Käufer anspricht, die mit dem klassischen Modell unzufrieden sind.
### Strukturelle Vorteile gegenüber Wettbewerbern
- Niedrige Fixkosten → keine Notwendigkeit für aggressive Tarifmodelle
- Solo-Entwicklung → schnelle, ehrliche Produktentscheidungen ohne Vertriebs-Druck
- 100.000 PMs Archiv + gewachsene Domain-Autorität → nicht reproduzierbares Asset
- Kein Investorendruck → kann bewusst klein, nachhaltig und langfristig wachsen
---
## 3. Realistische Erfolgsdefinition
**Erfolg heißt nicht:** Verdrängung der Marktführer, hohe Wachstumsraten, große Userzahlen.
**Erfolg heißt:**
- Stabiler Betrieb mit minimalen Fixkosten
- Wachsender Stamm zufriedener Bestandskunden
- Planbares monatliches Einkommen (mittelfristig 3.0008.000 €)
- Mit dem Alter: lukratives Nebeneinkommen
- Verkaufbar, falls gewünscht
**Zeithorizont:** 35 Jahre für stabile Einkommensbasis.
---
## 4. Was wir bewusst NICHT machen
Diese Liste ist wichtiger als jede Feature-Roadmap. Sie schützt vor Entscheidungen in schwachen Momenten.
- **Keine Lock-in-Verträge** mit 12-Monats-Mindestlaufzeit
- **Keine kostenlosen PMs** (zieht falsche Zielgruppe an)
- **Keine Fake-Urgency** im Marketing („Nur noch 2 Plätze frei")
- **Keine versteckten Gebühren** bei Standard-Korrekturen
- **Keine Vermarktung als „Reichweite zu X Journalisten"** ehrliches Versprechen statt PR-Floskeln
- **Keine aggressiven Pop-ups** beim Verlassen
- **Keine Newsletter-Pflichtanmeldung** im Bestellprozess versteckt
- **Keine „Auf Anfrage"-Preisverschleierung** klare Preise auf der Verkaufsseite
---
## 5. Differenzierungs-Hebel
### Hebel 1: Friction-freier Einstieg (sofort wirksam)
**Botschaft auf der Verkaufsseite:**
> „Keine Mindestlaufzeit. Monatlich kündbar. Faire Preise."
Direkter Konter gegen openPR (12 Monate Mindestlaufzeit, jährliche Vorauszahlung) und Pressebox (ähnliches Modell). Stärkster Hebel ohne Entwicklungsaufwand reine Marketingbotschaft.
### Hebel 2: SEO-Substanz und Vertrauen herausstellen
Auf der Verkaufsseite kommunizieren:
- „Online seit 2008"
- „Über 100.000 Pressemitteilungen archiviert"
- Domain-Autorität als Trust-Signal
- Echte Aufrufzahlen pro PM (statt nur „Reichweite versprochen")
Wirkt bei B2B-Käufern stark, weil Vertrauen signalisiert wird etwas, das KI-Tool-Plattformen mit 2 Jahren Marktpräsenz nicht liefern können.
### Hebel 3: Faires Korrektur- und Änderungsmodell
**Was Wettbewerber machen:** openPR berechnet 10 € pro Änderung, auch bei Tippfehlern.
**Was wir machen:**
- Tippfehler / Kontaktdaten-Korrekturen: kostenfrei (Self-Service)
- Inhaltliche Korrektur: mit transparentem Korrekturhinweis
- Updates: als Anhang an Original
- Tombstone bei Löschung statt Hard-Delete (SEO-Erhalt)
- Anonymisierung bei DSGVO-Anfragen ohne Diskussion
Adressiert echten Schmerz von Bestandskunden anderer Portale.
### Hebel 4: Smarte Add-ons mit Konversionslogik
Credit-System ermöglicht margenstarke Zusatzumsätze nach der Erstbuchung:
- Highlight-Buchung im Anschluss („7 Tage prominent platziert")
- KI-Bildgenerierung
- Cross-Post zwischen presseecho.de und businessportal24.de
- KI-Quality-Check / Stilverbesserung
- Newsletter-Erwähnung (wenn Newsletter aufgebaut)
### Hebel 5: Distribution über die richtigen Kanäle
**Falsch wäre:** Eigene Social-Media-Accounts der Portale aufbauen → kostet Zeit, baut keine Reichweite auf, kann Reputation schaden.
**Richtig ist:**
- **Share-Funktionen für Kunden:** Nach Veröffentlichung vorgefertigte Share-Texte für LinkedIn, X, Facebook, WhatsApp
- **LinkedIn priorisieren:** B2B-Distribution läuft 2026 dort, nicht auf X
- **Open-Graph- und Twitter-Card-Tags** sauber konfigurieren, damit geteilte Links gut aussehen
- **Optional Phase 3:** kuratierter „Best of"-Channel mit harter KI-Score-Schwelle (>85), nur wenn Zeit dafür da ist
### Hebel 6: Brand-Schutz absichern
- DPMA-Marken für presseecho.de und businessportal24.de prüfen, ggf. anmelden (~290 € pro Wortmarke)
- Google Ads Brand-Schutz-Kampagnen für eigene Portalnamen (~2050 €/Monat)
- Verhindert, dass Wettbewerber bei Markensuchen oben stehen
---
## 6. Preismodell (bewusst gegen den Markt)
|Tier|Preis|Pressemitteilungen|Inklusive|
|---|---|---|---|
|**Einzel**|29 € / Stück|1|Free-Stock, KI-Quali-Check|
|**Starter**|19 €/Mo. oder 190 €/Jahr|3/Monat, weitere à 15 €|Free-Stock, KI-Quali|
|**Business**|59 €/Mo. oder 590 €/Jahr|10/Monat|+ 1 Highlight, 5 Premium-Stock, 10 KI-Bilder|
|**Pro**|119 €/Mo. oder 1.190 €/Jahr|unbegrenzt (Fair Use)|+ 3 Highlights, größere Kontingente, Priority|
|**Agency**|249 €/Mo. oder 2.490 €/Jahr|unbegrenzt für bis zu 5 Marken|alles aus Pro × Marken, je weitere Marke 39 €/Mo.|
**Eckpunkte:**
- Alle Tarife monatlich kündbar
- Jahrespreise mit ~15 % Rabatt
- Credit-System für flexible Add-ons
- Mengenrabatte ab 3 PMs in 30 Tagen (z. B. 20 % auf nächste PM)
- Reaktivierungs-Gutschein für Bestandskunden zum Relaunch (z. B. 50 % auf nächste PM)
**Launch-Aktion:** „Erste PM für 9 € statt 29 €" einmalig pro Account, neue Accounts auf gleiche Firma/E-Mail werden erkannt.
---
## 7. Akquise-Strategie
### Phase 1: Bestandskundenreaktivierung (sofort)
- Persönliche Kontaktaufnahme zu aktiven Bestandskunden (20 Telefonate vor Relaunch wertvoller als jedes Marketingkonzept)
- Reaktivierungs-Mailing an inaktiven Stamm mit konkretem Gutschein
- Realistischer Erwartungswert: 515 % Reaktivierung bei persönlicher Ansprache, 13 % bei Massenmail
### Phase 2: Organische Sichtbarkeit (mittelfristig)
- Saubere Migration ohne SEO-Verlust (saubere Redirects, Tombstone-Modell, schnelle Ladezeiten)
- Bestandsarchiv als Long-Tail-Magnet pflegen
- Content-Marketing über das Portal selbst (redaktionelle Übersichten, Branchen-Specials)
### Phase 3: Empfehlungsmarketing (langfristig)
- Empfehlungs-System: Bestandskunden bekommen Rabatt für gebrachte Neukunden
- Funktioniert in B2B-Nischen besser als bezahltes Marketing, weil Empfehlungen das Vertrauensthema lösen
### Bezahlte Werbung realistisch eingesetzt
- **Brand-Schutz** (Pflicht): Eigene Brandnames bei Google Ads buchen (~2050 €/Monat)
- **Long-Tail-Branchenkeywords** (Test): „Pressemitteilung [Branche] veröffentlichen" mit kleinem Budget (200300 €/Monat) testen
- **Nicht** auf Hauptkeywords wie „Pressemitteilung veröffentlichen" bieten CPCs zu hoch, ROI bei Solo-Setup nicht gegeben
---
## 8. Connektar.de als Distribution-Partner
50 % des aktuellen Traffics läuft über connektar.de. Risiko und Asset zugleich.
**Strategie:**
- Weiterlaufen lassen, aber Abhängigkeit transparent machen
- Eigenständige Akquise parallel aufbauen
- Datenmodell vorbereiten: `source: distribution_partner`, separate Statistiken
- Qualitäts-SLA tracken (KI-Ablehnungsrate)
- Bei Bedarf in Zukunft: eigenes Kündigungsrecht bei Qualitätsproblemen verhandeln
---
## 9. Technisches Setup
- **Backend:** Laravel
- **Frontend:** Tailwind, responsive
- **Hosting:** Hetzner (~10 €/Monat)
- **Deployment:** Ploi
- **Tracking:** Umami self-hosted auf separater VM (DSGVO-konform, kein Cookie-Banner, kein Adblocker-Problem)
- **Zusätzlich:** Serverseitiger Aufruf-Counter in Laravel für verlässliche PM-Statistiken
---
## 10. Phasen-Roadmap
### Phase 1 MVP / Relaunch
- Backend-Migration auf Laravel
- Responsive Frontend mit Tailwind
- 1:1-Datenmigration der Bestands-PMs
- Magic-Link-Login für Self-Service-Änderungen
- Korrektur- und Tombstone-Modell
- KI-Vorprüfung neuer PMs
- Credit-System mit Tarifstruktur
- Share-Buttons mit Open-Graph-Tags
- Marketingbotschaft „Keine Mindestlaufzeit" prominent
- Brand-Schutz-Kampagnen aktivieren
- Bestandskundenreaktivierung
### Phase 2 Konsolidierung (36 Monate nach Launch)
- Cross-Post zwischen den beiden Portalen
- Highlight-Buchungen mit eleganter UX
- KI-Score-Anzeige für Kunden (Transparenz)
- Statistik-Dashboard für Kunden (eigene Aufrufzahlen)
- Empfehlungsmarketing-System
### Phase 3 Differenzierung (612 Monate)
- Schärfere Markentrennung presseecho.de vs. businessportal24.de
- LinkedIn-Auto-Post für Pro/Agency-Tier
- Optional: kuratierter Best-of-Social-Channel mit hoher Schwelle
- Newsletter-Aufbau, Newsletter-Erwähnung als Add-on
### Phase 4 Wachstum & Optimierung (laufend)
- Quartals-Rhythmus für kontinuierliche Verbesserungen
- Bestandskunden regelmäßig befragen
- Tarifmodell datenbasiert nachschärfen
- Bei stabiler Basis: ggf. weitere Add-ons / Premium-Features
---
## 11. Erfolgskontrollen und Stopps
Klare Stopps definieren, um nicht im Zombie-Modus zu landen:
**Nach 9 Monaten Live-Betrieb prüfen:**
- Wachsender Bestandskundenstamm?
- Planbarer monatlicher Umsatz erkennbar?
- Connektar-Anteil sinkend (Eigenakquise zieht)?
- Reaktivierungsrate alter Kunden im Erwartungsbereich?
**Falls keine positive Entwicklung sichtbar:**
- Konsolidierung auf eine Marke prüfen
- Verkaufsoption prüfen
- Reines Archiv-Modell mit minimaler Pflege erwägen
- Kein „weiter so" aus Trägheit
**Anti-Zombie-Regel:** Mindestens einmal pro Quartal ein halber Tag „Was muss verbessert werden". Ohne Ausnahme.
---
## 12. Strategische Leitlinien zusammengefasst
1. **Nische besetzen, nicht konkurrieren.**
2. **Modernes Verständnis von Pressemitteilungen** kommunizieren digitale Sichtbarkeit, SEO, ehrliche Versprechen.
3. **Friction-frei sein**, wo Wettbewerber Friction aufbauen (Mindestlaufzeit, Korrekturgebühren, intransparente Preise).
4. **100.000 PMs als Asset** kommunizieren nicht reproduzierbar.
5. **Empfehlungsmarketing in B2B-Nische** ist langfristig stärker als bezahltes Marketing.
6. **Geduld als Wettbewerbsvorteil** kein Investorendruck heißt: organisch wachsen können.
7. **Konsistenz schlägt Spektakel** kleine kontinuierliche Verbesserungen, alle 612 Monate ein nennenswertes Feature.
8. **Erfolg = stabiles Nebeneinkommen**, nicht Marktführerschaft.
9. **Bewusste Selbstverpflichtung** auf das, was man nicht macht.
10. **Bei jeder Feature-Entscheidung fragen:** Passt das zu einem ruhigen, fairen, durchdachten Portal oder zu einem aggressiven Wachstums-Tool?
---
_Diese Strategie ist ein lebendes Dokument. Sie sollte mindestens einmal jährlich überprüft und an die tatsächliche Marktentwicklung angepasst werden._

View file

@ -0,0 +1,445 @@
Stand: Mai 2026 Zweck: Ersatz bzw. Ergänzung der Abschnitte 8, 9, 10 sowie neue Abschnitte zur Score-Architektur, Boost-Eligibilität und zum Tool-Loop. Datenmodell-Ergänzungen am Ende.
> **IST-Stand 21.05.2026**: Dieses Update beschreibt Phase-2/3-Themen.
> Aktuell ist im Code **nichts** davon umgesetzt:
>
> - Keine Tarif-Stufen, kein Kontingent, keine Stripe-Anbindung.
> - Kein Score, keine Boost-Eligibilitaet, kein Tool-Loop.
> - Kein Auto-Refill, keine Credit-Pakete.
>
> Phase 8 baut lediglich einen **Quota-Stub** auf `users.press_release_quota`
> und `press_release_quota_used_this_month` als Vorbereitung fuer das
> Veroeffentlichungs-Modal. Das echte Tarif-Modul ersetzt diese Felder
> spaeter. Plan-Doku: `docs/PHASE-8-USER-PANEL-PLAN.md`.
---
## 8. Preismodell Tarife (überarbeitet)
### Grundlogik
Alle Tarife enthalten ein Kontingent an Pressemitteilungen sowie monatlich ausgeschüttete Bonus-Credits für Tools und Add-ons. Bonus-Credits aus Abos verfallen monatlich, gekaufte Credits bleiben 24 Monate gültig. So bleibt das Abo aktivierungsstark, ohne dass der Nutzer eigenes Geld verliert.
### Tier-Struktur
|Tier|Preis|PMs|Bonus-Credits/Mo.|Effektiver PM-Preis|Besonderheiten|
|---|---|---|---|---|---|
|**Einzel**|19 € / Stück|1|4 (verfallend nach 30 T)|19,00 €|Pay-as-you-go|
|**Starter**|19 €/Mo. (190 €/Jahr)|3|12|6,30 €|Free-Stock, KI-Quality-Check|
|**Business**|49 €/Mo. (490 €/Jahr)|10|30|4,90 €|Erweiterte Statistiken, optionaler Newsroom|
|**Pro**|99 €/Mo. (990 €/Jahr)|unbegrenzt (Fair Use)|60|< 2 |Eigener Newsroom, Priority, volles Statistik-Dashboard|
|**Agency**|199 €/Mo. (1.990 €/Jahr)|unbegrenzt für 5 Marken|120|< 1 |Multi-Redakteur-Workflow, API-Zugang, je weitere Marke 29 /Mo.|
Jahrespreise mit ca. 17 % Rabatt eingebaut. Fair Use im Pro-Tarif: Soft-Cap 50 PMs/Monat.
### Mehrwerte im Vergleich
|Feature|Einzel|Starter|Business|Pro|Agency|
|---|---|---|---|---|---|
|Pressemitteilungen|1|3/Mo.|10/Mo.|unbegr.|unbegr. (5 Marken)|
|Bonus-Credits|4 einmalig|12/Mo.|30/Mo.|60/Mo.|120/Mo.|
|Free-Stock-Bilder|✓|✓|✓|✓|✓|
|KI-Quality-Check|✓|✓|✓|✓|✓|
|Erweiterte Statistiken|||✓|✓|✓|
|Eigener Newsroom|||optional|inkl.|inkl.|
|Priority-Support||||✓|✓|
|Multi-Redakteur-Workflow|||||✓|
|API-Zugang|||||✓|
### Kommunikation
Die inkludierten Bonus-Credits sind Teil des Pakets, nicht zusätzliche Kosten. Reicht das Kontingent nicht (z. B. weil mehrere PMs mit aufwändigem Tooling veröffentlicht werden), kauft der Nutzer Credits nach diese bleiben 24 Monate erhalten und schaffen langfristige Bindung an die Plattform.
### Bestandskunden
Aktive Jahresabos behalten Preis bis zum nächsten Verlängerungstermin. Loyalty-Bonus 1020 % im ersten Verlängerungsjahr. Downgrade-Pfad anbieten.
### Einstiegsstrategie
In der Anfangsphase (erste 612 Monate nach Relaunch) bewusst günstiger einsteigen, um User-Base aufzubauen. Preise sind kalkuliert mit Spielraum für spätere Anpassung. Wichtig: Bestandskunden behalten ihre Konditionen.
---
## 9. Credit-System (überarbeitet)
### Grundregel
**1 Credit = 1 €** als Listenpreis-Anker. Alle Service-Preise werden in ganzen Credits ausgewiesen. Wer größere Pakete kauft, zahlt effektiv weniger pro Credit (Volumenrabatt), aber der Listenpreis bleibt stabil. So entfällt jede Kopfrechen-Übung im UI.
### Credit-Pakete
|Paket|Credits|Preis|Effektiv pro Credit|Ersparnis|
|---|---|---|---|---|
|Test|10|10 €|1,00 €||
|Standard|50|45 €|0,90 €|10 %|
|Plus|150|120 €|0,80 €|20 %|
|Pro|500|375 €|0,75 €|25 %|
|Business|1.500|1.050 €|0,70 €|30 %|
Ganzzahlige Beträge, keine Bruchteile im UI. Intern kann auf Cent-Ebene abgerechnet werden, aber nach außen sieht der Nutzer nur ganze Credits.
### Auto-Refill
Standardmäßig nach erstem Kauf aktiviert (mit Opt-Out):
- Trigger: bei < 10 Credits Restguthaben
- Aufladung: zuletzt gekauftes Paket (Default Standard, 50 Credits)
- Eindeutige Bestätigungs-Mail nach jeder automatischen Aufladung
### Gültigkeit
- Gekaufte Credits: 24 Monate ab Kauf
- Bonus-Credits aus Abos: monatlich verfallend
- Willkommens-Bonus (5 Credits einmalig bei Account-Anlage): 90 Tage
### Mini-Checkout (kontextuell)
1. User klickt z. B. „KI-Bild generieren"
2. Modal: _„Kostet 4 Credits. Du hast 2 Credits."_
3. Optionen:
- „Schnell aufladen: Standard-Paket (50 Credits, 45 €)" 1-Klick mit Saved Payment Method
- „Anderes Paket wählen"
- „Abbrechen"
4. Nach Aufladung wird Aktion automatisch ausgeführt
### Erstkauf
Stripe Checkout mit `setup_future_usage` für Saved Payment Method. Danach 1-Klick-Aufladung.
### Dashboard
- Credit-Stand oben rechts immer sichtbar
- Trennung sichtbar: Bonus-Credits (verfallend) vs. gekaufte Credits (24 Monate)
- Verlauf einsehbar (was wofür verbraucht)
- Rechnungs-PDFs für jede Aufladung
### Buchhaltung & Recht
- Credits = Vorauszahlung, bilanziell als Verbindlichkeit
- MwSt-Behandlung mit Steuerberater abstimmen (Kauf vs. Verbrauch)
- Verfall in AGB sauber dokumentieren
- Keine Auszahlung in Geld (sonst PSD2-Lizenzthema)
- EU-Auslandskunden: Reverse-Charge bei B2B mit USt-ID
---
## 10. Preisliste in Credits (überarbeitet)
Alle Preise in ganzen Credits (1 Credit = 1 €). Anker-Werte für die Startphase, iterativ anpassbar.
### Veröffentlichung
|Service|Credits|
|---|---|
|Standard-PM (Pay-as-you-go)|19|
|PM-Korrektur (Pfad C)|8|
|PM-Update (Pfad D)|4 _(im ersten Jahr ggf. kostenlos)_|
|Depublizierung (Pfad G)|1925|
### Bilder
|Service|Credits|
|---|---|
|Free-Stock (Unsplash, Pexels)|0|
|Premium-Stock (Adobe, Shutterstock)|8|
|KI-Bild generieren|4|
|KI-Bild Re-Generation|2|
### KI-Textservices
|Service|Credits|
|---|---|
|Quality-Check (Stil/Pressestil)|3|
|Lektorat|8|
|Pressetext-Optimierung (Headlines, SEO)|15|
|Headline-Booster (nur Headlines)|5|
|PM aus Stichworten generieren|25|
|Übersetzung (DE↔EN)|12|
### Platzierungen
|Service|Credits|
|---|---|
|Highlight Kategorie (3 Tage)|15|
|Highlight Kategorie (7 Tage)|30|
|Startseite-Highlight (24 h)|39|
|Startseite-Highlight (3 Tage)|89|
|Top-Slot Startseite (24 h)|119|
|Newsletter-Erwähnung|59|
|Social-Share (offizieller Kanal)|25|
Voraussetzung für alle Platzierungen: Mindest-Content-Score erreicht (siehe Abschnitt „Boost-Eligibilität").
### Distribution
|Service|Credits|
|---|---|
|PDF-Export mit Branding|2|
|Social-Snippet-Generierung|3|
|Verteiler-Versand (klein, branchenspezifisch)|39|
|Verteiler-Versand (mittel)|99|
|Verteiler-Versand (groß, branchenübergreifend)|199|
### Account / Profil
|Service|Credits|
|---|---|
|Verifiziertes Firmenprofil (einmalig)|79|
|Custom Subdomain (pro Jahr)|49|
|Erweiterte Statistiken (pro Monat)|15|
### Goodies (kostenlos, fördern Aktivität)
- PM-Updates kostenfrei im ersten Jahr (besseres Archiv)
- 3 Free-Stock-Bilder pro PM
- Erster KI-Quality-Check pro PM kostenfrei
- 5 Credits Willkommens-Bonus bei Account-Anlage (90 Tage gültig)
- Headline-Vorschlag (1 Variante) kostenfrei pro PM
---
## NEU Abschnitt 15: Score-Architektur
Die Plattform arbeitet mit drei voneinander unabhängigen Scores. Sie haben unterschiedliche Funktionen, werden unterschiedlich berechnet und an unterschiedlichen Stellen wirksam. Die Trennung ist zentral, weil sie unterschiedliche Datenmodelle und Update-Logiken betrifft.
### 15.1 Klassifikations-Score (Eintritts-Filter)
**Funktion:** Entscheidet, ob eine Pressemitteilung überhaupt veröffentlicht wird.
**Bereich:** Grün / Gelb / Rot (kategorial)
**Faktoren:**
- Werbung statt Pressemitteilung
- Beleidigend / diskriminierend
- Rechtlich heikel
- Spam-Muster
- Unseriöse Versprechen
**Auswirkung:**
- Grün: Direkte Veröffentlichung (optional 510 Min Verzögerung)
- Gelb: Manuelle Review-Queue
- Rot: Zurück an User mit Begründung
**Aktualisierung:** Einmalig bei Einreichung. Bei Änderung der PM (Pfad C/D) wird neu klassifiziert.
**Speicherung:** `press_releases.classification` plus vollständiges Audit-Log in `ki_audits`.
### 15.2 Content-Score (Qualitäts-Indikator)
**Funktion:** Misst die handwerkliche Qualität einer Pressemitteilung. Bestimmt organische Sichtbarkeit und Boost-Berechtigung.
**Bereich:** 0100 Punkte
**Faktoren (Vorschlag, iterativ verfeinerbar):**
|Kategorie|Gewichtung|Was zählt|
|---|---|---|
|Pressestil|20 %|Tonalität (informativ vs. werblich), passive vs. aktive Konstruktion, Zitate vorhanden|
|Struktur|15 %|Lead-Absatz vorhanden, sinnvolle Absatzstruktur, Pyramidaler Aufbau|
|Lesbarkeit|10 %|Flesch-Index für Deutsch, Satzlängen, Fachsprache angemessen|
|Vollständigkeit|15 %|Pressekontakt, Unternehmensinfo, Datum, Branche, Region|
|Bildmaterial|10 %|Mindestens 1 Bild, Auflösung, Alt-Text, Bildunterschrift|
|Quellen / Belege|10 %|Verlinkungen, Studien-Referenzen, Datenquellen|
|Headline-Stärke|10 %|Länge, Keyword-Relevanz, Klarheit|
|Originalität|10 %|Kein Boilerplate, kein Duplicate-Content, individueller Ton|
**Auswirkung:**
- **Organische Sichtbarkeit:** Listing-Position, Top-Story-Kandidat, Newsletter-Aufnahme, Trending in Branche
- **Boost-Berechtigung:** Schwellenwerte für kostenpflichtige Slots (siehe Abschnitt 16)
- **User-Feedback:** Sichtbar im Editor-Dashboard mit konkreten Verbesserungsvorschlägen
**Aktualisierung:** Bei Einreichung berechnet, bei jeder Änderung der PM neu berechnet. History pro PM in `content_scores`.
**Speicherung:** `press_releases.content_score` (aktueller Wert), `content_scores` (History mit Faktor-Breakdown).
### 15.3 Trust-Score (Reputations-Indikator)
**Funktion:** Bewertet die Zuverlässigkeit eines Publishers über Zeit. Reduziert Moderationslast und kann öffentliche Anerkennung bringen.
**Bereich:** 0100 oder Stufen (Bronze / Silber / Gold / Verifiziert)
**Faktoren:**
- Anzahl problemfrei veröffentlichter PMs
- Durchschnittlicher Content-Score über alle PMs
- Beschwerderate (Reports, Korrekturen, Depublizierungen)
- Account-Alter
- Verifikations-Status (verifiziertes Firmenprofil)
**Auswirkung:**
- **Moderation:** Lockerung der KI-Freigabe-Schwelle (mehr „Grün" automatisch)
- **Sichtbarkeit (optional):** öffentliches Verifizierungs-Badge auf Newsroom und PM-Seiten
- **Bevorzugung in Branchen-Übersichten** bei gleichem Content-Score
- **Bei Trust-Verlust:** Rückfall in strengere Moderation (auch nach Beschwerden, häufigen Korrekturen, Depublizierungen)
**Aktualisierung:** Rollierend, z. B. nächtlicher Cron-Job über die letzten 90 Tage Aktivität.
**Speicherung:** `accounts.trust_score`, `accounts.trust_tier` (Bronze/Silber/Gold/Verifiziert), History in `trust_score_log`.
### Offene Detail-Entscheidungen
- Trust auf User- oder auf Firmen-Ebene? (Empfehlung: Firmen-Ebene, weil Mitarbeiter wechseln)
- Trust-Verlust: ab welchen Schwellen?
- Verifizierungs-Badge: nur über kostenpflichtigen Verifizierungs-Prozess oder auch durch Trust-Score erreichbar?
---
## NEU Abschnitt 16: Boost-Eligibilität
Die Verbindung zwischen Score-System und kostenpflichtigen Sichtbarkeits-Slots. Grundprinzip: **Schlechter Content kann nicht in den Top-Slot gekauft werden.** Das schützt die redaktionelle Glaubwürdigkeit der Plattform und schafft den Anreiz, in Qualität zu investieren.
### Schwellenwerte je Slot-Typ
|Slot|Klassifikation|Min. Content-Score|
|---|---|---|
|Highlight Kategorie|Grün|50|
|Startseite-Highlight (24h / 3 T)|Grün|65|
|Top-Slot Startseite|Grün|75|
|Newsletter-Erwähnung|Grün|70|
|Social-Share (offizieller Kanal)|Grün|70|
|Verteiler-Versand (extern)|Grün|80|
PMs mit Klassifikation Gelb können nicht boostbar werden, auch nicht nach manueller Freigabe sie bleiben in regulärer Sichtbarkeit. PMs mit Klassifikation Rot werden nicht veröffentlicht und sind damit irrelevant.
### UI-Logik
Wenn ein User einen Boost-Slot bucht, dessen Schwelle seine PM nicht erreicht, sieht er statt des Buchungsformulars:
> _„Diese Pressemitteilung erreicht aktuell einen Content-Score von 60/100. Für den Top-Slot Startseite empfehlen wir mindestens 75 Punkte. So kannst du deinen Score verbessern:"_
>
> _[Pressetext-Optimierung 15 Credits → +1520 Punkte]_ _[Headline-Booster 5 Credits → +37 Punkte]_ _[Bild hinzufügen 4 Credits → +510 Punkte]_
Nach Tool-Anwendung wird der Score neu berechnet, der Slot kann dann gebucht werden.
### Effekt
Drei gewollte Konsequenzen:
1. **Plattform-Qualität bleibt hoch:** Premium-Slots zeigen nur qualitativ hochwertige Inhalte.
2. **Tools werden indirekt verkauft:** Wer den Slot will, muss in Qualität investieren entweder selbst oder über kostenpflichtige Tools.
3. **Glaubwürdigkeit für Leser bleibt erhalten:** Leser und Journalisten erkennen schnell, dass sichtbar platzierte Inhalte tatsächlich relevant sind.
### Sonderfall: Editorial-Pick
Unabhängig vom Boost-System kann die Redaktion (intern) PMs als „Empfehlung der Redaktion" hervorheben. Das ist ein redaktionelles Instrument, kein kommerzielles, und nicht buchbar. Wirkt als Vertrauensanker auf der Startseite.
---
## NEU Abschnitt 17: Tool-zu-Algorithmus-Loop
Der strategische Kern der Monetarisierungslogik. Der Loop verbindet drei Plattform-Ziele in einem geschlossenen System:
### Die drei Ziele
1. **Plattform-Qualität:** Hohe durchschnittliche Inhaltsqualität, damit Leser, Journalisten und Mediaplaner die Plattform als seriös wahrnehmen.
2. **Monetarisierung:** Umsatz aus Tools, Tarifen und Boost-Slots.
3. **Anreiz für Publisher:** Sichtbar gute Platzierungen für gute Inhalte als motivierender Faktor.
### Der Loop
```
Publisher schreibt PM
Content-Score wird berechnet (z. B. 55/100)
Publisher will Top-Slot buchen (Schwelle 75)
System empfiehlt: Pressetext-Optimierung (15 Credits)
Tool wird angewendet, Score steigt auf 78
Top-Slot wird gebucht (119 Credits)
PM erscheint prominent auf Startseite
Hohe Reichweite, gute Statistiken
Publisher sieht Wert, kommt wieder
Plattform-Durchschnittsqualität steigt
Mehr Leser, mehr Wert für nächsten Boost-Käufer
```
### Voraussetzungen für Funktionieren
- **Tools müssen tatsächlich gut sein.** Wenn das KI-Lektorat schlechter ist als das, was der Publisher selbst zustande bringt, kollabiert der Loop. → Tool-Qualität ist Wettbewerbsvorteil, hier wird investiert.
- **Score-Verbesserung muss spürbar und nachvollziehbar sein.** Der Publisher muss verstehen, was sein Tool-Einsatz konkret gebracht hat. → Score-Breakdown sichtbar, Vorher-Nachher-Vergleich.
- **Reichweite muss real sein.** Ein gekaufter Top-Slot muss tatsächlich Reichweite bringen. → Leser-Seite (Newsletter, SEO, Social) muss aktiv aufgebaut werden.
- **Boost-Schwellen dürfen nicht zu hoch sein.** Sonst wird der Loop frustrierend statt motivierend. → Schwellen iterativ kalibrieren auf Basis realer Score-Verteilung.
### Was das für den Build bedeutet
- **Tools haben strategische Priorität.** KI-Lektorat, Pressetext-Optimierung, Headline-Booster sind nicht nur Add-ons, sondern das Herzstück der Wertschöpfung.
- **Score-Anzeige muss früh implementiert werden.** Ohne sichtbaren Score kein Loop.
- **Statistik-Dashboard ist Pflicht für mittlere Tarife.** Ohne sichtbare Reichweiten-Daten erkennen Publisher den Wert ihres Investments nicht.
---
## 13. Datenmodell-Skizze Ergänzungen
Zusätzlich zu den bestehenden Tabellen aus dem Hauptkonzept:
```
content_scores
- id, press_release_id
- score (0-100), version (bei Neuberechnung)
- factors (JSON: pressestil, struktur, lesbarkeit, vollstaendigkeit,
bildmaterial, quellen, headline, originalitaet)
- calculated_at, calculation_reason (initial/edit/tool_applied)
placements
- id, press_release_id, account_id
- slot_type (kategorie_highlight, startseite_highlight, top_slot,
newsletter, social_share, verteiler_klein/mittel/gross)
- starts_at, ends_at
- credits_spent
- status (scheduled, active, completed, cancelled)
- eligibility_check_passed (bool, snapshot bei Buchung)
- eligibility_score_snapshot (Content-Score zum Zeitpunkt der Buchung)
- created_at
placement_inventory
- id, slot_type
- max_concurrent (z.B. 1 für Top-Slot, 3 für Startseite-Highlight)
- duration_options (JSON: [24h, 72h])
- min_content_score (75)
- min_classification ('green')
trust_score_log
- id, account_id
- score (0-100), tier (bronze/silber/gold/verifiziert)
- factors (JSON: pm_count, avg_content_score, complaints,
account_age_days)
- calculated_at
accounts (Ergänzungen)
- + trust_score (int, 0-100)
- + trust_tier (enum)
- + verified_business_profile (bool)
- + verified_at
```
Wichtige Logiken:
- **placement_inventory** definiert, wie viele Slots welcher Art parallel verfügbar sind. Bei Buchung wird geprüft: ist ein Slot für das gewünschte Zeitfenster frei? Wenn nicht: nächstmöglicher Termin anbieten oder ablehnen.
- **eligibility_score_snapshot** auf Placement-Ebene: damit nachvollziehbar bleibt, mit welchem Score eine PM zum Buchungszeitpunkt qualifiziert war. Wenn der Score später sinkt (etwa durch Korrektur), bleibt der gebuchte Slot bestehen, aber bei Verlängerung wird neu geprüft.
- **content_scores** mit Versionierung erlaubt nachträglich Auswertung: Welche Tools haben welchen Score-Effekt gehabt? Daten für Tool-Optimierung.
---
## Offene Punkte / nächste Entscheidungen (Update)
Zusätzlich zu den bereits dokumentierten Punkten:
- **Content-Score-Faktoren feinjustieren:** Welche Gewichtung passt für deutsche Pressemitteilungen? Iterativ kalibrieren mit echten Daten.
- **Boost-Schwellen kalibrieren:** Erst nach 100200 echten PMs sehen, wo die Score-Verteilung liegt. Schwellen ggf. anpassen.
- **Trust-Score: User vs. Firma:** Empfehlung Firma, aber Detail-Logik bei Mitarbeiterwechsel klären.
- **Tool-Qualität:** KI-Prompts für Lektorat und Pressetext-Optimierung müssen sehr sauber gebaut werden. Eigene Test-Suite mit Vorher/Nachher-PMs.
- **Slot-Inventory:** Wie viele Top-Slots parallel? Empfehlung 1 (sonst verliert er an Wert), Startseite-Highlight 3, Kategorie-Highlight 510 je Branche.
- **Editorial-Picks:** Wer wählt aus? Anfangs du selbst, später ggf. Redaktions-Account mit Frontend-Tool.

View file

@ -0,0 +1,197 @@
Stand: Mai 2026 Zweck: Ersetzt die Außenkommunikation des Content-Scores durch ein dreistufiges System. Aktualisiert Abschnitt 15.2 (Content-Score) und Abschnitt 16 (Boost-Eligibilität) aus dem ersten Konzept-Update.
> **IST-Stand 21.05.2026**: Score-System und Stufen-Anzeige sind nicht
> implementiert. Im Code gibt es weder ein `user_score`- noch ein
> `company_score`-Modell. Das Thema bleibt fuer Phase 3.
---
## Hintergrund
Der Content-Score (0100) bleibt als plattform-internes Steuerungsinstrument bestehen. Für die Außenkommunikation gegenüber Lesern wird die konkrete Punktzahl jedoch durch ein dreistufiges Stufen-System ersetzt. Gründe:
- Konkrete Punktzahlen wirken meta-fetischistisch und lenken vom Inhalt ab
- Vergleichbarkeit zwischen PMs ("warum nur 67?") erzeugt Konflikte ohne Mehrwert
- Goodhart's Law: Publisher würden auf die Zahl optimieren statt auf Qualität
- Stufen sind kulturell etabliert (Nutri-Score, Stiftung Warentest) und sofort verständlich
Die Punktzahl bleibt dort erhalten, wo sie produktiv ist: im Editor während des Schreibens, im Publisher-Dashboard und in Boost-Buchungs-Dialogen.
---
## 15.2 Content-Score (überarbeitet)
### Score und Stufen
Der Content-Score (0100) wird Plattform-intern unverändert berechnet. Für alle nach außen gerichteten Anzeigen gilt folgendes Mapping:
|Stufe|Score-Bereich|Bedeutung|
|---|---|---|
|**Standard**|30 59|Mindestqualität erreicht, regulär veröffentlicht|
|**Geprüft**|60 79|Solide Pressemitteilung, gute Substanz, redaktionelle Standards eingehalten|
|**Hochwertig**|80 100|Top-Qualität, redaktioneller Maßstab|
PMs unterhalb von Score 30 werden vom Klassifikations-Score (Grün/Gelb/Rot) abgefangen und entweder in die manuelle Review-Queue gegeben oder zurück an den Autor verwiesen. Auf der öffentlichen Plattform sind ausschließlich PMs ab Stufe Standard sichtbar.
Schwellenwerte sind als Anker zu verstehen und werden nach 100200 echten PMs anhand der tatsächlichen Score-Verteilung kalibriert.
### Sichtbarkeit pro Stufe
Nicht jede Stufe wird gleich behandelt. Standard wird nicht beworben weder als Badge noch als Label. Erst ab Stufe Geprüft erscheint sichtbar ein Vertrauensindikator. So wirkt das System nicht wie ein Stigma für Standard-PMs, sondern wie eine Auszeichnung für die besseren.
|Stufe|Auf Detailseite|In Listen|Im Newsletter-Filter|
|---|---|---|---|
|Standard|nichts angezeigt|nichts angezeigt|enthalten in "alle Meldungen"|
|Geprüft|Häkchen-Icon mit Tooltip "Geprüfte Pressemitteilung"|optional kleines Häkchen-Icon|"Geprüfte Pressemitteilungen"|
|Hochwertig|Label "Hochwertig" mit Stern-Icon|Stern-Icon neben Headline|"Hochwertige Pressemitteilungen"|
### Wo die Punktzahl sichtbar bleibt
Die genaue Punktzahl bleibt produktiv im geschützten Bereich:
**Im Editor während des Schreibens:**
> _"Aktueller Score: 67/100 Stufe: Geprüft. Noch 13 Punkte bis 'Hochwertig'. So verbessern Sie Ihre Pressemitteilung:_ _• Bild hinzufügen (+510 Punkte)_ _• Zitat einbauen (+35 Punkte)_ _• Lead-Absatz präziser fassen (+24 Punkte)"_
**Im Publisher-Dashboard:**
- Score pro PM mit Stufenanzeige
- Durchschnittsscore über alle PMs
- Trend über Zeit
- Score-Breakdown nach Faktoren
**In Boost-Buchungs-Dialogen:**
> _"Der Top-Slot Startseite erfordert Stufe 'Hochwertig' (Score 80+). Ihre Pressemitteilung erreicht aktuell Stufe 'Geprüft' (Score 67). So erreichen Sie 'Hochwertig':_ _[Pressetext-Optimierung 15 Credits, +1520 Punkte]_ _[Headline-Booster 5 Credits, +37 Punkte]"_
---
## 16. Boost-Eligibilität (überarbeitet)
Boost-Schwellen werden von konkreten Punktzahlen auf Stufen umgestellt. Die Schwellen werden Plattform-intern weiterhin auf Score-Ebene geprüft, kommuniziert wird gegenüber Publishern aber die Stufe.
### Schwellenwerte je Slot-Typ
|Slot|Klassifikation|Mindeststufe|Entspricht Score|
|---|---|---|---|
|Highlight Kategorie|Grün|Standard|≥ 30|
|Startseite-Highlight (24h / 3T)|Grün|Geprüft|≥ 60|
|Top-Slot Startseite|Grün|Hochwertig|≥ 80|
|Newsletter-Erwähnung|Grün|Geprüft|≥ 60|
|Social-Share (offizieller Kanal)|Grün|Geprüft|≥ 60|
|Verteiler-Versand (extern)|Grün|Hochwertig|≥ 80|
PMs mit Klassifikation Gelb können nicht boostbar werden, auch nicht nach manueller Freigabe sie bleiben in regulärer Sichtbarkeit. PMs mit Klassifikation Rot werden nicht veröffentlicht.
### UI-Logik beim Buchen
Wenn ein User einen Boost-Slot bucht, dessen Schwelle seine PM nicht erreicht, sieht er statt des Buchungsformulars die Tool-Empfehlungen aus dem Editor-Dialog. Der konkrete Score wird hier sichtbar, weil der Publisher die Distanz zur nächsten Stufe verstehen muss, um eine wirtschaftliche Entscheidung zu treffen (Tool kaufen oder anders boosten).
---
## Differenzierung: Hochwertig-Stufe vs. Editorial-Pick
Beide sind Vertrauenssignale, aber konzeptionell unterschiedlich. Sie müssen visuell und sprachlich klar unterscheidbar sein.
||Hochwertig-Stufe|Editorial-Pick|
|---|---|---|
|**Vergabe**|algorithmisch (Score ≥ 80)|manuell durch Redaktion|
|**Voraussetzung**|Content-Score|freie redaktionelle Auswahl|
|**Häufigkeit**|viele PMs|wenige, ausgewählte PMs|
|**Bezeichnung**|"Hochwertig"|"Auswahl der Redaktion"|
|**Symbol**|Stern-Icon|orange Auszeichnung (wie auf Startseite)|
|**Funktion**|Qualitätsindikator|redaktionelle Empfehlung|
Eine PM kann beide Auszeichnungen gleichzeitig tragen. In der Regel werden Editorial-Picks aus dem Pool der Hochwertig-PMs gewählt, aber das ist nicht zwingend die Redaktion hat freie Hand.
---
## Außenkommunikation Konkrete Labels und Texte
Damit das System Plattform-weit konsistent kommuniziert wird, hier die verbindlichen Texte und Labels:
### Detailseite Metadaten-Zeile
Statt bisher "Qualität: Sehr hoch (94)":
- **Standard:** kein Label
- **Geprüft:** Häkchen-Icon ✓ mit Tooltip "Geprüfte Pressemitteilung redaktionelle Standards eingehalten"
- **Hochwertig:** Stern-Icon ★ mit Label "Hochwertig" und Tooltip "Pressemitteilung mit besonders hoher Qualität"
### Newsletter-Block
Statt "Nur Meldungen ab Content-Score 80":
- **Tageszusammenfassung:** "Alle wichtigen Meldungen aus DACH"
- **Wochenrückblick:** "Die wichtigsten Meldungen der Woche"
- **Branchen-Alerts:** "Höchstens 2 Mails pro Woche. Nur hochwertige Pressemitteilungen aus dieser Branche."
### Erweiterte Suche Filter
Filter-Optionen:
- "Alle Pressemitteilungen"
- "Nur geprüfte Pressemitteilungen"
- "Nur hochwertige Pressemitteilungen"
- "Nur Auswahl der Redaktion"
### Vertrauens-Sektion auf der Plattform
Eine Erklärseite (z.B. /redaktion oder /qualitaet) erläutert das System öffentlich:
> _"Jede Pressemitteilung auf businessportal24 wird automatisch auf Qualität geprüft. Wir unterscheiden drei Stufen:_
>
> _• **Standard** erfüllt unsere Mindestanforderungen für Pressemitteilungen_ _• **Geprüft** solide Pressemitteilung mit guter Substanz_ _• **Hochwertig** Pressemitteilung in redaktioneller Spitzenqualität_
>
> _Zusätzlich vergibt unsere Redaktion das Sigel 'Auswahl der Redaktion' für Pressemitteilungen, die wir besonders empfehlen."_
---
## Auswirkungen auf das Datenmodell
Geringe Änderungen, da Score weiterhin intern als Zahl gespeichert wird:
```
press_releases (Ergänzung)
- + content_tier (enum: standard, gepruft, hochwertig)
wird automatisch aus content_score abgeleitet
kann als generated column oder per Trigger gepflegt werden
- + editorial_pick (bool, default false)
- + editorial_pick_at, editorial_pick_by (für Audit)
```
Die Stufen-Schwellen werden in einer zentralen Konfiguration gepflegt (z.B. `config/scoring.php` in Laravel), damit sie bei späterer Kalibrierung an einer Stelle anpassbar sind, ohne Code-Änderungen.
---
## Was unverändert bleibt
- **Content-Score (0100)** wird intern weiterhin berechnet und gespeichert
- **Score-Faktoren und Gewichtungen** bleiben wie in Abschnitt 15.2 des ersten Updates definiert
- **Klassifikations-Score (Grün/Gelb/Rot)** bleibt unverändert als Eintritts-Filter
- **Trust-Score** auf Account-Ebene bleibt unverändert
- **Tool-zu-Algorithmus-Loop** funktioniert identisch, nur mit Stufen statt Punktzahlen in der Außenkommunikation
- **Datenmodell** für `content_scores`, `placements`, `placement_inventory` bleibt unverändert
---
## Migrationsschritt für bestehendes Mockup
Die Detailseite-Mockup-Version vom 7. Mai zeigt aktuell "Qualität: Sehr hoch (94)" in der Metadaten-Zeile. Anpassung:
- Text "Qualität: Sehr hoch (94)" entfernen
- Stattdessen: Stern-Icon ★ + Label "Hochwertig" (für Stufe Hochwertig)
- Tooltip beim Hover über das Label
- Konsistenz prüfen mit Newsletter-Sidebar-Block (auch dort Score-Zahl entfernen, durch "hochwertige Pressemitteilungen" ersetzen)
---
## Offene Punkte / nächste Entscheidungen
- **Schwellenwerte kalibrieren:** Erst nach 100200 echten PMs sehen, wo die Score-Verteilung liegt. Schwellen ggf. anpassen, sodass Standard ca. 4050 %, Geprüft ca. 3545 %, Hochwertig ca. 1020 % der PMs umfasst (Anhaltswert).
- **Visuelle Symbole final wählen:** Stern für Hochwertig, Häkchen für Geprüft Alternativen prüfen (z.B. Medaille, Auszeichnungs-Symbol). Konsistenz mit bestehendem Icon-Set wahren.
- **Editorial-Pick-Symbol final festlegen:** Orange Auszeichnung wurde auf Startseite genutzt muss klar unterscheidbar bleiben vom Hochwertig-Symbol.
- **Tooltip-Texte feinjustieren:** kurze, prägnante Erklärung pro Stufe, übersetzungsfähig.
- **Erklärseite /redaktion oder /qualitaet:** als Vertrauens-Anker für Leser und Suchmaschinen erstellen.

View file

@ -0,0 +1,178 @@
**Datum:** 12. Mai 2026 **Status:** Strategie-Dokument **Vorgänger:** Konzept-Updates 1 (Score-Architektur), 2 (Score-Stufen), 3 (Multi-Brand-Architektur) **Bezug:** Presseportal-Strategie (lebendes Strategiepapier, Mai 2026)
---
## 1. Ausgangslage
Die Multi-Brand-Architektur (Update 3) hat die technische Trennung von Hub und Brand-Portalen geklärt. Offen blieb die inhaltliche Frage: **Wofür stehen die beiden Marken konkret und was dürfen sie versprechen?**
Frühere Positionierungs-Entwürfe haben Versprechen formuliert, die in einem Solo-Entwicklungs-Setup nicht eingehalten werden können:
- „Exklusive Analysen, Interviews, kuratierte Nischen-Informationen" für presseecho.de setzt redaktionelle Arbeit voraus, die nicht geleistet wird.
- „Breite öffentliche Wahrnehmung und Lead-Generierung" für businessportal24.com ist ein Reichweiten-Versprechen, mit dem große Player mit ganz anderen Budgets vermarkten.
Beide Formulierungen folgen dem Muster „großes Presseportal kopiert" genau das Anti-Pattern, das die übergeordnete Strategie ausschließt. Die Positionierung muss aus dem entstehen, was tatsächlich anders gemacht wird: Archiv-Stabilität, Qualitätsschwelle, faire Konditionen, Themen-Tiefe statt Timeline-Hype.
**Zusätzliche Einschränkung:** Beide Portale ziehen am Start aus demselben Migrations-Pool. Eine inhaltliche Differenzierung über getrennte Redaktionen oder exklusive Inhalte ist am Start nicht ehrlich darstellbar.
## 2. Leitidee: Differenzierung über Leseparadigma
Da die Inhalte zu Beginn identisch sind, wird die Differenzierung **nicht über das Was, sondern über das Wie** aufgebaut:
||businessportal24|presseecho|
|---|---|---|
|**Leitfrage**|„Was ist aktuell bei Unternehmen los?"|„Was läuft in dieser Branche / zu diesem Thema?"|
|**Logik**|Aktualitäts-/Zeitachsen-Logik|Themen-/Cluster-/Archiv-Logik|
|**Lesemodus**|„Was ist neu?"|„Was gibt es dazu?"|
|**Wert für Leser**|Übersicht über aktuelle Geschehnisse|Tiefe und Kontext zu einem Thema|
|**Wert für Publisher**|Sichtbarkeit _jetzt_|Auffindbarkeit _dauerhaft_|
Diese Differenzierung ist:
- **Solo-tauglich**: eine Codebasis, ein Inhalts-Pool, zwei Präsentations-Logiken
- **Ehrlich**: beide Portale liefern echten Mehrwert, nur für unterschiedliche Lesebedürfnisse
- **Selbstverstärkend**: mit der Zeit entscheiden Publisher selbst, welche Brand zu ihrem Inhalt passt die Differenzierung wächst organisch
## 3. businessportal24.com Positionierung
### Kern-USP
> _„Die Wirtschaftspresse für den deutschen Mittelstand aktuell, transparent, ohne Reichweiten-Marketing. Was hier veröffentlicht wird, ist nach Qualität geprüft und bleibt dauerhaft auffindbar."_
### Zielgruppen
**Publisher-Seite:** Mittelständische Unternehmen, Selbstständige, kleinere PR-Agenturen, regionale Akteure. Charakteristisch: Sie suchen _eine_ zuverlässige Veröffentlichungs-Adresse, nicht zehn Streuverteiler.
**Leser-Seite:** Journalisten und Multiplikatoren, die wissen wollen, was bei KMU gerade passiert; Wirtschaftsinteressierte; lokale/regionale Recherche.
### Thematischer Schwerpunkt
Unternehmensmeldungen, neue Produkte, Personalia, Standorte, Aufträge, Auszeichnungen, Wirtschaftsthemen aus der Breite des Mittelstands.
### Tonalität
Aktiv, klar, wirtschaftsnah, zugänglich. Nicht hip, aber lebendig. Erwachsen, nicht laut.
### Farbwelt
Energetisches Orange/Rot als Akzent auf neutraler Basis. Passt zum Profil „aktuell, lebhaft, KMU-aktiv". Wichtig: **keine SaaS-Landingpage-Gradienten**, sondern zurückhaltend eingesetzte Akzente. Das Wirtschafts-Charakter braucht typografische Dichte, keine Lifestyle-Optik.
### Was hier _nicht_ versprochen wird
- Reichweite oder Reichweiten-Garantien
- Lead-Generierung
- „Virale Verbreitung"
- SEO-Versprechen
- Exklusive Inhalte
## 4. presseecho.de Positionierung
### Kern-USP
> _„Das Branchenportal mit Themen-Gedächtnis wo Pressemitteilungen nicht in der Timeline verschwinden, sondern dauerhaft in Themen- und Branchenkontexten zugänglich bleiben."_
### Zielgruppen
**Publisher-Seite:** Unternehmen mit fachlich-spezifischer Kommunikation, B2B-Anbieter, Branchen-Akteure. Charakteristisch: Sie wollen, dass ihre Meldung auch in zwei Jahren noch im Themen-Kontext gefunden wird nicht nur 48 Stunden im Strom mitschwimmen.
**Leser-Seite:** Fachjournalisten, Branchenrecherche, Analysten, Studierende alle, die zu einem Thema _in die Tiefe_ gehen wollen statt nur „was war heute?".
### Thematischer Schwerpunkt
Am Start identisch zum gemeinsamen Pool. Differenzierung über die **Präsentation**: Themen- und Branchen-Cluster, sichtbare Archivtiefe, „dazu erschien auch …", längere Lese-Pfade, Themen-Historie.
### Tonalität
Ruhig, sachlich, expertenorientiert aber nicht steif. Eher „Fachbuchhandlung" als „Lifestyle-Magazin".
### Farbwelt
Dunkelgrün. Seriös, beständig, „Bibliotheks-Charakter". Idealer Kontrast zur aktiven Orange-Welt von businessportal24.
### Der ehrliche Mehrwert
Das ~10 Jahre alte Archiv mit knapp 100.000 Mitteilungen wird hier zum _Feature_, nicht zur Altlast. presseecho.de ist faktisch die Marke, in der die **Archivtiefe sichtbar** wird und damit das Asset, das in der Strategie als wichtigster Differenzierungs-Vorteil identifiziert wurde.
### Was hier _nicht_ versprochen wird
- Exklusive redaktionelle Inhalte
- Eigene Interviews oder Analysen
- Kuratierte Auswahl durch eine Redaktion
- Branchen-Newsletter mit Mehrwert über die Pressemitteilungen hinaus
- Experten-Bewertungen / -Rankings
Wenn diese Versprechen später eingelöst werden sollen, müssen sie _vorher_ operativ verfügbar sein nicht umgekehrt.
## 5. Markenarchitektur im Verhältnis zueinander
Die beiden Brands sind nicht Konkurrenz, sondern **komplementär**. Beim Veröffentlichen erhält der Publisher eine ehrliche Entscheidungshilfe:
- _„Mein Thema ist aktuell, ich will Sichtbarkeit jetzt"_ → businessportal24
- _„Mein Thema ist fachlich, ich will dauerhafte Auffindbarkeit im Kontext"_ → presseecho
- _„Beides"_ → Cross-Publishing (gegen Aufpreis im Credit-System)
Das erfüllt drei Funktionen gleichzeitig:
1. **Ehrliche Beratung** gegenüber dem Kunden keine künstliche Verknappung
2. **Verkaufslogik** für Cross-Publishing als Premium-Option
3. **Selbstverstärkende Differenzierung** Publisher sortieren ihre Inhalte selbst zur passenden Marke
## 6. Migrationspfad der Differenzierung
Die Differenzierung wird **nicht zum Start vollständig** geliefert. Sie wächst in drei Phasen, im Einklang mit der Anti-Zombie-Regel „nichts versprechen, was am Start nicht verfügbar ist".
### Phase 1 Migration (Monate 16)
Beide Portale zeigen praktisch dieselben Inhalte. Differenzierung lebt nur über:
- **Marken-Identität**: Logo, Farbwelt, Tagline, Tonalität der Marketing-Texte
- **Navigations-Logik**: Timeline-first (businessportal24) vs. Themen-first (presseecho)
- **Landing-Page-Sprache**: unterschiedliche Wertversprechen, gleiche Backend-Mechanik
Mehr ist in dieser Phase nicht ehrlich darstellbar.
### Phase 2 Aufbau (Monate 618)
- Cross-Publishing wird kostenpflichtig
- Default-Publishing geht zu _einer_ Brand Publisher entscheiden bewusst, wo es passt
- Erste organische Inhalts-Drift entsteht (regional/aktuell → businessportal24; fachlich/dauerhaft → presseecho)
- Brand-spezifische Navigations-Features werden ausgebaut (Themen-Cluster bei presseecho, Aktualitäts-Filter bei businessportal24)
### Phase 3 Reife (ab Monat 18+)
- presseecho.de hat erkennbare Themenwelten mit Archivtiefe
- businessportal24.com hat erkennbares „Was-ist-neu"-Profil
- Eventuell brand-spezifische Kleinfeatures: Themen-Newsletter bei presseecho, Branchen-Filter bei businessportal24
- Erst hier können erweiterte Markenversprechen formuliert werden und nur, wenn sie operativ tatsächlich abgedeckt sind
## 7. Verbindung zur Gesamtstrategie
Diese Positionierung folgt konsequent den Leitlinien aus dem Strategiepapier:
- **Bewusste Nische statt Konkurrenz**: kein Wettlauf mit Pressebox/openPR um Reichweite
- **Asset-Wert konservieren**: das Archiv wird über presseecho.de sichtbar gemacht statt versteckt
- **Geduld als Wettbewerbsvorteil**: Differenzierung wachsen lassen, statt am Start zu erfinden
- **Ehrliche Kommunikation**: nur versprechen, was operativ verfügbar ist
- **Konsistenz schlägt Spektakel**: ein klarer, langsam wachsender Markencharakter pro Portal
## 8. Implikationen für die nächsten Schritte
Daraus ergeben sich konkrete Aufgaben für die kommende Arbeitsphase:
- **Brand-Landing businessportal24.com/veroeffentlichen** Wertversprechen aus diesem Update, kurzer Vertriebs-Touchpoint, Übergabe in den Hub-Funnel
- **Brand-Landing presseecho.de/veroeffentlichen** Wertversprechen aus diesem Update, betont Archiv und Themen-Kontext
- **Frontend-Navigationskonzept** Timeline-first vs. Themen-first als sichtbarer Unterschied
- **Tonalitäts-Leitfaden** für Marketing-Texte je Brand (kann später wachsen, am Start reicht eine knappe Tabelle)
- **Cross-Publishing-Preislogik** im Credit-System (offener Punkt aus Update 3)
## 9. Offene Punkte
- Hub-Design-Sprache (offener Punkt aus Update 3) sollte sich farblich/atmosphärisch klar von beiden Brand-Welten unterscheiden, neutral-funktional bleiben
- Wann genau wechselt das Default-Publishing-Modell von „zu beiden Brands" auf „zu einer Brand" (Phase-1- vs. Phase-2-Übergang)
- Soll es eine sichtbare Verbindung der beiden Marken nach außen geben (z.B. „ein Service der Pressekonto-Familie") oder bleiben sie öffentlich strikt getrennt?
---
_Dieses Update ist Teil der Reihe lebender Konzept-Dokumente. Es legt fest, was die beiden Marken am Start versprechen dürfen und damit gleichzeitig, was sie noch nicht versprechen. Änderungen an dieser Positionierung sollten dokumentiert und mit dem Strategiepapier abgeglichen werden._

View file

@ -0,0 +1,166 @@
# businessportal24.com/veroeffentlichen
**Datum:** 12. Mai 2026 **Status:** Implementierungs-Konzept **Bezug:** Konzept-Update 3 (Multi-Brand-Architektur), Konzept-Update 4 (Positionierung & Markenversprechen)
---
## 1. Strategische Verortung
Diese Landing ist **kein Funnel** im klassischen SaaS-Sinn. Sie ist ein **kurzer Vertriebs-Touchpoint**, der drei Dinge leistet:
1. Den Markencharakter von businessportal24 vermitteln (aktuell, KMU-nah, erwachsen)
2. Vertrauen für die Veröffentlichungs-Entscheidung aufbauen
3. Sauber in den **Hub-Funnel** auf pressekonto.de übergeben, wo Account, Credits, Abrechnung und Tools liegen
Was hier _nicht_ hingehört: ausführliche Preistabellen, mehrstufige Onboarding-Flows, Account-Verwaltung, Dashboard-Vorschauen. Das ist Hub-Territorium.
**Tonalität:** wirtschaftsnah, klar, sachlich-lebendig. Nicht Marketing-Sprech. Nicht „SaaS-Begeisterung". Eher Tageszeitungs-Wirtschaftsteil als Stripe-Landingpage.
**Visuelle Linie:** typografische Dichte statt Whitespace-Verschwendung. Orange/Rot als **Akzent**, nicht als flächiger Gradient. Editorial-Anmutung: schmale Spalten, klare Hierarchie, sachliche Bildwelt (echte Pressefotos / Unternehmensfotos, keine generischen Stock-Illustrationen).
## 2. Seitenstruktur
### Above the fold
**Hero-Block**
- **H1** (eine der Varianten):
- „Pressemitteilung veröffentlichen. Geprüft. Dauerhaft auffindbar."
- „Die Wirtschaftspresse für den deutschen Mittelstand."
- „Veröffentlichen, wo Mitteilungen nicht verschwinden."
- **Sub** (USP-Kern, max. 2 Sätze): _„businessportal24 ist die Presseplattform für mittelständische Unternehmen, Selbstständige und PR-Agenturen. Jede Veröffentlichung wird auf Qualität geprüft und bleibt dauerhaft im Archiv auffindbar."_
- **Primary CTA**: „Jetzt veröffentlichen" → führt in den Hub-Funnel (pressekonto.de), Cross-Domain-Auth via Sanctum
- **Secondary CTA**: „So funktioniert's" → Scroll-Anker zum „Ablauf"-Abschnitt
**Was bewusst fehlt im Hero:**
- Kein „Jetzt 14 Tage kostenlos testen"
- Kein „Reichweite für Ihre Marke"-Versprechen
- Kein Hero-Bild mit lächelnden Stockfoto-Menschen
- Kein riesiger orangener Gradient-Banner (die SaaS-Optik, die im aktuellen Redesign-Feedback bemängelt wurde)
### Trust-Block: Was uns ausmacht
Vier knappe Kacheln, jeweils mit Headline + 12 Sätzen. Keine großen Icons (oder nur sehr zurückhaltend, monochrom). Editorial-Look statt Marketing-Look.
1. **Geprüfte Qualität** _„Jede Mitteilung durchläuft eine Qualitätsprüfung, bevor sie online geht. Keine SEO-Spam-Texte, keine reinen Werbeanzeigen."_
2. **Dauerhaft auffindbar** _„Pressemitteilungen bleiben im Archiv auch nach Jahren. Über 100.000 Mitteilungen aus mehr als einem Jahrzehnt sind weiterhin abrufbar."_
3. **Faire Konditionen** _„Transparente Preise, kein Abo-Zwang, keine Vertragsfallen. Sie zahlen, was Sie veröffentlichen."_
4. **Korrektur statt Löschung** _„Fehler in einer Mitteilung? Wir korrigieren statt zu löschen damit Verweise und Verlinkungen bestehen bleiben."_
### Ablauf: So funktioniert's
Drei Schritte, knapp gehalten. Bewusst entmystifiziert keine „Magic"-Sprache.
1. **Konto anlegen** mit E-Mail-Adresse. Kein Passwort nötig, Login per Magic-Link.
2. **Mitteilung einreichen** Text, Bild, Ansprechpartner. Eine Qualitätsprüfung läuft automatisch und durch eine kurze redaktionelle Sichtung.
3. **Veröffentlichung** nach Freigabe online und im Archiv. Bei Bedarf jederzeit korrigierbar.
Hinweis am Ende des Blocks (klein, sachlich): _„Die Veröffentlichung erfolgt über den zentralen Publisher-Bereich auf pressekonto.de."_ das setzt die Hub-Architektur transparent.
### Für wen das richtig ist
Kurzer Abschnitt mit konkreten Anlässen statt abstrakter Zielgruppen-Beschreibung. Macht die Eignung selbst-evident.
_„Typische Pressemitteilungen auf businessportal24:_
- _Neue Produkte oder Dienstleistungen_
- _Personalien und Geschäftsleitungs-Wechsel_
- _Auszeichnungen und Zertifizierungen_
- _Standort-Eröffnungen, Expansionen, Aufträge_
- _Veranstaltungs-Ankündigungen_
- _Studien und Marktanalysen aus Unternehmenshand"_
Anschließend ein Satz zur Eingrenzung das ist wichtig für die Qualitätsschwelle:
_„Nicht geeignet sind reine Werbeanzeigen, SEO-Linkbuilding-Texte oder Inhalte ohne nachvollziehbaren Pressewert."_
### Preise (kurz, mit Verweis)
Hier **kein** vollständiges Preismodell. Stattdessen:
- Ein Satz zur Logik: _„Sie kaufen Credits, die für Veröffentlichungen eingesetzt werden. Keine Mindestlaufzeit, keine versteckten Gebühren."_
- Ein Preis-Anker, damit der Besucher nicht ratlos bleibt: _„Eine Veröffentlichung ab X € Mengenrabatte verfügbar."_ (Konkreter Wert nachträglich)
- Link zum Hub: _„Vollständige Preisübersicht im Publisher-Bereich →"_
### Kurz-FAQ
Maximal 45 Fragen. Direkt, ohne Marketing-Worte.
- _„Wie schnell wird meine Mitteilung veröffentlicht?"_ Werktags üblicherweise innerhalb von 24 Stunden nach Einreichung.
- _„Bleibt meine Mitteilung dauerhaft online?"_ Ja. Mitteilungen werden nicht gelöscht. Korrekturen sind jederzeit möglich.
- _„Kann ich auch auf presseecho.de veröffentlichen?"_ Ja, über den zentralen Publisher-Bereich. Cross-Publishing ist optional verfügbar.
- _„Brauche ich ein Abo?"_ Nein. Sie kaufen Credits nach Bedarf, ohne Vertragsbindung.
- _„Was passiert bei einem Fehler in der Mitteilung?"_ Korrektur statt Löschung: Inhalte werden aktualisiert, die URL bleibt erhalten.
### Final CTA
Schlicht. Kein „Letzte Chance"-Pathos.
- **H2**: „Bereit zu veröffentlichen?"
- **Button**: „Konto anlegen" → Hub
- **Klein darunter**: „Oder zuerst Beispiele ansehen →" (Link zur Startseite des Portals)
### Footer
Standard, aber mit zwei brand-spezifischen Elementen:
- Hinweis auf den Betreiber: _„businessportal24 ist ein Service der Pressekonto-Gruppe."_ (oder ähnlich siehe offener Punkt aus Update 4)
- Cross-Link zu presseecho.de (dezent, eine Zeile): _„Für fachlich-spezifische Themen: presseecho.de"_
## 3. Visuelle Leitlinien
### Was diese Seite tun soll
- **Editorial wirken**, nicht promotional. Vorbild: Wirtschaftsteil einer überregionalen Zeitung, nicht Stripe/Linear/Notion.
- **Typografisch arbeiten**: klare Hierarchien, ausreichend Dichte, lesbare Spaltenbreiten (~6575 Zeichen).
- **Farben sparsam**: Orange/Rot als Akzent (Links, primärer CTA, dünne Trennlinien, gelegentliche Rubriken-Marker). Keine flächigen Gradienten, keine farbigen Cards als Hintergrund.
- **Sachlichkeit ausstrahlen**: Pressemitteilungen sind ein seriöses Produkt. Optik muss das transportieren.
### Was diese Seite _nicht_ tun darf
- **Keine** Hero-Gradienten in Orange (siehe Feedback zum ersten Redesign-Versuch)
- **Keine** generischen Briefkasten-/Megafon-Icons
- **Keine** „lifestyle"-Bildwelt mit lächelnden Menschen am Laptop
- **Kein** SaaS-Vokabular („Plattform", „Solution", „Empower your communications")
- **Keine** Pseudo-Trust-Elemente wie „4.9 Sterne", „10.000 zufriedene Kunden" wenn es echte Zahlen gibt, dann nüchtern darstellen
### Layout-Empfehlung
- **Maximalbreite** ~11001200px, zentriert, mit ausreichend Seitenrand
- **Hero** kompakt, nicht ganzseitig hoch Pressekontext heißt: Information schnell sichtbar
- **Sektions-Trenner** über Typografie und schmale Linien, nicht über farbige Background-Bänder
- **Mobile**: einspaltig, gleiche Reihenfolge, Hero noch kompakter
## 4. Technische Hinweise
- **Routing**: `businessportal24.com/veroeffentlichen` rendert eine eigene Brand-Landing-Route, die das geteilte Backend nutzt aber brand-spezifische Templates lädt
- **CTA-Übergabe**: Klick auf „Jetzt veröffentlichen" / „Konto anlegen" leitet zum Hub (`pressekonto.de/publisher/start?brand=businessportal24`), damit der Hub weiß, welche Brand der Einstiegspunkt war (für Default-Brand-Zuordnung und ggf. Tracking)
- **Cross-Domain-Auth**: Wie in Update 3 festgelegt via Sanctum. Wenn der Besucher bereits eingeloggt ist, springt er direkt ins Dashboard, sonst in den Onboarding-Flow
- **Tracking**: Brand-Landing trackt eigenen Funnel-Eintritt, der Hub übernimmt ab dort. So bleibt nachvollziehbar, welche Brand-Landing welche Conversion-Rate hat
## 5. Copy-Hinweise zur weiteren Verwendung
Die Copy-Vorschläge in Abschnitt 2 sind **Ausgangsmaterial**, nicht final. Sie verkörpern aber die Tonalität, die in Update 4 festgelegt wurde:
- aktiv, klar, wirtschaftsnah, zugänglich
- keine Reichweiten- oder Lead-Versprechen
- konkrete Beispiele statt abstrakte Zielgruppen-Floskeln
- ehrliche Eingrenzung (das „Nicht geeignet"-Statement)
Beim Schreiben weiterer Texte gilt: Im Zweifel **eine Aussage weniger** statt eine Aussage mehr. Pressecharakter heißt zurückhaltend, nicht prahlend.
## 6. Offene Punkte
- Konkrete Preis-Anker (X € pro Veröffentlichung) sobald die Preislogik aus Update 3 finalisiert ist
- Beispiel-Mitteilungen zum Anzeigen auf der Landing sinnvoll oder unnötig? (Die Startseite tut das ohnehin)
- Soll der Footer-Hinweis auf presseecho.de prominent oder eher unauffällig sein? Hängt an der Grundsatzentscheidung aus Update 4 (sichtbare Markenfamilie vs. strikt getrennt)
- A/B-Test-Strategie für die drei H1-Varianten sinnvoll erst, wenn Mindest-Traffic erreicht ist
---
_Dieses Konzept ist eine konkrete Implementierungs-Vorlage. Es schöpft die Positionierung aus Update 4 in seitenstrukturelle Form und benennt explizit, was diese Landing nicht leisten soll um die Anti-Zombie-Regel auf operativer Ebene umzusetzen._

View file

@ -0,0 +1,417 @@
# User Backend und Admin Backend
> **Stand der Doku**: 21.05.2026 — Phase 1 + Phase 7 (PM-Form-Refactor) sind umgesetzt.
> Aktueller Code-vs-Konzept-Abgleich: [`docs/STATUS-ABGLEICH-USER-PANEL.md`](../STATUS-ABGLEICH-USER-PANEL.md).
> Plan der naechsten Schritte: [`docs/PHASE-8-USER-PANEL-PLAN.md`](../PHASE-8-USER-PANEL-PLAN.md).
Dieses Konzept beschreibt das gemeinsame Backend aus zwei Perspektiven:
- **User Backend**: Self-Service-Bereich für Kunden/User zur Pflege eigener Firmen, Kontakte, Pressemitteilungen, Rechnungen, API-Tokens und Einstellungen. „Pressemappe" bleibt als öffentlicher/PR-bezogener Kontext erhalten, nicht als Hauptnavigation im User Backend.
- **Admin Backend**: Verwaltungsbereich für interne Admins/Editoren. Die bestehende Admin-Oberfläche bleibt in Phase 1 unverändert.
Beide Bereiche laufen technisch im gleichen Backend. Die sichtbaren Menüs, Aktionen und Daten werden über Rollen, Policies und Berechtigungen getrennt. Admins können sich über Impersonation als User einloggen, um dessen User-Backend-Sicht nachzuvollziehen und Inhalte zu prüfen oder zu korrigieren.
> Hinweis Routen-Namen: In der UI heißen die Firmen ueberall „Firmen". Aus
> historischen Gruenden tragen die zugehoerigen Routen weiterhin den
> Praefix `me.press-kits.*` (z. B. `me.press-kits.show`). Das ist nur
> ein Routen-Name, fachlich sind es Firmen. Eine Umbenennung der Routen
> ist nicht geplant, weil sie nur intern relevant ist.
Vorab siehe hierzu folgende Mechanik für Untermenüs https://pressekonto.test/settings/profile
# Aktualisierte Navigation
## Phasen-Farbcode
Für die weitere Planung werden Features farblich/phasenbasiert getrennt:
- **Grün / Phase 1**: auf dem bestehenden Datenmodell kurzfristig umsetzbar.
- **Gelb / Phase 2**: braucht kleinere neue Tabellen, Policies oder Services.
- **Rot / Später**: strategische Produkt-/Monetarisierungsthemen mit größerem Datenmodell- oder Rechtsaufwand.
## Umsetzungsstand Phase 1
Bereits umgesetzt:
- Firmen-Kontext-Switcher im User Backend mit „Alle Firmen" und Einzelfirma, platziert rechts in der Topbar.
- User-Backend-Navigation gegliedert in „Mein Bereich", „Finanzen" und „Konto".
- „Buchungen & Add-ons" ist als vorbereiteter Bereich eingebunden; Statistiken, Credits/Tarif, Zahlungsarten und Benachrichtigungen bleiben markierte spätere Punkte.
- Dashboard und PM-Liste reagieren auf den aktiven Firmen-Kontext.
- Firmen-Liste und Firmen-Detail auf Basis der bestehenden `companies`, `contacts` und `press_releases`.
- Zugriff auf Firmen ist auf eigene bzw. zugeordnete Firmen begrenzt.
- Öffnen einer Firma setzt die aktive Firma für den weiteren User-Backend-Kontext.
- Kontaktverwaltung innerhalb der Firma für Owner und Verantwortliche; Mitglieder bleiben lesend.
- Neue Pressemitteilungen übernehmen die aktive Firma als Vorauswahl.
- PM-Detail zeigt zugeordnete Pressekontakte sowie Status-/Verlaufsdaten.
- Rechnungen sind im User Backend in einer eigenen Finanznavigation eingeordnet; Legacy bleibt als Archivhinweis im Inhalt sichtbar.
- Rechnungen zeigen einen Hinweisblock; Rechnungsadresse wird im Profil als eigener Bereich gepflegt.
- Firmen-Stammdaten werden sichtbar in der Firma gepflegt; die Profilseite verweist nur noch auf die jeweilige Firma.
- Dashboard zeigt erste Datenqualitäts-Hinweise aus bestehenden Tabellen: Profil, Rechnungsadresse, Pressekontakte und Legacy-PMs ohne Firma.
Noch offen in Phase 1:
- Keine offenen Punkte aus der ersten grünen User-Backend-Ausbaustufe.
## Umsetzungsstand Phase 7 (PM-Form-Refactor)
Phase 7 ist mit Stand 21.05.2026 abgeschlossen. Sie hat das Form-Erlebnis für Pressemitteilungen vereinheitlicht und das Datenmodell um mehrere Felder erweitert. Details: `dev/frontend/hub-flux/19-PHASE-7-PRESS-RELEASE-FORM.md`.
Zusammenfassung:
- **Neue PM-Felder**: `subtitle`, `scheduled_at`, `embargo_at`, `boilerplate_override`, `no_export`. Migrationen liegen in `database/migrations/2026_05_20_*`.
- **HTML-Sanitizer**: Inhalt wird serverseitig durch `mews/purifier` gereinigt (`App\Services\PressRelease\PressReleaseHtmlSanitizer`).
- **Sidebar-Aufbau** in Customer- und Admin-Forms identisch (Status & Absenden, Kategorie, Portal-Pill, Pressekontakt, Themen-Tags, Veröffentlichung, Weitere Felder, Phase-2-Footer).
- **Pressekontakt-Pflichtfeld** aufgehoben — Auswahl bleibt empfohlen, ist aber technisch nullable. Eine Warn-Box im Sidebar-Card (Phase 8) macht das transparent.
- **Anhänge-/Downloads-UI deaktiviert** wegen ausstehendem Security-Review. Tabelle `press_release_attachments` und Manager-Komponente bleiben erhalten.
- **Background-Job** `php artisan press-releases:publish-scheduled` veröffentlicht geplante PMs (alle 5 Minuten via Scheduler).
- **UX**: `Flux::toast()` für alle Erfolg/Fehler-Meldungen, Smooth-Scroll zum ersten Validation-Fehler nach Save, `presubmitChecks` als kompakte Pflichtfeld-Übersicht im Sidebar.
## Geplante Phase 8
Plan-Doku: `docs/PHASE-8-USER-PANEL-PLAN.md`. Schwerpunkte:
1. Show-Page-Lücken aus Phase 7 schließen (Subtitle, Scheduling, Embargo, Boilerplate-Override).
2. Listen-Indikatoren für geplante Veröffentlichung und Embargo.
3. Firmen-Liste auf Mockup-Niveau (Counter-Strip, Saved-Views, Filter-Chips, Card/List-Toggle, Rollen-Legende).
4. SVG-Platzhalter-Set für PM-Titelbilder + Auswahl-Modal.
5. FluxUI `flux:file-upload` im Image-Manager inkl. Pflichtfeldern für Urheber/Lizenz/Rechte.
6. Veröffentlichungs-Modal mit rechtlichen Hinweisen und einem Kontingent-Stub (das echte Tarif-System kommt später).
## Topbar
Oben rechts über dem Content:
**Firmen-Kontext-Switcher** Dropdown „Aktive Firma: [bma.cc ▼]" mit drei Optionen. Vorteil: Die Sidebar bleibt schlank und einklappbar, ohne den aktiven Firmenkontext zu verlieren.
- Einzelne Firma wählen → filtert Dashboard, PMs, Kontakte, Statistiken auf diese Firma
- „Alle Firmen" → aggregierte Sicht
- „Firma anlegen" am Ende der Liste
Da die User-Firmen-Beziehung n:m mit Rollen ist (`member`, `responsible`, `owner`), zeige ich pro Eintrag ein dezentes Rollen-Icon. Das hilft dem User zu verstehen, wo er was darf.
Wichtig: Da das Portal über die Firma abgeleitet wird, ist der Switcher implizit auch der Portal-Switcher. Sauber gelöst, ohne zweites Konzept.
---
## Hauptnavigation (überarbeitet)
### Gruppe „Mein Bereich"
**1. Dashboard** Zusätzlich zu vorher genannten Elementen jetzt mit **Datenqualitäts-Hinweisen**, weil das Datenmodell zeigt, dass viele Pflichtfelder optional sind:
- „Rechnungsadresse fehlt Rechnungen können nicht erstellt werden"
- „3 Pressemitteilungen ohne Firmenzuordnung (Legacy)"
- „Profil unvollständig ergänzen für Verifizierung"
Diese Hinweise sind dismissible und verschwinden bei Erledigung. Das senkt deinen Support-Aufwand erheblich, weil User ihre eigenen Datenlücken sehen.
Phase: **Grün**, wenn die Hinweise auf vorhandene Tabellen beschränkt bleiben (`profile`, `billing_addresses`, `company_user`, `companies`, `press_releases`).
**2. Pressemitteilungen** Erweiterungen aus dem Datenmodell:
- **PM-Detailansicht** zeigt einen „Status & Verlauf"-Block (aus `press_release_status_logs` mit Status-Wechseln, Grund, Quelle, Zeitpunkt) — als Card auf der Show-Page, nicht als eigener Tab.
- Filter „PMs ohne Firma" (für Legacy-Migration)
- Filter „PMs mit Portalabweichung" (falls du das den Usern zeigen willst ich würde es eher in den Admin-Bereich legen)
- **Filter-Presets** (aus `user_filter_presets`): User kann seine eigenen Filter speichern, „Meine Entwürfe der letzten 30 Tage" etc.
- In der PM-Detail: zugeordnete `press_release_contact`-Kontakte als eigene Sektion
Stand 21.05.2026:
- **PM-Felder** umfassen jetzt zusaetzlich `subtitle`, `scheduled_at`, `embargo_at`, `boilerplate_override`, `no_export` (Phase 7).
- **Editor** ist `flux:editor` mit Absaetzen, fett, kursiv, Listen, Zitat, Links und Headings. Der Inhalt wird beim Speichern serverseitig durch HTMLPurifier (`mews/purifier`, gekapselt in `PressReleaseHtmlSanitizer`) bereinigt.
- **Pressekontakt-Auswahl** ist Single-Select aus den Kontakten der Firma, **optional** und mit Warn-Box, wenn leer. Das Pivot `press_release_contact` bleibt n:m, fuer den Customer-Flow wird aber maximal ein Kontakt pro PM gespeichert.
- **Anhaenge** sind im UI deaktiviert (Security-Review). Tabelle `press_release_attachments` und Service `PressReleaseAttachmentStorage` bleiben erhalten.
- **Filter-Presets** sind weiterhin **Gelb** (Tabelle existiert, UI noch nicht aktiv).
Phase: **Gruen** fuer Liste, Detail, Statusverlauf, Firmenpflicht, Untertitel, Scheduling, Embargo und Boilerplate-Override. **Gelb** fuer Filter-Presets. **Rot/Spaeter** fuer KI-Vorpruefung, Notice-and-Action und Korrektur-/Update-Hinweis-System (siehe `Presseportal Konzept für Relaunch.md`).
**3. Firmen** Klar strukturierter Detailbereich pro Firma, weil hier am meisten dranhängt:
- Tab **Stammdaten** (Firma, Logo, Branche, Footer-Code-Flag)
- Tab **Pressekontakte** wichtig: Kontakte hängen an der Firma, nicht direkt am User. Hier verwaltet der User die Liste der hinterlegten Pressekontakte. Direkte `contact_user`-Pivots würde ich für den User unsichtbar lassen, da sie eher technisches Artefakt sind.
- Tab **Pressemitteilungen** der Firma
- Tab **Statistik** der Firma
- Tab **Abrechnung** falls Zahlungsoptionen über `user_payment_option_company` an Firmen gehängt sind, hier sichtbar
- Eigentümer-Anzeige: konsolidiert aus `owner_user_id` UND `company_user.role = owner`. Falls beides existiert und divergiert → Datenqualitätshinweis (eher Admin-Thema, aber User sollte zumindest wissen, wer Owner ist)
Phase: **Grün** für Stammdaten, Kontakte, PMs und Eigentümeranzeige auf bestehendem Modell. **Gelb** für Team-Einladungen, Owner-Übertragung und firmenscharfe Abrechnung.
**4. Medien** Wie zuvor (Eigene / Stock / KI), aber Bilder kommen aus `press_release_images`. Konzeptionell sollte die Bibliothek über alle PMs/Firmen aggregieren. Wenn das Datenmodell aktuell nur 1:n PM→Bild ist, müsste das später auf eine eigenständige `media_library` mit polymorpher Verwendung in PMs umgebaut werden. Aber das ist Phase 2 fürs Konzept reicht erstmal die Aggregations-Sicht.
Phase: **Gelb** für eine aggregierte Ansicht vorhandener `press_release_images`; **Rot/Später** für eine echte wiederverwendbare Medienbibliothek.
**5. Statistiken** Reichweite/Performance aggregiert oder nach Switcher-Auswahl gefiltert.
Phase: **Grün**, soweit nur vorhandene `hits`, PM-Status und Zeiträume genutzt werden. Erweiterte Quellen, Verweildauer oder Demografie sind **Rot/Später**.
---
### Gruppe „Buchen & Bezahlen"
**6. Buchungen & Add-ons** _(neu als eigener Punkt)_ Zentraler Marktplatz für alles Verbrauchsbasierte:
- Highlights (Kategorie / Startseite / Top-Slot)
- KI-Services (Lektorat, Quality-Check, Übersetzung, Bildgenerierung)
- Premium-Stock
- Newsletter-Erwähnung
- Verteiler-Versand
- Verifiziertes Firmenprofil
- Custom Domain
Mit Tabs:
- **Verfügbar** (Marktplatz, alle buchbaren Services)
- **Aktive Buchungen** (was läuft gerade, wann endet es)
- **Verlauf** (was wurde wann gebucht)
Zusätzlich: Aus dem PM-Editor heraus immer noch der direkte „Highlight buchen"-Button als kontextueller Einstieg. Beide Wege koexistieren.
**7. Credits & Tarif** Wie zuvor, zwei Tabs.
**8. Rechnungen** Wie zuvor: aktuelle + Legacy als Archiv-Tab.
---
### Gruppe „Konto"
**9. Einstellungen** Tabs strukturiert auf Basis des Datenmodells:
- **Profil** (`profiles`-Daten: Anrede, Titel, Adresse, Geburtsdatum, Backlink, Statistik-/Footer-Code-Flags)
- **Rechnungsadresse** (`billing_addresses` getrennt von Profil, weil eigene Tabelle und oft abweichend)
- **Sicherheit** hier zeigt das Datenmodell Möglichkeiten, die du dem User geben solltest:
- Passwort & 2FA
- Aktive Sessions (`sessions`)
- **Magic-Link-Verlauf** (`magic_links` Zweck, Zeitpunkt, IP) wertvoll für Transparenz und Sicherheit
- Login-Verlauf
- **Benachrichtigungen** verbunden mit `newsletter_subscriptions`: User sieht hier seine Newsletter-Abos pro Portal, kann steuern
- **Zahlungsmethoden** (`user_payment_options` inkl. Verknüpfung zu Firmen, falls vorhanden)
- **Team** (für Agency-Tarif: `company_user`-Pivots verwalten, Rollen vergeben)
- **API & Integrationen**:
- Tokens (`personal_access_tokens` mit Berechtigungen, letzter Zugriff)
- **API-Nutzungs-Log** (`api_usage_logs` Methode, Pfad, Status, Dauer) als eigener Sub-Tab. Das ist Gold für API-User und entlastet deinen Support enorm.
- Webhooks
Phase: **Grün** für Profil, Rechnungsadresse, Sicherheit, Newsletter und API-Tokens. **Gelb** für Magic-Link-/Token-Request-Historie und API-Nutzungs-Log. **Rot/Später** für Webhooks.
Hinweis unten bei dem Namen ist ein Menü, wo auch noch einmal Settings verknüpft sind https://pressekonto.test/settings/profile
---
### Gruppe „Hilfe"
**10. Hilfe & Support** wie zuvor.
---
# Was sich konkret durch das Datenmodell geändert hat
|Feature|Wo verankert|Datenquelle|
|---|---|---|
|Datenqualitäts-Hinweise auf Dashboard|Dashboard|`profile`, `billing_address`, PM-`company_id`-Null-Checks|
|PM-Statusverlauf|PM-Detail, Tab „Verlauf"|`press_release_status_logs`|
|Filter-Presets|PM-Liste|`user_filter_presets`|
|Magic-Link-Historie|Einstellungen → Sicherheit|`magic_links`|
|API-Nutzungs-Log|Einstellungen → API|`api_usage_logs`|
|Newsletter-Abos|Einstellungen → Benachrichtigungen|`newsletter_subscriptions`|
|Pressekontakte je Firma|Firma → Bereich „Pressekontakte"|`contacts` via `company_id`|
|Eigentümer-Anzeige|Firma → Stammdaten|`owner_user_id` + `company_user.role`|
|Zahlungsoptionen pro Firma|Firma → Bereich „Abrechnung"|`user_payment_option_company`|
---
# Zwei strategische Punkte aus deinem Datenmodell, die ich aufwerfen würde
**1. Direkte `contact_user`-Pivots im User-UI verstecken** Das Datenmodell erlaubt, Kontakte direkt an User zu hängen (zusätzlich zur Pflicht-Zuordnung an eine Firma). Für den User-UI würde ich das **nicht** sichtbar machen das verwirrt. Kontakte werden über die Firma verwaltet. Punkt. Die direkte Pivot-Zuordnung kann technisch bleiben (z. B. „User darf diesen Kontakt sehen" über alle Firmen hinweg), aber UI-seitig bleibt es bei „Firma → Kontakte".
**2. PMs ohne Firma** Das Datenmodell erlaubt `company_id = null`. Im User-UI würde ich diese Fälle:
- Auf dem Dashboard als Hinweis listen („3 PMs ohne Firmenzuordnung jetzt zuordnen")
- In der PM-Liste als eigenen Filter
- Im PM-Editor als Pflichtfeld erzwingen (auch wenn DB es zulässt)
So drehst du die Datenqualität schrittweise sauber, ohne harte Migration.
---
# Firmen-Detail (User-Sicht)
> **IST-Stand 21.05.2026**: Die Firmen-Detailseite ist umgesetzt als
> **eine lange Seite mit Quick-Nav-Ankern** statt mit echten Tab-Wechseln.
> Die im folgenden beschriebene Tab-Struktur ist konzeptuell gleichwertig
> und kann bei Bedarf in eine echte Tab-Komponente umgezogen werden,
> ohne den Funktionsumfang zu aendern.
>
> **Route**: `/admin/me/press-kits/{company}` mit dem Routen-Namen
> `me.press-kits.show`. In der UI heisst der Bereich „Firmen".
## Aufruf
Drei Wege führen hierher:
- Klick auf einen Eintrag in der Firmen-Liste
- Klick auf den Firmennamen im Firmen-Kontext-Switcher (→ aktive Firma + Sprung in Detail)
- Tiefenlinks aus PM-Detail („zur Firma"), Statistik („Firma im Detail")
URL-Struktur: `/firmen/{id}` im User Backend (konzeptueller Zielzustand). IST: `/admin/me/press-kits/{id}` (siehe Routen-Name oben). Öffentliche Pressemappe bleibt ein separater PR-Kontext.
---
## Header (über allen Tabs sichtbar)
Kompakte Header-Karte mit:
- **Logo** (links, klickbar zum Ändern wenn berechtigt)
- **Firmenname** + dezenter `slug`-Hinweis
- **Status-Badges nebeneinander**:
- Portal (welches der beiden Portale)
- Verifizierungs-Status (Häkchen oder „Nicht verifiziert")
- Aktiv/Inaktiv
- Deine Rolle: Owner / Verantwortlich / Mitglied
- **Aktions-Menü** rechts:
- Primär: „Neue Pressemitteilung" (führt direkt in Editor mit dieser Firma vorausgewählt)
- Sekundär (Dropdown): „Verifizierung beantragen", „Custom Domain einrichten", „Als inaktiv markieren", „Firma übertragen"
Der Header bleibt beim Tab-Wechsel stehen, sodass Kontext (welche Firma, welche Rolle) nie verloren geht.
---
## Tab-Struktur (6 Tabs)
### Tab 1: Übersicht (Default)
Eine Mini-Dashboard-Sicht für genau diese Firma. Gibt dem User sofort das Gefühl „hier passiert was" beim Reinklicken.
Inhalte:
- **KPI-Reihe**: Anzahl PMs gesamt, Veröffentlicht in den letzten 30 Tagen, Aktive Highlights, Reichweite (30 Tage)
- **Letzte 5 Pressemitteilungen** dieser Firma mit Status und Datum
- **Pressekontakte-Block**: kompakte Liste, „X Kontakte hinterlegt", Sprung in Tab 3
- **Datenqualitäts-Hinweise** firmenspezifisch:
- „Logo fehlt"
- „Keine Pressekontakte hinterlegt Änderungs-Workflow nicht möglich"
- „Owner-Konflikt: `owner_user_id` und `company_user.role=owner` divergieren" (eher Admin-Hinweis, aber wenn es User betrifft, transparent zeigen)
- „Branche nicht gesetzt beeinträchtigt Auffindbarkeit"
- **Quick Actions**: „Neue PM", „Pressekontakt hinzufügen", „Highlight buchen"
### Tab 2: Stammdaten
Bearbeitbare Firmendaten. Felder gemäß `companies`-Tabelle:
- Firmenname *
- Logo (Upload, mit Preview)
- Kurzbeschreibung (12 Sätze für Listing-Ansichten)
- Lange Beschreibung (für die Firmenseite)
- Branche/Kategorie *
- Adresse: Straße, PLZ, Ort, Land
- Website-URL
- Footer-Code-Flag (mit kurzer Erklärung was es bewirkt)
- Aktivstatus (Toggle, mit Warnhinweis was passiert)
**Eigentümer-Block** (read-only für Nicht-Owner):
- Anzeige des konsolidierten Eigentümers
- Bei Divergenz zwischen `owner_user_id` und `company_user.role=owner`: gelber Warnhinweis mit „An Support melden"-Link
**Portal-Anzeige**:
- Read-only mit Tooltip: „Das Portal wird durch die Firma festgelegt und kann nicht im Self-Service geändert werden. Bei Bedarf bitte Support kontaktieren."
**Verifizierung**:
- Status anzeigen
- Wenn nicht verifiziert: CTA „Verifizierung beantragen" → führt zu Buchungen & Add-ons mit vorausgewähltem Service
### Tab 3: Pressekontakte
Verwaltung der `contacts` dieser Firma. Direkte `contact_user`-Pivots werden hier nicht angezeigt Kontakte gehören zur Firma, Punkt.
Liste mit:
- Name, Position, E-Mail, Telefon
- Status-Badge: „Magic-Link aktiv" / „Magic-Link inaktiv"
- Anzahl PMs, in denen dieser Kontakt referenziert ist (aus `press_release_contact`)
- Aktionen: Bearbeiten / Löschen / Test-Magic-Link senden
Oben: „+ Neuer Pressekontakt" mit Formular (Name, Position, E-Mail, Telefon, Magic-Link-Berechtigung ja/nein).
**Wichtiger Erklärungsblock** über der Liste (einmalig dismissible):
> Pressekontakte sind die offiziellen Ansprechpartner zu dieser Firma. Sie können sich per Magic-Link einloggen, um Pressemitteilungen zu korrigieren, zu aktualisieren oder DSGVO-Anfragen zu stellen. Hinterlegen Sie alle relevanten Kontakte, um den autorisierten Änderungs-Workflow zu ermöglichen.
Beim Löschen eines Kontakts: Warnung, falls dieser noch in PMs referenziert ist („In 12 PMs hinterlegt diese verlieren den Kontakt").
### Tab 4: Pressemitteilungen
Gefilterte PM-Liste auf `company_id` dieser Firma.
- Standard-Filter (Alle / Veröffentlicht / In Prüfung / Entwürfe / Depubliziert / Korrekturen)
- Volltextsuche
- „+ Neue Pressemitteilung" mit dieser Firma vorausgewählt
- Pro Eintrag: Titel, Status, Datum, zugeordnete Pressekontakte, Reichweite, Aktionen
Bulk-Aktionen für Power-User: Mehrere PMs auswählen → „Pressekontakte bulk zuordnen", „Exportieren als PDF".
### Tab 5: Statistik
Reichweite und Performance dieser Firma:
- Views, Klicks, Verweildauer im Zeitverlauf (30/90/365 Tage)
- Top-PMs nach Reichweite
- Verteilung nach Quelle (organisch, Newsletter, Distribution-Partner)
- Kategorien-Heatmap (welche Themen performen)
- Aktive Highlights & Buchungen, die dieser Firma zugeordnet sind
- Im Pro-Tarif zusätzlich: Demografie, Geräte, Suchbegriffe
Export-Button (CSV/PDF) sinnvoll für Reportings, die User intern an Marketing/Geschäftsführung weiterleiten.
### Tab 6: Abrechnung
Hier wird's etwas heikel, weil Abrechnung hauptsächlich am User hängt, aber `user_payment_option_company` einen firmenscharfen Bezug erlaubt.
Inhalte:
- **Zahlungsmethoden für diese Firma** Liste der `user_payment_options`, die per Pivot mit dieser Firma verknüpft sind
- „Zahlungsmethode dieser Firma zuordnen" (aus den vorhandenen User-Zahlungsmethoden auswählen)
- **Rechnungen mit Firmenbezug** PMs/Buchungen, die diese Firma betreffen, mit den entsprechenden Rechnungen
- **Klarer Erklärtext oben**:
> Rechnungen werden grundsätzlich auf Ihren User-Account ausgestellt. Hier sehen Sie Zahlungsmethoden und Buchungen, die speziell dieser Firma zugeordnet sind. Eine vollständige Übersicht aller Rechnungen finden Sie unter „Rechnungen".
Dieser Tab ist nur für Owner sichtbar Member und Verantwortliche haben hier nichts verloren.
---
## Rollen-Logik (aus `company_user.role`)
Klare Sichtbarkeits- und Bearbeitungsregeln:
**Owner**: Alle Tabs, alle Aktionen. Kann Firma deaktivieren, übertragen, Pressekontakte verwalten, Stammdaten ändern, Abrechnung sehen.
**Verantwortlich**: Übersicht, Stammdaten (read-only), Pressekontakte (verwalten), PMs (verwalten + erstellen), Statistik. Kein Tab Abrechnung. Stammdaten-Änderungen mit Hinweis „Nur Owner kann ändern".
**Mitglied**: Übersicht, Stammdaten (read-only), Pressekontakte (read-only), PMs (eigene erstellen, nur eigene bearbeiten), Statistik. Kein Tab Abrechnung.
Im Header die Rolle als Badge zeigen, damit der User immer weiß, was er darf, ohne dass er es durch Klicken herausfindet.
---
## Verknüpfungen zu anderen Bereichen
- **Switcher** (Topbar rechts): Auswahl einer Firma scrollt globale Filter auf diese Firma, der direkte Sprung ins Detail bleibt aber ein expliziter Klick
- **PM-Editor**: PMs werden mit `company_id` erstellt, das Feld ist Pflicht (auch wenn DB nullable). Aus dem Firmen-Detail ist es vorausgewählt.
- **Buchungen & Add-ons**: Highlights, KI-Services, Verifizierung etc. werden in der Buchungs-Sektion abgewickelt, aber aus dem Firmen-Detail kontextuell verlinkt
- **Statistiken (Hauptpunkt)**: Aggregierte Sicht über alle Firmen vs. firmenspezifische Sicht hier im Tab. Beide Wege koexistieren.
- **Einstellungen → Team**: Beim Agency-Tarif können User andere User zur Firma einladen (`company_user`-Pivot mit Rolle setzen). Verlinkung von hier aus sinnvoll.
---
## Offene Designentscheidungen
**1. Firmenwechsel-Bestätigung** Wenn ein User im Firmen-Detail arbeitet und über den Switcher die Firma wechselt sofort wechseln oder Warnung „ungespeicherte Änderungen"? Ich würde Standard-Browser-Verhalten beibehalten (`beforeunload` bei dirty forms), kein eigener Dialog.
**2. Firma deaktivieren vs. löschen** Im Datenmodell ist Aktiv/Inaktiv vorhanden. Echtes Löschen ist heikel wegen verknüpfter PMs, Rechnungen, Kontakte. Ich würde dem User **nur** Deaktivieren anbieten echtes Löschen läuft über Support-Anfrage. Senkt deine Risiken bei DSGVO-Konflikten.
**3. Owner-Übertragung** „Firma übertragen" ist ein sensibler Vorgang. Ich würde einen eigenen Wizard mit E-Mail-Bestätigung beim neuen Owner verlangen (ähnlich GitHub-Repo-Transfer). Macht den Punkt komplexer, aber sauber.
**4. Pressekontakt-Zuordnung beim PM-Erstellen** Beim Anlegen einer neuen PM: Sollen alle Pressekontakte der Firma automatisch zugeordnet werden, oder muss der User explizit auswählen? Ich tendiere zu „alle vorausgewählt, abwählbar" gibt dem User eine Voreinstellung, die in 90 % der Fälle stimmt.
---

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
# Checkliste User Backend
Stand: 21.05.2026 (Phase 7 abgeschlossen, Phase 8 in Planung)
Diese Checkliste fasst den aktuellen Stand des User Backends zusammen und trennt erledigte Punkte von den naechsten sinnvollen Umsetzungsschritten.
Begleitende Dokumente:
- `docs/STATUS-ABGLEICH-USER-PANEL.md` — Konzept-vs-Code-Abgleich pro Page.
- `docs/PHASE-8-USER-PANEL-PLAN.md` — Detail-Plan der naechsten Sub-Paeckchen.
- `dev/frontend/hub-flux/19-PHASE-7-PRESS-RELEASE-FORM.md` — Phase-7-Abschluss.
- `dev/frontend/hub-flux/PROGRESS.md` — Tagebuch der Hub-Migration.
## Erledigt
- [x] User Backend und Admin Backend konzeptionell getrennt, technisch weiter im gemeinsamen Backend mit rollenbasierter Sicht.
- [x] User-Backend-Navigation in die Bereiche „Mein Bereich", „Finanzen" und „Konto" gegliedert.
- [x] Firmen-Kontext-Switcher aus der Sidebar in eine Topbar rechts ueber dem Content verschoben.
- [x] Topbar technisch in `<flux:main>` integriert, damit sie nicht mit Sidebar oder Content kollidiert.
- [x] Topbar visuell vom Content abgesetzt.
- [x] Firmen-Kontext mit „Alle Firmen" und Einzelfirma als globale User-Backend-Auswahl umgesetzt.
- [x] Dashboard und Pressemitteilungs-Liste reagieren auf den aktiven Firmen-Kontext.
- [x] Firmen-Liste und Firmen-Detail im User Backend umgesetzt.
- [x] Terminologie in der Navigation von „Pressemappen" auf „Firmen" vereinheitlicht.
- [x] Legacy-URLs fuer `pressemappen` auf neue `firmen`-Routen weitergeleitet.
- [x] Firmen-Stammdaten im Firmen-Detail bearbeitbar gemacht, inklusive Logo.
- [x] Legacy-Company-Logos bevorzugt lokal aufgeloest, um 403-Fehler durch externe Alt-URLs zu vermeiden.
- [x] Firmen-Logos in Admin- und User-Ansichten auf sinnvolle Groessen begrenzt.
- [x] Kontaktverwaltung innerhalb einer Firma umgesetzt.
- [x] Rechte fuer Firmen- und Kontaktverwaltung getrennt: Owner/Verantwortliche duerfen bearbeiten, Mitglieder bleiben lesend.
- [x] Neue Pressemitteilungen uebernehmen aktive Firma als Vorauswahl.
- [x] Portal der Pressemitteilung wird aus der Firma abgeleitet statt manuell gewaehlt.
- [x] Pressemitteilungs-Detail zeigt zugeordnete Pressekontakte.
- [x] Pressemitteilungs-Detail zeigt Status- und Verlaufsdaten.
- [x] Profilseite auf persoenliches Profil reduziert; Firmendaten werden in der jeweiligen Firma gepflegt.
- [x] Rechnungsadresse als eigener sichtbarer Bereich im Profil aufgenommen.
- [x] Rechnungen in die Finanznavigation verschoben.
- [x] Rechnungsseite mit Hinweisblock zu Legacy-Rechnungen und Link zur Rechnungsadresse versehen.
- [x] „Buchungen & Add-ons" als vorbereiteter Bereich eingebunden.
- [x] API-Tokens unter „Konto" als „API & Integrationen" eingeordnet.
- [x] Dashboard zeigt erste Datenqualitaets-Hinweise aus bestehenden Tabellen.
- [x] Phase-1-Dokumentation in `Admin-User.md` aktualisiert.
- [x] Regressionstests fuer Firmen-Kontext, Navigation, Profil, Firmen und Sicherheitsgrenzen ergaenzt.
## Naechste sinnvolle Schritte
- [x] Topbar fachlich abrunden: links optional Seitentitel/Kontext aufnehmen, rechts Firmen-Switcher und spaeter weitere kompakte Tools.
- [x] Mobile Darstellung der Topbar pruefen und bei Bedarf vereinfachen, damit der Switcher auf kleinen Displays nicht zu breit wird.
- [x] Firmen-Switcher um direkten Link „Firma oeffnen" oder „Firma verwalten" fuer die aktive Firma erweitern.
- [x] Leere und fehlerhafte Zustaende in Firmen, Kontakte, Pressemitteilungen und Rechnungen visuell vereinheitlichen.
- [x] Dashboard-Hinweise klickbar machen, sodass User direkt zur passenden Korrekturstelle springen.
- [x] Pressemitteilungs-Liste um Filter fuer „ohne Firma", Status und aktive Firma sauber abrunden.
- [x] Firmen-Detail in klare Tabs oder Sektionen aufteilen: Stammdaten, Pressekontakte, Pressemitteilungen, Abrechnung, Statistik.
- [x] Rechnungsadresse validieren und klarer anzeigen, welche Daten fuer kuenftige Rechnungen fehlen.
- [x] Sicherheit/Konto-Bereich weiter ausbauen: Passwort, 2FA, Sessions und Login-Hinweise konsolidieren.
- [x] UI-Texte und Begriffe final durchgehen: Firma, Pressemappe, Pressemitteilung, User Backend, Admin Backend.
- [x] Breitere visuelle QA im User Backend: Tabellenabstaende, Karten, Header, responsive Verhalten.
## Phase 7 — Pressemitteilungs-Form-Refactor (abgeschlossen)
- [x] Pressemitteilungen unterstuetzen einen Untertitel (`press_releases.subtitle`).
- [x] Pressemitteilungen unterstuetzen geplante Veroeffentlichung (`scheduled_at`) und Embargo (`embargo_at`).
- [x] Pressemitteilungen unterstuetzen ein optionales Boilerplate-Override (`boilerplate_override`).
- [x] PM-Inhalt wird vor dem Speichern serverseitig sanitiert (HTMLPurifier via `mews/purifier`, gekapselt in `PressReleaseHtmlSanitizer`).
- [x] Customer- und Admin-Forms verwenden den gleichen Sidebar-Aufbau:
Status & Absenden, Kategorie, Portal (als farbiger Pill), Pressekontakt, Themen-Tags, Veroeffentlichung, Weitere Felder, Phase-2-Footer.
- [x] Pressekontakt-Pflichtfeld aufgehoben — Auswahl bleibt empfohlen, ist aber technisch nullable.
- [x] Anhaenge-/Downloads-UI ist im Customer- und Admin-Editor deaktiviert (Tabelle `press_release_attachments` und Manager-Komponente bleiben fuer einen spaeteren Security-Review erhalten).
- [x] Hintergrund-Command `php artisan press-releases:publish-scheduled` veroeffentlicht geplante PMs (Scheduler-Eintrag in `routes/console.php`, Intervall 5 Min).
- [x] FluxUI Toast (`Flux::toast()`) wird konsistent fuer Erfolg, Fehler und Validation-Probleme in PM-Forms und Status-Aktionen verwendet.
- [x] Smooth-Scrolling zum ersten Validation-Fehler nach Save (`resources/js/portal-form-hooks.js`).
- [x] Pre-Submit-Check-Liste (`@computed presubmitChecks`) zeigt vor dem Einreichen offene Pflichtfelder und Empfehlungen.
## Phase 8 — User-Panel-Konsolidierung (in Planung)
Vollstaendiger Plan: `docs/PHASE-8-USER-PANEL-PLAN.md`.
- [ ] Show-Page-Luecken schliessen (Subtitle, Scheduling, Embargo, Boilerplate-Override) — Customer + Admin (8A).
- [ ] Listen-Indikatoren fuer geplante Veroeffentlichung und Embargo (8B).
- [ ] Pressekontakt-Warn-Box in Sidebar-Card, wenn kein Kontakt gewaehlt (8C).
- [ ] Doku-Pflege: `docs/user-admin/*` an IST-Stand ziehen (8D, dieses Dokument).
- [ ] Firmen-Liste auf Mockup-Niveau (Counter-Strip, Saved-Views, Filter-Chips, Card/List-Toggle, Rollen-Legende) (8E).
- [ ] Set wiederverwendbarer SVG-Platzhalter fuer PM-Titelbilder + Auswahl-Modal (8F).
- [ ] Titelbild-Schema in `press_releases` (Default-Platzhalter pro PM, `PressReleaseCoverImage`-Resolver) (8G).
- [ ] FluxUI `flux:file-upload` im Image-Manager inkl. Pflichtfelder fuer Urheber, Lizenz-Typ, Lizenz-URL, Rechte-Bestaetigung (8H).
- [ ] Veroeffentlichungs-Modal mit rechtlichen Hinweisen + Kontingent-Anzeige (Customer) (8I).
- [ ] Kontingent-Stub im Datenmodell (Spalten auf `users`, monatlicher Reset-Command) als Vorbereitung fuer das Tarif-Modul (8J).
- [ ] Tests, Pint, Build, Roadmap-Update (8K).
## Phase 2 / spaeter
- [ ] Magic-Link-Zugriff fuer Firmen-E-Mail-Adressen konzipieren und umsetzen.
- [ ] Separate `token_requests`-Tabelle fuer nicht-userbasierte Zugriffe anlegen.
- [ ] Zugriff per Firmen-E-Mail so begrenzen, dass nur passende Firmen und Pressemitteilungen sichtbar werden.
- [ ] Trust Score fuer User/Firmen konzipieren und im Admin Backend justierbar machen.
- [ ] Moderationslogik an Trust Score und Freigabeprozess anbinden.
- [ ] Aufbewahrungsfristen fuer Magic Links, Token Requests, API Logs und Statuslogs definieren und technisch absichern.
- [ ] Admin-editierbare Textvorlagen fuer neutrale Tombstone-/Entfernungs- und Systemtexte einbauen.
- [ ] API-Nutzungs-Log im User Backend sichtbar machen.
- [ ] Benachrichtigungen und Newsletter-Abos im Konto-Bereich ausbauen.
- [ ] Zahlungsarten und firmenbezogene Zahlungsoptionen im User Backend aktivieren.
- [ ] Credits, Tarife und Add-ons an ein echtes Preismodell anbinden.
- [ ] Statistikbereich fuer Firmen und Pressemitteilungen umsetzen.
- [ ] Medienbereich aus vorhandenen Pressemitteilungsbildern ableiten; spaeter echte Medienbibliothek pruefen.
- [ ] Team-/Rollenverwaltung fuer Firmen im User Backend ergaenzen.
## Hinweise
- Phase 1 ist funktional abgeschlossen; Phase 7 (PM-Form-Refactor) ebenfalls — siehe Plan-Doku oben.
- Phase 8 fokussiert das User-Panel: Firmen-Liste auf Mockup-Niveau, PM-Titelbilder mit SVG-Platzhaltern und Veroeffentlichungs-Modal mit rechtlichen Hinweisen.
- Die Admin-Oberflaeche bekommt in Phase 8 nur die Phase-7-Parallelitaeten (Show-Page-Felder, Listen-Indikatoren); groessere Admin-Aenderungen kommen erst mit Phase 2.
- Anhaenge sind aktuell aus Sicherheitsgruenden deaktiviert, Tabelle und Komponente bleiben aber erhalten und werden in einem separaten Audit-Track reaktiviert.

View file

@ -0,0 +1,305 @@
# User-Admin: Zusammenhänge und relevante Daten
Stand: 2026-05-21 (aktualisiert nach Phase 7)
Diese Notiz beschreibt den User als fachlichen Mittelpunkt für die weitere Konzeption des Admin-User-Bereichs. Grundlage sind die aktuellen Models und Migrationen im Laravel-Projekt.
> **Was sich seit dem ursprünglichen Stand (2026-05-05) geändert hat**:
>
> - Pressemitteilungen haben zusätzliche Felder: `subtitle`, `scheduled_at`,
> `embargo_at`, `boilerplate_override`, `no_export` (Phase 7).
> - Neue Tabelle `press_release_attachments` (Modell `PressReleaseAttachment`).
> UI ist temporär deaktiviert (Security-Review), Tabelle und Service
> bleiben aber erhalten.
> - Neuer Service-Layer für PMs: `PressReleaseService` (Status-Übergänge,
> Scheduling-Auflösung), `PressReleaseHtmlSanitizer` (HTMLPurifier-Wrapper),
> `PressReleaseAttachmentStorage`.
> - Neuer Console-Command `php artisan press-releases:publish-scheduled`
> (Scheduler-Intervall 5 Min) veröffentlicht geplante PMs automatisch.
> - Pivot `press_release_contact` bleibt n:m, der Customer-Flow speichert
> aber nur einen Kontakt pro PM und das Feld ist nullable.
## Zentraler Ausgangspunkt
Ein `User` ist nicht nur ein Login-Konto, sondern bündelt im System mehrere fachliche Bereiche:
- Zugang und Status: Name, E-Mail, Verifikation, Passwort, 2FA, Aktiv/Inaktiv, Super-Admin-Flag, Rollen/Berechtigungen.
- Portal- und Legacy-Zuordnung: `portal`, `registration_type`, `language`, `legacy_portal`, `legacy_id`.
- CRM-Zuordnung: Firmen, Firmenrollen, Kontakte.
- Content-Zuordnung: Pressemitteilungen, Bilder, Statusverlauf.
- Abrechnung und Verträge: Rechnungsadresse, Zahlungsoptionen, Zahlungen, Rechnungen, Legacy-Rechnungen.
- API und Sicherheit: Sanctum-Tokens, API-Nutzung, Magic-Links.
- Kommunikation und Arbeitskomfort: Newsletter-Abos, Filter-Presets.
## Datenmodell im Überblick
```mermaid
erDiagram
users ||--o| profiles : "hat"
users ||--o| billing_addresses : "hat"
users ||--o{ companies : "owner_user_id"
users }o--o{ companies : "company_user.role"
users }o--o{ contacts : "contact_user"
companies ||--o{ contacts : "hat"
users ||--o{ press_releases : "autor"
companies ||--o{ press_releases : "zugeordnet"
contacts }o--o{ press_releases : "press_release_contact"
press_releases ||--o{ press_release_images : "hat"
press_releases ||--o{ press_release_status_logs : "hat"
users ||--o{ press_release_status_logs : "changed_by_user_id"
users ||--o{ user_payment_options : "hat"
user_payment_options }o--o{ companies : "user_payment_option_company"
user_payment_options ||--o{ user_payments : "hat"
user_payments ||--o{ invoices : "erzeugt"
users ||--o{ invoices : "hat"
users ||--o{ legacy_invoices : "hat"
users ||--o{ newsletter_subscriptions : "hat"
users ||--o{ magic_links : "hat"
users ||--o{ user_filter_presets : "hat"
users ||--o{ api_usage_logs : "hat"
```
## Direkte Relationen am User
`profile`
- Kardinalität: 1:0..1.
- Tabelle: `profiles`, Foreign Key `user_id` ist eindeutig.
- Inhalt: persönliche Profildaten, Anrede, Titel, Vor-/Nachname, Telefon, Adresse, Land, Geburtsdatum, Backlink, Statistik-/Footer-Code-Flags, Validierungs-/Vertragsdaten, Steuerdaten.
- Relevanz im Admin: Datenqualität, Legacy-Profil, persönliche Stammdaten, Vertrag/Validierung.
`billingAddress`
- Kardinalität: 1:0..1.
- Tabelle: `billing_addresses`, Foreign Key `user_id` ist eindeutig.
- Inhalt: Rechnungsname, Adresse, PLZ, Ort, Land.
- Relevanz im Admin: Rechnungsfähigkeit, fehlende Abrechnungsdaten, Kundendatenpflege.
`ownedCompanies`
- Kardinalität: 1:n.
- Tabelle: `companies`, Foreign Key `owner_user_id`.
- Bedeutung: Der User ist fachlicher Eigentümer einer Firma, auch ohne Pivot-Eintrag in `company_user`.
- Relevanz im Admin: wichtig für Rechte im Customer-Profil und für klare Verantwortlichkeit.
`companies`
- Kardinalität: n:m.
- Pivot: `company_user` mit `company_id`, `user_id`, `role`.
- Rollen: `member`, `responsible`, `owner`.
- Bedeutung: Ein User kann mehrere Firmen haben; eine Firma kann mehreren Usern zugeordnet sein.
- Relevanz im Admin: Hauptstruktur für Kundenkonto, Berechtigungen in der Firmenpflege und spätere User-Detailansicht.
`contacts`
- Kardinalität: n:m.
- Pivot: `contact_user` mit `contact_id`, `user_id`.
- Zusätzlich gehört jeder Kontakt zwingend zu genau einer Firma über `contacts.company_id`.
- Bedeutung: Kontakte können direkt Usern zugeordnet sein, fachlich sind sie aber an Firmen aufgehängt.
- Relevanz im Admin: Ansprechpartner und Zuordnungsqualität.
`pressReleases`
- Kardinalität: 1:n.
- Tabelle: `press_releases`, Foreign Key `user_id`.
- Zusätzlich: optionale Firmenzuordnung über `company_id` und Pflichtkategorie über `category_id`.
- Wichtig: Datenbankseitig ist `company_id` nullable, fachlich sollte eine Customer-PM einer Firma zugeordnet sein. Der Customer-Flow erzwingt aktuell eine eigene Firma und leitet daraus das Portal ab.
- Relevanz im Admin: Content-Historie, Status, Veröffentlichungen, Qualität, Freigabeprozess.
`newsletterSubscriptions`
- Kardinalität: 1:n.
- Tabelle: `newsletter_subscriptions`, Foreign Key `user_id`.
- Inhalt: Portal, Name, E-Mail, IP, Bestätigung, Subscribe/Unsubscribe, Legacy-Zuordnung.
- Relevanz im Admin: Kommunikationsstatus und Opt-in/Opt-out-Historie.
`billing / payments`
- `userPaymentOptions`: 1:n über `user_payment_options.user_id`.
- `invoices`: 1:n über `invoices.user_id`.
- `legacyInvoices`: 1:n über `legacy_invoices.user_id`.
- Indirekt: `user_payment_options` hat n:m zu Firmen über `user_payment_option_company`; `user_payments` hängt an `user_payment_options`; `invoices` können zusätzlich an `user_payments` und `invoice_billing_addresses` hängen.
- Relevanz im Admin: Vertrags-/Abo-Status, aktive API-Freischaltung, Rechnungen, Legacy-Archiv.
`tokens`
- Kommt über Laravel Sanctum `HasApiTokens`.
- Tabelle: `personal_access_tokens` mit polymorphem `tokenable_type` / `tokenable_id`.
- Im Customer-Bereich werden Tokens für API-Zugriffe verwaltet.
- Relevanz im Admin: API-Zugang, genutzte Berechtigungen, letzter Zugriff.
`magicLinks`
- Kardinalität: 1:n.
- Tabelle: `magic_links`, Foreign Key `user_id`.
- Inhalt: Token-Hash, Zweck, Payload, Ablauf, Verbrauch, IPs.
- Relevanz im Admin: Auth-/Support-Kontext, Magic-Login-Historie.
`filterPresets`
- Kardinalität: 1:n.
- Tabelle: `user_filter_presets`, Foreign Key `user_id`.
- Inhalt: Seite, Name, Default-Flag, letzte Nutzung, Filter-JSON.
- Relevanz im Admin: eher Arbeitskomfort/Personalisierung, nicht Kern-Kundendaten.
`apiUsageLogs`
- Kardinalität: faktisch 1:n über `api_usage_logs.user_id`, aber aktuell keine Relation im `User`-Model.
- Inhalt: Token, Methode, Pfad, Route, Status, IP, User-Agent, Dauer, Zeitpunkt.
- Relevanz im Admin: API-Aktivität, Support und Missbrauchsanalyse.
`roles` und `permissions`
- Kommen über Spatie `HasRoles`.
- Tabellen u. a. `roles`, `permissions`, `model_has_roles`, `model_has_permissions`, `role_has_permissions`.
- Relevanz im Admin: Trennung zwischen Admin, Editor, Customer, Super-Admin und feineren Berechtigungen wie `press-releases:publish` oder `users:manage`.
## Firmen, Kontakte und Pressemitteilungen
Die fachliche CRM-/Content-Struktur läuft im Kern so:
- User hat mehrere Firmen über `company_user`.
- User kann zusätzlich direkter Eigentümer einer Firma über `companies.owner_user_id` sein.
- Firma hat mehrere Kontakte über `contacts.company_id`.
- Firma hat mehrere Pressemitteilungen über `press_releases.company_id`.
- Pressemitteilung hat genau einen Autor/User über `press_releases.user_id`.
- Pressemitteilung kann mehrere Kontakte über `press_release_contact` referenzieren.
- Kontakt kann mehreren Usern direkt zugeordnet sein über `contact_user`, bleibt aber immer an genau eine Firma gebunden.
Daraus ergibt sich für den User-Admin eine wichtige Sicht:
- User-Zentrum: Wer ist der Account?
- Firmen-Zentrum: Welche Firmen gehören dazu, in welcher Rolle?
- Kontakt-Zentrum: Welche Ansprechpartner hängen an diesen Firmen und/oder am User?
- Content-Zentrum: Welche Pressemitteilungen wurden vom User erstellt und welcher Firma sind sie zugeordnet?
- Abrechnungs-Zentrum: Welche Zahloptionen, Rechnungen und Legacy-Rechnungen hängen am User und ggf. an Firmen?
## Pressemitteilungen im Detail
Eine Pressemitteilung (`press_releases`) enthält:
- Identifikation: `id`, `uuid`, `slug`, `legacy_portal`, `legacy_id`.
- Zuordnung: `portal`, `language`, `user_id`, `company_id`, `category_id`.
- Inhalt: `title`, `subtitle`, `text`, `backlink_url`, `keywords`, `boilerplate_override`.
- Status/Publikation: `status`, `published_at`, `scheduled_at`, `embargo_at`, `no_export`, `hits`, `teaser_begin`, `teaser_end`.
- Medien: Bilder über `press_release_images`, Anhänge über `press_release_attachments` (UI deaktiviert).
- Kontakte: Pivot `press_release_contact`.
- Verlauf: Statuslogs über `press_release_status_logs`, inklusive `changed_by_user_id`, Statuswechsel, Grund und Quelle.
Service-Layer rund um PMs:
- `App\Services\PressRelease\PressReleaseService` — kapselt die Übergänge `submitForReview`, `publish`, `reject`, `archive`, `deleteFromAdmin` sowie das Auflösen von `scheduled_at`/`embargo_at` für die echte Veröffentlichung.
- `App\Services\PressRelease\PressReleaseHtmlSanitizer` — Wrapper um `mews/purifier` mit definierter Tag-/Attribut-Whitelist.
- `App\Services\PressRelease\PressReleaseAttachmentStorage` — kapselt Datei-Operationen auf dem `public`-Disk (UI aktuell deaktiviert).
- `App\Console\Commands\PublishScheduledPressReleases` — wird per Scheduler (alle 5 Min) ausgeführt und veröffentlicht freigegebene PMs zum geplanten Zeitpunkt, respektiert Embargo.
Fachliche Konsequenz:
- Für Kunden sollte `company_id` praktisch Pflicht sein, obwohl die DB es nullable erlaubt.
- Für Admins kann `company_id = null` als Migrations-/Altfall auftauchen und sollte im User-Admin sichtbar markiert werden.
- `portal` sollte mit der Firma konsistent sein. Im Customer-Flow wird das Portal aus der Firma abgeleitet; im Admin-Bereich sollte eine bewusste Korrektur möglich sein.
## User-relevante Tabellen
Kernkonto:
- `users`
- `profiles`
- `billing_addresses`
- `sessions`
- `password_reset_tokens`
- `personal_access_tokens`
- `magic_links`
- Spatie Permission-Tabellen
CRM:
- `companies`
- `company_user`
- `contacts`
- `contact_user`
Content:
- `press_releases`
- `press_release_images`
- `press_release_attachments` (UI temporaer deaktiviert)
- `press_release_contact`
- `press_release_status_logs`
- `categories`
- `category_translations`
Billing:
- `billing_addresses`
- `user_payment_options`
- `user_payment_option_company`
- `user_payments`
- `payment_options`
- `payment_option_translations`
- `invoices`
- `invoice_billing_addresses`
- `legacy_invoices`
Kommunikation/API/Arbeitskomfort:
- `newsletter_subscriptions`
- `api_usage_logs`
- `user_filter_presets`
## Relevante Inhalte für den Admin-User-Bereich
Für eine optimierte Admin-User-Ansicht sollten diese Blöcke geplant werden:
- Account: Status, Portal, Sprache, Registrierungstyp, Rollen, Berechtigungen, Super-Admin, letzte Aktivität, letzte IP, E-Mail-Verifikation, 2FA-Status.
- Datenqualität: Profil vorhanden, Rechnungsadresse vorhanden, Firma vorhanden, Kontakte vorhanden, veröffentlichte PMs vorhanden, Legacy-Zuordnung vorhanden.
- Firmen: Firmenliste mit Rolle, Eigentümerstatus, Portal, Aktivstatus, Logo, Footer-Code-Flag, Kontakt-/PM-Anzahl.
- Kontakte: Kontakte je Firma plus direkte User-Kontakte aus `contact_user`.
- Pressemitteilungen: Gesamtzahl, Statusverteilung, letzte PMs, veröffentlichte PMs, PMs ohne Firma, PMs mit Portalabweichung.
- Abrechnung: Rechnungsadresse, aktive Zahlungsoptionen, verknüpfte Firmen zu Zahlungsoptionen, Zahlungen, Rechnungen, Legacy-Rechnungen.
- API: Token-Anzahl, Berechtigungen, letzte Nutzung, API-Usage-Logs.
- Support/Admin-Aktionen: User bearbeiten, Rollen/Berechtigungen ändern, Impersonation, Magic-Link-Kontext, ggf. Account deaktivieren.
## Offene Punkte für die weitere Konzeption
- `User` hat keine direkte `apiUsageLogs()`-Relation, obwohl `api_usage_logs.user_id` existiert. Für Admin-Detailansichten wäre eine Relation sinnvoll.
- `press_releases.company_id` ist nullable. Fachlich sollte geklärt werden, ob das nur Legacy-/Admin-Fälle erlaubt oder langfristig verboten werden soll.
- `contacts` gehören immer zu einer Firma, können aber zusätzlich direkt Usern zugeordnet werden. Für die Oberfläche muss klar sein, ob "User-Kontakte" nur direkte Pivot-Kontakte meint oder alle Kontakte seiner Firmen.
- Eine Firma kann über `owner_user_id` und zusätzlich über `company_user.role = owner` eine Eigentümerinformation haben. Das sollte im Admin eindeutig dargestellt und ggf. harmonisiert werden.
- Billing hängt primär am User, Zahlungsoptionen können aber über Pivot auch Firmen betreffen. Für den Admin sollte klar werden, ob Rechnungen immer User-Rechnungen sind oder später firmenscharf betrachtet werden sollen.
- Portal-Scope ist auf Firmen, Kontakte, Pressemitteilungen und Newsletter aktiv. Admin-Auswertungen nutzen teils `withoutGlobalScopes()`. In der User-Admin-Konzeption muss entschieden werden, wann portalübergreifend gesucht und angezeigt wird.
## Quellen im Code
Modelle:
- `app/Models/User.php`
- `app/Models/Company.php`
- `app/Models/Contact.php`
- `app/Models/PressRelease.php`
- `app/Models/PressReleaseImage.php`
- `app/Models/PressReleaseAttachment.php`
- `app/Models/PressReleaseStatusLog.php`
- `app/Models/Profile.php`
- `app/Models/BillingAddress.php`
- `app/Models/Invoice.php`
- `app/Models/LegacyInvoice.php`
- `app/Models/UserPaymentOption.php`
- `app/Models/UserPayment.php`
- `app/Models/NewsletterSubscription.php`
- `app/Models/MagicLink.php`
- `app/Models/UserFilterPreset.php`
- `app/Models/ApiUsageLog.php`
Services / Commands:
- `app/Services/PressRelease/PressReleaseService.php`
- `app/Services/PressRelease/PressReleaseHtmlSanitizer.php`
- `app/Services/PressRelease/PressReleaseAttachmentStorage.php`
- `app/Services/Image/ImageService.php`
- `app/Console/Commands/PublishScheduledPressReleases.php`
- `routes/console.php` (Scheduler-Eintraege)
Migrationen:
- `database/migrations/*`

View file

@ -10,4 +10,4 @@ test('api v1 documentation is publicly available', function () {
->assertSee('openapi: 3.1.0', false)
->assertSee('/press-releases:', false)
->assertSee('Legacy API keys are no longer supported.', false);
});
})->skip('OpenAPI-Spec docs/api/v1.yml ist noch nicht erstellt (eigener API-Doku-Track, siehe dev/migration 2026/07-API-MIGRATION.md). Assertions bleiben erhalten; Skip entfernen, sobald die Datei vorliegt.');

View file

@ -3,10 +3,14 @@
use App\Models\User;
use Livewire\Volt\Volt;
test('profile page is displayed', function () {
$this->actingAs($user = User::factory()->create());
test('profile settings route redirects to the customer profile page', function () {
$this->actingAs(User::factory()->create());
$this->get('/settings/profile')->assertOk();
// Das Profil ist seit dem Hub-/Customer-Portal-Umbau unter
// /admin/me/profile (route `me.profile`) erreichbar. /settings/profile
// bleibt als kanonischer Redirect bestehen. Das Rendern der Zielseite
// deckt CustomerProfileSecurityTest über die Volt-Komponente ab.
$this->get('/settings/profile')->assertRedirect('/admin/me/profile');
});
test('profile information can be updated', function () {