@extends('layouts.dashboard') @section('content') @php /** * Vendor → Staff analytics → one member, for the selected reporting window. * Role-aware metrics plus an activity trend, an hour-of-day work pattern, the * money this member settled at the till and a recent-activity log — all drawn * from the real audit trails and payment ledger (StaffController::memberAnalytics()). * * @var array $member id, name, role, email, status * @var array $stats the member's metric block for the window (may be all-zero) * @var array $roles role => ['label'] (context label; colour via role_pill_class()) * @var array $rangeLinks ['options' => [days => label], 'selected' => string] * @var int $rangeDays the selected window, in days * @var bool $showMoney false for a manager: takings stay with the owner * @var array $trendChart @var array $trendMeta actions per day * @var array $hoursChart @var array $hoursMeta hour-of-day work pattern * @var array $salesChart @var array $salesMeta money settled per day * @var array $kpiSparks per-metric daily series for the KPI mini trends * @var array $recent merged recent actions [{kind,ref,status,detail?,at}] */ $role = (string) $member['role']; $status = (string) $member['status']; $rmeta = $roles[$role] ?? ['label' => role_label($role)]; $g = static fn (string $k): int => (int) ($stats[$k] ?? 0); $m = static fn (string $k): float => (float) ($stats[$k] ?? 0); $lastAt = $stats['last_at'] ?? null; $kpiSparks = $kpiSparks ?? []; $showMoney = $showMoney ?? true; // KPI cards chosen by what this member's role can actually DO, so each page leads with the // work they own — and a role composed on the admin Roles screen lands on the right set // without a branch being written for it. A role that runs the whole floor (it can read the // order list) gets the general set; a narrow one gets the cards for its speciality. // Each tuple: [label, value, accent, FA5 glyph, sparkKey, "View all" href|''] for the // shared kpi-card 'panel' variant. sparkKey selects this member's daily series from // $kpiSparks (built in StaffController::memberAnalytics from the audit trails). $rolePerms = \App\Models\Role::permissionsFor($role); $holds = static fn (string $perm): bool => in_array($perm, $rolePerms, true); $isWide = $holds('orders.view'); $ordersUrl = route('vendor.orders'); $kitchenUrl = route('vendor.kitchen'); $resvUrl = route('vendor.reservations'); if ($role === 'driver') { // A rider's work is the last leg, not the kitchen: the order-confirmation and // preparation counters a manager is judged on are always zero for them, so the // card set follows the run itself — picked up, delivered, and the bills they // collected on the doorstep. $kpis = [ [t_raw('vendor.staff.kpi_marked_ready'), $g('ready'), 'blue', 'fa-box', 'ready', $ordersUrl], [t_raw('vendor.staff.kpi_delivered'), $g('delivered'), 'green', 'fa-check-circle', 'delivered', $ordersUrl], [t_raw('vendor.staff.kpi_orders_handled'), $g('orders_touched'), 'violet', 'fa-receipt', 'total_actions', $ordersUrl], [t_raw('vendor.staff.kpi_actions_logged'), $g('total_actions'), 'slate', 'fa-chart-line', 'total_actions', ''], ]; } elseif (!$isWide && $holds('kitchen.advance')) { $kpis = [ [t_raw('vendor.staff.kpi_orders_prepared'), $g('prepared'), 'blue', 'fa-receipt', 'prepared', $kitchenUrl], [t_raw('vendor.staff.kpi_items_prepared'), $g('items'), 'amber', 'fa-utensils', 'items', $kitchenUrl], [t_raw('vendor.staff.kpi_marked_ready'), $g('ready'), 'green', 'fa-box', 'ready', $ordersUrl], [t_raw('vendor.staff.kpi_actions_logged'), $g('total_actions'), 'slate', 'fa-chart-line', 'total_actions', ''], ]; } elseif (!$isWide && $holds('reservations.manage')) { $kpis = [ [t_raw('vendor.staff.kpi_reservations'), $g('reservations'), 'violet', 'fa-calendar-check', 'reservations', $resvUrl], [t_raw('vendor.staff.col_tables_seated'), $g('seated'), 'teal', 'fa-chair', 'seated', $resvUrl], [t_raw('vendor.staff.kpi_guests_seated'), $g('guests'), 'green', 'fa-users', 'guests', $resvUrl], [t_raw('vendor.staff.kpi_completed'), $g('completed'), 'blue', 'fa-check-circle', 'completed', $resvUrl], ]; } else { // the owner, and anyone who runs the floor end to end $kpis = [ [t_raw('vendor.staff.kpi_orders_confirmed'), $g('confirmed'), 'blue', 'fa-check-circle', 'confirmed', $ordersUrl], [t_raw('vendor.staff.kpi_orders_prepared'), $g('prepared'), 'amber', 'fa-utensils', 'prepared', $kitchenUrl], [t_raw('vendor.staff.kpi_cancellations'), $g('cancelled'), 'rose', 'fa-ban', 'cancelled', $ordersUrl], [t_raw('vendor.staff.kpi_actions_logged'), $g('total_actions'), 'slate', 'fa-chart-line', 'total_actions', ''], ]; } // Recent-activity presentation: order status -> verb/icon; reservations use their detail line. $orderVerb = [ 'pending' => [t_raw('vendor.staff.verb_logged_new_order'), 'fa-receipt'], 'confirmed' => [t_raw('vendor.staff.verb_accepted_order'), 'fa-check'], 'preparing' => [t_raw('vendor.staff.verb_started_preparing'), 'fa-fire'], 'out_for_delivery' => [t_raw('vendor.staff.kpi_marked_ready'), 'fa-box'], 'delivered' => [t_raw('vendor.staff.verb_completed_order'), 'fa-check-circle'], 'cancelled' => [t_raw('vendor.staff.verb_cancelled_order'), 'fa-times-circle'], ]; // A member who logged no workflow actions may still have settled bills (a cashier // on the till all shift), so the empty state has to consider both. $hasWork = $g('total_actions') > 0 || $g('orders_paid') > 0; @endphp @php /* .qm-d2: the dashboard density standard (app.css §35) */ @endphp
@php /* The member's identity wears the profile hero every detail page wears now: brand-wash band (avatar, name, pills — the period picker and the way back on the right) over the facts as a stat band. */ @endphp
{!! brandmark_html($member['avatar'] ?? null, (string) $member['name'], 'lg', 'qm-brandmark--round') !!}

