@extends('layouts.public') @section('content') @php use App\Models\Order; /** @var array $order @var array|null $restaurant @var array $items @var array $history */ // The two late steps read differently for pickup / dine-in (no rider, no delivery). $mode = $order['service_mode'] ?? 'delivery'; // Friendly tender label: an in-person POS card sale is stored as 'offline', so // show it as "Card" (matching the ledger + vendor views), and 'cod' as "Cash". $payLabel = ['cod' => t_raw('status.payment_method.cash'), 'offline' => t_raw('status.payment_method.card'), 'stripe' => t_raw('status.payment_method.card'), 'paypal' => t_raw('status.payment_method.paypal')]; // A saved-card order stores its brand+last4 in payment_ref (offline method) — show it. $payShown = ((string) $order['payment_method'] === 'offline' && !empty($order['payment_ref'])) ? (string) $order['payment_ref'] : ($payLabel[(string) $order['payment_method']] ?? ucfirst((string) $order['payment_method'])); $lateLabels = match ($mode) { 'pickup' => ['out_for_delivery' => t_raw('order.status_pickup_out'), 'delivered' => t_raw('order.status_pickup_delivered')], 'dine_in' => ['out_for_delivery' => t_raw('order.status_dinein_out'), 'delivered' => t_raw('order.status_dinein_delivered')], default => ['out_for_delivery' => t_raw('order.status_on_the_way'), 'delivered' => t_raw('status.order.delivered')], }; $steps = ['pending' => t_raw('order.status_placed'), 'confirmed' => t_raw('status.order.confirmed'), 'preparing' => t_raw('status.order.preparing'), 'out_for_delivery' => $lateLabels['out_for_delivery'], 'delivered' => $lateLabels['delivered']]; $order_status = $order['status']; $cancelled = $order_status === 'cancelled'; $statusKeys = array_keys($steps); $currentIdx = array_search($order_status, $statusKeys, true); if ($currentIdx === false) { $currentIdx = -1; } // Driver progress along the route (0..1) based on status $progress = match ($order_status) { 'pending' => 0.05, 'confirmed' => 0.2, 'preparing' => 0.35, 'out_for_delivery' => 0.7, 'delivered' => 1.0, default => 0.0, }; /* --------------------------------------------------------------------------- * Call / SMS quick actions. * * The restaurant's own number is public - it is printed on the receipt below and * on its storefront page - so it shows for the whole life of the order. * * The rider's number is NOT public, and the rule that makes this feature safe is * that it exists on the page only inside the delivery window. Before pickup there * is nothing to ask a rider (the order is still in the kitchen, and on a marketplace * the job can be reassigned), and once the food is delivered the reason to hold a * stranger's phone number is over. So it is rendered for 'out_for_delivery' and for * nothing else - not 'preparing' even after a rider is assigned, and not 'delivered' * however soon the page is reloaded. Pickup and dine-in never have a rider at all. * * A number that is missing produces NO button rather than a dead one: `tel:` with an * empty target fails silently on a phone, which is worse than not offering the call. * ------------------------------------------------------------------------- */ $dialable = static fn ($raw): string => preg_replace('/[^0-9+]/', '', trim((string) $raw)); $restPhone = $dialable($restaurant['phone'] ?? ''); $driverPhone = ''; if ($mode === 'delivery' && $order_status === 'out_for_delivery' && (int) ($order['driver_user_id'] ?? 0) > 0) { $driverRow = (new \App\Models\Driver())->forUser((int) $order['driver_user_id']); $driverPhone = $dialable($driverRow['phone'] ?? ''); } @endphp

{{ t_raw('order.track_title', [':number' => $order['order_number']]) }}

@php /* The page's messages belong UNDER its title band, inside the page the reader is looking at — not floating above it. getFlashes() consumes, so the layout's own call further out finds nothing and does not draw it twice. */ @endphp
@partial('flash')
@if ($order['service_mode'] === 'delivery' && !$cancelled) @if ($mapsLive) @else @endif @if ($mapsLive) @endif @endif

{{ t_raw('order.track_status_heading') }}

@if ($cancelled)
{{ t_raw('order.track_cancelled_notice') }}
@else
    @foreach ($steps as $key => $label) @php $idx = array_search($key, $statusKeys, true); /* Terminal step never spins: "Delivered · In progress" told a customer their finished order was still happening (same rule as the status API). */ $isLast = $idx === count($statusKeys) - 1; $state = $idx < $currentIdx ? 'done' : ($idx === $currentIdx ? ($isLast ? 'done' : 'current') : 'upcoming'); $stateMeta = [ 'done' => ['icon' => 'fa-check', 'text' => t('common.completed')], 'current' => ['icon' => 'fa-circle-notch', 'text' => t('order.track_step_in_progress')], 'upcoming' => ['icon' => 'fa-circle', 'text' => t('order.track_step_upcoming')], ][$state]; @endphp
  • {{ $label }} {{ $stateMeta['text'] }}
    @php $h = null; foreach ($history as $hh) { if ($hh['status'] === $key) { $h = $hh; } } @endphp @if ($h) @if (!empty($h['note']))
    {{ $h['note'] }}
    @endif
    {!! fmt_datetime($h['created_at']) !!}
    @endif
  • @endforeach
