R2.2: HubPageData - Plan-Katalog + echte Plattform-Kennzahlen fuer die Hub-Landing
Liefert Plan::active(), Einzel-PM-Preis und stuendlich gecachte echte Zahlen (Archiv-Mitteilungen gesamt/je Portal, aktive Newsrooms, Branchen) an web.pressekonto. Tests: Katalog-Preise statt Fake-Liste, keine toten Links/erfundenen Referenzen, Rechtsseiten auf der Hub-Domain erreichbar. (Gehoert zum Web-Commit der Hub-Ueberarbeitung.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5226f5de2b
commit
f6edb4aec5
3 changed files with 104 additions and 10 deletions
49
app/Services/Web/HubPageData.php
Normal file
49
app/Services/Web/HubPageData.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Web;
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Models\Category;
|
||||
use App\Models\Company;
|
||||
use App\Models\Plan;
|
||||
use App\Models\PressRelease;
|
||||
use App\Scopes\PortalScope;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/**
|
||||
* Datenaufbereitung der Hub-Landing (pressekonto.de): aktiver Tarif-Katalog
|
||||
* plus echte Plattform-Kennzahlen statt statischer Marketing-Zahlen.
|
||||
*/
|
||||
class HubPageData
|
||||
{
|
||||
private const StatsCacheKey = 'web.hub.landing-stats';
|
||||
|
||||
private const StatsTtlSeconds = 3600;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function build(): array
|
||||
{
|
||||
return [
|
||||
'plans' => Plan::query()->active()->get(),
|
||||
'singlePmPriceCents' => (int) config('billing.single_pm_price_cents'),
|
||||
'hubStats' => Cache::remember(self::StatsCacheKey, self::StatsTtlSeconds, function (): array {
|
||||
$published = PressRelease::query()
|
||||
->withoutGlobalScope(PortalScope::class)
|
||||
->visibleInArchive();
|
||||
|
||||
return [
|
||||
'releases_total' => (clone $published)->count(),
|
||||
'releases_presseecho' => (clone $published)->forPortalValues([Portal::Presseecho->value, Portal::Both->value])->count(),
|
||||
'releases_businessportal' => (clone $published)->forPortalValues([Portal::Businessportal24->value, Portal::Both->value])->count(),
|
||||
'newsrooms' => Company::query()
|
||||
->withoutGlobalScope(PortalScope::class)
|
||||
->whereHas('pressReleases', fn ($query) => $query->withoutGlobalScope(PortalScope::class)->visibleInArchive())
|
||||
->count(),
|
||||
'categories' => Category::query()->withoutGlobalScope(PortalScope::class)->count(),
|
||||
];
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ use App\Models\Category;
|
|||
use App\Models\Company;
|
||||
use App\Models\Plan;
|
||||
use App\Scopes\PortalScope;
|
||||
use App\Services\Web\HubPageData;
|
||||
use App\Support\DomainAssetContext;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ Route::get('/', function () use ($applyWebDomainConfig) {
|
|||
if (str_contains($domain, 'pressekonto')) {
|
||||
$applyWebDomainConfig('pressekonto');
|
||||
|
||||
return view('web.pressekonto');
|
||||
return view('web.pressekonto', app(HubPageData::class)->build());
|
||||
}
|
||||
|
||||
return redirect(request()->getSchemeAndHttpHost().'/'.SetEdition::DEFAULT_EDITION, 301);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
<?php
|
||||
|
||||
use Database\Seeders\PlanSeeder;
|
||||
use Tests\TestCase;
|
||||
|
||||
test('pressekonto hub homepage renders the publisher landing shell', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->seed(PlanSeeder::class);
|
||||
|
||||
$this->get('https://pressekonto.test/')
|
||||
->assertSuccessful()
|
||||
->assertSee('presse', false)
|
||||
|
|
@ -14,15 +17,57 @@ test('pressekonto hub homepage renders the publisher landing shell', function ()
|
|||
->assertSeeText('So funktioniert es')
|
||||
->assertSeeText('Vier Schritte')
|
||||
->assertSeeText('Tarife')
|
||||
->assertSeeText('Starter')
|
||||
->assertSeeText('Standard')
|
||||
->assertSeeText('Pro')
|
||||
->assertSeeText('Enterprise')
|
||||
->assertSeeText('Hinter pressekonto')
|
||||
->assertSeeText('Die Plattform in Zahlen')
|
||||
->assertSeeText('Plattform im Überblick')
|
||||
->assertSeeText('Häufige Fragen')
|
||||
->assertSeeText('Loslegen')
|
||||
->assertSeeText('Alle Systeme betriebsbereit');
|
||||
->assertSeeText('Loslegen');
|
||||
});
|
||||
|
||||
test('pressekonto hub pricing is driven by the plan catalog', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->seed(PlanSeeder::class);
|
||||
|
||||
$response = $this->get('https://pressekonto.test/')->assertSuccessful();
|
||||
|
||||
// Alle aktiven Tarife aus dem Katalog inkl. Kontingent; die frühere
|
||||
// hartcodierte Fake-Preisliste (0/49/149 €, "Standard") ist raus.
|
||||
$response
|
||||
->assertSeeText('Starter')
|
||||
->assertSeeText('Business')
|
||||
->assertSeeText('Pro')
|
||||
->assertSeeText('Agency')
|
||||
->assertSee('29 €', false)
|
||||
->assertSee('199 €', false)
|
||||
->assertDontSeeText('Standard')
|
||||
->assertDontSee('149 €', false);
|
||||
});
|
||||
|
||||
test('pressekonto hub homepage has no dead links or fabricated references', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->seed(PlanSeeder::class);
|
||||
|
||||
$response = $this->get('https://pressekonto.test/')->assertSuccessful();
|
||||
$content = $response->getContent();
|
||||
|
||||
expect($content)
|
||||
->not->toContain('href="#"')
|
||||
->not->toContain('Siemens')
|
||||
->not->toContain('Alle Systeme betriebsbereit')
|
||||
->not->toContain('14 Tage')
|
||||
->not->toContain('ISO 27001');
|
||||
|
||||
// Rechtsseiten sind auf der Hub-Domain verlinkt und erreichbar.
|
||||
expect($content)->toContain('/de/impressum')->toContain('/de/datenschutz');
|
||||
});
|
||||
|
||||
test('legal pages are reachable on the hub domain', function () {
|
||||
/** @var TestCase $this */
|
||||
$this->get('https://pressekonto.test/de/impressum')->assertSuccessful();
|
||||
$this->get('https://pressekonto.test/de/datenschutz')->assertSuccessful();
|
||||
$this->get('https://pressekonto.test/de/agb')->assertSuccessful();
|
||||
$this->get('https://pressekonto.test/de/cookies')->assertSuccessful();
|
||||
});
|
||||
|
||||
test('pressekonto hub homepage uses the brand-mark splitting without TLDs', function () {
|
||||
|
|
@ -42,10 +87,9 @@ test('pressekonto hub homepage uses the brand-mark splitting without TLDs', func
|
|||
->toContain('>echo<')
|
||||
->toContain('>konto<');
|
||||
|
||||
// pressekonto darf ausschliesslich in der Vertriebs-Mailto-Adresse als Domain auftauchen,
|
||||
// niemals als Marken-Schreibweise im Lese-Text.
|
||||
expect(substr_count($content, 'pressekonto.de'))
|
||||
->toBe(substr_count($content, 'vertrieb@pressekonto.de'));
|
||||
// pressekonto darf als Domain nirgends im Lese-Text auftauchen — nur die
|
||||
// gesplittete Marken-Schreibweise.
|
||||
expect($content)->not->toContain('pressekonto.de');
|
||||
});
|
||||
|
||||
test('pressekonto hub homepage loads the hub theme assets, not portal admin', function () {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue