WS-4: Bestandsschutz im Kundenbereich als echtes Paket darstellen
/admin/me/buchungen-add-ons: Bestandsschutz raus aus dem gedrängten "Aktueller Tarif"-Panel, eigener prominenter Paket-Block: - Portal-Badge (fehlte bisher), Schild-Icon, Akzentleiste, Rahmen (ring) - größere Typo, Netto-Preis + Intervall, Feature-Liste (unbegrenzte PMs, Konditionen unverändert, nächste Rechnung), Bündel-Hinweis bei mehreren Posten - Helper legacyPortalLabel()/legacyIntervalLabel() im Volt-Component - BookingsPageTest auf die neue Darstellung + Portal-Anzeige aktualisiert Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
be7d1799a5
commit
afefa86711
2 changed files with 119 additions and 24 deletions
|
|
@ -1,8 +1,10 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Portal;
|
||||
use App\Enums\SinglePurchaseStatus;
|
||||
use App\Enums\UserPaymentOptionStatus;
|
||||
use App\Models\Plan;
|
||||
use App\Models\UserPaymentOption;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Volt\Component;
|
||||
|
|
@ -30,6 +32,28 @@ new #[Layout('components.layouts.app'), Title('Buchungen & Add-ons')] class exte
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Lesbares Portal-Label für einen Bestandsschutz-Posten (aus den
|
||||
* Legacy-Konditionen). Fällt auf „Portal unbekannt" zurück.
|
||||
*/
|
||||
public function legacyPortalLabel(UserPaymentOption $option): string
|
||||
{
|
||||
$value = data_get($option->legacy_conditions, 'legacy_portal');
|
||||
|
||||
return Portal::tryFrom((string) $value)?->label() ?? __('Portal unbekannt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Abrechnungsintervall eines Bestandsschutz-Postens als Label.
|
||||
*/
|
||||
public function legacyIntervalLabel(UserPaymentOption $option): string
|
||||
{
|
||||
return match (data_get($option->legacy_conditions, 'interval')) {
|
||||
'monthly' => __('Monat'),
|
||||
default => __('Jahr'),
|
||||
};
|
||||
}
|
||||
|
||||
public function with(): array
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
|
@ -127,17 +151,97 @@ new #[Layout('components.layouts.app'), Title('Buchungen & Add-ons')] class exte
|
|||
</div>
|
||||
</header>
|
||||
|
||||
{{-- ============== BESTANDSSCHUTZ-PAKETE ============== --}}
|
||||
{{-- Migrierte Legacy-Vereinbarungen als echte, gebuchte Pakete dargestellt:
|
||||
eigener Block, Rahmen, Icon, Portal-Badge, klare Typo. --}}
|
||||
@if ($legacyOptions->isNotEmpty())
|
||||
<section class="space-y-4">
|
||||
<div>
|
||||
<span class="section-eyebrow">{{ __('Ihr Bestandsschutz') }}</span>
|
||||
<h2 class="text-[22px] font-bold tracking-[-0.4px] mt-1 mb-1 text-[color:var(--color-ink)]">
|
||||
{{ trans_choice('Ihr gebuchtes Paket|Ihre gebuchten Pakete', $legacyOptions->count()) }}
|
||||
</h2>
|
||||
<p class="text-[12.5px] text-[color:var(--color-ink-3)] max-w-[640px] m-0">
|
||||
{{ __('Ihre bisherigen Konditionen gelten unverändert weiter — unbegrenzte Pressemitteilungen, Abrechnung wie gewohnt per Rechnung.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div @class(['grid gap-4', 'md:grid-cols-2' => $legacyOptions->count() > 1])>
|
||||
@foreach ($legacyOptions as $option)
|
||||
@php
|
||||
$netCents = (int) data_get($option->legacy_conditions, 'net_cents', 0);
|
||||
$legacyName = data_get($option->legacy_conditions, 'name')
|
||||
?? ($option->paymentOption?->article_number ?? __('Bestehende Vereinbarung'));
|
||||
@endphp
|
||||
<article class="panel overflow-hidden ring-1 ring-[color:var(--color-ok)]/25" wire:key="legacy-{{ $option->id }}">
|
||||
{{-- Akzent-Leiste --}}
|
||||
<div class="h-1 bg-[color:var(--color-ok)]/60"></div>
|
||||
<div class="p-6 space-y-5">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex items-start gap-4 min-w-0">
|
||||
<div class="w-12 h-12 rounded-[8px] flex items-center justify-center flex-shrink-0
|
||||
bg-[color:var(--color-ok-soft)] border border-[color:var(--color-ok)]/30 text-[color:var(--color-gain-deep)]">
|
||||
<flux:icon.shield-check class="size-6" />
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center gap-2 mb-1.5 flex-wrap">
|
||||
<span class="badge ok dot">{{ __('Bestandsschutz') }}</span>
|
||||
<span class="badge hub">{{ $this->legacyPortalLabel($option) }}</span>
|
||||
</div>
|
||||
<h3 class="text-[18px] font-bold tracking-[-0.3px] text-[color:var(--color-ink)] m-0 break-words">
|
||||
{{ $legacyName }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right flex-shrink-0">
|
||||
<div class="text-[26px] font-bold tracking-[-0.6px] leading-none text-[color:var(--color-ink)]">
|
||||
{{ $this->formatEuro($netCents) }}
|
||||
</div>
|
||||
<div class="text-[11px] text-[color:var(--color-ink-3)] mt-1">
|
||||
{{ __('netto / :interval', ['interval' => $this->legacyIntervalLabel($option)]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="m-0 p-0 list-none space-y-2.5 text-[12.5px] text-[color:var(--color-ink-2)]
|
||||
border-t border-[color:var(--color-bg-rule)] pt-4">
|
||||
<li class="flex items-start gap-2">
|
||||
<flux:icon.check class="size-4 flex-shrink-0 mt-0.5 text-[color:var(--color-gain-deep)]" />
|
||||
<span><strong class="text-[color:var(--color-ink)]">{{ __('Unbegrenzte') }}</strong> {{ __('Pressemitteilungen') }}</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<flux:icon.check class="size-4 flex-shrink-0 mt-0.5 text-[color:var(--color-gain-deep)]" />
|
||||
{{ __('Konditionen gelten unverändert weiter') }}
|
||||
</li>
|
||||
@if ($option->current_period_end)
|
||||
<li class="flex items-start gap-2">
|
||||
<flux:icon.calendar-days class="size-4 flex-shrink-0 mt-0.5 text-[color:var(--color-ink-3)]" />
|
||||
{{ __('Nächste Rechnung am :date', ['date' => $option->current_period_end->format('d.m.Y')]) }}
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if ($legacyOptions->count() > 1)
|
||||
<p class="text-[11.5px] text-[color:var(--color-ink-3)] m-0">
|
||||
{{ __('Ihre Pakete werden gemeinsam geführt und sind nur zusammen kündbar (keine Einzelkündigung).') }}
|
||||
</p>
|
||||
@endif
|
||||
</section>
|
||||
@endif
|
||||
|
||||
{{-- ============== AKTUELLER TARIF ============== --}}
|
||||
{{-- Erscheint erst, wenn eine Buchung existiert — vorher würde hier nur
|
||||
„kein Tarif" stehen und das Kontingent wäre irreführend. --}}
|
||||
@if ($subscription || $legacyOptions->isNotEmpty() || $openPurchases->isNotEmpty())
|
||||
@if ($subscription || $openPurchases->isNotEmpty())
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Aktueller Tarif') }}</span>
|
||||
@if ($currentPlan)
|
||||
<span class="badge hub">{{ $currentPlan->name }}</span>
|
||||
@elseif ($legacyOptions->isNotEmpty())
|
||||
<span class="badge ok dot">{{ __('Bestandstarif') }}</span>
|
||||
@else
|
||||
<span class="badge hub">{{ __('Einzel-PM') }}</span>
|
||||
@endif
|
||||
|
|
@ -164,23 +268,6 @@ new #[Layout('components.layouts.app'), Title('Buchungen & Add-ons')] class exte
|
|||
{{ __('Gekündigt — läuft am :date aus.', ['date' => $subscription->ends_at?->format('d.m.Y')]) }}
|
||||
</p>
|
||||
@endif
|
||||
@elseif ($legacyOptions->isNotEmpty())
|
||||
@foreach ($legacyOptions as $option)
|
||||
<div>
|
||||
<div class="text-[15px] font-semibold text-[color:var(--color-ink)]">
|
||||
{{ data_get($option->legacy_conditions, 'name') ?? ($option->paymentOption?->article_number ?? __('Bestehende Vereinbarung')) }}
|
||||
</div>
|
||||
<p class="text-[12.5px] text-[color:var(--color-ink-2)] mt-1 mb-0">
|
||||
{{ __('Unbegrenzte Pressemitteilungen (Bestandsschutz).') }}
|
||||
@if ($option->current_period_end)
|
||||
{{ __('Nächste Rechnung am :date.', ['date' => $option->current_period_end->format('d.m.Y')]) }}
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
@endforeach
|
||||
<p class="text-[11.5px] text-[color:var(--color-ink-3)] m-0">
|
||||
{{ __('Ihre bisherigen Konditionen gelten unverändert weiter; die Abrechnung erfolgt wie gewohnt per Rechnung.') }}
|
||||
</p>
|
||||
@elseif ($openPurchases->isNotEmpty())
|
||||
<div class="text-[15px] font-semibold text-[color:var(--color-ink)]">
|
||||
{{ trans_choice(':count Einzel-Pressemitteilung verfügbar|:count Einzel-Pressemitteilungen verfügbar', $openPurchases->count(), ['count' => $openPurchases->count()]) }}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue