generated from boboko/starter
cart drawer and general structure
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
{{--
|
||||
<x-checkout::add-to-cart :purchasable="$variantId" />
|
||||
|
||||
A self-contained add-to-cart form. Posts the line via bbk-add-to-cart-controller
|
||||
(fetch) and hands the rendered cart body to the drawer over the
|
||||
`bbk-cart:changed` window event.
|
||||
|
||||
Props:
|
||||
purchasable ProductVariant id. Omit to render no hidden id field — the host
|
||||
must then supply [data-bbk-purchasable-input] itself (e.g. a
|
||||
variant picker writing the selected id into it).
|
||||
quantity Integer for the hidden quantity field, or false to omit it
|
||||
(the host then puts its own name="quantity" control in the slot).
|
||||
|
||||
The button and any quantity control come from the slot, so the host owns all
|
||||
appearance. Extra attributes (class, etc.) land on the <form>.
|
||||
--}}
|
||||
@props([
|
||||
'purchasable' => null,
|
||||
'quantity' => 1,
|
||||
'action' => null,
|
||||
])
|
||||
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ $action ?? route('checkout.cart.add', app()->getLocale()) }}"
|
||||
data-controller="bbk-add-to-cart"
|
||||
data-action="bbk-add-to-cart#add"
|
||||
{{ $attributes->class('bbk-add-to-cart') }}
|
||||
>
|
||||
@csrf
|
||||
|
||||
@if (! is_null($purchasable))
|
||||
<input type="hidden" name="purchasable_id" value="{{ $purchasable }}" data-bbk-purchasable-input>
|
||||
@endif
|
||||
|
||||
@if ($quantity !== false)
|
||||
<input type="hidden" name="quantity" value="{{ $quantity }}">
|
||||
@endif
|
||||
|
||||
{{ $slot }}
|
||||
</form>
|
||||
@@ -0,0 +1,31 @@
|
||||
{{--
|
||||
Slide-in cart drawer. Rendered once, globally, from the app layout
|
||||
(@include('checkout::drawer')). Structure only — all styling lives in
|
||||
resources/css/checkout.css under @layer bbk-checkout; the host restyles the
|
||||
.bbk-* classes from its own stylesheet. No host components, no Tailwind.
|
||||
--}}
|
||||
<div class="bbk-cart" data-controller="bbk-cart" hidden>
|
||||
<div class="bbk-cart-backdrop" data-action="bbk-cart#close"></div>
|
||||
|
||||
<aside
|
||||
class="bbk-cart-panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="bbk-cart-heading"
|
||||
data-bbk-cart-target="panel"
|
||||
>
|
||||
<header class="bbk-cart-panel-header">
|
||||
<h2 class="bbk-cart-heading" id="bbk-cart-heading">{{ __('checkout.cart.title') }}</h2>
|
||||
<button
|
||||
type="button"
|
||||
class="bbk-cart-dismiss"
|
||||
data-action="bbk-cart#close"
|
||||
aria-label="{{ __('checkout.cart.close') }}"
|
||||
>×</button>
|
||||
</header>
|
||||
|
||||
<div class="bbk-cart-panel-body" data-bbk-cart-target="body" aria-live="polite">
|
||||
@include('checkout::partials.cart-body')
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
@@ -0,0 +1,102 @@
|
||||
{{--
|
||||
Server-rendered cart contents. Rendered inline on first page load inside
|
||||
checkout/drawer.blade.php, and re-fetched + swapped into the drawer by
|
||||
bbk-cart-controller after every mutation. $cart / $lines come from the view
|
||||
composer in CheckoutModuleServiceProvider.
|
||||
|
||||
The data-bbk-cart-* attributes on the root are the module's read API for the
|
||||
host (e.g. the header bag-icon count) — bbk-cart-controller reads them after
|
||||
each swap and re-emits them on the `bbk-cart:updated` window event.
|
||||
--}}
|
||||
@php($count = $lines->sum('quantity'))
|
||||
|
||||
{{-- @dump($lines) --}}
|
||||
|
||||
<div
|
||||
class="bbk-cart-content"
|
||||
data-bbk-cart-count="{{ $count }}"
|
||||
data-bbk-cart-total="{{ $cart?->total?->value ?? 0 }}"
|
||||
>
|
||||
@if ($lines->isEmpty())
|
||||
<p class="bbk-cart-empty">{{ __('checkout.cart.empty') }}</p>
|
||||
@else
|
||||
<ul class="bbk-cart-items">
|
||||
@each('checkout::partials.cart-line', $lines, 'line')
|
||||
</ul>
|
||||
|
||||
<div class="bbk-cart-summary">
|
||||
<div class="bbk-cart-coupon">
|
||||
@if ($cart?->coupon_code)
|
||||
<div class="bbk-cart-coupon-applied">
|
||||
<span class="bbk-cart-coupon-code">{{ $cart->coupon_code }}</span>
|
||||
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ route('checkout.cart.coupon.remove', app()->getLocale()) }}"
|
||||
data-action="submit->bbk-cart#submit"
|
||||
>
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="bbk-cart-coupon-remove">
|
||||
{{ __('checkout.cart.coupon_remove') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@else
|
||||
<form
|
||||
class="bbk-cart-coupon-form"
|
||||
method="POST"
|
||||
action="{{ route('checkout.cart.coupon.apply', app()->getLocale()) }}"
|
||||
data-action="submit->bbk-cart#submit"
|
||||
>
|
||||
@csrf
|
||||
<label class="bbk-visually-hidden" for="bbk-coupon-code">
|
||||
{{ __('checkout.cart.coupon_label') }}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="code"
|
||||
id="bbk-coupon-code"
|
||||
class="bbk-cart-coupon-input"
|
||||
placeholder="{{ __('checkout.cart.coupon_placeholder') }}"
|
||||
autocomplete="off"
|
||||
required
|
||||
>
|
||||
<button type="submit" class="bbk-cart-coupon-submit">
|
||||
{{ __('checkout.cart.coupon_apply') }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@if ($couponError ?? false)
|
||||
<p class="bbk-cart-coupon-error" role="alert">{{ __('checkout.cart.coupon_invalid') }}</p>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if ($cart?->discountTotal?->value > 0)
|
||||
<div class="bbk-cart-summary-row bbk-cart-summary-row--discount">
|
||||
<span>{{ __('checkout.cart.discount') }}</span>
|
||||
<span>−{{ $cart->discountTotal->formatted() }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="bbk-cart-summary-row">
|
||||
<span>{{ __('checkout.cart.subtotal') }}</span>
|
||||
<span>{{ $cart?->subTotal?->formatted() }}</span>
|
||||
</div>
|
||||
|
||||
{{-- Always shown, even with no discount — equals subtotal then,
|
||||
diverges once one's applied. --}}
|
||||
<div class="bbk-cart-summary-row bbk-cart-summary-row--total">
|
||||
<span>{{ __('checkout.cart.total') }}</span>
|
||||
<span>{{ $cart?->total?->formatted() }}</span>
|
||||
</div>
|
||||
|
||||
{{-- TODO: point at the checkout page once that slice exists —
|
||||
the drawer is the cart, there's no cart page for this to fall back to. --}}
|
||||
<button type="button" class="bbk-cart-checkout" disabled>
|
||||
{{ __('checkout.cart.checkout') }}
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,77 @@
|
||||
{{--
|
||||
One cart line. $line is a Lunar\Models\CartLine (iteration var set by
|
||||
@each in cart-body). The two forms post through bbk-cart-controller
|
||||
(fetch + method spoofing) and the response re-renders cart-body.
|
||||
--}}
|
||||
@php
|
||||
$variant = $line->purchasable;
|
||||
$product = $variant?->product;
|
||||
$name = $product?->translateAttribute('name') ?? $variant?->sku ?? '—';
|
||||
$thumb = $product?->getThumbnailImage() ?: null;
|
||||
@endphp
|
||||
|
||||
<li class="bbk-cart-item" data-bbk-line-id="{{ $line->id }}">
|
||||
<div class="bbk-cart-item-media">
|
||||
@if ($thumb)
|
||||
<img src="{{ $thumb }}" alt="{{ $name }}" width="72" height="72" loading="lazy">
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="bbk-cart-item-detail">
|
||||
<p class="bbk-cart-item-title">{{ $name }}</p>
|
||||
<p class="bbk-cart-item-unit">{{ $line->unitPrice?->formatted() }}</p>
|
||||
|
||||
<form
|
||||
class="bbk-cart-qty"
|
||||
method="POST"
|
||||
action="{{ route('checkout.cart.update', ['locale' => app()->getLocale(), 'line' => $line->id]) }}"
|
||||
>
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<button
|
||||
type="button"
|
||||
class="bbk-cart-qty-btn"
|
||||
data-action="bbk-cart#step"
|
||||
data-bbk-cart-dir-param="-1"
|
||||
aria-label="{{ __('checkout.cart.decrease') }}"
|
||||
>−</button>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
name="quantity"
|
||||
value="{{ $line->quantity }}"
|
||||
min="0"
|
||||
inputmode="numeric"
|
||||
class="bbk-cart-qty-input"
|
||||
data-action="change->bbk-cart#submit"
|
||||
aria-label="{{ __('checkout.cart.quantity') }}"
|
||||
>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="bbk-cart-qty-btn"
|
||||
data-action="bbk-cart#step"
|
||||
data-bbk-cart-dir-param="1"
|
||||
aria-label="{{ __('checkout.cart.increase') }}"
|
||||
>+</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="bbk-cart-item-aside">
|
||||
<p class="bbk-cart-item-total">{{ $line->subTotal?->formatted() }}</p>
|
||||
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ route('checkout.cart.remove', ['locale' => app()->getLocale(), 'line' => $line->id]) }}"
|
||||
data-action="submit->bbk-cart#submit"
|
||||
>
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button
|
||||
type="submit"
|
||||
class="bbk-cart-item-remove"
|
||||
aria-label="{{ __('checkout.cart.remove') }}"
|
||||
>×</button>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
@@ -41,10 +41,22 @@
|
||||
</a>
|
||||
@endforeach
|
||||
|
||||
{{-- Cart --}}
|
||||
<a href="{{ url('/'.app()->getLocale().'/cart') }}" class="flex items-center justify-center w-10 h-10 hover:opacity-70 transition-opacity" aria-label="Cart">
|
||||
{{-- Cart — opens the checkout module's drawer, no separate cart page --}}
|
||||
<button
|
||||
type="button"
|
||||
class="relative flex items-center justify-center w-10 h-10 hover:opacity-70 transition-opacity"
|
||||
aria-label="Cart"
|
||||
aria-haspopup="dialog"
|
||||
data-controller="cart-count"
|
||||
data-action="cart-count#open"
|
||||
>
|
||||
<x-ui.icon name="bag" :size="40" />
|
||||
</a>
|
||||
<span
|
||||
data-cart-count-target="badge"
|
||||
class="absolute -top-1 -right-1 min-w-5 h-5 px-1 rounded-full bg-black text-neutral-200 text-xs font-bold flex items-center justify-center"
|
||||
hidden
|
||||
>0</span>
|
||||
</button>
|
||||
|
||||
{{-- Search --}}
|
||||
<button
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{ \Illuminate\Support\Facades\Vite::asset('resources/fonts/manrope/manrope-v20-greek_latin-700.woff2') }}">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{ \Illuminate\Support\Facades\Vite::asset('resources/fonts/manrope/manrope-v20-greek_latin-800.woff2') }}">
|
||||
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
@vite(['resources/css/checkout.css', 'resources/css/app.css', 'resources/js/app.js'])
|
||||
|
||||
@auth('staff')
|
||||
@php($stoicToken = \App\Services\Stoic::token(auth('staff')->user()->email))
|
||||
@@ -52,6 +52,8 @@
|
||||
<body class="min-h-screen antialiased">
|
||||
<x-header />
|
||||
|
||||
@include('checkout::drawer')
|
||||
|
||||
<main id="main-content" class="min-h-[calc(100vh-12rem)] sm:min-h-[calc(100vh-8rem)]">
|
||||
@yield('content')
|
||||
</main>
|
||||
|
||||
@@ -170,10 +170,14 @@ class="absolute bottom-6 right-8 text-white text-sm"
|
||||
<x-ui.color-swatch :variants="$product['variants']" :option="$option" />
|
||||
@endif
|
||||
|
||||
<div class="flex items-stretch gap-10">
|
||||
<x-checkout::add-to-cart
|
||||
:purchasable="$variantsData[0]['id'] ?? null"
|
||||
:quantity="false"
|
||||
class="flex items-stretch gap-10"
|
||||
>
|
||||
<x-ui.quantity name="quantity" />
|
||||
<x-ui.button class="flex-1">{{ __('storefront.product.add_to_cart') }}</x-ui.button>
|
||||
</div>
|
||||
<x-ui.button type="submit" class="flex-1">{{ __('storefront.product.add_to_cart') }}</x-ui.button>
|
||||
</x-checkout::add-to-cart>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user