Kunden koennen eigene veroeffentlichte PMs kostenpflichtig aus der
oeffentlichen Ansicht nehmen: Panel auf der PM-Detailseite (inkl.
T1neu-Unternehmer-Checkbox und Doppelausweis netto/brutto), Stripe-
Ad-hoc-Checkout wie Credit-Topups, Erfuellung im Webhook — die PM
wechselt auf archived + depublished_at, die oeffentliche URL zeigt
dauerhaft einen Tombstone (410 Gone, ohne Inhalte, mit ehrlichem
Drittarchive-Hinweis). Doppelbuchungs-Bremse pro PM; Status-Log mit
Quelle "depublication". Gate: ohne BILLING_DEPUBLICATION_PRICE_CENTS
bleibt der Weg deaktiviert und /melden zeigt die neutrale
Preisformulierung [EXTERN: Preis festlegen, O6].
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Zwei eigenstaendige Formulare unter /melden/rechtswidrig (DSA Art. 16:
URL, Name, E-Mail, Begruendung, Wahrheitsversicherung) und
/melden/kontaktdaten (DSGVO-Antrag) auf allen drei Domains. Gemeldete
URLs werden automatisch der passenden veroeffentlichten PM zugeordnet;
ohne Treffer bleibt die URL im payload. Jede Meldung erhaelt eine
Vorgangsnummer (M-{id}, analog K-{id} der Kuendigung), die meldende
Person unverzueglich eine Eingangsbestaetigung (DSA Art. 16 Abs. 4) und
das Melde-Postfach einen internen Hinweis — auch im bestehenden
Per-PM-Flow. Admin-Queue zeigt Vorgangsnummer, Name und gemeldete URL;
/melden verlinkt die neuen Formulare als ersten Weg.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unter dem Artikelinhalt steht jetzt eine eigene Zeile mit "Inhalt melden"
und "Eigene Daten entfernen" (inkl. Kostenlos-Hinweis) — bisher waren die
Wege nur in der Sidebar-Box "Weiterfuehrend" auffindbar. Erfuellt die
DSA-Anforderung eines leicht zugaenglichen Meldewegs direkt am Inhalt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/richtlinien rendert jetzt alle aktiven Kategorien aus der neuen
rejection_categories-Tabelle (stabiler code als Anker, public_label/desc,
optionale Beispiele) statt der statischen K1-K6-Liste. Kategorien werden
nie geloescht, nur deaktiviert; neue Kategorien erscheinen ohne
Textaenderung und ohne neue AGB-Version (AGB par. 7 Abs. 3 verweist nicht
abschliessend). Seeder legt K1-K6 idempotent an; Versionszeile der Seite
kommt aus legal_documents. Go-Live-Checkliste um beide Seeder ergaenzt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Jede veroeffentlichte Fassung eines Rechtstexts ist eine Zeile mit
type/version/effective_from — alte Fassungen sind unloeschbar, zukuenftige
werden erst ab Stichtag wirksam (LegalDocument::current). Seeder legt die
fuenf aktuellen Fassungen an (AGB 0.6, Datenschutz 0.3, Richtlinien 0.1,
Meldeverfahren 0.1, Impressum 0.1). Die Unternehmer-Bestaetigung (T1neu)
referenziert jetzt die konkrete Fassung (legal_document_id am
Buchungsdatensatz, restrictOnDelete); AGB-Seite und Buchungs-Modal ziehen
Version/Stand aus der DB. config/legal.php bleibt reiner Fallback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Selbst gehostetes Umami (sys.adametz.media) je Brand: Website-IDs aus
dev/release/umami.md in config/domains.php, Snippet via x-web.umami im
web-master-Layout auf allen oeffentlichen Seiten der drei Domains.
UMAMI_ENABLED=false haelt es aus, bis die Webseiten live gehen
(User-Vorgabe 06.07.; Go-Live-Checkliste WS-7 ergaenzt). Damit stimmt
Datenschutz Abschnitt 5 mit dem Code ueberein. User bestaetigt ausserdem:
Log-/Backup-Fristen und Stripe-Entitaet passen wie eingetragen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
User-Entscheidung 06.07.: info@pressekonto.com ist die Haupt-E-Mail fuer
alle rechtlichen Anliegen. Zentral in config/legal.php (LEGAL_CONTACT_EMAIL),
verwendet von Impressum, AGB, Datenschutz, /melden und /richtlinien auf
allen drei Domains. Support-Kontakt bleibt je Brand (CONTACT_EMAIL_*),
REPORT_EMAIL_* ist nur noch optionales separates DSA-Postfach. Nebenbei
den verunglueckten Enterprise-Hinweis auf der Buchungsseite repariert
(E-Mail war ohne Trennung angeklebt).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stand & Wiedereinstieg mit Klaerungsliste Rechtstexte, [EXTERN]-Punkten
und R3.1-Restbacklog aktualisiert. Nebenbei: Pint-Formatierung sidebar.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Neues Layout components/emails/layout (Wortmarke, 'Publisher - Hub'-
Tagline, Brand-Familien-Fusszeile) + Button-Komponente; alle System-
Mails (Magic-Link, Go-Live-Reset, Kontakt, PM published/rejected,
Kuendigung, Widerruf) darauf umgestellt
- Framework-Auth-Mails (Passwort-Reset, E-Mail-Verifizierung) via
toMailUsing auf eigene Views im gleichen CI statt Laravel-Default
- Anmeldelink-Betreff vereinheitlicht; EmailBrandingTest sichert das
Branding ueber alle Mails ab
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die 356-BGB-Erlöschensbestätigung entfiel mit der B2B-Umstellung; an ihre
Stelle tritt der von AGB par. 13 Abs. 1 geforderte Hinweis auf die
dauerhafte Archivierung (Depublikation nur als kostenpflichtiger Service,
gesetzliche Ansprueche unberuehrt). Bestätigungszeitpunkt wird weiter
protokolliert (Spalte withdrawal_waiver_consented_at, historischer Name).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- /de/api komplett neu: reale Endpunkte, Felder, Token-Abilities,
Rate-Limit und Statuscodes der API v1 (angelehnt an die Legacy-Doku
von businessportal24.com/service/documentation.html); OAuth/SDKs/
Webhooks/Integrations-Behauptungen und api.example.com entfernt
- docs/api/v1.yml (OpenAPI 3.1) erstellt — die docs.api.v1-Route lief
bisher auf eine nie vorhandene Datei (500er aus Hub-Footer/FAQ)
- Token-Vergabe korrekt beschrieben: Selfservice im Konto mit
Buchungs-Gate statt 'auf Anfrage'; X-ApiKey-Migrationshinweis
- ApiDocumentationTest entskippt + Seiten-Test ergaenzt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- GET kontakt/ueber-uns -> 301 auf die Ausgaben-Startseite;
kontakt.store-POST bleibt fuer das Hub-Kontakt-Modal
- Legacy-Aliases (contact/kontakt/ueber-uns) buendeln auf die Startseite
- Spoke-Footer: Kontakt als mailto (Betreiber-Fallback bis R2.1),
Ueber-uns-Link raus, Selfservice-Kuendigungslink ergaenzt
- Sitemap-Konstanten bereinigt, Tests angepasst
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Security-Header-Middleware (nosniff, X-Frame-Options, Referrer-/
Permissions-Policy) global, proxy-unabhaengig; CSP bewusst separat.
- Firmenlogo-Upload im Create-Formular durch den ImageService-Pfad
ersetzt (Mime-Whitelist ohne SVG, Re-Encode, UUID-Name) - vorher
rohe store()-Ablage ohne Server-Validierung (SVG/XSS moeglich).
- Rate-Limits ergaenzt: Passwort-Reset (3/E-Mail + 15/IP pro Std.),
Registrierung (10/IP pro Std.), throttle auf magic-login- und
pm-vorschau-Consume-Routen (Defense-in-Depth).
- EnsureUserIsCustomer als persistente Livewire-Middleware
(symmetrisch zum Admin-Gate, greift auch bei Folge-Requests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
team/partner/karriere/presse (erfundene Personen, Fake-Partner,
Bewerben-Buttons ohne Ziel) samt Routen entfernt (User-Entscheidung
03.07.: lieber weniger Seiten, aber ehrlich). Altes Scaffold
(livewire web.burger-menu/header/footer) wurde nur von diesen Seiten
genutzt und ist mit raus. Legacy-.html-Links: team/partner/karriere
-> 301 Startseite; presse/* behaelt die bestehende 301-Regel auf /faq.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
karriere/partner/presse/team loesen die Brand jetzt pro Request ueber
WebPortalContext auf (config('app.theme') ist bei Route::view-Seiten
ohne Controller nicht gesetzt) und haben Meta-Descriptions;
businessportal24-Startseite auf das config-getriebene meta_title/
meta_description-Muster von presseecho vereinheitlicht.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vierte Bestaetigung im Einreichungs-Modal (sofortige Ausfuehrung,
Erloeschen des Widerrufsrechts mit Vertragserfuellung); Zeitpunkt wird
beim Submit als withdrawal_waiver_consented_at an der PM protokolliert.
Index-Schnell-Submit (wire:confirm) durch das gemeinsame Modal ersetzt -
der Pfad umging bisher alle rechtlichen Bestaetigungen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Kuendigungsbutton (Par. 312k BGB) im Hub-Footer unter dem
Widerrufsbutton, staendig verfuegbar; fuehrt unmittelbar auf die
Bestaetigungsseite mit dem Formular und der gesetzeskonformen
Bestaetigungsschaltflaeche 'jetzt kuendigen'. Eigener Error-Bag,
damit das Kontakt-Modal im Footer nicht mit aufspringt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Oeffentliche Bestaetigungsseite /de/kuendigung ohne Login: Art der
Kuendigung (Grund nur bei ausserordentlich), Zuordnungsdaten,
Beendigungszeitpunkt; Honeypot + Rate-Limit wie beim Widerruf.
Sofortige automatische Zugangsbestaetigung per E-Mail (Par. 312k
Abs. 4 BGB) + Betreiber-Mail. Kuendigungen laufen in der
Recht-&-Compliance-Queue unter den Widerrufen auf (K-Vorgaenge,
eigener Filter, Erledigt/Wieder-oeffnen, vierte KPI-Karte).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
x-web.static-page ist jetzt host-aware: Auf der pressekonto-Domain laden
Utility-Bar + Hub-Header/-Footer (inkl. Kontakt- und Widerrufs-Modal,
damit die Footer-Buttons ueberall funktionieren) statt des
Spoke-Geruests; Breadcrumb fuehrt zur Hub-Landing. Hub-Anker-Navigation
(#tarife/#funktion/#familie/#faq) absolut auf die Landing, damit sie
auch von Unterseiten aus funktioniert. Spokes unveraendert (Test).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Eigene Widerrufe-Sektion unter der Anfragen-Tabelle in
/admin/legal-requests: Vorgangsnummer, Zuordnungsdaten, Statusfilter
(offen/alle/erledigt, eigener Paginator), Erledigt/Wieder-oeffnen-Aktion
mit resolved_at; KPI-Karte "Offene Widerrufe". Dazu
WithdrawalRequest-Factory (+resolved-State) fuer die Tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gut sichtbare Schaltflaeche "Vertrag widerrufen" im Footer; das Modal
fuehrt zweistufig durch den Widerruf: Schritt 1 nur Zuordnungsdaten,
Schritt 2 Kontrollansicht mit Bestaetigungsbutton "Jetzt widerrufen".
Erfolgs-/Fehlerzustand oeffnet automatisch (getrennter Error-Bag,
kollidiert nicht mit dem Kontakt-Modal).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
POST /widerruf (host-agnostisch, throttled, Honeypot, eigener
withdrawal-Error-Bag): persistiert nur Zuordnungsdaten (Name, E-Mail,
optionale Bestellnummer - KEIN Grund, darf nicht verlangt werden) und
versendet sofort die gesetzlich vorgeschriebene automatische
Eingangsbestaetigung mit Zeitstempel an den Verbraucher plus eine
interne Benachrichtigung an die Brand-Kontaktadresse des Hosts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ersetzt den TODO-Platzhalter: Name/E-Mail/Nachricht, Honeypots,
Erfolgs-Flash statt Formular nach Versand, Datenschutz-Hinweis.
Styling analog legal-request. Telefon/Adresse sind weiterhin
Platzhalterdaten (R3.2, [EXTERN]).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bisher griffen die Laravel-Default-Seiten. Gemeinsames Inline-CSS-Layout
ohne Vite-Abhaengigkeit (rendert auch bei kaputtem Asset-Build), nutzt
die geteilte domainName-Variable mit Fallback, noindex, Dark-Mode via
prefers-color-scheme.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bisher wurde der NullNewsletterSyncClient unbedingt gebunden -
NEWSLETTER_SYNC_ENABLED=true mit echtem Provider haette stumm nichts
getan. Jetzt: Platzhalter-Provider -> No-Op-Client, aktivierter aber
nicht implementierter Provider -> RuntimeException (fail fast). Das
Admin-UI weist beim Test-Sync auf den Platzhalter hin.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Feld existierte seit der Migration-2026-Erweiterung, wurde aber nirgends
geschrieben. Jetzt gesetzt bei: Volt-Registrierung (bestaetigte AGB-/
Datenschutz-Checkbox), Magic-Link-Provisioning und Google-OAuth (beide
via CustomerAccountProvisioner, Konto-Anlage nutzerinitiiert).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Uebersicht /admin/me/press-releases war auch fuer leere Konten ~2s: der OR-Filter in scopeAccessibleBy zwang die Paginator-count(*) zum Full-Scan (EXPLAIN type=ALL, ~174k Zeilen) und den Zeilen-Fetch bei 0 Treffern zum vollen created_at-Index-Scan (~1s). Neue PressRelease::accessiblePageFor() nutzt dieselbe index-freundliche UNION wie accessibleStatusCountsFor(): schmale UNION liefert die Seiten-IDs ueber user_id-/company_id-Index, danach nur 25 Modelle hydratisiert; Total via schnellem UNION-count. Messung: leer ~2000ms -> 167ms, groesstes Konto (20.830 PMs) ~740ms -> 468ms.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
role- und billing-Feldgrids aus create+edit in <x-portal.user-role-fields> und <x-portal.user-billing-fields> gezogen (nur das innere Grid; Panel-/Card-Wrapper bleiben pro Seite). Nebenbei-Bugfix: create zeigt jetzt den billing.name-Validierungsfehler (Feld wurde via userBillingRules() schon validiert, Fehler aber nie gerendert). company-link-picker + contact-fields bleiben bewusst inline (divergiert bzw. nur in edit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Status-Badge-Farblogik (ok/warn/err/muted) aus 7 divergenten match-Bloecken in eine Enum-Methode gezogen. Archiviert/Entwurf jetzt konsistent 'muted' (grau) auch auf Detail-/Edit-/Log-Seiten (vorher dort 'hub'-Akzent). Toter statusColor-Block (Flux-Farben, nie referenziert) in customer/show entfernt. flux:badge :color in admin/users bleibt bewusst separat (eigenes Farbvokabular, Einzel-Call-Site).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Self-contained Alpine-Toggle (Anzeigen/Verbergen) pro Feld; Props name/label/
autocomplete/placeholder/required/autofocus/show-error, id default auth-<name>,
Slot :labelAction fuer den 'Passwort vergessen?'-Link (login). Ersetzt die
field-pw-wrap-Bloecke in login, register (2), reset-password (2), confirm-
password (1). Die nun ungenutzten form-level x-data={showPassword...} entfernt.
Nebeneffekt/Bugfix: register+reset nutzten :placeholder="__(...)" auf rohem
<input> -> wurde als Alpine-Bind woertlich ausgegeben und schlug fehl (leerer
Platzhalter). Als Component-Prop wird __() korrekt in PHP aufgeloest, der
Hinweistext (z. B. 'Mindestens 8 Zeichen') erscheint jetzt.
Bestaetigungsfelder ohne Fehleranzeige (show-error=false, wie zuvor).
116 Auth-Tests gruen, alle Views kompilieren.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Analyse der beiden ~40/160-Zeilen-save()-Methoden (users/create vs edit): sie
teilen nur den User-Attribut-Payload und den byte-identischen Firmen-Sync-Loop
(Bearbeiten hat zusaetzlich Profil/Kontakte/volle Billing-CRUD). Ein voller
UserUpsertService waere Over-Engineering (ueberwiegend Branches) — daher nur
der wirklich gemeinsame Persistenz-Kern:
User::syncCompaniesWithRoles(array $companyIds, array $roles) baut das
Pivot-Payload (fehlende Rolle => 'member') und synct. Beide Formulare nutzen
es; im Bearbeiten kommen die linkedCompanyIds nun direkt aus $validated.
Test: UserSyncCompaniesTest (Rollen-Zuordnung, member-Default, Detach).
29 User-Tests gruen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Component um optionalen Default-Slot erweitert (rendert unter den Aktionen)
fuer Zusatzinhalt wie die Onboarding-Schritte der Leerzustaende.
Umgestellt:
- customer/press-releases/index: 3 States (Suche ohne Treffer, Filter ohne
Treffer=warm, gar keine PMs inkl. Onboarding-Grid im Default-Slot)
- customer/press-kits/index: 2 States (Filter=warm, keine Firma inkl.
Onboarding-Grid), Panel-Wrapper bleibt erhalten
Der Such-Titel behaelt das originale ASCII-Anfuehrungszeichen (via einfach-
gequotetem Attribut mit escaptem PHP-String). Alle Views kompilieren,
162 Customer-/PM-Tests gruen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Zweite Struktur-Component: buendelt den empty-stage-Block (runder Icon-Rahmen
+ Titel + Subtext + optionale Aktionen). Props: title, subtitle, variant
(default|warm|err); Slots :icon und :actions.
Erst-Umstellung: die zwei Leerzustaende der Admin-PM-Liste (gefiltert = warm
mit 'Filter zuruecksetzen', leer = Standard mit 'Neue PM anlegen'). Weitere
Vorkommen (customer/press-releases, customer/press-kits) folgen nach Sicht-
pruefung. 36 Tests gruen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Neun weitere Standard-Header (md) auf <x-portal.page-header>:
- roles/index (bedingter Anlegen-Button), roles/create (Guard-Badge via
:meta), roles/edit (ID + Guard + bedingte Systemrolle via :meta)
- footer-codes/index, footer-codes/create, footer-codes/edit (ID + Global +
Aktiv/Inaktiv via :meta, Titel als Beschreibung)
- presets/index, presets/create, presets/edit (ID via :meta)
Roles-/FooterCode-/Preset-Tests (23) gruen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>