checkout
This commit is contained in:
143
assets/views/shop/checkout_payment.html
Normal file
143
assets/views/shop/checkout_payment.html
Normal file
@@ -0,0 +1,143 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "macros/ui.html" as ui %}
|
||||
|
||||
{% block title %}{{ t(key="checkout-title", lang=lang | default(value='sk')) }}{% endblock title %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{{ ui::checkout_steps(active="payment", lang=lang | default(value='sk')) }}
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
{% if packeta_api_key %}<script src="https://widget.packeta.com/v6/www/js/library.js"></script>{% endif %}
|
||||
|
||||
<h1 class="text-3xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-title", lang=lang | default(value='sk')) }}</h1>
|
||||
|
||||
<form method="post" action="/checkout/payment" hx-boost="false"
|
||||
x-data="{
|
||||
paymentMethod: '',
|
||||
carrier: '',
|
||||
carrierPrice: 0,
|
||||
requiresPoint: false,
|
||||
pointId: '',
|
||||
pointName: '',
|
||||
subtotal: {{ subtotal_cents }},
|
||||
packetaKey: '{{ packeta_api_key }}',
|
||||
fmt(c) { return (c / 100).toFixed(2) },
|
||||
pickPoint() {
|
||||
Packeta.Widget.pick(this.packetaKey, (point) => {
|
||||
if (point) { this.pointId = String(point.id); this.pointName = point.formatedValue || point.name }
|
||||
})
|
||||
},
|
||||
get canSubmit() {
|
||||
return this.paymentMethod && this.carrier && (!this.requiresPoint || this.pointId)
|
||||
}
|
||||
}"
|
||||
class="mt-6 grid gap-8 lg:grid-cols-3">
|
||||
{{ ui::csrf_field() }}
|
||||
|
||||
<div class="space-y-6 lg:col-span-2">
|
||||
<!-- carrier -->
|
||||
<fieldset class="space-y-3 rounded-radius border border-outline bg-surface p-6 dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<legend class="px-1 text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-carrier", lang=lang | default(value='sk')) }}{{ ui::req() }}</legend>
|
||||
{% for m in shipping_methods %}
|
||||
<label class="flex cursor-pointer items-center justify-between gap-3 rounded-radius border border-outline px-4 py-3 transition has-[:checked]:border-primary dark:border-outline-dark dark:has-[:checked]:border-primary-dark">
|
||||
<span class="flex items-center gap-3">
|
||||
<!-- Penguin radio dot inline (the @change mixes nested single+double quotes, can't pass through a Tera macro arg) -->
|
||||
<input type="radio" name="carrier_code" value="{{ m.code }}" required
|
||||
@change="carrier='{{ m.code }}'; carrierPrice={{ m.price_cents }}; requiresPoint={{ m.requires_pickup_point }}; pointId=''; pointName=''"
|
||||
class="before:content[''] relative h-4 w-4 appearance-none rounded-full border border-outline bg-surface before:invisible before:absolute before:left-1/2 before:top-1/2 before:h-1.5 before:w-1.5 before:-translate-x-1/2 before:-translate-y-1/2 before:rounded-full before:bg-on-primary checked:border-primary checked:bg-primary checked:before:visible focus:outline-2 focus:outline-offset-2 focus:outline-outline-strong checked:focus:outline-primary disabled:cursor-not-allowed dark:border-outline-dark dark:bg-surface-dark dark:before:bg-on-primary-dark dark:checked:border-primary-dark dark:checked:bg-primary-dark dark:focus:outline-outline-dark-strong dark:checked:focus:outline-primary-dark">
|
||||
<span class="font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ m.name }}</span>
|
||||
</span>
|
||||
<span class="tabular-nums text-on-surface/80 dark:text-on-surface-dark/80">{{ m.price }} €</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
|
||||
<!-- pickup point (carriers that need one, e.g. Packeta) -->
|
||||
<div x-show="requiresPoint" x-cloak class="space-y-2 pt-1">
|
||||
<input type="hidden" name="pickup_point_id" x-model="pointId">
|
||||
<input type="hidden" name="pickup_point_name" x-model="pointName">
|
||||
{% if packeta_api_key %}
|
||||
<button type="button" @click="pickPoint()"
|
||||
class="inline-flex items-center rounded-radius border border-outline px-4 py-2 text-sm font-medium text-on-surface transition hover:bg-surface-alt dark:border-outline-dark dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
|
||||
{{ t(key="checkout-pick-point", lang=lang | default(value='sk')) }}
|
||||
</button>
|
||||
<p x-show="pointName" x-cloak class="text-sm text-on-surface/80 dark:text-on-surface-dark/80">
|
||||
<span class="font-medium">{{ t(key="checkout-chosen-point", lang=lang | default(value='sk')) }}:</span> <span x-text="pointName"></span>
|
||||
</p>
|
||||
{% else %}
|
||||
<label class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-pickup-point", lang=lang | default(value='sk')) }}</label>
|
||||
<input type="text" x-model="pointName" @input="pointId = pointName"
|
||||
class="w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface focus:outline-2 focus:outline-primary dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark">
|
||||
{% endif %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- payment -->
|
||||
<fieldset class="space-y-3 rounded-radius border border-outline bg-surface p-6 dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<legend class="px-1 text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-payment", lang=lang | default(value='sk')) }}{{ ui::req() }}</legend>
|
||||
{% if payment_methods | length > 0 %}
|
||||
{% for method in payment_methods %}
|
||||
<label class="flex cursor-pointer items-center gap-3 rounded-radius border border-outline px-4 py-3 transition has-[:checked]:border-primary dark:border-outline-dark dark:has-[:checked]:border-primary-dark">
|
||||
{{ ui::radio(name="payment_method", value=method.code, attrs='required x-model="paymentMethod"') }}
|
||||
<span class="font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key=method.label_key, lang=lang | default(value='sk')) }}</span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="payment-none", lang=lang | default(value='sk')) }}</p>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<label for="note" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-note", lang=lang | default(value='sk')) }}</label>
|
||||
{{ ui::textarea(name="note", id="note", rows="3") }}
|
||||
</div>
|
||||
|
||||
{% if logged_in_customer and not profile_filled %}
|
||||
<!-- offered only when the profile has no saved address yet; if it was filled
|
||||
in advance we leave it untouched -->
|
||||
{{ ui::checkbox(name="save_profile", id="save_profile", label=t(key="checkout-save-profile", lang=lang | default(value='sk'))) }}
|
||||
{% endif %}
|
||||
|
||||
{% if can_create_account %}
|
||||
<!-- guests may turn this order into an account (typed by their choice in step 1) -->
|
||||
<div class="space-y-1.5 rounded-radius border border-outline bg-surface p-4 dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
{{ ui::checkbox(name="create_account", id="create_account", label=t(key="checkout-create-account", lang=lang | default(value='sk'))) }}
|
||||
<p class="pl-6 text-xs text-on-surface/60 dark:text-on-surface-dark/60">{{ t(key="checkout-create-account-hint", lang=lang | default(value='sk')) }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<a href="/checkout/info" class="inline-flex items-center gap-1.5 text-sm text-on-surface/70 transition hover:text-primary dark:text-on-surface-dark/70 dark:hover:text-primary-dark">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" /></svg>
|
||||
{{ t(key="checkout-back-info", lang=lang | default(value='sk')) }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- summary -->
|
||||
<aside class="h-fit space-y-4 rounded-radius border border-outline bg-surface p-6 dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<h2 class="text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="checkout-summary", lang=lang | default(value='sk')) }}</h2>
|
||||
<ul class="space-y-2 text-sm">
|
||||
{% for item in items %}
|
||||
<li class="flex justify-between gap-2">
|
||||
<span class="text-on-surface/80 dark:text-on-surface-dark/80">{{ item.name }} × {{ item.quantity }}</span>
|
||||
<span class="tabular-nums">{{ item.line_total }} €</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="space-y-1 border-t border-outline pt-3 text-sm dark:border-outline-dark">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="checkout-subtotal", lang=lang | default(value='sk')) }}</span>
|
||||
<span class="tabular-nums">{{ subtotal }} €</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="checkout-shipping-cost", lang=lang | default(value='sk')) }}</span>
|
||||
<span class="tabular-nums" x-text="fmt(carrierPrice) + ' €'"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between border-t border-outline pt-3 text-base font-bold dark:border-outline-dark">
|
||||
<span>{{ t(key="cart-total", lang=lang | default(value='sk')) }}</span>
|
||||
<span class="tabular-nums text-primary dark:text-primary-dark" x-text="fmt(subtotal + carrierPrice) + ' €'"></span>
|
||||
</div>
|
||||
{{ ui::button(label=t(key="checkout-place-order", lang=lang | default(value='sk')), type="submit", attrs=':disabled="!canSubmit"', extra="w-full", size="px-6 py-2.5 text-sm") }}
|
||||
</aside>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
Reference in New Issue
Block a user