Badge "Boost bis TT.MM.JJJJ" in der Status-Spalte; Boost-Enden der
Seite in einer Query aggregiert statt isBoosted() je Zeile.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Geboostete PMs besetzen Lead- und Top-Slots vor der Chronologie
(HomePageData/CategoryPageData ueber boosted()-Scope) und tragen das
Badge "Top-Platzierung". Abgelaufene Boosts wirken nicht.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die im Spoke-Frontend-Audit entfernten Anzeige-Flaechen (focus-ad,
feed-ad, Inline-Ad) kehren als dezente Eigenwerbung zurueck:
- neue Komponente x-web.ad-slot im frueheren "ANZEIGE"-Stil mit Hinweis
"Hier koennte Ihre Werbung stehen" (DE/EN)
- eingebaut an den drei frueheren Stellen: Hero-Sidebar der Startseiten,
Feed nach der dritten Meldung, PM-Detailseite nach der
Unternehmensbeschreibung (nicht in der privaten Vorschau)
- Klick oeffnet das schwebende Kontakt-Widget mit vorgewaehltem neuen
Thema "Werbung buchen" (ContactMessageTopic::Advertising); Anfragen
landen wie gewohnt in der Admin-Queue "Kontakt"
Co-authored-by: Cursor <cursoragent@cursor.com>
Submit-Modal auf zwei Auswahloptionen reduziert (kein Label vs.
"Ganz oder ueberwiegend mit generativer KI erstellt"); alle vier
Enum-Werte bleiben fuer API/Bestand gueltig und mappen per formOption()
auf die zwei Gruppen. Oeffentliche Detailseite rendert jetzt Text-Label
("KI-generiert: Nach Angaben ...") und Bild-Badge (KI-generiert/
KI-bearbeitet, abgedunkelter Hintergrund) — fehlte dort bisher komplett.
Bildnachweis-Vorschlag ohne "Foto:"-Praefix (stand sonst doppelt).
Einreich-Toast + FAQ: automatische Pruefung in Minuten statt 24 h.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Boost-Laufzeiten und Nachweis oeffnen jetzt ein Bestaetigungs-Modal mit
Preis, Guthaben und Restsaldo; erst "Kostenpflichtig buchen" bucht ab.
Boost-Buttons primary mit explizitem Kauf-Label.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ein vergessener Postman-Platzhalter (PM_ID) lief als String in die
int-typisierten Controller-Parameter. Numerische where-Constraints auf
den v1- und /service-Gruppen; Legacy faellt in den 410-Catch-all.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der Kontotyp ist kein verlaesslicher Marker (connektar ist agency,
nicht apiuser) — submitForReview traegt jetzt die Quelle ('customer'
Panel, 'api' Legacy/v1) ins Status-Log, notifyAuthor prueft den
letzten Review-Uebergang. Kein Schema-Change noetig.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Idempotenter Command (nur local/staging): aktiver Legacy-API-User mit
Grandfathering, importiertem X-ApiKey, api:immediate-publication,
Firma + Pressekontakt; gibt Key + curl-Sammlung des /service-Flows aus.
Live gegen die lokale App verifiziert (Firma -> Kontakt -> PM sofort
published -> GET 200 im Alt-Format).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Befund: v1 hatte keine KI-Deklarationsfelder — Submit lief seit der
KI-Kennzeichnungspflicht (09.07.) immer auf 422 und haette alle
Legacy-API-Kunden blockiert (Suite uebersah es: Factory deklariert).
- v1-Submit verlangt ai_text_declaration; der Call gilt dokumentiert
als verbindliche Bestaetigung (Art. 50 KI-VO) und schliesst die
Archiv-Kenntnisnahme (AGB par. 13) ein
- Bild-Upload verlangt ai_declaration (original/ai_generated/ai_edited)
- Legacy-/service-Aliasse defaulten auf none/original — das
API-Kunden-Mailing (R6.4) weist auf die Erklaerungswirkung hin
- OpenAPI v1.yml + lang-Hinweis aktualisiert; Tests erweitert
(Submit/Image/LegacyCompatibility)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
composer run dev startete queue:listen ohne --queue-Angabe, also nur
default — die KI-Klassifikationsjobs (Queue classification) blieben
lokal liegen und PMs steckten in der manuellen Review fest, obwohl
das Auto-Routing (Rot -> Ablehnung, Gelb/Gruen -> Auto-Publish)
laengst implementiert ist. Prod-Runbook war bereits korrekt
(--queue=classification,default).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Meldung/DSGVO/Persoenlichkeitsrecht auf die am 06.07. freigegebenen
/melden-Texte gezogen: kostenlose manuelle Pruefung, unverzuegliche
Eingangsbestaetigung mit Vorgangsnummer (sofern E-Mail), Entscheidung
mit Begruendung; DSGVO ohne pauschale 30-Tage-Zusage, stattdessen
Bestaetigungslink/Nachweis wie im Meldeverfahren
- ENTWURF-TODO entfernt, Querverweis auf /melden ergaenzt
- Render-Test erweitert (Vorgangsnummer, kostenlos, melden-Link)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Meta-Zeile zeigt Aufrufe (hits inkl. aktuellem Aufruf) in beiden
Hero-Varianten; in der Vorschau ausgeblendet
- Teilen-Buttons melden den Kanal an release.share (fetch keepalive,
CSRF-Meta); Link-kopieren trackt nur bei erfolgreichem Copy
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- press_release_events (type view/share, channel, ohne Personenbezug):
Model/Enums/Factory, Cascade-Delete an der PM
- Detailaufruf loggt View-Event zusaetzlich zum Legacy-hits-Zaehler
(bleibt bewusst einfacher Page-Request-Zaehler, User-Vorgabe 10.07.)
- POST press-release/{slug}/share (throttle 30/min, nur archiv-sichtbare
PMs des Portals) loggt Teilen-Klicks je Kanal (LinkedIn/X/E-Mail/Copy)
- Kundenpanel-PM-Seite: neue Kachel Geteilt mit Kanal-Aufschluesselung
- lang: web.detail.views (de/en)
- Tests: Web/PressReleaseTrackingTest (7)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7-Commit-WIP-Aufraeumung + R5.3/Buchungsdaten im Stand-Block, R6.5-WIP-Punkt
abgehakt, neuer Block 'Wo es morgen weitergeht'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
OperationalAlert loggt critical + Best-Effort-Mail an MONITORING_ALERT_EMAIL
(Fallback LEGAL_CONTACT_EMAIL, feature-flagged). Alle 6 Scheduler-Tasks mit
onFailure, Queue::failing-Listener; .env.example: LOG_STACK=daily als Prod-Soll.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cashier spiegelt die Periode nicht lokal; neuer SubscriptionPeriodResolver
holt start_date + current_period_end (Item-Ebene) gecacht, Fallback null.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Der docs/-Mount kam nicht mit; die Route /docs/api/v1 lief dadurch auf 500.
Liest jetzt public/api/v1.yml mit 404-Guard.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Kategorie-Pflichtfeld direkt unter die Firma statt in die Sidebar.
Titelbild-Upload öffnet ein Modal: erst Bild wählen, dann erscheinen
die gestrafften Schritte (Herkunft & Bildnachweis mit Vorschlag aus
dem Urheber, Personen & Rechte, Bestätigung), Optionales eingeklappt.
Bildrechte-Checkbox im Einreichen-Modal nur noch mit eigenem Titelbild.
Co-authored-by: Cursor <cursoragent@cursor.com>
Selbstdeklaration des KI-Einsatzes je PM (Text) und je Bild inkl.
Submit-Gate im PressReleaseService, versioniertem Audit-Trail
(press_release_ai_declarations) und öffentlichem KI-Label in
Einreicher-Stimme. Bestätigung im Submit-Modal ist Pflicht.
Co-authored-by: Cursor <cursoragent@cursor.com>
Zwei Bugs im Scroll-zum-Fehler-Helper: '[wire\:submit]' matchte das
<form> selbst, sodass jeder Klick in ein Feld den Scroll armierte
(sichtbar beim USt-ID-Live-Check im Profil); und der leere, versteckte
Flux-Error-Container unter jedem Feld galt wegen Whitespace als
':not(:empty)' — fokussiert wurde dadurch grundlos das erste Feld.
Enter-Submit wird jetzt über das submit-Event erkannt, Fehlerfelder
nur noch bei echtem Fehlertext.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
USt-IDs laufen ab — Kunden bestellten anderswo über veraltete IDs
steuerfrei. Vor jedem Checkout wird die Profil-USt-ID jetzt einmal per
eVatR (BZSt) geprüft (Ergebnis 6 h gecacht): Eine definitiv abgelehnte
ID geht nicht an Stripe, nicht mehr passende eu_vat-Einträge am
Stripe-Customer werden entfernt — Stripe Tax rechnet dann wieder mit
Steuer statt Reverse Charge. Fail-open bei API-Ausfall und deutschen
IDs (eVatR prüft nur ausländische EU-IDs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
„Eingelöste Käufe" nennt jetzt Kauftyp und PM-Titel (z. B.
„Depublikation: <Titel>") über das neue kompakte shortLabel() am
SinglePurchaseType. Die Checkout-Erfolgsbanner (Buchung, Tarifwechsel,
vorgemerkter Wechsel) und die Extra-PM-Bestätigung nutzen das
etablierte grüne Success-Muster (ok-soft) statt Hub-Blau.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drei Lücken im bezahlten Depublikations-Flow (T12) geschlossen:
- Webhook setzt den Kauf jetzt auf status=consumed (vorher nur
consumed_at) — erst damit erscheint er unter „Eingelöste Käufe".
- Einmalkäufe (Depublikation, Einzel-PM, Credit-Topup) aktivieren
invoice_creation im Stripe-Checkout: Ohne das Flag erzeugt Stripe im
Payment-Mode keine Rechnung und die STR-Spiegelung lief ins Leere.
Abo-Checkouts bleiben unverändert (Parameter dort unzulässig).
- Neue Bestätigungs-Mail PressReleaseDepublished an den Käufer
(nicht zwingend der PM-Autor) nach vollzogener Depublikation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A5 des Umbaus Agentur-Attribution → User-Profil: Kunden-Profil erhält
den Abschnitt „Agentur-Angaben" (Feld Agentur-/Verteilername, öffentlich
als Übermittler sichtbar). Firma-Anlegen (Kunde + Admin) und
Admin-Firma-Bearbeiten kommen ohne Typ-Feld aus — Neuanlagen sind immer
type=company. Firmen-Übersicht zeigt kein Typ-Label mehr. Admin-User-
Formulare pflegen agency_name; die Detailseite zeigt den Übermittler
als Badge. CompanyFactory erzeugt keine zufälligen Agentur-Typen mehr.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A4 des Umbaus Agentur-Attribution → User-Profil: Agentur-Firmen mit
PMs oder Pressekontakten werden zu type=company umtypisiert (sie sind
faktisch Newsrooms), leere Agentur-Firmen soft-gelöscht. Geschützt vor
Löschung (nur Umtypisierung): Mitgliedschaften Dritter in company_user
oder Verknüpfungen in user_payment_option_company. --dry-run +
JSON-Report; im Deploy-Runbook nach legacy:migrate-agency-names
eingereiht.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A3 des Umbaus Agentur-Attribution → User-Profil: Eager Loading der
Detailseite/Vorschau lädt user:id,agency_name; die Legacy-Relation
PressRelease::submittingAgency() (Pseudo-Firma type=agency via
owner_user_id) entfällt. Tests decken Verteiler-Fall, Namensgleichheit
und PM ohne Übermittler ab.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rendert unter dem Inhalt „Original-Inhalt von {Firma} und übermittelt
von {Übermittler}" (Legacy-Konvention), Übermittler aus dem Profilfeld
users.agency_name; bei Namensgleichheit kollabiert die Zeile zu
„Inhalt übermittelt von …", ohne Übermittler zu „Original-Inhalt von …".
Nur die Attributions-Hunks aus dem laufenden Web-Umbau; die übrigen
Working-Tree-Änderungen an View/Sprachdateien bleiben unangetastet.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Übernimmt je Owner den Namen seiner Legacy-Agentur-Firma(en) ins neue
Profilfeld (A2 des Umbaus Agentur-Attribution → User-Profil). Regeln:
zuletzt aktualisierter Datensatz gewinnt bei Mehrfach-Namen (alle
Varianten im Report), bereits gesetzte Werte werden nie überschrieben,
Owner-lose Agentur-Firmen werden nur reportet. --dry-run + JSON-Report
nach storage/app/private/migration/; im Deploy-Runbook (Erst-Einrichtung)
nach dem Firmen-/User-Import eingereiht.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Erster Schritt des Umbaus Agentur-Attribution → User-Profil
(dev/agentur-attribution/entwicklungsplan.md, A1): Die Übermittler-
Angabe wird Profil-Eigenschaft des einliefernden Accounts statt
Pseudo-Firma vom Typ „agency".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der Klick auf "Kostenpflichtig depublizieren" legte sofort einen
Pending-Kauf an; wer bei Stripe abbrach, sah danach dauerhaft "liegt
bereits ein Depublikations-Vorgang vor". Jetzt sperrt nur ein bezahlter
Vorgang — ein offener Pending-Kauf wird beim nächsten Klick
wiederverwendet (kein Duplikat), und die Detailseite zeigt das Angebot
weiterhin an.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Veröffentlichte PMs zeigten im Backend nur den Status-Badge, ohne Weg
zur öffentlichen Seite. Im Seitenkopf stehen jetzt Direktlinks — bei
Portal::Both einer je Portal (QA-Befund 07.07.).
Die URL-Logik ist als PressRelease::publicPortalLinks() zentralisiert
(vorher doppelt in der Published-Mail und fehlerhaft im Nachweis-PDF:
dort fehlte das /de/press-release/-Segment, die dokumentierte URL lief
ins Leere; bei Both nennt der Nachweis jetzt beide URLs). Edition folgt
der PM-Sprache statt hart „de".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die create-Migration baut das ENUM dynamisch aus
SinglePurchaseType::cases(); Datenbanken von vor dem T12-Case kannten
depublication daher nicht und der Depublikations-Checkout brach mit
"Data truncated for column 'type'" ab. Die Migration gleicht das ENUM
per MODIFY COLUMN auf den aktuellen Case-Stand ab (nur MySQL; frische
DBs bekommen die volle Liste ohnehin über die create-Migration).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der seltene Ausnahme-Service stand zwischen Add-ons und Inhalt; jetzt
steht er als letzte Sektion unter dem Inhalt (QA-Befund 07.07.). Dazu
zwei UI-Tests: Sektion + Unternehmer-Bestätigungs-Protokoll auf der
Detailseite, Ausblenden ohne Preis bzw. bei laufendem Vorgang.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scheiterte ein Boost- oder Nachweis-Kauf am Wallet-Guthaben, gab es nur
einen flüchtigen Toast ohne Weg zur Lösung. Jetzt öffnet sich ein Modal
mit Kosten, Guthaben und fehlender Differenz sowie einem Direktlink
"Credits aufladen" zur Wallet-Sektion der Buchungsseite (neuer
#credits-Anker).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Das Modal war ein unstrukturierter Stapel aus zwei Boxen und losen
Switches; der Kontingent-Badge wurde vom Flex-Layout zusammengedrückt
und brach um ("3 /" / "3"). Jetzt: drei beschriftete Panels (Rechtliche
Hinweise, PM-Kontingent, Ihre Bestätigungen) im portalweiten
panel/panel-head-Stil, die Switches als getrennte Zeilen mit
Fortschrittszähler (x / 4) im Panel-Kopf, Badge mit shrink-0 und
whitespace-nowrap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bei abgelehnten Pressemitteilungen stand die Begründung bisher nur auf
der Detailseite. Der Editor zeigt jetzt dasselbe Hinweis-Panel (Begründung
+ Ablehnungsdatum) über der Schreibfläche, damit der Kunde beim
Überarbeiten sieht, was zu ändern ist.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
QA-Befund 07.07.: Die Filter-/Suchzeile unter den aktuellen Rechnungen
entfällt; das Rechnungsarchiv (Altsystem) erscheint nur noch, wenn der
User dort tatsächlich importierte Rechnungen hat — Neukunden sehen es
nicht mehr. Seitenbeschreibung passt sich entsprechend an.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
QA-Entscheidung 07.07.: Eine Buchung ist nicht rückabwickelbar — es darf
nie eine negative Proration-Rechnung oder ein Stripe-Guthaben entstehen.
- StripeCheckoutService::isDowngrade: sofortiger Wechsel nur, wenn der
Periodenpreis des Ziel-Tarifs den aktuellen erreicht (Wechsel-Rechnung
damit garantiert >= 0); sonst Downgrade
- scheduleDowngrade: Stripe Subscription Schedule stellt das Abo zum Ende
der bezahlten Periode um (proration_behavior none), hebt eine
vorgemerkte Kündigung auf und spiegelt die Vormerkung lokal
(neue Spalten scheduled_stripe_price/scheduled_change_at/
stripe_schedule_id an subscriptions)
- Sofort-Upgrade löst eine bestehende Vormerkung vorher auf (release)
- Webhook customer.subscription.updated räumt die Vormerkung nach
Vollzug ab
- Buchungs-Modal unterscheidet Upgrade (sofort, Differenz-Rechnung) und
Downgrade (Vormerkung, keine Zahlung heute); Banner + Hinweis auf der
Paket-Karte zeigen den vorgemerkten Wechsel mit Datum
- Kontingent-Sperre aus 4b7f6d3 entfernt — ohne Gutschrift gegenstandslos
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>