@endif

{{ t_raw('order.track_details_heading') }}

@if ($restaurant)

{{ $restaurant['name'] }}

@endif @php /* The payment's own state, said next to the method — the receipt block below always carried it, but up here the ORDER pill sat right after the word "PayPal", where "Pending" read as the payment being pending. The product's owner misread it exactly that way. Same label the receipt uses, so the two lines can never disagree. */ @endphp

{{ status_label('service_mode', (string) $order['service_mode']) }} · {{ $payShown }} · {{ status_label('payment', (string) $order['payment_status']) }} · {{ Order::statusLabel($order['status'], $mode) }}

@php $isGateway = in_array((string) $order['payment_method'], ['stripe', 'paypal'], true); $awaitingPayment = $isGateway && (string) $order['payment_status'] === 'unpaid' && !$cancelled; @endphp @if (!empty($order['scheduled_at']))

{!! t_raw('order.track_scheduled_for', [':time' => fmt_datetime($order['scheduled_at'], true)]) !!}

@endif @if ($awaitingPayment)
{{ t_raw('order.track_awaiting_payment') }}
@endif @if (!empty($order['delivery_address']))

{{ $order['delivery_address'] }}

@endif @if ($restPhone !== '' || $driverPhone !== '')
@php /* Label left, actions right - the same qm-order-line row this card already uses for every other pair on the panel. Each link carries its own aria-label because "Call / Call" alone does not say who. */ @endphp @if ($restPhone !== '') @endif @if ($driverPhone !== '') @endif @endif
@foreach ($items as $it)
{{ (int) $it['quantity'] }}× {{ $it['item_name'] }}{{ money($it['line_total']) }}
@endforeach
{{ t_raw('checkout.subtotal') }}{{ money($order['subtotal']) }}
@if ((float) $order['delivery_fee'] > 0)
{{ t_raw('checkout.delivery') }}{{ money($order['delivery_fee']) }}
@endif @if ((float) ($order['service_charge'] ?? 0) > 0)
{{ t_raw('checkout.service_charge') }}{{ money($order['service_charge']) }}
@endif @if ((float) ($order['tip_amount'] ?? 0) > 0)
{{ t_raw('checkout.tip') }}{{ money($order['tip_amount']) }}
@endif @if ((float) $order['discount'] > 0)
{{ t_raw('common.discount') }}−{{ money($order['discount']) }}
@endif @php $loyDiscount = (float) ($order['loyalty_discount'] ?? 0); $loyRewardLabel = (string) ($order['loyalty_reward_label'] ?? ''); $loyEarned = (int) ($order['loyalty_points_earned'] ?? 0); $loyLabel = (string) ($restaurant['loyalty_points_label'] ?? 'points'); @endphp @if ($loyDiscount > 0 || $loyRewardLabel !== '')
{{ t_raw('order.track_reward_label') }}{!! $loyRewardLabel !== '' ? ' · ' . e($loyRewardLabel) : '' !!}−{{ money($loyDiscount) }}
@endif @if ((float) $order['tax'] > 0)
{!! \App\Services\Tax::isInclusive($restaurant) ? t('checkout.tax_label_incl', [':label' => \App\Services\Tax::label($restaurant)]) : e(\App\Services\Tax::label($restaurant)) !!}{{ money($order['tax']) }}
@endif
{{ t_raw('checkout.total') }}{{ money($order['total']) }}
@if ($loyEarned > 0) {{ t_raw('order.track_points_earned', [':points' => loyalty_points($loyEarned, $loyLabel)]) }} @endif @if ($awaitingPayment) {{ t_raw('order.track_complete_payment_btn') }} @endif @if ((string) $order['status'] === 'pending')
@csrf
@endif @php $canReorder = \App\Support\Auth::is('customer') && (int) ($order['customer_user_id'] ?? 0) === (int) \App\Support\Auth::id(); @endphp @if ($canReorder)
@csrf
@else {{ t_raw('order.track_order_again_btn') }} @endif
@php // Print-only receipt (hidden on screen via css/print.css). Rendered from the order's // own stored figures so the printed copy always matches what the customer was charged. $rcptAddr = trim(implode(', ', array_filter([(string) ($restaurant['address'] ?? ''), (string) ($restaurant['city'] ?? '')]))); $siteName = (string) setting('site_name', 'QuickMunch'); @endphp @if (!$cancelled) @php /* Live status polling config (consumed by the second IIFE in order-track.js). */ @endphp @endif
@endsection