id = $id;
$pr = $this->getMyPR();
$this->authorize('view', $pr);
}
public function submitForReview(): void
{
$pr = $this->getMyPR();
$this->authorize('submitForReview', $pr);
try {
app(PressReleaseService::class)->submitForReview($pr);
} catch (BlacklistViolationException $e) {
Flux::modal('confirm-submit-review')->close();
Flux::toast(
heading: __('Automatisch abgelehnt'),
text: __('Unzulässiges Wort gefunden: ":word". Bitte überarbeiten.', ['word' => $e->word]),
variant: 'danger',
duration: 8000,
);
return;
} catch (BookingRequiredException|QuotaExceededException $e) {
Flux::modal('confirm-submit-review')->close();
Flux::toast(
heading: __('Nicht eingereicht'),
text: $e->getMessage(),
variant: 'warning',
duration: 8000,
);
return;
}
Flux::modal('confirm-submit-review')->close();
Flux::toast(
heading: __('Eingereicht'),
text: __('Die Redaktion prüft die Pressemitteilung typischerweise innerhalb von 24 h.'),
variant: 'success',
);
}
public function generateShareLink(MagicLinkGenerator $generator): void
{
$pr = $this->getMyPR();
$this->authorize('view', $pr);
$share = $generator->createPressReleaseShareLink($pr, auth()->user());
$this->shareUrl = $share['url'];
$this->shareExpiresAt = $share['expires_at']->format('d.m.Y H:i');
Flux::toast(text: __('Vorschau-Link wurde erzeugt.'), variant: 'success');
}
public function with(): array
{
$pr = $this->getMyPR();
$this->authorize('view', $pr);
$categoryName = $pr->category?->translations->firstWhere('locale', 'de')?->name ?? '–';
$latestRejection = null;
if ($pr->status->value === 'rejected') {
$latestRejection = $pr->statusLogs
->firstWhere(fn ($log) => $log->to_status?->value === 'rejected');
}
$cover = app(PressReleaseCoverImage::class);
$user = auth()->user();
return [
'pr' => $pr,
'categoryName' => $categoryName,
'coverUrl' => $cover->coverUrl($pr, 'cover'),
'coverIsPlaceholder' => $cover->coverIsPlaceholder($pr),
'titleImage' => $pr->images()->orderByDesc('is_preview')->orderBy('sort_order')->orderBy('id')->first(),
'quotaTotal' => $user->pressReleaseQuotaTotal(),
'quotaRemaining' => $user->pressReleaseQuotaRemaining(),
'canEdit' => auth()->user()->can('update', $pr)
&& in_array($pr->status->value, ['draft', 'rejected']),
'latestRejection' => $latestRejection,
'contacts' => $pr->contacts,
'statusLogs' => $pr->statusLogs,
'statusColor' => match($pr->status->value) {
'published' => 'green',
'review' => 'yellow',
'rejected' => 'red',
'archived' => 'blue',
default => 'zinc',
},
];
}
private function getMyPR(): PressRelease
{
return PressRelease::withoutGlobalScopes()
->where('user_id', auth()->id())
->with([
'company:id,name,email,phone',
'category.translations',
'contacts' => fn ($query) => $query
->withoutGlobalScopes()
->orderBy('last_name')
->orderBy('first_name')
->select(['contacts.id', 'contacts.company_id', 'contacts.first_name', 'contacts.last_name', 'contacts.responsibility', 'contacts.email', 'contacts.phone']),
'statusLogs.changedBy:id,name,email',
])
->findOrFail($this->id);
}
}; ?>
@php
$statusClass = match ($pr->status->value) {
'published' => 'ok',
'review' => 'warn',
'rejected' => 'err',
default => 'hub',
};
@endphp
{{-- Flash-Banner ersetzt durch
im Layout. --}}
{{-- ============== PAGE HEADER ============== --}}
{{-- ============== SHARE-LINK ERFOLG ============== --}}
@if ($shareUrl)
{{ __('Öffentlicher Vorschau-Link erstellt') }}
{{ __('gültig bis :date', ['date' => $shareExpiresAt]) }}
@endif
{{-- ============== REJECTION-HINWEIS ============== --}}
@if ($pr->status === PressReleaseStatus::Rejected && $latestRejection)
{{ __('Diese Pressemitteilung wurde abgelehnt') }}
{{ __('Handlung erforderlich') }}
@if ($latestRejection->reason)
{{ __('Begründung') }}:
{{ $latestRejection->reason }}
@else
{{ __('Bitte überarbeiten Sie den Inhalt und reichen Sie die Pressemitteilung erneut ein.') }}
@endif
{{ __('Abgelehnt am') }} {{ $latestRejection->created_at->format('d.m.Y H:i') }}
@endif
{{-- ============== STATUS-WORKFLOW (oben, farblich abgehoben) ============== --}}
@if ($pr->status === PressReleaseStatus::Draft || $pr->status === PressReleaseStatus::Rejected)
{{ __('Status-Workflow') }}
status === PressReleaseStatus::Rejected ? 'err' : 'hub'])>
{{ $pr->status === PressReleaseStatus::Rejected ? __('Überarbeiten') : __('Entwurf') }}
{{ $pr->status === PressReleaseStatus::Rejected
? __('Sie können den Text bearbeiten und erneut zur Prüfung einreichen.')
: __('Reichen Sie den Entwurf ein, sobald er vollständig ist.') }}
@if ($canEdit)
{{ __('Bearbeiten') }}
@endif
{{ $pr->status === PressReleaseStatus::Rejected ? __('Erneut einreichen') : __('Zur Prüfung einreichen') }}
@endif
@if ($pr->status === PressReleaseStatus::Published)
{{ __('Status-Workflow') }}
{{ __('Live') }}
{{ __('Diese Pressemitteilung ist veröffentlicht (seit :date).', ['date' => $pr->published_at?->format('d.m.Y H:i') ?? '–']) }}
@endif
@if ($pr->status === PressReleaseStatus::Review)
{{ __('Status-Workflow') }}
{{ __('Geduld bitte') }}
{{ __('Ihre Pressemitteilung wird gerade geprüft. Sie werden benachrichtigt, sobald eine Entscheidung vorliegt.') }}
@endif
{{-- ============== KONTAKTE + STATUS/VERLAUF ============== --}}
{{ __('Zugeordnete Pressekontakte') }}
@if ($pr->company)
{{ __('Firma') }}
@endif
{{ __('Kontakte, die dieser Pressemitteilung zugeordnet sind.') }}
@forelse ($contacts as $contact)
{{ trim(($contact->first_name ?? '').' '.($contact->last_name ?? '')) ?: __('Kontakt ohne Name') }}
{{ $contact->responsibility ?: __('Keine Rolle hinterlegt') }}
@if ($contact->email)
{{ $contact->email }}
@endif
@if ($contact->phone)
{{ $contact->phone }}
@endif
@empty
{{ __('Dieser Pressemitteilung ist noch kein Pressekontakt zugeordnet.') }}
@if ($pr->company)
{{ __('Kontakte in der Firma prüfen.') }}
@endif
@endforelse
{{ __('Status & Verlauf') }}
{{ $pr->status->label() }}
{{ __('Aktueller Status') }}
{{ $pr->status->label() }}
{{ __('Erstellt') }}
{{ $pr->created_at?->format('d.m.Y H:i') ?? '–' }}
{{ __('Veröffentlicht') }}
{{ $pr->published_at?->format('d.m.Y H:i') ?? '–' }}
{{ __('Aufrufe') }}
{{ number_format($pr->hits, 0, ',', '.') }}
@if ($pr->scheduled_at)
{{ __('Geplante Veröffentlichung') }}
{{ $pr->scheduledAtLocal()->format('d.m.Y H:i') }}
@endif
@if ($pr->embargo_at)
{{ __('Sperrfrist bis') }}
{{ $pr->embargoAtLocal()->format('d.m.Y H:i') }}
@endif
{{ __('Portal') }}
{{ $pr->portal?->label() ?? '–' }}
{{ __('Kategorie') }}
{{ $categoryName }}
{{ __('Sprache') }}
{{ strtoupper($pr->language) }}
@if (filled($pr->keywords))
{{ __('Themen') }}
@foreach (array_filter(array_map('trim', explode(',', $pr->keywords))) as $keyword)
{{ $keyword }}
@endforeach
@endif
@if ($pr->backlink_url)
@endif
@if ($pr->no_export)
{{ __('Kein Export aktiv (PM wird nicht über Feeds verteilt).') }}
@endif
@if ($statusLogs->isNotEmpty())
@foreach ($statusLogs as $log)
-
@php
$logClass = match ($log->to_status?->value) {
'published' => 'ok',
'review' => 'warn',
'rejected' => 'err',
default => 'hub',
};
@endphp
{{ $log->to_status?->label() }}
{{ $log->created_at->format('d.m.Y H:i') }}
@if ($log->changedBy)
{{ __('durch :name', ['name' => $log->changedBy->name]) }}
@endif
@if ($log->reason)
{{ $log->reason }}
@endif
@endforeach
@else
{{ __('Noch keine Statusänderungen protokolliert.') }}
@endif
{{-- ============== TITELBILD (Hero) ============== --}}
{{-- Harte Obergrenze 1280x580 px: Container deckelt Breite und Seitenverhältnis,
damit das Bild auf großen Screens nicht über die Detailgröße hinauswächst. --}}
@if ($coverIsPlaceholder)
{{ __('Platzhalter-Titelbild — laden Sie im Editor ein eigenes Bild hoch.') }}
@elseif ($titleImage && ($titleImage->copyright || $titleImage->is_ai_generated))
{{-- Bildnachweis + KI-Kennzeichnung (Art. 50 EU AI Act) --}}
@if ($titleImage->is_ai_generated)
{{ __('KI-generiert') }}
@endif
{{ $titleImage->copyright ?? __('Bild: KI-generiert') }}
@endif
{{-- ============== INHALT ============== --}}
{{ __('Inhalt') }}
{!! $pr->renderedText() !!}
{{-- ============== VERÖFFENTLICHUNGS-MODAL ============== --}}
@if ($pr->status === PressReleaseStatus::Draft || $pr->status === PressReleaseStatus::Rejected)
@endif
{{-- ============== BOILERPLATE-OVERRIDE ============== --}}
@if ($pr->boilerplate_override)
{{ __('Eigener Abbinder (Boilerplate)') }}
{{ __('Override') }}
{{ __('Dieser Text wird für diese Pressemitteilung anstelle des Standard-Abbinders der Firma verwendet.') }}
{{ $pr->boilerplate_override }}
@endif