Add-ons: Modal statt Toast bei fehlendem Credit-Guthaben

Scheiterte ein Boost- oder Nachweis-Kauf am Wallet-Guthaben, gab es nur
einen flüchtigen Toast ohne Weg zur Lösung. Jetzt öffnet sich ein Modal
mit Kosten, Guthaben und fehlender Differenz sowie einem Direktlink
"Credits aufladen" zur Wallet-Sektion der Buchungsseite (neuer
#credits-Anker).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-07 14:30:57 +00:00
parent 0f8f0b2d31
commit 9600a4f887
3 changed files with 99 additions and 13 deletions

View file

@ -775,7 +775,7 @@ new #[Layout('components.layouts.app'), Title('Buchungen & Add-ons')] class exte
</article>
{{-- ============== CREDIT-WALLET & ADD-ONS ============== --}}
<section class="space-y-4">
<section id="credits" class="space-y-4">
<div>
<span class="section-eyebrow">{{ __('Credit-Wallet & Add-ons') }}</span>
<h2 class="text-[22px] font-bold tracking-[-0.4px] mt-1 mb-1 text-[color:var(--color-ink)]">

View file

@ -35,6 +35,14 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
public bool $depublicationConfirmed = false;
/**
* Kontext fürs „Nicht genügend Credits"-Modal (QA-Befund 07.07.):
* ['label' => Add-on-Name, 'required' => int, 'available' => int].
*
* @var array{label: string, required: int, available: int}|null
*/
public ?array $creditShortfall = null;
public function mount(int $id): void
{
$this->id = $id;
@ -112,12 +120,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
variant: 'success',
);
} catch (InsufficientCreditsException $e) {
Flux::toast(
heading: __('Nicht genügend Credits'),
text: __('Der Boost kostet :need Credits, Ihr Guthaben beträgt :have. Laden Sie unter „Buchungen & Add-ons" auf.', ['need' => $e->required, 'have' => $e->available]),
variant: 'warning',
duration: 8000,
);
$this->showCreditShortfallModal(__('Der Boost'), $e);
} catch (BoostNotAllowedException $e) {
Flux::toast(text: $e->getMessage(), variant: 'warning', duration: 8000);
}
@ -140,15 +143,25 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
variant: 'success',
);
} catch (InsufficientCreditsException $e) {
Flux::toast(
heading: __('Nicht genügend Credits'),
text: __('Der Nachweis kostet :need Credits, Ihr Guthaben beträgt :have.', ['need' => $e->required, 'have' => $e->available]),
variant: 'warning',
duration: 8000,
);
$this->showCreditShortfallModal(__('Der Veröffentlichungsnachweis'), $e);
}
}
/**
* Statt eines flüchtigen Toasts ein Modal mit Aufladen-Link öffnen
* (QA-Befund 07.07.): der Kunde soll direkt zur Wallet finden.
*/
private function showCreditShortfallModal(string $label, InsufficientCreditsException $e): void
{
$this->creditShortfall = [
'label' => $label,
'required' => $e->required,
'available' => $e->available,
];
Flux::modal('insufficient-credits')->show();
}
/**
* Unternehmer-Pflicht-Checkbox vor dem Depublikations-Checkout (T1neu):
* protokolliert wie im Buchungs-Modal — der Checkout-Controller verlangt
@ -930,4 +943,41 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
</div>
</article>
@endif
{{-- ============== MODAL: NICHT GENÜGEND CREDITS ==============
Öffnet sich serverseitig, wenn ein Add-on-Kauf (Boost/Nachweis)
am Wallet-Guthaben scheitert — mit Direktlink zum Aufladen. --}}
<flux:modal name="insufficient-credits" class="w-full max-w-md">
<div class="space-y-5">
<div>
<flux:text class="eyebrow muted">{{ __('Credit-Wallet') }}</flux:text>
<flux:heading size="lg">{{ __('Nicht genügend Credits') }}</flux:heading>
</div>
@if ($creditShortfall)
<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)]">
<p class="m-0">
{{ __(':label kostet :required Credits — Ihr Guthaben beträgt :available Credits. Es fehlen :missing Credits.', [
'label' => $creditShortfall['label'],
'required' => $creditShortfall['required'],
'available' => $creditShortfall['available'],
'missing' => max(0, $creditShortfall['required'] - $creditShortfall['available']),
]) }}
</p>
<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)]">
{{ __('Guthaben laden Sie unter „Buchungen & Add-ons" auf — es steht sofort zur Verfügung (1 Credit = 1 €, größere Pakete enthalten Bonus-Credits).') }}
</p>
</div>
@endif
<div class="flex justify-end gap-2">
<flux:modal.close>
<flux:button variant="filled">{{ __('Schließen') }}</flux:button>
</flux:modal.close>
<flux:button variant="primary" icon="wallet" href="{{ route('me.bookings.index') }}#credits" wire:navigate>
{{ __('Credits aufladen') }}
</flux:button>
</div>
</div>
</flux:modal>
</div>