admin: Boost vorzeitig beenden, Berechtigungsliste audit:admins
Admin ▸ Pressemitteilung zeigt laufende Boosts; „Boost beenden“ verlangt einen Grund und billing:manage, beendet laufende und angeschlossene Zeiträume, verwirft die Seiten-Caches und protokolliert „Boost beendet“. Credits werden nicht zurückgebucht. audit:admins listet alle Konten mit Rechten über den Kundenbereich hinaus (Super-Admin, Rollen außer customer/api-only, direkte Permissions), auch gesperrte und gelöschte, als Markdown mit 2FA, Passkeys, Tokens und letztem Sitzungswiderruf. E-Mail standardmäßig nur mit Domain (Nachweis BN-02). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2150d4b7ae
commit
fce7baea2b
6 changed files with 419 additions and 0 deletions
|
|
@ -7,6 +7,7 @@ use App\Enums\AiImageDeclaration;
|
|||
use App\Services\PressRelease\ImageRightsConfirmationRequiredException;
|
||||
use App\Enums\ImagePrecheckCategory;
|
||||
use App\Services\Image\Precheck\ImagePrecheckReviewService;
|
||||
use App\Services\PressRelease\BoostService;
|
||||
use App\Services\PressRelease\Images\ImageCorrectionService;
|
||||
use App\Services\PressRelease\PressReleaseService;
|
||||
use App\Services\PressRelease\QuotaExceededException;
|
||||
|
|
@ -116,6 +117,31 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
Flux::modal('confirm-show-archive')->close();
|
||||
}
|
||||
|
||||
public string $boostEndReason = '';
|
||||
|
||||
/**
|
||||
* Gebuchte Platzierung vorzeitig beenden (Rechtspaket 10/2026, U10):
|
||||
* etwa nach einem Test-Boost oder wenn die Platzierung nicht mehr
|
||||
* vertretbar ist. Ohne Rückbuchung; nur mit Abrechnungsrecht.
|
||||
*/
|
||||
public function endBoost(BoostService $boosts): void
|
||||
{
|
||||
$this->authorize('billing:manage');
|
||||
|
||||
$this->validate([
|
||||
'boostEndReason' => ['required', 'string', 'min:5', 'max:490'],
|
||||
]);
|
||||
|
||||
$ended = $boosts->endActive($this->findAdminPR($this->id), auth()->user(), trim($this->boostEndReason));
|
||||
|
||||
$this->boostEndReason = '';
|
||||
|
||||
Flux::toast(
|
||||
text: $ended > 0 ? __('Gebuchte Platzierung beendet.') : __('Kein laufender Boost.'),
|
||||
variant: $ended > 0 ? 'success' : 'warning',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Korrekturweg (Bildkennzeichnung E.6): Bildnachweis oder
|
||||
* Portalkennzeichnung mit Pflichtgrund ändern, Bild vorläufig zurückhalten.
|
||||
|
|
@ -275,6 +301,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
'latestRejection' => $latestRejection,
|
||||
'coverUrl' => $cover->coverUrl($pr, 'cover'),
|
||||
'coverIsPlaceholder' => $cover->coverIsPlaceholder($pr),
|
||||
'activeBoosts' => $pr->boosts()->active()->orderBy('ends_at')->get(),
|
||||
'categoryName' => $pr->category?->translations->firstWhere('locale', 'de')?->name
|
||||
?? $pr->category?->translations->first()?->name
|
||||
?? '–',
|
||||
|
|
@ -489,6 +516,35 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
</article>
|
||||
@endif
|
||||
|
||||
{{-- ============== GEBUCHTE PLATZIERUNG (BOOST) ============== --}}
|
||||
@if ($activeBoosts->isNotEmpty())
|
||||
<article class="panel" data-active-boosts>
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Gebuchte Platzierung') }}</span>
|
||||
<span class="badge">{{ __('läuft') }}</span>
|
||||
</div>
|
||||
<div class="p-5 space-y-3 max-w-[760px]">
|
||||
<ul class="m-0 list-disc pl-5 text-[13px]">
|
||||
@foreach ($activeBoosts as $activeBoost)
|
||||
<li>
|
||||
{{ $activeBoost->placement->label() }} · {{ __(':days Tage', ['days' => $activeBoost->days]) }} · {{ __(':credits Credits', ['credits' => $activeBoost->credits_charged]) }}
|
||||
<span class="text-[color:var(--color-ink-4)]">({{ $activeBoost->starts_at->format('d.m.Y H:i') }} – {{ $activeBoost->ends_at->format('d.m.Y H:i') }})</span>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@can('billing:manage')
|
||||
<p class="m-0 text-[12.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ __('Beenden nimmt die Meldung sofort von Start- und Branchenseite. Die Buchung bleibt als Nachweis erhalten; Credits werden nicht zurückgebucht.') }}
|
||||
</p>
|
||||
<flux:textarea wire:model="boostEndReason" :label="__('Grund (Pflicht)')" rows="2" />
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<flux:button variant="danger" wire:click="endBoost" wire:confirm="{{ __('Gebuchte Platzierung jetzt beenden?') }}">{{ __('Boost beenden') }}</flux:button>
|
||||
</div>
|
||||
@endcan
|
||||
</div>
|
||||
</article>
|
||||
@endif
|
||||
|
||||
{{-- ============== REJECTION-HINWEIS ============== --}}
|
||||
@if ($pr->status === \App\Enums\PressReleaseStatus::Rejected && $latestRejection)
|
||||
<article class="panel" style="border-color:var(--color-err); border-left-width:3px;">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue