CluPilotCloud/resources/views/livewire/checkout.blade.php

223 lines
14 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{{--
The order, before it is placed.
Two columns, and which is which matters: the LEFT one is what you are buying
and what will happen, the RIGHT one is what it costs and the button that
spends the money. The summary sticks, so on a long left column the total and
the button stay in view the shape every enterprise checkout has, for the
reason that the figure you are agreeing to should never be scrolled away from
the agreement.
The terms acceptance sits at the BOTTOM of the right column, immediately
above that button. It used to be at the top of the package list, as a
condition of looking at prices; it is a condition of buying, and this is the
moment of buying.
--}}
@php
use Illuminate\Support\Number;
$eur = fn (int $cents) => Number::currency($cents / 100, in: $lines['currency'] ?? 'EUR', locale: app()->getLocale());
@endphp
<div class="space-y-6">
<header class="flex flex-wrap items-end gap-x-6 gap-y-4 border-b border-line pb-5 animate-rise">
<div class="min-w-0 flex-1">
<p class="lbl">{{ __('checkout.eyebrow') }}</p>
<h1 class="mt-[7px] text-[23px] font-bold leading-[1.12] tracking-[-0.03em] text-ink min-[901px]:text-[30px]">
{{ __('checkout.title') }}
</h1>
</div>
<a href="{{ route('order') }}" wire:navigate
class="inline-flex items-center gap-2 text-sm font-semibold text-muted transition-colors hover:text-ink">
<x-ui.icon name="arrow-left" class="size-4" />{{ __('checkout.back_to_plans') }}
</a>
</header>
@if ($contract)
<x-ui.alert variant="info">
{{ __('order.already_customer') }}
<a href="{{ route('billing') }}" class="font-semibold underline">{{ __('order.to_billing') }}</a>
</x-ui.alert>
@elseif ($package === null)
{{-- A package key nobody knows. Reached from a retyped address or from a
package taken off sale between the two pages. --}}
<x-ui.alert variant="warning">
{{ __('checkout.unknown_plan') }}
<a href="{{ route('order') }}" class="font-semibold underline">{{ __('checkout.back_to_plans') }}</a>
</x-ui.alert>
@else
@error('plan')<x-ui.alert variant="error">{{ $message }}</x-ui.alert>@enderror
@error('term')<x-ui.alert variant="error">{{ $message }}</x-ui.alert>@enderror
@error('terms_accepted')<x-ui.alert variant="error">{{ $message }}</x-ui.alert>@enderror
@error('checkout')<x-ui.alert variant="error">{{ $message }}</x-ui.alert>@enderror
<div class="grid gap-5 lg:grid-cols-[minmax(0,1fr)_400px] lg:items-start">
{{-- ── What you are buying ──────────────────────────────────────── --}}
<div class="space-y-5">
<section class="overflow-hidden rounded-lg border border-line bg-surface shadow-xs animate-rise">
<div class="flex flex-wrap items-baseline gap-x-3 gap-y-1 border-b border-line px-6 py-4">
<h2 class="text-md font-bold tracking-[-0.01em] text-ink">{{ $package['name'] }}</h2>
<span class="rounded-pill border border-line bg-surface-2 px-2.5 py-0.5 text-xs font-semibold text-body">
{{ __('order.term_'.$term) }}
</span>
@if ($package['audience'] ?? '')
<span class="text-sm text-muted">{{ $package['audience'] }}</span>
@endif
</div>
<dl class="grid grid-cols-2 gap-px bg-line sm:grid-cols-4">
@foreach ([
__('order.storage') => $package['quota_gb'] >= 1000 ? ($package['quota_gb'] / 1000).' TB' : $package['quota_gb'].' GB',
__('order.traffic') => $package['traffic_gb'] >= 1000 ? ($package['traffic_gb'] / 1000).' TB' : $package['traffic_gb'].' GB',
__('order.seats') => __('order.up_to', ['count' => $package['seats']]),
__('checkout.performance') => __('billing.perf.'.($package['performance'] ?? 'standard')),
] as $label => $value)
<div class="bg-surface px-6 py-4">
<dt class="text-xs text-muted">{{ $label }}</dt>
<dd class="mt-1 font-mono text-sm tabular-nums text-ink">{{ $value }}</dd>
</div>
@endforeach
</dl>
<div class="border-t border-line px-6 py-4">
<x-ui.delivery :state="$package['delivery']" />
</div>
</section>
{{-- What happens after the button. Three steps, because "you are
redirected to a payment provider" is the point at which a
first-time buyer wants to know what they are getting into. --}}
<section class="rounded-lg border border-line bg-surface p-6 shadow-xs animate-rise [animation-delay:60ms]">
<h2 class="text-sm font-bold text-ink">{{ __('checkout.next_title') }}</h2>
<ol class="mt-4 space-y-4">
@foreach (['pay', 'build', 'credentials'] as $i => $step)
<li class="flex gap-3.5">
<span class="mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-pill bg-ink text-xs font-bold text-bg">{{ $i + 1 }}</span>
<span class="min-w-0">
<b class="block text-sm font-semibold text-ink">{{ __('checkout.step_'.$step.'_title') }}</b>
<span class="mt-0.5 block text-xs leading-relaxed text-muted">{{ __('checkout.step_'.$step.'_body') }}</span>
</span>
</li>
@endforeach
</ol>
</section>
{{-- The three standing facts about the sale. Same band as the
order page, so the two pages read as one flow. --}}
<div class="grid gap-px overflow-hidden rounded-lg border border-line bg-line shadow-xs sm:grid-cols-3 animate-rise [animation-delay:120ms]">
@foreach ([
['icon' => 'refresh', 'title' => __('order.note_cancel_title'), 'body' => __('order.note_cancel_body')],
['icon' => 'lock', 'title' => __('order.note_payment_title'), 'body' => __('order.note_payment_body')],
['icon' => 'shield', 'title' => __('checkout.note_withdrawal_title'), 'body' => __('checkout.note_withdrawal_body')],
] as $note)
<div class="bg-surface p-5">
<p class="flex items-center gap-2 text-sm font-semibold text-ink">
<x-ui.icon :name="$note['icon']" class="size-4 text-accent-text" />{{ $note['title'] }}
</p>
<p class="mt-2 text-xs leading-relaxed text-muted">{{ $note['body'] }}</p>
</div>
@endforeach
</div>
</div>
{{-- ── What it costs, and the button ────────────────────────────── --}}
<form method="POST" action="{{ route('checkout.start') }}"
x-data="{ terms: false }"
class="rounded-lg border border-line bg-surface shadow-md animate-rise [animation-delay:40ms] lg:sticky lg:top-6">
@csrf
<input type="hidden" name="plan" value="{{ $package['key'] }}" />
<input type="hidden" name="term" value="{{ $term }}" />
<input type="hidden" name="terms_accepted" x-bind:value="terms ? '1' : ''" />
<div class="border-b border-line px-6 py-4">
<h2 class="text-sm font-bold text-ink">{{ __('checkout.summary_title') }}</h2>
</div>
<dl class="space-y-3 px-6 py-5 text-sm">
<div class="flex items-baseline justify-between gap-4">
<dt class="text-muted">
{{ $package['name'] }} ·
{{ $lines['yearly'] ? __('order.per_year_label') : __('order.per_month_label') }}
</dt>
<dd class="font-mono tabular-nums text-ink">{{ $eur($lines['recurring_net']) }}</dd>
</div>
@if ($lines['yearly'] && $lines['free_months'] > 0)
<div class="flex items-baseline justify-between gap-4 text-success">
<dt>{{ trans_choice('order.free_months_hint', $lines['free_months'], ['count' => $lines['free_months']]) }}</dt>
<dd class="font-mono tabular-nums"> {{ $eur($lines['twelve_months_gross'] - $lines['recurring_gross']) }}</dd>
</div>
@endif
@if ($lines['setup_net'] > 0)
<div class="flex items-baseline justify-between gap-4">
<dt class="text-muted">{{ __('checkout.setup_line') }}</dt>
<dd class="font-mono tabular-nums text-ink">{{ $eur($lines['setup_net']) }}</dd>
</div>
@endif
<div class="flex items-baseline justify-between gap-4 border-t border-line pt-3">
<dt class="text-muted">{{ __('checkout.tax_line', ['rate' => $vat]) }}</dt>
<dd class="font-mono tabular-nums text-ink">{{ $eur($lines['today_tax']) }}</dd>
</div>
{{-- The one figure that will actually be charged, and it is
the largest thing in the panel. --}}
<div class="flex items-baseline justify-between gap-4 border-t border-line pt-3">
<dt class="text-sm font-semibold text-ink">{{ __('checkout.total_today') }}</dt>
<dd class="font-mono text-xl font-bold tabular-nums tracking-[-0.02em] text-ink">{{ $eur($lines['today_gross']) }}</dd>
</div>
{{-- And what comes after it, because the total above includes
a one-off and the next charge will not. --}}
<p class="text-xs leading-relaxed text-muted">
{{ __($lines['yearly'] ? 'checkout.then_yearly' : 'checkout.then_monthly', [
'amount' => $eur($lines['recurring_gross']),
]) }}
@if ($lines['yearly'])
· {{ __('order.per_month_equivalent', ['amount' => $eur($lines['per_month_gross'])]) }}
@endif
</p>
</dl>
{{-- ── The acceptance, directly above the button ─────────────
Not at the top of the previous page as a condition of
looking at prices: it is a condition of buying, and this is
the button that buys. The server rule is the real gate
(CheckoutController validates `accepted`), so with
JavaScript off the field arrives empty and the checkout is
refused with the sentence explaining why. --}}
<div class="border-t border-line bg-surface-2 px-6 py-5">
<label class="flex cursor-pointer items-start gap-3">
<input type="checkbox" x-model="terms"
class="mt-0.5 size-4 shrink-0 rounded-sm border-line-strong text-accent-active focus:ring-accent-active" />
<span class="min-w-0 text-sm leading-relaxed text-body">
{{-- A new tab: following the link must not lose the
page with the order on it. --}}
{!! __('order.terms_label', [
'link' => '<a href="'.route('legal.agb').'" target="_blank" rel="noopener"
class="font-semibold text-accent-text underline underline-offset-4">'
.e(__('order.terms_link')).'</a>',
]) !!}
</span>
</label>
<p class="mt-2 pl-7 text-xs leading-relaxed text-muted">{{ __('order.terms_why') }}</p>
<button type="submit"
class="mt-5 inline-flex w-full items-center justify-center gap-2 rounded bg-ink px-4 py-3 text-sm font-semibold text-bg transition-colors hover:bg-accent-text"
x-bind:disabled="!terms"
x-bind:class="terms ? '' : 'cursor-not-allowed opacity-50'">
<x-ui.icon name="lock" class="size-4" />{{ __('checkout.pay_cta') }}
</button>
<p class="mt-2 text-center text-[11px] leading-snug text-muted" x-show="!terms" x-cloak>
{{ __('order.terms_first') }}
</p>
<p class="mt-3 text-center text-[11px] leading-snug text-muted">{{ __('checkout.stripe_note') }}</p>
</div>
</form>
</div>
@endif
</div>