@php $pillClass = [ 'up' => 'bg-emerald-50 text-emerald-700', 'down' => 'bg-rose-50 text-rose-700', 'muted' => 'bg-slate-100 text-slate-500', ]; $kpis = [ [ 'label' => __('Total Products'), 'value' => number_format($stats['total']), 'icon' => 'cube', 'accent' => '#124a61', 'sub' => ['text' => number_format($stats['active']) . ' ' . __('active') . ' · ' . number_format($stats['inactive']) . ' ' . __('inactive'), 'tone' => 'muted', 'arrow' => null], ], [ 'label' => __('Active Products'), 'value' => number_format($stats['active']), 'icon' => 'check-circle', 'accent' => '#10b981', 'sub' => ['text' => $stats['active_pct'] . '% ' . __('of catalogue'), 'tone' => $stats['active_pct'] >= 50 ? 'up' : 'muted', 'arrow' => null], ], [ 'label' => __('Avg. Margin'), 'value' => $stats['avg_margin'] !== null ? number_format($stats['avg_margin'], 2) . '%' : '—', 'icon' => 'receipt-percent', 'accent' => '#f59e0b', 'sub' => ['text' => trans_choice('{1} across :count priced item|[2,*] across :count priced items', $stats['priced'], ['count' => number_format($stats['priced'])]), 'tone' => $stats['avg_margin'] === null ? 'muted' : ($stats['avg_margin'] >= 25 ? 'up' : ($stats['avg_margin'] < 10 ? 'down' : 'muted')), 'arrow' => null], ], [ 'label' => __('Needs Pricing'), 'value' => number_format($stats['uncosted']), 'icon' => $stats['uncosted'] > 0 ? 'exclamation-triangle' : 'check-circle', 'accent' => $stats['uncosted'] > 0 ? '#f43f5e' : '#10b981', 'sub' => $stats['uncosted'] > 0 ? ['text' => __('Uncosted, set a recipe or cost'), 'tone' => 'down', 'arrow' => null] : ['text' => __('Every product is costed'), 'tone' => 'up', 'arrow' => null], ], ]; // Revenue is a financial figure: hidden from Production (and anyone without // financial visibility), shown to management just before "Needs Pricing". if ($canViewRevenue) { array_splice($kpis, 3, 0, [[ 'label' => __('Revenue (MTD)'), 'value' => $baseCode . ' ' . number_format($stats['revenue_mtd'], 2), 'icon' => 'banknotes', 'accent' => '#a855f7', 'sub' => $stats['revenue_delta'] !== null ? ['text' => number_format(abs($stats['revenue_delta']), 1) . '% ' . __('vs last month'), 'tone' => $stats['revenue_delta'] >= 0 ? 'up' : 'down', 'arrow' => $stats['revenue_delta'] >= 0 ? 'up' : 'down'] : ['text' => __('This month'), 'tone' => 'muted', 'arrow' => null], ]]); } @endphp
{{ $kpi['label'] }}
{{ $kpi['value'] }}
@if (! empty($kpi['sub']['arrow']))| {{ __('Product') }} | {{ __('Category') }} | {{ __('Sell Price') }} | {{ __('Avg. Cost') }} | {{ __('Margin') }} | @if ($canViewRevenue){{ __('Revenue (MTD)') }} | @endif{{ __('Actions') }} |
|---|---|---|---|---|---|---|
|
{{ $product->name }} @if ($product->is_bespoke) |
@if ($product->category) {{ $product->category->name }} @else — @endif | {{ $baseCode }} {{ number_format((float) $product->default_price, 2) }} | {{ $baseCode }} {{ number_format((float) $product->cost, 2) }} | {{ $margin === null ? '—' : number_format($margin, 2) . '%' }} | @if ($canViewRevenue){{ $baseCode }} {{ number_format($revenueMtdByProduct[$product->id] ?? 0, 2) }} | @endif
@if ($canManage || $canQuote)
@if ($canQuote)
@endif
@else
—
@endif
|
{{ $product->name }}
@if ($product->is_bespoke){{ $product->sku ? 'SKU: ' . $product->sku : '' }} @if ($product->category) · {{ $product->category->name }} @endif
{{ $baseCode }} {{ number_format((float) $product->default_price, 2) }} / {{ $product->unit ?: '—' }}
{{ __('Margin') }}: {{ $margin === null ? '—' : number_format($margin, 2) . '%' }} @if ($canViewRevenue) · {{ __('Revenue') }}: {{ $baseCode }} {{ number_format($revenueMtdByProduct[$product->id] ?? 0, 2) }} @endif
{{ __('Showing :from to :to of :total results', ['from' => $products->firstItem() ?? 0, 'to' => $products->lastItem() ?? 0, 'total' => $products->total()]) }}
{{ __('By revenue this month') }}
{{ $row['name'] }}
{{ $baseCode }} {{ number_format($row['revenue'], 0) }}
{{ trans_choice('{0} no invoices|{1} :count invoice|[2,*] :count invoices', $row['uses'], ['count' => $row['uses']]) }}
{{ __('No sales recorded this month yet.') }}
@endforelse{{ __('Last 6 months') }}
{{ __('No categorised products yet.') }}
@endif{{ $addProduct->name }}
{{ $baseCode }} {{ number_format((float) $addProduct->default_price, 2) }} @if ($addProduct->pricing_mode === 'per_sqm') · {{ __('per m²') }} @elseif ($addProduct->pricing_mode === 'per_linear_m') · {{ __('per linear m') }} @endif
{{ __('No editable quotes yet, create one on the Quotes page first.') }}
@endif
{{ __('Spreads per-job labour/overhead onto a unit.') }}
{{ __('Enable to price DTF vs Embroidery separately.') }}
{{ __('What this product consumes to produce, drives the computed cost.') }}
{{ __('No recipe lines yet. Add materials, machine time, or outsourced work above.') }}
@else
{{ __('Auto-fills the unit cost from jobs/day; override below if needed.') }}
{{ __('Valued per m² at the ink cost set in Costing.') }}
{{ __('Computed Cost') }}
{{ $baseCodeModal }} {{ number_format($recipe['cost'], 2) }}
{{ __('Suggested Price') }}
{{ $baseCodeModal }} {{ number_format($recipe['price'], 2) }}
{{ __('Margin') }}
{{ $recipe['margin'] === null ? '—' : number_format($recipe['margin'], 2) . '%' }}
{{ strtoupper($m) }}
{{ __('Cost breakdown') }}
{{ __('Cost-plus uplift') }}: +{{ number_format($recipe['uplift'], 1) }}% {{ __('(materials buffer + markup + risk + misc, summed)') }}
{{ __('Saving writes the computed cost into the product. Suggested price = cost ×(1 + buffer + markup + risk + misc), matching the costing workbook.') }}
{{ __('Optional break pricing by quantity (e.g. 1-24, 25-100). Applied automatically on quotes.') }}