- FAQ komplett neu: B2B-Hinweis, echte Datei-Limits (Bilder 16 MB,
Anhaenge inkl. Office/ZIP 25 MB), Video-Einbettungs-Behauptung raus,
Stripe/Rechnung als Zahlungswege, Kuendigungs- und API-Antworten mit
echten Zielen, kein Export-/Farben-/Wizard-Versprechen mehr
- Hilfe-Center: tote '#'-Links durch echte Ziele ersetzt (Registrieren,
Kuendigungsseite, Konto-Login), Support-Kontakt als mailto, erfundene
Geschaeftszeiten entfernt
- Veroeffentlichen: Fantasie-Preismodell ('ab 89 EUR', Credits je
Veroeffentlichung, Cross-Publishing separat) durch echte Konditionen
ersetzt (Einzel-PM + Tarife aus DB/config via VeroeffentlichenPageData);
Magic-Link-Registrierungs-Claim korrigiert
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
96 lines
3.6 KiB
PHP
96 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Web;
|
|
|
|
use App\Enums\PressReleaseStatus;
|
|
use App\Models\Company;
|
|
use App\Models\Plan;
|
|
use App\Models\PressRelease;
|
|
use App\Scopes\PortalScope;
|
|
use App\Support\EditorialClock;
|
|
use App\Support\WebPortalContext;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
/**
|
|
* Datenaufbereitung der Veröffentlichen-Marketingseite (Einreichung läuft im
|
|
* Publisher-Hub): Archiv-Kennzahlen, Beispiel-Meldung und aktive Newsrooms.
|
|
*/
|
|
class VeroeffentlichenPageData
|
|
{
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function build(WebPortalContext $context): array
|
|
{
|
|
$portalValues = $context->portalValues;
|
|
$language = $context->language;
|
|
$referenceNow = EditorialClock::reference($portalValues, $language);
|
|
$recentSince = $referenceNow->copy()->subDays(30);
|
|
|
|
$publishedBase = static fn (): Builder => PressRelease::query()
|
|
->withoutGlobalScope(PortalScope::class)
|
|
->forPortalValues($portalValues)
|
|
->forLanguage($language)
|
|
->visibleInArchive();
|
|
|
|
$archiveTotal = $publishedBase()->count();
|
|
$publishedToday = $publishedBase()->whereDate('published_at', $referenceNow->toDateString())->count();
|
|
$oldest = $publishedBase()->min('published_at');
|
|
$oldestYear = $oldest ? (int) Carbon::parse($oldest)->format('Y') : null;
|
|
|
|
$inReview = PressRelease::query()
|
|
->withoutGlobalScope(PortalScope::class)
|
|
->whereIn('portal', $portalValues)
|
|
->where('status', PressReleaseStatus::Review)
|
|
->where('language', $language)
|
|
->count();
|
|
|
|
$exampleRelease = $publishedBase()
|
|
->with([
|
|
'company',
|
|
'category.translations' => fn ($query) => $query->where('locale', $language),
|
|
])
|
|
->orderByDesc('published_at')
|
|
->first();
|
|
|
|
$newsroomFilter = static function (Builder $query) use ($portalValues, $language, $recentSince): void {
|
|
$query
|
|
->forPortalValues($portalValues)
|
|
->forLanguage($language)
|
|
->published()
|
|
->where('published_at', '>=', $recentSince);
|
|
};
|
|
|
|
$newsroomQuery = Company::query()
|
|
->withoutGlobalScope(PortalScope::class)
|
|
->whereIn('portal', $portalValues)
|
|
->where('is_active', true)
|
|
->whereHas('pressReleases', fn (Builder $query) => $newsroomFilter($query));
|
|
|
|
$activeNewsroomsTotal = (clone $newsroomQuery)->count();
|
|
$activeNewsroomNames = (clone $newsroomQuery)
|
|
->withCount(['pressReleases as recent_count' => fn (Builder $query) => $newsroomFilter($query)])
|
|
->orderByDesc('recent_count')
|
|
->limit(10)
|
|
->pluck('name')
|
|
->all();
|
|
|
|
// Preis-Anker aus denselben Quellen wie die Preisseite (PreisePageData),
|
|
// damit die Marketingseite nie von den echten Konditionen abweicht.
|
|
$cheapestPlan = Plan::query()->active()->orderBy('monthly_price_cents')->first();
|
|
|
|
return [
|
|
'referenceNow' => $referenceNow,
|
|
'archiveTotal' => $archiveTotal,
|
|
'publishedToday' => $publishedToday,
|
|
'inReview' => $inReview,
|
|
'oldestYear' => $oldestYear,
|
|
'exampleRelease' => $exampleRelease,
|
|
'activeNewsroomNames' => $activeNewsroomNames,
|
|
'activeNewsroomsTotal' => $activeNewsroomsTotal,
|
|
'singlePmPriceCents' => (int) config('billing.single_pm_price_cents'),
|
|
'cheapestPlan' => $cheapestPlan,
|
|
];
|
|
}
|
|
}
|