Add-ons: Kaufbestaetigung vor Credit-Abbuchung + deutlichere Buttons
Boost-Laufzeiten und Nachweis oeffnen jetzt ein Bestaetigungs-Modal mit Preis, Guthaben und Restsaldo; erst "Kostenpflichtig buchen" bucht ab. Boost-Buttons primary mit explizitem Kauf-Label. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3ae57d9dba
commit
c8836564e9
2 changed files with 191 additions and 15 deletions
|
|
@ -48,15 +48,25 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
*/
|
||||
public ?array $creditShortfall = null;
|
||||
|
||||
/**
|
||||
* Kontext fürs Kauf-Bestätigungs-Modal (Entscheidung 10.07.): Add-ons
|
||||
* buchen erst nach explizitem „Kostenpflichtig buchen", damit ein Klick
|
||||
* auf eine Laufzeit nicht sofort Credits abbucht.
|
||||
*
|
||||
* @var array{type: 'boost'|'proof', label: string, credits: int, days: int|null}|null
|
||||
*/
|
||||
public ?array $pendingPurchase = null;
|
||||
|
||||
public function mount(int $id): void
|
||||
{
|
||||
$this->id = $id;
|
||||
$pr = $this->getMyPR();
|
||||
$this->authorize('view', $pr);
|
||||
|
||||
// KI-Grad vorbelegen (bereits deklariert); die verbindliche
|
||||
// Bestätigung muss bei jedem Submit erneut gesetzt werden.
|
||||
$this->aiTextDeclaration = $pr->ai_text_declaration?->value;
|
||||
// KI-Grad vorbelegen (bereits deklariert), gemappt auf die zwei
|
||||
// Formular-Optionen; die verbindliche Bestätigung muss bei jedem
|
||||
// Submit erneut gesetzt werden.
|
||||
$this->aiTextDeclaration = $pr->ai_text_declaration?->formOption()->value;
|
||||
}
|
||||
|
||||
public function submitForReview(): void
|
||||
|
|
@ -99,7 +109,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
|
||||
Flux::toast(
|
||||
heading: __('Eingereicht'),
|
||||
text: __('Die Inhaltsprüfung ist typischerweise innerhalb von 24 h abgeschlossen.'),
|
||||
text: __('Die automatische Inhaltsprüfung ist in der Regel innerhalb weniger Minuten abgeschlossen.'),
|
||||
variant: 'success',
|
||||
);
|
||||
}
|
||||
|
|
@ -117,6 +127,67 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
Flux::toast(text: __('Vorschau-Link wurde erzeugt.'), variant: 'success');
|
||||
}
|
||||
|
||||
/**
|
||||
* Kauf-Bestätigung für einen Boost öffnen (Abbuchung erst in
|
||||
* buyPendingAddOn).
|
||||
*/
|
||||
public function confirmBoost(int $days): void
|
||||
{
|
||||
$options = app(CreditPricingService::class)->boostOptions();
|
||||
|
||||
if (! isset($options[$days])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pendingPurchase = [
|
||||
'type' => 'boost',
|
||||
'label' => __('Boost — :days Tage Platzierung', ['days' => $days]),
|
||||
'credits' => $options[$days],
|
||||
'days' => $days,
|
||||
];
|
||||
|
||||
Flux::modal('confirm-addon-purchase')->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Kauf-Bestätigung für den Veröffentlichungsnachweis öffnen.
|
||||
*/
|
||||
public function confirmProof(): void
|
||||
{
|
||||
$this->pendingPurchase = [
|
||||
'type' => 'proof',
|
||||
'label' => __('Veröffentlichungsnachweis (PDF)'),
|
||||
'credits' => app(ProofPdfService::class)->priceCredits(),
|
||||
'days' => null,
|
||||
];
|
||||
|
||||
Flux::modal('confirm-addon-purchase')->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bestätigten Add-on-Kauf ausführen — der einzige Weg, auf dem die
|
||||
* Buttons der Add-on-Sektion tatsächlich Credits abbuchen.
|
||||
*/
|
||||
public function buyPendingAddOn(): void
|
||||
{
|
||||
$pending = $this->pendingPurchase;
|
||||
$this->pendingPurchase = null;
|
||||
|
||||
Flux::modal('confirm-addon-purchase')->close();
|
||||
|
||||
if ($pending === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($pending['type'] === 'boost') {
|
||||
$this->buyBoost($pending['days']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->buyProof();
|
||||
}
|
||||
|
||||
/**
|
||||
* Boost (Platzierung) aus der Wallet buchen — nur veröffentlichte, grüne
|
||||
* PMs sind boostbar (Gate im BoostService).
|
||||
|
|
@ -571,9 +642,9 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
@if ($canBoost)
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@foreach ($boostOptions as $days => $credits)
|
||||
<flux:button size="sm" variant="filled"
|
||||
wire:click="buyBoost({{ $days }})" wire:loading.attr="disabled">
|
||||
{{ __(':days Tage · :credits Credits', ['days' => $days, 'credits' => $credits]) }}
|
||||
<flux:button size="sm" variant="primary"
|
||||
wire:click="confirmBoost({{ $days }})" wire:loading.attr="disabled">
|
||||
{{ __(':days Tage für :credits Credits buchen', ['days' => $days, 'credits' => $credits]) }}
|
||||
</flux:button>
|
||||
@endforeach
|
||||
</div>
|
||||
|
|
@ -607,7 +678,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
{{ __('Nachweis herunterladen') }}
|
||||
</flux:button>
|
||||
@else
|
||||
<flux:button size="sm" variant="primary" wire:click="buyProof" wire:loading.attr="disabled">
|
||||
<flux:button size="sm" variant="primary" wire:click="confirmProof" wire:loading.attr="disabled">
|
||||
{{ __('Für :credits Credits kaufen', ['credits' => $proofPrice]) }}
|
||||
</flux:button>
|
||||
@endif
|
||||
|
|
@ -890,14 +961,14 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
<span>{{ __('Platzhalter-Titelbild — laden Sie im Editor ein eigenes Bild hoch.') }}</span>
|
||||
</div>
|
||||
@elseif ($titleImage && ($titleImage->copyright || $titleImage->is_ai_generated || $titleImage->ai_declaration?->publicLabel()))
|
||||
{{-- Bildnachweis + KI-Kennzeichnung (Art. 50 EU AI Act) --}}
|
||||
{{-- Bildnachweis + KI-Kennzeichnung (Art. 50 EU AI Act): kurzes
|
||||
Badge („KI-generiert" / „KI-bearbeitet"), kein Satz-Wortlaut. --}}
|
||||
<div class="flex flex-wrap items-center gap-2 border-t border-[color:var(--color-bg-rule)] px-5 py-2.5 text-[12px] text-[color:var(--color-ink-3)]">
|
||||
@if ($titleImage->is_ai_generated)
|
||||
<span class="badge hub">{{ __('KI-generiert') }}</span>
|
||||
@if ($aiImageBadge = $titleImage->ai_declaration?->publicLabel() ?? ($titleImage->is_ai_generated ? __('KI-generiert') : null))
|
||||
<span class="badge hub">{{ $aiImageBadge }}</span>
|
||||
@endif
|
||||
<span>{{ $titleImage->copyright ?? __('Bild: KI-generiert') }}</span>
|
||||
@if ($aiImageHint = $titleImage->ai_declaration?->publicLabel())
|
||||
<span>· {{ $aiImageHint }}</span>
|
||||
@if ($titleImage->copyright)
|
||||
<span>{{ $titleImage->copyright }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
|
@ -1004,6 +1075,49 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
</article>
|
||||
@endif
|
||||
|
||||
{{-- ============== MODAL: ADD-ON-KAUF BESTÄTIGEN ==============
|
||||
Vorgeschaltete Bestätigung (Entscheidung 10.07.): kein Add-on-Klick
|
||||
bucht direkt Credits ab — erst „Kostenpflichtig buchen" kauft. --}}
|
||||
<flux:modal name="confirm-addon-purchase" class="w-full max-w-md">
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<flux:text class="eyebrow muted">{{ __('Add-on') }}</flux:text>
|
||||
<flux:heading size="lg">{{ __('Kauf bestätigen') }}</flux:heading>
|
||||
</div>
|
||||
|
||||
@if ($pendingPurchase)
|
||||
<div class="rounded-[6px] border border-[color:var(--color-bg-rule)] bg-[color:var(--color-bg-elev)] p-4 text-[12.5px] leading-[1.6] text-[color:var(--color-ink-2)]">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<span>{{ $pendingPurchase['label'] }}</span>
|
||||
<span class="font-bold whitespace-nowrap text-[color:var(--color-ink)]">{{ __(':n Credits', ['n' => $pendingPurchase['credits']]) }}</span>
|
||||
</div>
|
||||
<p class="m-0 mt-3 pt-3 border-t border-[color:var(--color-bg-rule)] text-[11.5px] text-[color:var(--color-ink-3)]">
|
||||
@if ($creditBalance >= $pendingPurchase['credits'])
|
||||
{{ __('Der Betrag wird sofort von Ihrem Guthaben abgebucht: :available Credits → :rest Credits.', [
|
||||
'available' => $creditBalance,
|
||||
'rest' => $creditBalance - $pendingPurchase['credits'],
|
||||
]) }}
|
||||
@else
|
||||
{{ __('Ihr Guthaben (:available Credits) reicht dafür nicht aus — es fehlen :missing Credits.', [
|
||||
'available' => $creditBalance,
|
||||
'missing' => $pendingPurchase['credits'] - $creditBalance,
|
||||
]) }}
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex justify-end gap-2">
|
||||
<flux:modal.close>
|
||||
<flux:button variant="filled">{{ __('Abbrechen') }}</flux:button>
|
||||
</flux:modal.close>
|
||||
<flux:button variant="primary" wire:click="buyPendingAddOn" wire:loading.attr="disabled">
|
||||
{{ __('Kostenpflichtig buchen') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
</flux:modal>
|
||||
|
||||
{{-- ============== MODAL: NICHT GENÜGEND CREDITS ==============
|
||||
Öffnet sich serverseitig, wenn ein Add-on-Kauf (Boost/Nachweis)
|
||||
am Wallet-Guthaben scheitert — mit Direktlink zum Aufladen. --}}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue