Add-on-Modal: Pflicht-Schalter + Verlaengerungs-Hinweis bei laufendem Boost
Kauf-Button bleibt deaktiviert, bis der Bestaetigungs-Schalter aktiv ist (serverseitig abgesichert). Laeuft bereits ein Boost, warnt das Modal, dass die Buchung die Laufzeit verlaengert, mit altem und neuem Enddatum. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
51da374173
commit
6e1267f00f
2 changed files with 79 additions and 4 deletions
|
|
@ -51,12 +51,19 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
/**
|
||||
* 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.
|
||||
* auf eine Laufzeit nicht sofort Credits abbucht. Läuft bereits ein
|
||||
* Boost, tragen extendsUntil/newEndsAt den Verlängerungs-Hinweis.
|
||||
*
|
||||
* @var array{type: 'boost'|'proof', label: string, credits: int, days: int|null}|null
|
||||
* @var array{type: 'boost'|'proof', label: string, credits: int, days: int|null, extendsUntil: string|null, newEndsAt: string|null}|null
|
||||
*/
|
||||
public ?array $pendingPurchase = null;
|
||||
|
||||
/**
|
||||
* Pflicht-Schalter im Kauf-Modal: ohne aktive Bestätigung führt
|
||||
* buyPendingAddOn keine Abbuchung aus.
|
||||
*/
|
||||
public bool $purchaseConfirmed = false;
|
||||
|
||||
public function mount(int $id): void
|
||||
{
|
||||
$this->id = $id;
|
||||
|
|
@ -139,11 +146,18 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
return;
|
||||
}
|
||||
|
||||
// Läuft bereits ein Boost, verlängert die Buchung dessen Laufzeit —
|
||||
// das Modal weist ausdrücklich darauf hin (Entscheidung 10.07.).
|
||||
$activeUntil = app(BoostService::class)->activeUntil($this->getMyPR());
|
||||
|
||||
$this->purchaseConfirmed = false;
|
||||
$this->pendingPurchase = [
|
||||
'type' => 'boost',
|
||||
'label' => __('Boost — :days Tage Platzierung', ['days' => $days]),
|
||||
'credits' => $options[$days],
|
||||
'days' => $days,
|
||||
'extendsUntil' => $activeUntil?->format('d.m.Y H:i'),
|
||||
'newEndsAt' => $activeUntil?->copy()->addDays($days)->format('d.m.Y H:i'),
|
||||
];
|
||||
|
||||
Flux::modal('confirm-addon-purchase')->show();
|
||||
|
|
@ -154,11 +168,14 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
*/
|
||||
public function confirmProof(): void
|
||||
{
|
||||
$this->purchaseConfirmed = false;
|
||||
$this->pendingPurchase = [
|
||||
'type' => 'proof',
|
||||
'label' => __('Veröffentlichungsnachweis (PDF)'),
|
||||
'credits' => app(ProofPdfService::class)->priceCredits(),
|
||||
'days' => null,
|
||||
'extendsUntil' => null,
|
||||
'newEndsAt' => null,
|
||||
];
|
||||
|
||||
Flux::modal('confirm-addon-purchase')->show();
|
||||
|
|
@ -170,8 +187,20 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
*/
|
||||
public function buyPendingAddOn(): void
|
||||
{
|
||||
// Serverseitige Absicherung des Pflicht-Schalters (der Button ist
|
||||
// clientseitig zusätzlich deaktiviert).
|
||||
if ($this->pendingPurchase !== null && ! $this->purchaseConfirmed) {
|
||||
Flux::toast(
|
||||
text: __('Bitte bestätigen Sie die kostenpflichtige Buchung über den Schalter.'),
|
||||
variant: 'warning',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$pending = $this->pendingPurchase;
|
||||
$this->pendingPurchase = null;
|
||||
$this->purchaseConfirmed = false;
|
||||
|
||||
Flux::modal('confirm-addon-purchase')->close();
|
||||
|
||||
|
|
@ -1086,6 +1115,19 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
</div>
|
||||
|
||||
@if ($pendingPurchase)
|
||||
@if ($pendingPurchase['extendsUntil'])
|
||||
{{-- Verlängerungs-Hinweis: für diese PM läuft bereits ein Boost. --}}
|
||||
<div class="rounded-[6px] border-l-[3px] border border-[color:var(--color-warn)] bg-[color:var(--color-warn-soft)] p-4 text-[12.5px] leading-[1.6] text-[color:var(--color-ink)]">
|
||||
<p class="m-0 font-bold">{{ __('Diese Buchung verlängert den laufenden Boost.') }}</p>
|
||||
<p class="m-0 mt-1 text-[color:var(--color-ink-2)]">
|
||||
{{ __('Aktuell aktiv bis :until — die neue Laufzeit schließt daran an und endet am :end.', [
|
||||
'until' => $pendingPurchase['extendsUntil'],
|
||||
'end' => $pendingPurchase['newEndsAt'],
|
||||
]) }}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<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>
|
||||
|
|
@ -1105,13 +1147,17 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
@endif
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<flux:switch wire:model.live="purchaseConfirmed"
|
||||
label="{{ __('Ich bestätige die kostenpflichtige Buchung über :n Credits.', ['n' => $pendingPurchase['credits']]) }}" />
|
||||
@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">
|
||||
<flux:button variant="primary" wire:click="buyPendingAddOn" wire:loading.attr="disabled"
|
||||
:disabled="! $purchaseConfirmed">
|
||||
{{ __('Kostenpflichtig buchen') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ test('add-on buttons only debit after the purchase confirmation modal', function
|
|||
'label' => 'Boost — 14 Tage Platzierung',
|
||||
'credits' => 20,
|
||||
'days' => 14,
|
||||
'extendsUntil' => null,
|
||||
'newEndsAt' => null,
|
||||
])
|
||||
->assertSee('Kauf bestätigen')
|
||||
->assertSee('Kostenpflichtig buchen');
|
||||
|
|
@ -77,13 +79,39 @@ test('add-on buttons only debit after the purchase confirmation modal', function
|
|||
expect($customer->fresh()->creditBalance())->toBe(40);
|
||||
expect($pr->fresh()->isBoosted())->toBeFalse();
|
||||
|
||||
// Ohne den Pflicht-Schalter bleibt der Kauf aus und das Modal offen.
|
||||
$component->call('buyPendingAddOn')
|
||||
->assertSet('pendingPurchase', null);
|
||||
->assertSet('pendingPurchase.type', 'boost');
|
||||
|
||||
expect($customer->fresh()->creditBalance())->toBe(40);
|
||||
expect($pr->fresh()->isBoosted())->toBeFalse();
|
||||
|
||||
$component->set('purchaseConfirmed', true)
|
||||
->call('buyPendingAddOn')
|
||||
->assertSet('pendingPurchase', null)
|
||||
->assertSet('purchaseConfirmed', false);
|
||||
|
||||
expect($customer->fresh()->creditBalance())->toBe(20);
|
||||
expect($pr->fresh()->isBoosted())->toBeTrue();
|
||||
});
|
||||
|
||||
test('the confirmation modal warns when the purchase extends a running boost', function () {
|
||||
/** @var TestCase $this */
|
||||
['customer' => $customer, 'pr' => $pr] = publishedGreenPrForOwner();
|
||||
$this->wallet->credit($customer, 40);
|
||||
|
||||
$activeUntil = now()->addDays(3)->setTime(12, 0);
|
||||
Boost::factory()->for($pr)->for($customer)->create(['ends_at' => $activeUntil]);
|
||||
|
||||
$this->actingAs($customer);
|
||||
|
||||
LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id])
|
||||
->call('confirmBoost', 7)
|
||||
->assertSet('pendingPurchase.extendsUntil', $activeUntil->format('d.m.Y H:i'))
|
||||
->assertSet('pendingPurchase.newEndsAt', $activeUntil->copy()->addDays(7)->format('d.m.Y H:i'))
|
||||
->assertSee('Diese Buchung verlängert den laufenden Boost.');
|
||||
});
|
||||
|
||||
test('the proof purchase runs through the confirmation modal as well', function () {
|
||||
/** @var TestCase $this */
|
||||
['customer' => $customer, 'pr' => $pr] = publishedGreenPrForOwner();
|
||||
|
|
@ -95,6 +123,7 @@ test('the proof purchase runs through the confirmation modal as well', function
|
|||
->call('confirmProof')
|
||||
->assertSet('pendingPurchase.type', 'proof')
|
||||
->assertSet('pendingPurchase.credits', 3)
|
||||
->set('purchaseConfirmed', true)
|
||||
->call('buyPendingAddOn')
|
||||
->assertSee('Nachweis herunterladen');
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue