From 9600a4f8874b57920cb8b1a9e1097f88a6457260 Mon Sep 17 00:00:00 2001 From: Kevin Adametz Date: Tue, 7 Jul 2026 14:30:57 +0000 Subject: [PATCH] Add-ons: Modal statt Toast bei fehlendem Credit-Guthaben MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../livewire/customer/bookings.blade.php | 2 +- .../customer/press-releases/show.blade.php | 74 ++++++++++++++++--- tests/Feature/PressReleaseShowAddonsTest.php | 36 +++++++++ 3 files changed, 99 insertions(+), 13 deletions(-) diff --git a/resources/views/livewire/customer/bookings.blade.php b/resources/views/livewire/customer/bookings.blade.php index 4fe0153b..8718e5f0 100644 --- a/resources/views/livewire/customer/bookings.blade.php +++ b/resources/views/livewire/customer/bookings.blade.php @@ -775,7 +775,7 @@ new #[Layout('components.layouts.app'), Title('Buchungen & Add-ons')] class exte {{-- ============== CREDIT-WALLET & ADD-ONS ============== --}} -
+
{{ __('Credit-Wallet & Add-ons') }}

diff --git a/resources/views/livewire/customer/press-releases/show.blade.php b/resources/views/livewire/customer/press-releases/show.blade.php index 81d0b6f9..1611efbd 100644 --- a/resources/views/livewire/customer/press-releases/show.blade.php +++ b/resources/views/livewire/customer/press-releases/show.blade.php @@ -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

@endif + + {{-- ============== MODAL: NICHT GENÜGEND CREDITS ============== + Öffnet sich serverseitig, wenn ein Add-on-Kauf (Boost/Nachweis) + am Wallet-Guthaben scheitert — mit Direktlink zum Aufladen. --}} + +
+
+ {{ __('Credit-Wallet') }} + {{ __('Nicht genügend Credits') }} +
+ + @if ($creditShortfall) +
+

+ {{ __(':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']), + ]) }} +

+

+ {{ __('Guthaben laden Sie unter „Buchungen & Add-ons" auf — es steht sofort zur Verfügung (1 Credit = 1 €, größere Pakete enthalten Bonus-Credits).') }} +

+
+ @endif + +
+ + {{ __('Schließen') }} + + + {{ __('Credits aufladen') }} + +
+
+
diff --git a/tests/Feature/PressReleaseShowAddonsTest.php b/tests/Feature/PressReleaseShowAddonsTest.php index 4bb57138..d33f6abe 100644 --- a/tests/Feature/PressReleaseShowAddonsTest.php +++ b/tests/Feature/PressReleaseShowAddonsTest.php @@ -83,6 +83,42 @@ test('buying a proof from the detail page unlocks the download', function () { expect($customer->fresh()->creditBalance())->toBe(7); }); +test('an add-on purchase without enough credits opens the shortfall modal with a topup link', function () { + /** @var TestCase $this */ + ['customer' => $customer, 'pr' => $pr] = publishedGreenPrForOwner(); + $this->wallet->credit($customer, 1); + + $this->actingAs($customer); + + LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id]) + ->call('buyProof') + ->assertSet('creditShortfall', [ + 'label' => 'Der Veröffentlichungsnachweis', + 'required' => 3, + 'available' => 1, + ]) + ->assertSee('Nicht genügend Credits') + ->assertSee('Es fehlen 2 Credits.') + ->assertSee('Credits aufladen') + ->assertSee(route('me.bookings.index').'#credits', escape: false); + + expect($customer->fresh()->creditBalance())->toBe(1); // nichts abgebucht +}); + +test('a boost without enough credits opens the shortfall modal instead of booking', function () { + /** @var TestCase $this */ + ['customer' => $customer, 'pr' => $pr] = publishedGreenPrForOwner(); + + $this->actingAs($customer); + + LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id]) + ->call('buyBoost', 7) + ->assertSet('creditShortfall.label', 'Der Boost') + ->assertSee('Nicht genügend Credits'); + + expect($pr->fresh()->isBoosted())->toBeFalse(); +}); + test('a non-green release cannot be boosted from the detail page', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = publishedGreenPrForOwner();