{{ $member['name'] }}

@php /* One row, not two flex items. .qm-tbl-id-text is a column flex, so a pill placed directly in it is blockified and stretched to the whole cell — both badges came out full width and stacked, and their ms-1 did nothing because the margin was horizontal while the stacking was vertical. The list table avoids this by keeping its pill inside a text run; this header carries two, so it gets a row. gap-2 spaces them, which is why the margin utilities come off. */ @endphp
{{ $rmeta['label'] }} {!! $status === 'active' ? t('common.active') : t('common.status_suspended') !!}
@foreach ($rangeLinks['options'] as $val => $text) @php $on = ((string) $val === $rangeLinks['selected']); @endphp {{ $text }} @endforeach
{{ t_raw('vendor.staff.link_all_staff') }}
{{ t_raw('common.email') }}
{{ $member['email'] }}
{{ t_raw('vendor.staff.text_last_active') }}
{!! $lastAt ? e(time_ago((string) $lastAt)) : dash() !!}
@if (!$hasWork)

{{ t_raw('vendor.staff.no_activity_for_title', [':name' => $member['name']]) }}

{{ t_raw('vendor.staff.no_activity_in_window') }}

@else
@foreach ($kpis as [$label, $value, $accent, $icon, $sparkKey, $cardHref]) @partial('kpi-card', ['variant' => 'panel', 'accent' => $accent, 'icon' => $icon, 'label' => $label, 'value' => number_format((int) $value), 'spark' => $kpiSparks[$sparkKey] ?? [], 'href' => $cardHref]) @endforeach @if ($showMoney) @partial('kpi-card', ['variant' => 'panel', 'accent' => 'green', 'icon' => 'fa-cash-register', 'label' => t_raw('vendor.staff.kpi_sales_taken'), 'value' => money($m('sales')), 'spark' => $kpiSparks['sales'] ?? [], 'meta' => '' . t('vendor.staff.meta_settled_at_till') . '']) @partial('kpi-card', ['variant' => 'panel', 'accent' => 'pink', 'icon' => 'fa-hand-holding-usd', 'label' => t_raw('vendor.staff.kpi_tips'), 'value' => money($m('tips')), 'meta' => '' . t('vendor.staff.meta_tips_on_settled') . '']) @partial('kpi-card', ['variant' => 'panel', 'accent' => 'slate', 'icon' => 'fa-balance-scale', 'label' => t_raw('vendor.staff.kpi_avg_check'), 'value' => $g('orders_paid') > 0 ? money($m('avg_check')) : '—', 'meta' => '' . t('vendor.staff.meta_across_n_bills', [':n' => number_format($g('orders_paid'))]) . '']) @endif
@partial('chart-card', ['heading' => t_raw('vendor.staff.chart_activity_trend'), 'sub' => t_raw('vendor.staff.sub_member_actions'), 'cfg' => $trendChart, 'meta' => $trendMeta, 'accent' => 'blue', 'icon' => 'fa-chart-line'])
@partial('chart-card', ['heading' => t_raw('vendor.staff.chart_hours_pattern'), 'sub' => t_raw('vendor.staff.sub_member_hours'), 'cfg' => $hoursChart, 'meta' => $hoursMeta, 'accent' => 'violet', 'icon' => 'fa-clock'])
@if ($showMoney)
@partial('chart-card', ['heading' => t_raw('vendor.staff.chart_sales_taken'), 'sub' => t_raw('vendor.staff.sub_sales_taken'), 'cfg' => $salesChart, 'meta' => $salesMeta, 'accent' => 'green', 'icon' => 'fa-cash-register'])
@endif

{{ t_raw('vendor.staff.heading_recent_activity') }}

@if ($recent) @else

{{ t_raw('vendor.staff.empty_no_recent_actions') }}

@endif
@endif
@php /* /.qm-d2 */ @endphp @endsection