@extends('layouts.dashboard')
@section('content')
@php
/**
* Platform Analytics → Sales: marketplace money across every restaurant —
* collected revenue over time, by tender, sales channel, service mode and hour,
* with the seats each dine-in ticket filled.
*
* @var array $revenue @var array $paidOrders @var array $placed @var array $discounts
* @var array $tips @var array $refunds @var array $aov @var array $perHead
* @var int $coversTotal @var array $seated @var array $seatWait
* @var array $byMode @var array $channelRows
* @var array $operators @var array $operatorsPager @var array $topRest @var array $restPager
* @var array $revChart @var array $revMeta @var array $channelChart @var array $channelMeta
* @var array $hourChart @var array $hourMeta @var array $lossChart @var array $lossMeta
* @var array $tenderChart @var array $tenderMeta @var array $sourceChart @var array $sourceMeta
* @var array $perHeadChart @var array $perHeadMeta
* @var int $rangeDays @var string $rangeMode @var array $rangeLinks
*/
$kpiMeta = static fn (array $m, string $caption): string =>
kpi_trend((int) $m['pct'], (string) $m['dir']) . '' . $caption . ' ';
@endphp
@php /* .qm-d2: the dashboard density standard (app.css §35) */
@endphp
{{ t_raw('dashboard.sec.snapshot') }}
{{ t_raw('admin.analytics.sales_sub') }}
@foreach ($rangeLinks['options'] as $val => $text)
@php $on = ((string) $val === $rangeLinks['selected']); @endphp
{{ $text }}
@endforeach
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'green', 'icon' => 'fa-coins', 'label' => t_raw('common.revenue'),
'value' => money($revenue['total']), 'spark' => $revenue['cur'],
'meta' => $kpiMeta($revenue, t('admin.analytics.meta_vs_prior'))])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'blue', 'icon' => 'fa-receipt', 'label' => t_raw('common.paid_orders'),
'value' => number_format($paidOrders['total']), 'href' => route('admin.orders'), 'spark' => $paidOrders['cur'],
'meta' => $kpiMeta($paidOrders, t('admin.analytics.meta_of_n_placed', [':n' => number_format($placed['total'])]))])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'violet', 'icon' => 'fa-chart-line', 'label' => t_raw('common.average_order'),
'value' => money($aov['total']), 'spark' => $aov['cur'],
'meta' => $kpiMeta($aov, t('admin.analytics.meta_per_paid_order'))])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'teal', 'icon' => 'fa-user-friends', 'label' => t_raw('admin.analytics.kpi_spend_per_head'),
'value' => $perHead['total'] > 0 ? money($perHead['total']) : "\u{2014}", 'spark' => $perHead['cur'],
'meta' => $perHead['total'] > 0
? $kpiMeta($perHead, t('admin.analytics.meta_across_n_covers', [':n' => number_format($coversTotal)]))
: '' . t('admin.analytics.meta_no_covers_recorded') . ' '])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'amber', 'icon' => 'fa-tags', 'label' => t_raw('admin.analytics.kpi_discounts_given'),
'value' => money($discounts['total']), 'href' => route('admin.analytics.promotions'), 'spark' => $discounts['cur'],
'meta' => $kpiMeta($discounts, t('admin.analytics.meta_coupons_rewards'))])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'orange', 'icon' => 'fa-hand-holding-usd', 'label' => t_raw('admin.analytics.kpi_tips'),
'value' => money($tips['total']), 'spark' => $tips['cur'],
'meta' => $kpiMeta($tips, t('admin.analytics.meta_on_paid_orders'))])
@partial('kpi-card', ['variant' => 'panel', 'accent' => 'slate', 'icon' => 'fa-undo', 'label' => t_raw('common.refunded'),
'value' => money($refunds['total']), 'spark' => $refunds['cur'],
'meta' => $kpiMeta($refunds, t('admin.analytics.meta_returned_to_customers'))])
{{ t_raw('dashboard.sec.trends') }}
@partial('chart-card', ['heading' => t_raw('admin.analytics.chart_revenue_over_time'), 'sub' => t_raw('admin.analytics.chart_revenue_over_time_caption'), 'cfg' => $revChart, 'meta' => $revMeta, 'accent' => 'green', 'opts' => ['id' => 'admSalesRevChart', 'url' => route('admin.chart-series', ['chart' => 'salesRev']), 'selected' => $rangeLinks['selected'], 'options' => $rangeLinks['options'], 'control' => 'compact']])
@partial('chart-card', ['heading' => t_raw('admin.analytics.chart_channel_mix'), 'sub' => t_raw('admin.analytics.chart_channel_mix_caption'), 'cfg' => $channelChart, 'meta' => $channelMeta, 'icon' => 'fa-stream', 'accent' => 'blue', 'opts' => ['id' => 'admSalesChannelChart', 'url' => route('admin.chart-series', ['chart' => 'salesChannel']), 'selected' => $rangeLinks['selected'], 'options' => $rangeLinks['options'], 'control' => 'compact']])
@partial('chart-card', ['heading' => t_raw('admin.analytics.chart_revenue_by_hour'), 'sub' => t_raw('admin.analytics.chart_when_money_made'), 'cfg' => $hourChart, 'meta' => $hourMeta, 'icon' => 'fa-th', 'accent' => 'rose'])
@partial('chart-card', ['heading' => t_raw('admin.analytics.chart_leakage'), 'sub' => t_raw('admin.analytics.chart_leakage_caption'), 'cfg' => $lossChart, 'meta' => $lossMeta, 'icon' => 'fa-undo', 'accent' => 'amber', 'opts' => ['id' => 'admSalesLossChart', 'url' => route('admin.chart-series', ['chart' => 'salesLoss']), 'selected' => $rangeLinks['selected'], 'options' => $rangeLinks['options'], 'control' => 'compact']])
{{ t_raw('dashboard.sec.breakdown') }}
@partial('chart-card', ['heading' => t_raw('admin.analytics.chart_takings_by_tender'), 'sub' => t_raw('admin.analytics.chart_net_of_refunds'), 'cfg' => $tenderChart, 'meta' => $tenderMeta, 'icon' => 'fa-chart-pie', 'accent' => 'blue'])
@partial('chart-card', ['heading' => t_raw('admin.analytics.chart_revenue_by_channel'), 'sub' => t_raw('admin.analytics.chart_revenue_by_channel_caption'), 'cfg' => $sourceChart, 'meta' => $sourceMeta, 'icon' => 'fa-chart-pie', 'accent' => 'violet'])
@partial('chart-card', ['heading' => t_raw('admin.analytics.chart_spend_per_head_by_mode'), 'sub' => t_raw('admin.analytics.chart_spend_per_head_caption'), 'cfg' => $perHeadChart, 'meta' => $perHeadMeta, 'icon' => 'fa-user-friends', 'accent' => 'teal'])
{{ t_raw('admin.analytics.h_channel_performance') }}
{{ t_raw('admin.analytics.caption_channels') }}
{{ t_raw('admin.analytics.col_channel') }}
{{ t_raw('common.orders') }}
{{ t_raw('admin.analytics.col_collected') }}
{{ t_raw('common.average_order') }}
{{ t_raw('common.covers') }}
{{ t_raw('admin.analytics.col_last_order') }}
@if ($channelRows)
@foreach ($channelRows as $row)
@php $paid = (int) $row['paid_orders']; @endphp
{{ status_label('order_source', $row['source']) }}
{{ number_format((int) $row['orders_c']) }}
{{ money($row['revenue']) }}
{!! $paid > 0 ? money((float) $row['revenue'] / $paid) : dash() !!}
{!! (int) $row['covers'] > 0 ? number_format((int) $row['covers']) : dash() !!}
{!! fmt_day($row['last_order']) !!}
@endforeach
@else
@partial('table-empty', ['colspan' => 6, 'icon' => 'fa-stream', 'msg' => t('admin.analytics.empty_no_orders_period')])
@endif
@php /* The tip rides inside the heading: a .d-flex wrapper around an h2 is
tightened to 8px by the density standard (app.css §35), which left this
table 8px closer to its title than Channel performance's beside it. A
bare h2 wears the same 16px as its neighbour, so the two cards share
one rhythm with no extra rules. */
@endphp
{{ t_raw('admin.analytics.h_service_modes') }}
{{ t_raw('admin.analytics.caption_modes') }}
{{ t_raw('admin.analytics.col_mode') }}
{{ t_raw('common.orders') }}
{{ t_raw('admin.analytics.col_collected') }}
{{ t_raw('admin.analytics.kpi_spend_per_head') }}
@if ($byMode)
@foreach ($byMode as $m)
{{ $m['label'] }}
{{ number_format((int) $m['orders']) }}
{{ money($m['revenue']) }}
{!! (int) $m['covers'] > 0 ? money($m['revenue'] / (int) $m['covers']) : dash() !!}
@endforeach
@else
@partial('table-empty', ['colspan' => 4, 'icon' => 'fa-chart-bar', 'msg' => t('admin.analytics.empty_no_orders_period')])
@endif
{{ t_raw('admin.analytics.caption_top_rest_by_revenue') }}
{{ t_raw('common.restaurant') }}
{{ t_raw('common.orders') }}
{{ t_raw('admin.analytics.col_collected') }}
{{ t_raw('common.average_order') }}
{{ t_raw('common.covers') }}
{{ t_raw('common.commission') }}
{{ t_raw('admin.analytics.col_last_order') }}
@if ($topRest)
@foreach ($topRest as $t)
@php $c = (int) $t['orders_c']; @endphp
{!! restaurant_logo_html($t, 'md') !!}
{{ $t['name'] }} @if (!empty($t['city'])){{ $t['city'] }} @endif
{{ number_format($c) }}
{{ money($t['collected']) }}
{!! $c > 0 ? money((float) $t['collected'] / $c) : dash() !!}
{!! (int) $t['covers'] > 0 ? number_format((int) $t['covers']) : dash() !!}
{!! (float) $t['commission'] > 0 ? money($t['commission']) : dash() !!}
{!! fmt_day($t['last_order']) !!}
@endforeach
@else
@partial('table-empty', ['colspan' => 7, 'icon' => 'fa-store', 'msg' => t('admin.analytics.empty_no_orders_period')])
@endif
@partial('table-toolbar', ['pager' => $restPager, 'base' => '/admin/analytics/sales'])
{{ t_raw('admin.analytics.caption_cash_handling') }}
{{ t_raw('admin.analytics.col_taken_by') }}
{{ t_raw('admin.analytics.col_captures') }}
{{ t_raw('admin.analytics.col_taken') }}
{{ t_raw('common.refunded') }}
{{ t_raw('nav.restaurants') }}
{{ t_raw('common.when') }}
@if ($operators)
@foreach ($operators as $op)
{!! brandmark_html($op['avatar'] ?? null, (string) $op['name'], 'md', 'qm-brandmark--round') !!}
{{ $op['name'] }} {{ role_label((string) $op['role']) }}
{{ number_format((int) $op['captures']) }}
{{ money($op['taken']) }}
{!! (float) $op['refunded'] > 0 ? money($op['refunded']) : dash() !!}
{{ number_format((int) $op['restaurants_c']) }}
{!! fmt_datetime($op['last_seen']) !!}
@endforeach
@else
@partial('table-empty', ['colspan' => 6, 'icon' => 'fa-cash-register', 'msg' => t('admin.analytics.empty_no_operators')])
@endif
@partial('table-toolbar', ['pager' => $operatorsPager, 'base' => '/admin/analytics/sales'])
@php /* /.qm-d2 */
@endphp
@endsection