376 lines
22 KiB
PHP
376 lines
22 KiB
PHP
<div class="max-w-5xl mx-auto space-y-6">
|
||
|
||
{{-- HEADER --}}
|
||
<div class="flex items-center gap-3">
|
||
<a href="{{ route('subscription.index') }}"
|
||
class="w-8 h-8 rounded-lg border border-gray-200 flex items-center justify-center text-gray-400 hover:text-gray-600 hover:bg-gray-50 transition-colors">
|
||
<x-heroicon-o-arrow-left class="w-4 h-4"/>
|
||
</a>
|
||
<div>
|
||
@if($this->checkoutType === 'cancel')
|
||
<h1 class="text-xl font-semibold text-gray-800">{{ t('checkout.switch_free') }}</h1>
|
||
<p class="text-sm text-gray-400">{{ t('checkout.cancel_immediately') }}</p>
|
||
@elseif($this->checkoutType === 'update')
|
||
<h1 class="text-xl font-semibold text-gray-800">{{ t('checkout.change_plan') }}</h1>
|
||
<p class="text-sm text-gray-400">{{ t('checkout.switch_to') }} <span class="font-medium text-gray-600">{{ $plan->name }}</span> – {{ t('checkout.prorated') }}</p>
|
||
@else
|
||
<h1 class="text-xl font-semibold text-gray-800">{{ t('checkout.title') }}</h1>
|
||
<p class="text-sm text-gray-400">{{ t('checkout.switch_to') }} <span class="font-medium text-gray-600">{{ $plan->name }}</span></p>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="grid lg:grid-cols-[1fr_380px] gap-6 items-start">
|
||
|
||
{{-- ============================================================
|
||
LEFT — ORDER SUMMARY
|
||
============================================================ --}}
|
||
<div class="space-y-5">
|
||
|
||
{{-- BILLING TOGGLE (nur bei paid plans) --}}
|
||
@if($this->checkoutType !== 'cancel')
|
||
<div class="bg-white border border-gray-100 rounded-2xl shadow-sm p-5 space-y-4">
|
||
|
||
<div class="text-sm font-semibold text-gray-700">{{ t('checkout.billing_period') }}</div>
|
||
|
||
<div class="grid grid-cols-2 gap-3">
|
||
|
||
{{-- Monthly --}}
|
||
<button wire:click="$set('billing', 'monthly')"
|
||
class="relative flex flex-col p-4 rounded-xl border-2 text-left transition-all
|
||
{{ $billing === 'monthly'
|
||
? 'border-indigo-500 bg-indigo-50'
|
||
: 'border-gray-200 hover:border-gray-300 bg-white' }}">
|
||
|
||
<div class="text-sm font-semibold {{ $billing === 'monthly' ? 'text-indigo-700' : 'text-gray-700' }}">
|
||
{{ t('checkout.monthly') }}
|
||
</div>
|
||
<div class="text-lg font-bold mt-1 {{ $billing === 'monthly' ? 'text-indigo-600' : 'text-gray-900' }}">
|
||
{{ number_format($this->monthlyPrice() / 100, 2, ',', '.') }} €
|
||
</div>
|
||
<div class="text-xs {{ $billing === 'monthly' ? 'text-indigo-400' : 'text-gray-400' }} mt-0.5">
|
||
{{ t('checkout.per_month') }}
|
||
</div>
|
||
|
||
@if($billing === 'monthly')
|
||
<div class="absolute top-3 right-3 w-5 h-5 rounded-full bg-indigo-500 flex items-center justify-center">
|
||
<x-heroicon-o-check class="w-3 h-3 text-white"/>
|
||
</div>
|
||
@endif
|
||
</button>
|
||
|
||
{{-- Yearly --}}
|
||
<button wire:click="$set('billing', 'yearly')"
|
||
class="relative flex flex-col p-4 rounded-xl border-2 text-left transition-all
|
||
{{ $billing === 'yearly'
|
||
? 'border-indigo-500 bg-indigo-50'
|
||
: 'border-gray-200 hover:border-gray-300 bg-white' }}">
|
||
|
||
@if($plan->yearly_discount_months > 0)
|
||
<div class="absolute top-3 right-3">
|
||
@if($billing !== 'yearly')
|
||
<span class="text-[10px] font-bold px-1.5 py-0.5 rounded-md bg-green-100 text-green-700">
|
||
{{ $plan->yearly_discount_months }} {{ t('checkout.months_free') }}
|
||
</span>
|
||
@else
|
||
<div class="w-5 h-5 rounded-full bg-indigo-500 flex items-center justify-center">
|
||
<x-heroicon-o-check class="w-3 h-3 text-white"/>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
@endif
|
||
|
||
<div class="text-sm font-semibold {{ $billing === 'yearly' ? 'text-indigo-700' : 'text-gray-700' }}">
|
||
{{ t('checkout.yearly') }}
|
||
</div>
|
||
<div class="text-lg font-bold mt-1 {{ $billing === 'yearly' ? 'text-indigo-600' : 'text-gray-900' }}">
|
||
{{ number_format(($this->yearlyPrice() / 100) / 12, 2, ',', '.') }} €
|
||
</div>
|
||
<div class="text-xs {{ $billing === 'yearly' ? 'text-indigo-400' : 'text-gray-400' }} mt-0.5">
|
||
{{ t('checkout.per_month_yearly') }}
|
||
</div>
|
||
</button>
|
||
|
||
</div>
|
||
|
||
@if($billing === 'yearly' && $this->savings() > 0)
|
||
<div class="flex items-center gap-2 bg-green-50 border border-green-100 rounded-xl px-3 py-2">
|
||
<x-heroicon-o-tag class="w-4 h-4 text-green-500 shrink-0"/>
|
||
<span class="text-sm text-green-700">
|
||
{{ t('checkout.save_comparison', ['amount' => number_format($this->savings() / 100, 2, ',', '.') . ' €']) }}
|
||
</span>
|
||
</div>
|
||
@endif
|
||
|
||
</div>
|
||
@endif {{-- end billing toggle --}}
|
||
|
||
|
||
{{-- FEATURES --}}
|
||
<div class="bg-white border border-gray-100 rounded-2xl shadow-sm p-5">
|
||
|
||
<div class="text-sm font-semibold text-gray-700 mb-4">{{ t('checkout.included', ['plan' => $plan->name]) }}</div>
|
||
|
||
@if($features->isEmpty())
|
||
<p class="text-sm text-gray-400">{{ t('checkout.all_features') }}</p>
|
||
@else
|
||
<div class="grid sm:grid-cols-2 gap-2">
|
||
@foreach($features as $feature)
|
||
<div class="flex items-center gap-2.5">
|
||
<div class="w-5 h-5 rounded-full bg-green-50 flex items-center justify-center shrink-0">
|
||
<x-heroicon-o-check class="w-3 h-3 text-green-500"/>
|
||
</div>
|
||
<span class="text-sm text-gray-600">{{ $feature->label }}</span>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
@endif
|
||
|
||
@if($plan->credit_limit)
|
||
<div class="mt-4 pt-4 border-t border-gray-50 flex items-center gap-2">
|
||
<x-heroicon-o-bolt class="w-4 h-4 text-indigo-400"/>
|
||
<span class="text-sm text-gray-600">
|
||
<span class="font-semibold text-gray-800">{{ number_format($plan->credit_limit) }}</span>
|
||
{{ t('checkout.credits_month') }}
|
||
</span>
|
||
</div>
|
||
@endif
|
||
|
||
</div>
|
||
|
||
|
||
{{-- TRUST SIGNALS --}}
|
||
<div class="bg-white border border-gray-100 rounded-2xl shadow-sm p-5">
|
||
|
||
<div class="grid sm:grid-cols-3 gap-4">
|
||
@foreach([
|
||
['icon' => 'shield-check', 'title' => t('checkout.secure_payment'), 'desc' => t('checkout.ssl_stripe')],
|
||
['icon' => 'arrow-uturn-left', 'title' => t('checkout.cancel_anytime'), 'desc' => t('checkout.no_minimum')],
|
||
['icon' => 'lock-closed', 'title' => t('checkout.gdpr'), 'desc' => t('checkout.eu_data')],
|
||
] as $trust)
|
||
<div class="flex items-start gap-3">
|
||
<div class="w-8 h-8 rounded-lg bg-gray-50 border border-gray-100 flex items-center justify-center shrink-0">
|
||
@if($trust['icon'] === 'shield-check') <x-heroicon-o-shield-check class="w-4 h-4 text-gray-500"/>
|
||
@elseif($trust['icon'] === 'arrow-uturn-left') <x-heroicon-o-arrow-uturn-left class="w-4 h-4 text-gray-500"/>
|
||
@else <x-heroicon-o-lock-closed class="w-4 h-4 text-gray-500"/>
|
||
@endif
|
||
</div>
|
||
<div>
|
||
<p class="text-xs font-semibold text-gray-700">{{ $trust['title'] }}</p>
|
||
<p class="text-[11px] text-gray-400 mt-0.5">{{ $trust['desc'] }}</p>
|
||
</div>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
|
||
{{-- Stripe badge --}}
|
||
<div class="mt-4 pt-4 border-t border-gray-50 flex items-center gap-2 text-xs text-gray-400">
|
||
<svg class="h-4" viewBox="0 0 60 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M5.45 10.22c0-.76.62-1.05 1.65-1.05 1.48 0 3.35.45 4.83 1.25V6.17a12.83 12.83 0 0 0-4.83-.88C3.26 5.29 1 7.16 1 10.4c0 5.02 6.91 4.22 6.91 6.39 0 .9-.78 1.19-1.87 1.19-1.62 0-3.69-.67-5.33-1.57v4.3c1.82.78 3.65 1.11 5.33 1.11 4.06 0 6.85-2 6.85-5.28-.01-5.42-6.94-4.46-6.94-6.32zM21.1 2.38l-4.42.94-.01 14.52c0 2.68 2.01 4.66 4.69 4.66 1.48 0 2.56-.27 3.16-.6v-3.58c-.58.23-3.43 1.06-3.43-1.6V9.76h3.43V6.02H21.1V2.38zm9.19 4.43-4.55.97v14.7h4.55V6.81zm-4.55 4.64v10.06h4.55V11.45h-4.55zM30.74 4.3a2.65 2.65 0 1 0 0-5.3 2.65 2.65 0 0 0 0 5.3zm9.4 11.8L37.6 6.02h-4.97l5.56 16.46h3.87l5.6-16.46h-4.86l-2.66 10.08zm17.12-10.5c-1.48 0-2.43.7-2.97 1.17l-.2-1h-4.05v21.06l4.6-.97.01-5.1c.55.4 1.36.97 2.7.97 2.73 0 5.22-2.2 5.22-7.05-.01-4.44-2.52-7.08-5.31-7.08zm-.93 10.9c-.9 0-1.43-.32-1.8-.72l-.02-5.67c.4-.45.94-.75 1.82-.75 1.4 0 2.36 1.57 2.36 3.55 0 2.03-.94 3.59-2.36 3.59z" fill="#6772E5"/>
|
||
</svg>
|
||
{{ t('checkout.stripe_note') }}
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
{{-- ============================================================
|
||
RIGHT — PRICE SUMMARY + PAY BUTTON
|
||
============================================================ --}}
|
||
<div class="space-y-4 lg:sticky lg:top-24">
|
||
|
||
{{-- Summary Card --}}
|
||
<div class="bg-white border border-gray-100 rounded-2xl shadow-sm overflow-hidden">
|
||
|
||
<div class="bg-indigo-600 px-5 py-4">
|
||
<div class="flex items-center justify-between">
|
||
<div>
|
||
<p class="text-indigo-200 text-xs font-medium">Plan</p>
|
||
<p class="text-white font-bold text-lg">{{ $plan->name }}</p>
|
||
</div>
|
||
<div class="text-right">
|
||
<p class="text-indigo-200 text-xs font-medium">{{ t('checkout.total') }}</p>
|
||
<p class="text-white font-bold text-2xl tabular-nums">
|
||
{{ number_format($this->activePrice() / 100, 2, ',', '.') }} €
|
||
</p>
|
||
<p class="text-indigo-200 text-[10px]">
|
||
/ {{ $billing === 'yearly' ? t('common.year') : t('common.month') }}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="px-5 py-4 space-y-3">
|
||
|
||
{{-- Line items --}}
|
||
<div class="space-y-2 text-sm">
|
||
|
||
<div class="flex justify-between text-gray-600">
|
||
<span>{{ $plan->name }} – {{ $billing === 'yearly' ? t('checkout.yearly') : t('checkout.monthly') }}</span>
|
||
<span class="font-medium text-gray-800 tabular-nums">
|
||
{{ number_format($this->activePrice() / 100, 2, ',', '.') }} €
|
||
</span>
|
||
</div>
|
||
|
||
@if($billing === 'yearly' && $this->savings() > 0)
|
||
<div class="flex justify-between text-green-600">
|
||
<span>{{ t('checkout.yearly_discount') }}</span>
|
||
<span class="font-medium tabular-nums">
|
||
−{{ number_format($this->savings() / 100, 2, ',', '.') }} €
|
||
</span>
|
||
</div>
|
||
@endif
|
||
|
||
<div class="border-t border-gray-100 pt-2 flex justify-between font-semibold text-gray-800">
|
||
<span>{{ t('checkout.total_incl_vat') }}</span>
|
||
<span class="tabular-nums">{{ number_format($this->activePrice() / 100, 2, ',', '.') }} €</span>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Renewal info --}}
|
||
<div class="bg-gray-50 rounded-xl px-3 py-2 text-xs text-gray-500">
|
||
<x-heroicon-o-arrow-path class="w-3.5 h-3.5 inline-block mr-1 text-gray-400"/>
|
||
{{ t('checkout.auto_renew', ['period' => $billing === 'yearly' ? t('checkout.period.12months') : t('checkout.period.30days')]) }}
|
||
{{ t('checkout.cancel_anytime') }}
|
||
</div>
|
||
|
||
{{-- Info-Hinweis je nach Typ --}}
|
||
@if($this->checkoutType === 'cancel')
|
||
<div class="bg-amber-50 border border-amber-100 rounded-xl px-3 py-2.5 text-xs text-amber-700 space-y-1">
|
||
<div class="font-semibold flex items-center gap-1.5">
|
||
<x-heroicon-o-exclamation-triangle class="w-3.5 h-3.5"/>
|
||
{{ t('checkout.cancel_immediate') }}
|
||
</div>
|
||
<p>{{ t('checkout.cancel_desc') }}</p>
|
||
</div>
|
||
@elseif($this->checkoutType === 'update')
|
||
<div class="bg-indigo-50 border border-indigo-100 rounded-xl px-3 py-2.5 text-xs text-indigo-700 space-y-1">
|
||
<div class="font-semibold flex items-center gap-1.5">
|
||
<x-heroicon-o-bolt class="w-3.5 h-3.5"/>
|
||
{{ t('checkout.switch_immediate') }}
|
||
</div>
|
||
<p>{{ t('checkout.switch_desc') }}</p>
|
||
</div>
|
||
@endif
|
||
|
||
{{-- Widerrufsrecht-Checkboxen (nur bei paid plans) --}}
|
||
@if($this->checkoutType !== 'cancel')
|
||
<div class="space-y-3 border border-amber-100 bg-amber-50 rounded-xl p-4">
|
||
<div class="flex items-start gap-2.5">
|
||
<x-heroicon-o-information-circle class="w-4 h-4 text-amber-500 shrink-0 mt-0.5"/>
|
||
<p class="text-[11px] text-amber-700 font-medium leading-relaxed">
|
||
{{ t('checkout.waiver_info') }}
|
||
</p>
|
||
</div>
|
||
<label class="flex items-start gap-3 cursor-pointer group">
|
||
<input
|
||
type="checkbox"
|
||
wire:model.live="rightAcknowledged"
|
||
class="mt-0.5 rounded border-amber-300 text-amber-500 focus:ring-amber-400 shrink-0"
|
||
/>
|
||
<span class="text-[11px] text-amber-800 leading-relaxed">
|
||
{{ t('checkout.waiver_right_acknowledged') }}
|
||
</span>
|
||
</label>
|
||
<label class="flex items-start gap-3 cursor-pointer group">
|
||
<input
|
||
type="checkbox"
|
||
wire:model.live="waiverConfirmed"
|
||
class="mt-0.5 rounded border-amber-300 text-amber-500 focus:ring-amber-400 shrink-0"
|
||
/>
|
||
<span class="text-[11px] text-amber-800 leading-relaxed">
|
||
{{ t('checkout.waiver_confirmed') }}
|
||
</span>
|
||
</label>
|
||
</div>
|
||
@endif
|
||
|
||
{{-- CTA --}}
|
||
@if($this->checkoutType === 'cancel')
|
||
<button
|
||
wire:click="startCheckout"
|
||
wire:loading.attr="disabled"
|
||
class="w-full flex items-center justify-center gap-2 px-5 py-3.5 rounded-xl bg-amber-500 text-white font-semibold hover:bg-amber-600 active:scale-95 transition-all shadow-sm disabled:opacity-60">
|
||
<span wire:loading.remove wire:target="startCheckout">
|
||
<x-heroicon-o-x-circle class="w-4 h-4 inline-block mr-1 opacity-70"/>
|
||
{{ t('checkout.cancel_btn') }}
|
||
</span>
|
||
<span wire:loading wire:target="startCheckout" class="flex items-center gap-2">
|
||
<svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
|
||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
|
||
</svg>
|
||
{{ t('checkout.cancelling') }}
|
||
</span>
|
||
</button>
|
||
@elseif($this->checkoutType === 'update')
|
||
<button
|
||
wire:click="startCheckout"
|
||
wire:loading.attr="disabled"
|
||
:disabled="!$wire.rightAcknowledged || !$wire.waiverConfirmed"
|
||
class="w-full flex items-center justify-center gap-2 px-5 py-3.5 rounded-xl bg-indigo-600 text-white font-semibold hover:bg-indigo-700 active:scale-95 transition-all shadow-sm shadow-indigo-200 disabled:opacity-60">
|
||
<span wire:loading.remove wire:target="startCheckout">
|
||
<x-heroicon-o-arrow-path class="w-4 h-4 inline-block mr-1 opacity-70"/>
|
||
{{ t('checkout.switch_btn', ['plan' => $plan->name]) }}
|
||
</span>
|
||
<span wire:loading wire:target="startCheckout" class="flex items-center gap-2">
|
||
<svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
|
||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
|
||
</svg>
|
||
{{ t('checkout.switching') }}
|
||
</span>
|
||
</button>
|
||
@else
|
||
<button
|
||
wire:click="startCheckout"
|
||
wire:loading.attr="disabled"
|
||
:disabled="!$wire.rightAcknowledged || !$wire.waiverConfirmed"
|
||
class="w-full flex items-center justify-center gap-2 px-5 py-3.5 rounded-xl bg-indigo-600 text-white font-semibold hover:bg-indigo-700 active:scale-95 transition-all shadow-sm shadow-indigo-200 disabled:opacity-60">
|
||
<span wire:loading.remove wire:target="startCheckout">
|
||
<x-heroicon-o-lock-closed class="w-4 h-4 inline-block mr-1 opacity-70"/>
|
||
{{ t('checkout.pay_now') }}
|
||
</span>
|
||
<span wire:loading wire:target="startCheckout" class="flex items-center gap-2">
|
||
<svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
|
||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
|
||
</svg>
|
||
{{ t('checkout.redirecting') }}
|
||
</span>
|
||
</button>
|
||
@endif
|
||
|
||
<p class="text-[11px] text-center text-gray-400">
|
||
@if($this->checkoutType === 'new')
|
||
{{ t('checkout.redirect_stripe') }}
|
||
@elseif($this->checkoutType === 'update')
|
||
{{ t('checkout.no_checkout') }}
|
||
@else
|
||
{{ t('checkout.cancel_effective') }}
|
||
@endif
|
||
</p>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Current user info --}}
|
||
<div class="bg-white border border-gray-100 rounded-xl shadow-sm px-4 py-3 flex items-center gap-3">
|
||
<div class="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center text-indigo-600 font-bold text-sm shrink-0">
|
||
{{ strtoupper(substr(auth()->user()->name, 0, 1)) }}
|
||
</div>
|
||
<div class="min-w-0">
|
||
<p class="text-sm font-medium text-gray-800 truncate">{{ auth()->user()->name }}</p>
|
||
<p class="text-xs text-gray-400 truncate">{{ auth()->user()->email }}</p>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|