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

@ -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/*`