@extends('layouts.dashboard')
@section('content')
@php
/**
* Operation Analytics → Reservations: the front-of-house report. No-shows as a
* rate rather than a count, how far ahead guests commit, how long a table stays
* occupied, where the bookings come from and when the covers land.
*
* @var array $kpis @var array $paceChart @var array $paceMeta @var array $statusChart @var array $statusMeta
* @var array $sourceChart @var array $sourceMeta @var array $partyChart @var array $partyMeta
* @var array $turnChart @var array $turnMeta @var array $heatChart @var array $heatMeta
* @var array $rows @var array $pager @var int $rangeDays @var array $rangeLinks @var bool $showMoney
*/
// The booking pills reuse the same status colours the Reservations desk uses, so a
// row reads identically whichever page the manager is standing on.
$statusTone = [
'pending' => 'amber', 'confirmed' => 'blue', 'seated' => 'green', 'completed' => 'green',
'cancelled' => 'grey', 'no_show' => 'red', 'waitlisted' => 'amber',
];
$cols = 9 + ($showMoney ? 1 : 0);
@endphp
@php /* .qm-d2: the dashboard density standard (app.css §35) */
@endphp
{{ t_raw('dashboard.sec.snapshot') }}
{{ t_raw('vendor.analytics.subtitle_reservations') }}
@foreach ($rangeLinks['options'] as $val => $text)
@php $on = ((string) $val === $rangeLinks['selected']); @endphp
{{ $text }}
@endforeach
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'blue', 'icon' => 'fa-calendar-check', 'label' => t_raw('nav.reservations'),
'value' => number_format($kpis['bookings']['value']), 'spark' => $kpis['bookings']['spark'], 'meta' => $kpis['bookings']['meta']])
@php /* Reported as a RATE: ten no-shows out of twelve bookings and ten out of
four hundred are the same count and completely different problems. */
@endphp
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'rose', 'icon' => 'fa-user-slash', 'label' => t_raw('vendor.analytics.kpi_no_show_rate'),
'value' => $kpis['noShow']['value'] !== null ? number_format($kpis['noShow']['value'], 1) . '%' : '—',
'spark' => $kpis['noShow']['spark'], 'meta' => $kpis['noShow']['meta']])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'green', 'icon' => 'fa-chair', 'label' => t_raw('vendor.analytics.kpi_seated_rate'),
'value' => $kpis['seated']['value'] !== null ? number_format($kpis['seated']['value'], 1) . '%' : '—',
'spark' => $kpis['seated']['spark'], 'meta' => $kpis['seated']['meta']])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'violet', 'icon' => 'fa-hourglass-start', 'label' => t_raw('vendor.analytics.kpi_lead_time'),
'value' => $kpis['lead']['value'] ?? '—', 'spark' => $kpis['lead']['spark'], 'meta' => $kpis['lead']['meta']])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'teal', 'icon' => 'fa-stopwatch', 'label' => t_raw('vendor.analytics.kpi_turn_time'),
'value' => $kpis['turn']['value'] ?? '—',
'spark' => $kpis['turn']['spark'], 'meta' => $kpis['turn']['meta']])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'amber', 'icon' => 'fa-user-friends', 'label' => t_raw('vendor.analytics.kpi_covers'),
'value' => number_format($kpis['covers']['value']), 'spark' => $kpis['covers']['spark'], 'meta' => $kpis['covers']['meta']])
{{ t_raw('dashboard.sec.trends') }}
@partial('chart-card', ['heading' => t_raw('vendor.analytics.chart_booking_pace'), 'sub' => t_raw('vendor.analytics.chart_booking_pace_sub'), 'cfg' => $paceChart, 'meta' => $paceMeta, 'accent' => 'blue', 'opts' => ['id' => 'resvPaceChart', 'url' => route('vendor.chart-series', ['chart' => 'resvPace']), 'selected' => $rangeLinks['selected'], 'options' => $rangeLinks['options'], 'control' => 'compact']])
@partial('chart-card', ['heading' => t_raw('vendor.analytics.chart_booking_status'), 'sub' => t_raw('vendor.analytics.chart_booking_status_sub'), 'cfg' => $statusChart, 'meta' => $statusMeta, 'icon' => 'fa-chart-pie', 'accent' => 'rose'])
@partial('chart-card', ['heading' => t_raw('vendor.analytics.chart_booking_channels'), 'sub' => t_raw('vendor.analytics.chart_booking_channels_sub'), 'cfg' => $sourceChart, 'meta' => $sourceMeta, 'icon' => 'fa-chart-pie', 'accent' => 'violet'])
@partial('chart-card', ['heading' => t_raw('vendor.analytics.chart_party_sizes'), 'sub' => t_raw('vendor.analytics.chart_party_sizes_sub'), 'cfg' => $partyChart, 'meta' => $partyMeta, 'icon' => 'fa-chart-bar', 'accent' => 'amber', 'opts' => ['id' => 'resvPartyChart', 'url' => route('vendor.chart-series', ['chart' => 'resvParty']), 'selected' => $rangeLinks['selected'], 'options' => $rangeLinks['options'], 'control' => 'compact']])
@partial('chart-card', ['heading' => t_raw('vendor.analytics.chart_turn_time'), 'sub' => t_raw('vendor.analytics.chart_turn_time_sub'), 'cfg' => $turnChart, 'meta' => $turnMeta, 'icon' => 'fa-stopwatch', 'accent' => 'teal', 'opts' => ['id' => 'resvTurnChart', 'url' => route('vendor.chart-series', ['chart' => 'resvTurn']), 'selected' => $rangeLinks['selected'], 'options' => $rangeLinks['options'], 'control' => 'compact']])
@partial('chart-card', ['heading' => t_raw('vendor.analytics.chart_seating_clock'), 'sub' => t_raw('vendor.analytics.chart_seating_clock_sub'), 'cfg' => $heatChart, 'meta' => $heatMeta, 'icon' => 'fa-th', 'accent' => 'teal'])
{{ t_raw('dashboard.sec.breakdown') }}
{{ t_raw('vendor.analytics.heading_bookings') }}
{{ t_raw('vendor.analytics.hint_bookings') }}
{{ t_raw('vendor.analytics.caption_bookings') }}
| {{ t_raw('vendor.analytics.col_guest') }} |
{{ t_raw('vendor.analytics.kpi_covers') }} |
{{ t_raw('vendor.analytics.col_booked_on') }} |
{{ t_raw('vendor.analytics.col_sitting') }} |
{{ t_raw('vendor.analytics.col_lead_time') }} |
{{ t_raw('vendor.analytics.col_seated') }} |
{{ t_raw('vendor.analytics.col_turn_time') }} |
{{ t_raw('common.status') }} |
{{ t_raw('vendor.analytics.col_channel') }} |
@if ($showMoney){{ t_raw('vendor.analytics.col_deposit') }} | @endif
@if ($rows)
@foreach ($rows as $r)
@php $st = (string) $r['status']; @endphp
{!! brandmark_html($r['avatar'] ?? null, (string) $r['guest_name'], 'md', 'qm-brandmark--round') !!}
{{ (string) $r['guest_name'] }}
{!! or_dash($r['guest_phone']) !!}
|
{{ number_format((int) $r['party_size']) }} |
{!! fmt_day($r['created_at']) !!} |
{!! fmt_datetime($r['reserved_at']) !!} |
@php /* Lead and turn time exist only once the guest actually sat down
and left; an unfinished booking reports nothing, never a zero. */
@endphp
{!! or_dash($r['lead_label']) !!} |
{!! $r['seated_at'] !== null ? fmt_clock($r['seated_at']) : dash() !!} |
{!! or_dash($r['turn_label']) !!} |
{{ status_label('reservation', $st) }} |
{{ status_label('reservation_source', (string) $r['source']) }} |
@if ($showMoney)
{!! $r['deposit_amount'] !== null ? money($r['deposit_amount']) : dash() !!} |
@endif
@endforeach
@else
@partial('table-empty', ['colspan' => $cols, 'icon' => 'fa-calendar-check', 'msg' => t_raw('vendor.analytics.empty_no_bookings')])
@endif
@partial('table-toolbar', ['pager' => $pager, 'base' => '/vendor/analytics/reservations'])
@php /* /.qm-d2 */
@endphp
@endsection