web: Transparenzseiten — was kostet, ohne Abo, loeschen/aendern (Roadmap 2.4)

Drei Seiten mit unterschiedlicher Suchintention, DE/EN, Hub + Spokes:
- /was-kostet-eine-pressemitteilung: kurze Antwort im ersten Bildschirm,
  vier Abrechnungsmodelle ohne Anbieternamen und fremde Preise, sechs
  Prueffragen fuer Folgekosten, "Was bei uns gilt" (sieben Zusagen mit
  Einloesung), Rechenbeispiel Einzelabrechnung gegen Starter-Monate mit
  Zeilen fuer beide Seiten.
- /pressemitteilung-veroeffentlichen-ohne-abo: Ablauf, enthalten / nicht
  enthalten, nichts verfaellt, wann sich ein Tarif lohnt.
- /pressemitteilung-loeschen-oder-aendern-lassen: fuenf Wege als
  Tabelle (Korrektur, Aenderung, DSGVO, Depublikation, DSA), Antragsweg
  ueber /melden/pm, Korrektur statt Loeschung, Absatz fuer Meldungen auf
  fremden Portalen ohne Zusagen ueber Dritte.

PricingTransparencyData liefert alle Zahlen aus config/billing, credits,
legal und dem Starter-Tarif; TransparencyPageController leitet die Marke
aus dem Host ab. Tabellen-Typografie fuer .pm-body. Verlinkt aus
Preisuebersicht, Hilfe-Center, Sitemaps und llms.txt.

Test TransparencyPagesTest (9) erzwingt u. a., dass im Rechenbeispiel
beide Modelle Zeilen gewinnen. Protokoll Teil 5, Roadmap 2.4 abgehakt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-17 12:00:28 +00:00
parent 4616bb7aad
commit 106edb1384
18 changed files with 953 additions and 4 deletions

View file

@ -48,6 +48,8 @@ class LlmsTxtController extends Controller
'- [Was eine gute Pressemitteilung ausmacht]('.$base.'/de/gute-pressemitteilung): Anlass, Aufbau, Sprache, Prüfablauf',
'- [FAQ]('.$base.'/de/faq)',
'- [Richtlinien]('.$base.'/de/richtlinien): Was veröffentlicht wird und was nicht',
'- [Was kostet eine Pressemitteilung?]('.$base.'/de/was-kostet-eine-pressemitteilung): Abrechnungsmodelle, Prüfliste für Folgekosten, Rechenbeispiel',
'- [Pressemitteilung löschen oder ändern lassen]('.$base.'/de/pressemitteilung-loeschen-oder-aendern-lassen)',
'- [API]('.$base.'/de/api): Einlieferung per REST-API; OpenAPI-Spezifikation unter '.$base.'/docs/api/v1',
'',
'## Portale',
@ -106,6 +108,9 @@ class LlmsTxtController extends Controller
'',
'- [Veröffentlichen]('.$base.'/de/veroeffentlichen): Wie eine Meldung auf das Portal kommt',
'- [Preise]('.$base.'/de/preise)',
'- [Was kostet eine Pressemitteilung?]('.$base.'/de/was-kostet-eine-pressemitteilung): Abrechnungsmodelle, Prüfliste für Folgekosten, Rechenbeispiel',
'- [Pressemitteilung veröffentlichen ohne Abo]('.$base.'/de/pressemitteilung-veroeffentlichen-ohne-abo)',
'- [Pressemitteilung löschen oder ändern lassen]('.$base.'/de/pressemitteilung-loeschen-oder-aendern-lassen): Korrektur, Änderung, DSGVO, Depublikation',
'- [Was eine gute Pressemitteilung ausmacht]('.$base.'/de/gute-pressemitteilung)',
'- [Hilfe]('.$base.'/de/hilfe)',
'- [API]('.$base.'/de/api): Einlieferung per REST-API über den Publisher-Bereich '.$hub,

View file

@ -26,12 +26,14 @@ class SitemapController extends Controller
/** @var list<string> */
private const HUB_PAGES = [
'faq', 'hilfe', 'gute-pressemitteilung', 'api', 'kuendigung',
'was-kostet-eine-pressemitteilung', 'pressemitteilung-veroeffentlichen-ohne-abo', 'pressemitteilung-loeschen-oder-aendern-lassen',
'impressum', 'datenschutz', 'agb', 'richtlinien', 'melden',
];
/** @var list<string> */
private const PORTAL_PAGES = [
'preise', 'veroeffentlichen', 'newsrooms', 'faq', 'hilfe', 'gute-pressemitteilung', 'api',
'was-kostet-eine-pressemitteilung', 'pressemitteilung-veroeffentlichen-ohne-abo', 'pressemitteilung-loeschen-oder-aendern-lassen',
'impressum', 'datenschutz', 'agb', 'richtlinien', 'melden',
];

View file

@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers\Web;
use App\Http\Controllers\Controller;
use App\Services\Web\PricingTransparencyData;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
/**
* Transparenzseiten (Roadmap 2.4): drei Seiten mit unterschiedlicher
* Suchintention, alle mit denselben Zahlen aus PricingTransparencyData.
*/
class TransparencyPageController extends Controller
{
public function cost(Request $request, PricingTransparencyData $data): View
{
return view('web.was-kostet-eine-pressemitteilung', $this->pageData($request, $data));
}
public function noSubscription(Request $request, PricingTransparencyData $data): View
{
return view('web.pressemitteilung-veroeffentlichen-ohne-abo', $this->pageData($request, $data));
}
public function changeOrDelete(Request $request, PricingTransparencyData $data): View
{
return view('web.pressemitteilung-loeschen-oder-aendern-lassen', $this->pageData($request, $data));
}
/**
* Markenname aus dem Host (wie /melden) — der Theme-Provider ist in
* Tests noch nicht auf den Request-Host gelaufen.
*
* @return array<string, mixed>
*/
private function pageData(Request $request, PricingTransparencyData $data): array
{
$host = $request->getHost();
$brandKey = str_contains($host, 'pressekonto')
? 'pressekonto'
: (str_contains($host, 'presseecho') ? 'presseecho' : 'businessportal24');
$brand = (array) config('domains.domains.'.$brandKey.'.brand', []);
return [
...$data->build(),
'brandLabel' => ($brand['name'] ?? '').($brand['accent'] ?? ''),
];
}
}