order emails theming, sold out product card, contact page hcaptcha

This commit is contained in:
elvira
2026-09-25 17:44:42 +03:00
parent 3347febb3c
commit e2d7bbb043
21 changed files with 445 additions and 39 deletions
+5 -1
View File
@@ -19,7 +19,7 @@ final class ProductCard
{
/**
* @param array<string, mixed> $product one item from ProductService's localized array shape
* @return array{name: ?string, price: ?string, image: ?string, href: string, variantId: ?int, hasCustomFields: bool}
* @return array{name: ?string, price: ?string, image: ?string, href: string, variantId: ?int, hasCustomFields: bool, soldOut: bool}
*/
public static function fromIndexed(array $product): array
{
@@ -38,6 +38,10 @@ public static function fromIndexed(array $product): array
// can't be quick-added from a card — the card links to the
// product page instead, even when every field is optional.
'hasCustomFields' => ! empty($product['custom_fields']),
// Index-time stock (see boboko-core's ProductIndexer `in_stock`),
// so only as fresh as the last reindex. A document missing the
// field is treated as in stock rather than hiding its cart button.
'soldOut' => ! ($product['in_stock'] ?? true),
];
}
}
+9
View File
@@ -2,6 +2,7 @@
namespace App\Http\Requests;
use App\Rules\HCaptcha;
use Illuminate\Foundation\Http\FormRequest;
class ContactRequest extends FormRequest
@@ -17,6 +18,14 @@ public function rules(): array
'name' => ['required', 'string', 'max:100'],
'email' => ['required', 'email', 'max:255'],
'message' => ['required', 'string', 'max:5000'],
'h-captcha-response' => ['bail', 'required', new HCaptcha],
];
}
public function messages(): array
{
return [
'h-captcha-response.required' => __('storefront.auth.captcha_failed'),
];
}
}
+1 -1
View File
@@ -40,7 +40,7 @@
'email' => env('CONTACT_EMAIL'),
],
// Bot check on guest forms (login), verified by App\Rules\HCaptcha. For
// Bot check on guest forms (login, contact), verified by App\Rules\HCaptcha. For
// local dev use hCaptcha's test keys, the real ones reject localhost:
// sitekey 10000000-ffff-ffff-ffff-000000000001,
// secret 0x0000000000000000000000000000000000000000.
@@ -26,6 +26,7 @@
:href="$product['href'] ?? '#'"
:variant-id="$product['variantId'] ?? null"
:has-custom-fields="$product['hasCustomFields'] ?? false"
:sold-out="$product['soldOut'] ?? false"
/>
@endforeach
</div>
@@ -5,6 +5,8 @@
'href' => '#',
'variantId' => null,
'hasCustomFields' => false,
// Shows the "sold" star and drops the quick add-to-cart/personalize button.
'soldOut' => false,
// Heart toggle at the top right of the image; needs productId.
'productId' => null,
'wishlist' => false,
@@ -36,7 +38,9 @@ class="w-full h-auto block"
<x-wishlist-button :product-id="$productId" class="absolute top-4 right-4 z-10" />
@endif
@if ($hasCustomFields)
@if ($soldOut)
<x-ui.sold-badge class="absolute top-2 right-2 {{ $wishlist && $productId ? 'mt-12' : '' }}" />
@elseif ($hasCustomFields)
{{-- Custom fields have to be filled in on the product page. --}}
<x-ui.button
:href="$href"
@@ -0,0 +1,8 @@
{{-- Black "sold" star for out-of-stock products. Position it from the caller
(e.g. class="absolute top-2 right-2"). --}}
<div {{ $attributes->class('z-10 flex h-20 w-22 items-center justify-center text-black') }}>
<svg viewBox="0 0 87 80" class="absolute inset-0 size-full" fill="currentColor" aria-hidden="true">
<path d="M56.942,0l2.607,18.642,19.143-3.363L69.468,31.842,87,40,69.468,48.158l9.224,16.563L59.549,61.358,56.942,80,43.5,66.4,30.058,80,27.451,61.358,8.308,64.721l9.224-16.563L0,40l17.532-8.158L8.308,15.279l19.143,3.363L30.058,0,43.5,13.6Z"/>
</svg>
<span class="relative text-base italic font-black text-white uppercase">{{ __('storefront.product.sold') }}</span>
</div>
+18 -3
View File
@@ -3,6 +3,11 @@
@section('title', __('storefront.nav.contact'))
@section('description', 'Επικοινώνησε μαζί μας για οποιαδήποτε απορία ή ιδέα έχεις και θα σου απαντήσουμε το συντομότερο δυνατό.')
{{-- Only on pages with a captcha, not in the global layout. --}}
@push('scripts')
<script src="https://js.hcaptcha.com/1/api.js?hl={{ app()->getLocale() }}" async defer></script>
@endpush
@section('content')
<section class="grid grid-cols-1 lg:grid-cols-2 lg:divide-x lg:divide-black lg:min-h-[calc(100vh-102px)]">
@@ -30,25 +35,35 @@
@csrf
<div class="grid grid-cols-1 gap-8 sm:grid-cols-2">
<x-ui.field label="Το όνομά σου" for="contact-name" :error="$errors->first('name')">
<x-ui.field label="Το όνομά σου *" for="contact-name" :error="$errors->first('name')">
<x-ui.input id="contact-name" name="name" autocomplete="name" maxlength="100" :value="old('name')" :required="true"
:aria-invalid="$errors->has('name') ? 'true' : null"
:aria-describedby="$errors->has('name') ? 'contact-name-error' : null" />
</x-ui.field>
<x-ui.field label="Το email σου" for="contact-email" :error="$errors->first('email')">
<x-ui.field label="Το email σου *" for="contact-email" :error="$errors->first('email')">
<x-ui.input id="contact-email" name="email" type="email" autocomplete="email" maxlength="255" :value="old('email')" :required="true"
:aria-invalid="$errors->has('email') ? 'true' : null"
:aria-describedby="$errors->has('email') ? 'contact-email-error' : null" />
</x-ui.field>
</div>
<x-ui.field label="Το μήνυμά σου" for="contact-message" :error="$errors->first('message')">
<x-ui.field label="Το μήνυμά σου *" for="contact-message" :error="$errors->first('message')">
<x-ui.textarea id="contact-message" name="message" rows="5" maxlength="5000" :required="true"
:aria-invalid="$errors->has('message') ? 'true' : null"
:aria-describedby="$errors->has('message') ? 'contact-message-error' : null">{{ old('message') }}</x-ui.textarea>
</x-ui.field>
{{-- hCaptcha checkbox, verified by App\Rules\HCaptcha (see auth/login). --}}
<x-ui.field for="contact-captcha" :error="$errors->first('h-captcha-response')">
<div
id="contact-captcha"
class="h-captcha min-h-[78px]"
data-sitekey="{{ config('services.hcaptcha.sitekey') }}"
@if ($errors->has('h-captcha-response')) aria-describedby="contact-captcha-error" @endif
></div>
</x-ui.field>
@if (session('contact_error'))
<p role="alert" class="text-sm text-red-600">{{ session('contact_error') }}</p>
@endif
+2 -1
View File
@@ -45,6 +45,7 @@
@media only screen and (max-width: 480px) {
.email-container { width: 100% !important; max-width: 100% !important; }
.email-header { padding: 24px 20px 12px !important; }
.email-gutter { padding: 0 !important; }
.email-card { padding: 28px 20px !important; }
.email-footer { padding: 0 20px 32px !important; }
}
@@ -69,7 +70,7 @@
</td>
</tr>
<tr>
<td style="padding:0 24px;">
<td class="email-gutter" style="padding:0 24px;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" bgcolor="#ffffff" style="background-color:#ffffff;border-radius:0;">
<tr>
<td class="email-card" style="padding:40px 32px;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:16px;line-height:1.5;color:#000000;text-align:left;">
@@ -0,0 +1,15 @@
{{--
@include('emails.partials.address', ['address' => $order->shippingAddress])
An order address, read-only, same fields as the account order page.
--}}
<p style="margin:0;font-size:15px;line-height:1.6;">
{{ trim($address->first_name.' '.$address->last_name) }}<br>
@if ($address->company_name){{ $address->company_name }}<br>@endif
@if ($address->tax_identifier)ΑΦΜ: {{ $address->tax_identifier }}<br>@endif
{{ $address->line_one }}<br>
@if ($address->line_two){{ $address->line_two }}<br>@endif
{{ trim($address->postcode.' '.$address->city) }}<br>
@if ($address->state){{ Lang::has("core::states.{$address->state}") ? __("core::states.{$address->state}") : $address->state }}<br>@endif
@if ($address->contact_phone){{ $address->contact_phone }}@endif
</p>
@@ -0,0 +1,13 @@
{{--
@include('emails.partials.button', ['url' => ..., 'label' => ...])
Bulletproof button: the padding sits on the link (whole area clickable)
with mso-padding-alt on the cell so Outlook keeps the same size.
--}}
<table role="presentation" cellpadding="0" cellspacing="0" style="margin:0;">
<tr>
<td bgcolor="#18c28a" style="background-color:#18c28a;border:1px solid #000000;border-radius:0;mso-padding-alt:14px 28px;">
<a href="{{ $url }}" target="_blank" style="display:inline-block;padding:14px 28px;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:16px;line-height:1.2;font-weight:700;color:#000000;text-decoration:none;">{{ $label }}</a>
</td>
</tr>
</table>
@@ -0,0 +1,2 @@
{{-- @include('emails.partials.section-heading', ['text' => ...]) --}}
<h2 style="margin:0 0 12px;padding:0 0 8px;border-bottom:1px solid #000000;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:18px;line-height:1.3;font-weight:800;color:#000000;">{{ $text }}</h2>
+10 -2
View File
@@ -43,7 +43,7 @@ class="absolute left-4 lg:left-8 top-1/2 z-10 -translate-y-1/2 hover:-translate-
class="{{ $loop->first ? '' : 'hidden' }} group h-full flex flex-col justify-center pb-8 relative"
>
<div class="relative flex items-center justify-center">
<a href="{{ $product['href'] }}" class="block border border-black bg-white flex items-center justify-center overflow-hidden max-w-xs xl:max-w-sm">
<a href="{{ $product['href'] }}" class="relative block border border-black bg-white flex items-center justify-center overflow-hidden max-w-xs xl:max-w-sm">
@if($product['image'])
<img
src="{{ $product['image'] }}"
@@ -58,9 +58,16 @@ class="w-full h-full object-cover"
@else
<span class="text-neutral-500 text-sm">{{ __('storefront.product.no_image') }}</span>
@endif
{{-- Inside the link so it sits on the image's corner, not the full-width slide's. --}}
@if ($product['soldOut'] ?? false)
<x-ui.sold-badge class="absolute top-2 right-2" />
@endif
</a>
@if ($product['hasCustomFields'] ?? false)
@if ($product['soldOut'] ?? false)
{{-- Sold: no quick add-to-cart/personalize, same as x-ui.product-card. --}}
@elseif ($product['hasCustomFields'] ?? false)
<x-ui.button
:href="$product['href']"
size="md"
@@ -173,6 +180,7 @@ class="max-w-xs"
:href="$product['href']"
:variant-id="$product['variantId'] ?? null"
:has-custom-fields="$product['hasCustomFields'] ?? false"
:sold-out="$product['soldOut'] ?? false"
/>
@endforeach
</div>
@@ -1,3 +1,26 @@
<p>Hi,</p>
{{-- Core passes only the order reference here (and the amount for payment emails). Greek on purpose: sent from the queue in the app default locale. --}}
@extends('emails.layout')
<p>Payment of <strong>{{ $amount }}</strong> for your order <strong>{{ $reference }}</strong> has been captured.</p>
@section('title', "Λάβαμε την πληρωμή σου")
@section('preheader', "Λάβαμε την πληρωμή {$amount} για την παραγγελία #{$reference}.")
@section('content')
<h1 style="margin:0 0 16px;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:22px;line-height:1.3;font-weight:700;color:#000000;">
Λάβαμε την πληρωμή σου
</h1>
<p style="margin:0 0 32px;">Λάβαμε την πληρωμή σου <strong>{{ $amount }}</strong> για την παραγγελία <strong>#{{ $reference }}</strong>.</p>
@include('emails.partials.button', [
'url' => route('account.orders', ['locale' => 'el']),
'label' => 'Οι παραγγελίες μου',
])
@endsection
@section('footer')
<p style="margin:0;">
Έχεις ερώτηση για την παραγγελία σου;
<a href="{{ route('contact', ['locale' => 'el']) }}" style="color:#7a7a7a;text-decoration:underline;">Επικοινώνησε μαζί μας</a>.
</p>
@endsection
@@ -1,3 +1,26 @@
<p>Hi,</p>
{{-- Core passes only the order reference here (and the amount for payment emails). Greek on purpose: sent from the queue in the app default locale. --}}
@extends('emails.layout')
<p>Your order <strong>{{ $reference }}</strong> is complete. Thanks for shopping with us!</p>
@section('title', "Η παραγγελία σου ολοκληρώθηκε")
@section('preheader', "Η παραγγελία σου #{$reference} ολοκληρώθηκε.")
@section('content')
<h1 style="margin:0 0 16px;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:22px;line-height:1.3;font-weight:700;color:#000000;">
Η παραγγελία σου ολοκληρώθηκε
</h1>
<p style="margin:0 0 32px;">Η παραγγελία σου <strong>#{{ $reference }}</strong> ολοκληρώθηκε. Ευχαριστούμε που ψώνισες από εμάς!</p>
@include('emails.partials.button', [
'url' => route('account.orders', ['locale' => 'el']),
'label' => 'Οι παραγγελίες μου',
])
@endsection
@section('footer')
<p style="margin:0;">
Έχεις ερώτηση για την παραγγελία σου;
<a href="{{ route('contact', ['locale' => 'el']) }}" style="color:#7a7a7a;text-decoration:underline;">Επικοινώνησε μαζί μας</a>.
</p>
@endsection
@@ -1,3 +1,26 @@
<p>Hi,</p>
{{-- Core passes only the order reference here (and the amount for payment emails). Greek on purpose: sent from the queue in the app default locale. --}}
@extends('emails.layout')
<p>Good news — your order <strong>{{ $reference }}</strong> has been delivered.</p>
@section('title', "Η παραγγελία σου παραδόθηκε")
@section('preheader', "Η παραγγελία σου #{$reference} παραδόθηκε.")
@section('content')
<h1 style="margin:0 0 16px;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:22px;line-height:1.3;font-weight:700;color:#000000;">
Η παραγγελία σου παραδόθηκε
</h1>
<p style="margin:0 0 32px;">Η παραγγελία σου <strong>#{{ $reference }}</strong> παραδόθηκε. Ελπίζουμε να σου αρέσει!</p>
@include('emails.partials.button', [
'url' => route('account.orders', ['locale' => 'el']),
'label' => 'Οι παραγγελίες μου',
])
@endsection
@section('footer')
<p style="margin:0;">
Έχεις ερώτηση για την παραγγελία σου;
<a href="{{ route('contact', ['locale' => 'el']) }}" style="color:#7a7a7a;text-decoration:underline;">Επικοινώνησε μαζί μας</a>.
</p>
@endsection
@@ -1,3 +1,26 @@
<p>Hi,</p>
{{-- Core passes only the order reference here (and the amount for payment emails). Greek on purpose: sent from the queue in the app default locale. --}}
@extends('emails.layout')
<p>Your order <strong>{{ $reference }}</strong> is on its way.</p>
@section('title', "Η παραγγελία σου στάλθηκε")
@section('preheader', "Η παραγγελία σου #{$reference} είναι καθ’ οδόν.")
@section('content')
<h1 style="margin:0 0 16px;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:22px;line-height:1.3;font-weight:700;color:#000000;">
Η παραγγελία σου στάλθηκε
</h1>
<p style="margin:0 0 32px;">Η παραγγελία σου <strong>#{{ $reference }}</strong> είναι καθ’ οδόν. Θα τη λάβεις σύντομα.</p>
@include('emails.partials.button', [
'url' => route('account.orders', ['locale' => 'el']),
'label' => 'Οι παραγγελίες μου',
])
@endsection
@section('footer')
<p style="margin:0;">
Έχεις ερώτηση για την παραγγελία σου;
<a href="{{ route('contact', ['locale' => 'el']) }}" style="color:#7a7a7a;text-decoration:underline;">Επικοινώνησε μαζί μας</a>.
</p>
@endsection
@@ -1,3 +1,26 @@
<p>Hi,</p>
{{-- Core passes only the order reference here (and the amount for payment emails). Greek on purpose: sent from the queue in the app default locale. --}}
@extends('emails.layout')
<p>Your order <strong>{{ $reference }}</strong> is ready for pickup in store.</p>
@section('title', "Η παραγγελία σου είναι έτοιμη για παραλαβή")
@section('preheader', "Η παραγγελία σου #{$reference} σε περιμένει στο κατάστημα.")
@section('content')
<h1 style="margin:0 0 16px;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:22px;line-height:1.3;font-weight:700;color:#000000;">
Η παραγγελία σου είναι έτοιμη για παραλαβή
</h1>
<p style="margin:0 0 32px;">Η παραγγελία σου <strong>#{{ $reference }}</strong> είναι έτοιμη και σε περιμένει στο κατάστημα.</p>
@include('emails.partials.button', [
'url' => route('account.orders', ['locale' => 'el']),
'label' => 'Οι παραγγελίες μου',
])
@endsection
@section('footer')
<p style="margin:0;">
Έχεις ερώτηση για την παραγγελία σου;
<a href="{{ route('contact', ['locale' => 'el']) }}" style="color:#7a7a7a;text-decoration:underline;">Επικοινώνησε μαζί μας</a>.
</p>
@endsection
@@ -1,19 +1,175 @@
<p>Hi,</p>
{{--
Order confirmation (boboko-core's OrderPlacedNotification).
<p>Thanks for your order! Your order <strong>{{ $reference }}</strong> is confirmed.</p>
Core only passes $reference, $total and $lines, so the order itself is
reached through a line (every OrderLine belongs to it). Interim until core
passes the Order to this view.
<ul>
@foreach ($lines as $line)
<li>
{{ $line->quantity }} &times; {{ $line->description }} — {{ $line->total?->formatted }}
{{-- Text answers only — photo answers are deliberately left out of emails. --}}
@foreach ($line->meta['custom_fields'] ?? [] as $field)
@if ($field['type'] !== 'file')
<br>{{ $field['label'] }}: {!! nl2br(e($field['value'])) !!}
@endif
@endforeach
</li>
@endforeach
</ul>
Sent from the queue with no locale of its own, so it always renders in the
app default (Greek). Copy is Greek on purpose, like the other core emails.
--}}
@extends('emails.layout')
<p>Total: <strong>{{ $total }}</strong></p>
@php
$order = $lines->first()->order;
$productLines = $lines->where('type', '!=', 'shipping');
$shippingLine = $lines->firstWhere('type', 'shipping');
// By type, like the account order page: the method may since have been
// disabled, but the order still shows what was used.
$paymentMethodName = \Modules\Core\Payment\Models\PaymentMethod::where('type', $order->meta['payment_method'] ?? null)
->first()
?->translate('name');
// Lunar doesn't copy the coupon onto the order; the cart row keeps it.
$couponCode = $order->cart?->coupon_code;
$email = $order->billingAddress?->contact_email ?? $order->shippingAddress?->contact_email;
$comments = $order->shippingAddress?->delivery_instructions;
$muted = 'color:#5c5c5c;font-size:14px;';
$font = "font-family:'Manrope',Arial,Helvetica,sans-serif;";
@endphp
@section('title', "Η παραγγελία σου #{$reference} επιβεβαιώθηκε")
@section('preheader', "Ευχαριστούμε! Λάβαμε την παραγγελία σου #{$reference}, σύνολο {$total}.")
@section('content')
<h1 style="margin:0 0 16px;{{ $font }}font-size:22px;line-height:1.3;font-weight:700;color:#000000;">
Ευχαριστούμε για την παραγγελία σου!
</h1>
<p style="margin:0 0 24px;">
Λάβαμε την παραγγελία σου <strong>#{{ $reference }}</strong>
στις {{ $order->placed_at?->format('d/m/Y') }}.
Θα σε ενημερώσουμε με email μόλις σταλεί.
</p>
<div style="margin:0 0 40px;">
@include('emails.partials.button', [
'url' => route('account.orders.show', ['locale' => 'el', 'orderId' => $order->id]),
'label' => 'Δες την παραγγελία σου',
])
</div>
{{-- Items --}}
@include('emails.partials.section-heading', ['text' => 'Προϊόντα'])
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 8px;">
@foreach ($productLines as $line)
<tr>
<td width="64" valign="top" style="width:64px;padding:12px 12px 12px 0;{{ $loop->last ? '' : 'border-bottom:1px solid #e5e5e5;' }}">
@if ($thumb = $line->purchasable?->getThumbnailImage())
<img src="{{ $thumb }}" width="64" height="64" alt="{{ $line->description }}" style="display:block;width:64px;height:64px;object-fit:cover;background-color:#e5e5e5;border:0;{{ $font }}font-size:11px;color:#5c5c5c;">
@else
<div style="width:64px;height:64px;background-color:#e5e5e5;"></div>
@endif
</td>
<td valign="top" style="padding:12px 12px 12px 0;{{ $font }}font-size:15px;line-height:1.4;color:#000000;{{ $loop->last ? '' : 'border-bottom:1px solid #e5e5e5;' }}">
<strong>{{ $line->description }}</strong>
@if ($line->option)
<br><span style="{{ $muted }}">{{ $line->option }}</span>
@endif
{{-- Text answers only: photo answers are deliberately left out of emails. --}}
@foreach ($line->meta['custom_fields'] ?? [] as $field)
@if ($field['type'] !== 'file' && filled($field['value'] ?? null))
<br><span style="{{ $muted }}">{{ $field['label'] }}: {!! nl2br(e($field['value'])) !!}</span>
@endif
@endforeach
<br><span style="{{ $muted }}">
Ποσότητα: {{ $line->quantity }}@if ($line->quantity > 1) &times; {{ $line->unit_price->formatted() }}@endif
</span>
</td>
<td valign="top" align="right" style="padding:12px 0;white-space:nowrap;{{ $font }}font-size:15px;line-height:1.4;font-weight:700;color:#000000;{{ $loop->last ? '' : 'border-bottom:1px solid #e5e5e5;' }}">
{{ $line->sub_total?->formatted() }}
</td>
</tr>
@endforeach
</table>
{{-- Totals --}}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 40px;border-top:1px solid #000000;">
<tr>
<td style="padding:12px 0 4px;{{ $font }}font-size:15px;color:#000000;">Υποσύνολο</td>
<td align="right" style="padding:12px 0 4px;white-space:nowrap;{{ $font }}font-size:15px;color:#000000;">{{ $order->sub_total?->formatted() }}</td>
</tr>
@if ($order->discount_total?->value > 0)
<tr>
<td style="padding:4px 0;{{ $font }}font-size:15px;color:#000000;">
Έκπτωση@if ($couponCode) <span style="{{ $muted }}">({{ $couponCode }})</span>@endif
</td>
<td align="right" style="padding:4px 0;white-space:nowrap;{{ $font }}font-size:15px;color:#000000;">&minus;{{ $order->discount_total->formatted() }}</td>
</tr>
@endif
<tr>
<td style="padding:4px 0;{{ $font }}font-size:15px;color:#000000;">
Μεταφορικά@if ($shippingLine) <span style="{{ $muted }}">({{ $shippingLine->description }})</span>@endif
</td>
<td align="right" style="padding:4px 0;white-space:nowrap;{{ $font }}font-size:15px;color:#000000;">
{{ $order->shipping_total?->value > 0 ? $order->shipping_total->formatted() : 'Δωρεάν' }}
</td>
</tr>
<tr>
<td style="padding:12px 0 0;border-top:1px solid #000000;{{ $font }}font-size:20px;line-height:1.3;font-weight:800;color:#000000;">Σύνολο</td>
<td align="right" style="padding:12px 0 0;border-top:1px solid #000000;white-space:nowrap;{{ $font }}font-size:20px;line-height:1.3;font-weight:800;color:#000000;">{{ $total }}</td>
</tr>
{{-- Prices include VAT (lunar.pricing.stored_inclusive_of_tax), so this is informational. --}}
@if ($order->tax_total?->value > 0)
<tr>
<td colspan="2" align="right" style="padding:4px 0 0;{{ $font }}{{ $muted }}">Περιλαμβάνεται Φ.Π.Α. {{ $order->tax_total->formatted() }}</td>
</tr>
@endif
</table>
{{-- Payment + shipping --}}
@include('emails.partials.section-heading', ['text' => 'Πληρωμή και αποστολή'])
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 40px;">
@foreach (array_filter([
'Τρόπος πληρωμής' => $paymentMethodName,
'Τρόπος αποστολής' => $shippingLine?->description,
'Παραστατικό' => ($order->meta['wants_invoice'] ?? false) ? 'Τιμολόγιο' : 'Απόδειξη',
'Email' => $email,
]) as $label => $value)
<tr>
<td valign="top" style="padding:4px 12px 4px 0;{{ $font }}{{ $muted }}line-height:1.5;">{{ $label }}</td>
<td valign="top" align="right" style="padding:4px 0;{{ $font }}font-size:15px;line-height:1.5;color:#000000;word-break:break-word;">{{ $value }}</td>
</tr>
@endforeach
</table>
{{-- Addresses: stacked, never side by side, so they read well on a phone. --}}
@if ($order->shippingAddress)
@include('emails.partials.section-heading', ['text' => 'Διεύθυνση αποστολής'])
<div style="margin:0 0 {{ $comments ? '16px' : '40px' }};">
@include('emails.partials.address', ['address' => $order->shippingAddress])
</div>
@if ($comments)
<p style="margin:0 0 40px;font-size:15px;line-height:1.6;">
<span style="{{ $muted }}">Σχόλια</span><br>
{!! nl2br(e($comments)) !!}
</p>
@endif
@endif
@if ($order->billingAddress)
@include('emails.partials.section-heading', ['text' => 'Στοιχεία χρέωσης'])
@include('emails.partials.address', ['address' => $order->billingAddress])
@endif
@endsection
@section('footer')
<p style="margin:0;">
Έχεις ερώτηση για την παραγγελία σου;
<a href="{{ route('contact', ['locale' => 'el']) }}" style="color:#7a7a7a;text-decoration:underline;">Επικοινώνησε μαζί μας</a>.
</p>
@endsection
@@ -1,3 +1,26 @@
<p>Hi,</p>
{{-- Core passes only the order reference here (and the amount for payment emails). Greek on purpose: sent from the queue in the app default locale. --}}
@extends('emails.layout')
<p>A refund of <strong>{{ $amount }}</strong> has been issued for your order <strong>{{ $reference }}</strong>.</p>
@section('title', "Έγινε επιστροφή χρημάτων")
@section('preheader', "Επιστρέψαμε {$amount} για την παραγγελία #{$reference}.")
@section('content')
<h1 style="margin:0 0 16px;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:22px;line-height:1.3;font-weight:700;color:#000000;">
Έγινε επιστροφή χρημάτων
</h1>
<p style="margin:0 0 32px;">Σου επιστρέψαμε <strong>{{ $amount }}</strong> για την παραγγελία <strong>#{{ $reference }}</strong>.</p>
@include('emails.partials.button', [
'url' => route('account.orders', ['locale' => 'el']),
'label' => 'Οι παραγγελίες μου',
])
@endsection
@section('footer')
<p style="margin:0;">
Έχεις ερώτηση για την παραγγελία σου;
<a href="{{ route('contact', ['locale' => 'el']) }}" style="color:#7a7a7a;text-decoration:underline;">Επικοινώνησε μαζί μας</a>.
</p>
@endsection
@@ -1,3 +1,34 @@
<p>Hi,</p>
{{-- Core passes only the order reference here (and the amount for payment emails). Greek on purpose: sent from the queue in the app default locale. --}}
@extends('emails.layout')
<p>Your order <strong>{{ $reference }}</strong> is now: <strong>{{ $statusLabel }}</strong></p>
@php
// Core passes Lunar's English label; map it back to the status key so the
// shared <x-order-status> can show the translated one. Interim, until core
// passes the order (or its status) to this view.
$status = collect(config('lunar.orders.statuses'))
->search(fn (array $config) => $config['label'] === $statusLabel) ?: $statusLabel;
@endphp
@section('title', "Ενημέρωση παραγγελίας")
@section('preheader', "Η παραγγελία σου #{$reference} ενημερώθηκε.")
@section('content')
<h1 style="margin:0 0 16px;font-family:'Manrope',Arial,Helvetica,sans-serif;font-size:22px;line-height:1.3;font-weight:700;color:#000000;">
Η παραγγελία σου ενημερώθηκε
</h1>
<p style="margin:0 0 32px;">Η κατάσταση της παραγγελίας σου <strong>#{{ $reference }}</strong> είναι τώρα: <strong><x-order-status :status="$status" /></strong></p>
@include('emails.partials.button', [
'url' => route('account.orders', ['locale' => 'el']),
'label' => 'Οι παραγγελίες μου',
])
@endsection
@section('footer')
<p style="margin:0;">
Έχεις ερώτηση για την παραγγελία σου;
<a href="{{ route('contact', ['locale' => 'el']) }}" style="color:#7a7a7a;text-decoration:underline;">Επικοινώνησε μαζί μας</a>.
</p>
@endsection
+1
View File
@@ -19,6 +19,7 @@
:href="$product['href']"
:variant-id="$product['variantId']"
:has-custom-fields="$product['hasCustomFields']"
:sold-out="$product['soldOut']"
:product-id="$product['id']"
:wishlist="true"
/>