@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
{{ __('Manage Categories') }} @if ($canManage) {{ __('New Product') }} @endif
@foreach ($kpis as $kpi)

{{ $kpi['label'] }}

{{ $kpi['value'] }}

@if (! empty($kpi['sub']['arrow'])) @endif {{ $kpi['sub']['text'] }}
@endforeach
@if ($products->isEmpty())
@if ($canManage && $search === '' && $categoryFilter === '' && $statusFilter === '' && $typeFilter === '') {{ __('New Product') }} @endif
@else
@foreach ($products as $product) @php $margin = $product->margin; @endphp

{{ $product->name }}

@if ($product->is_bespoke) {{ __('Bespoke') }} @endif

{{ $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

@if ($product->is_active) {{ __('Active') }} @else {{ __('Inactive') }} @endif
@if ($canManage || $canQuote)
@if ($canQuote) @endif @if ($canManage) @endif
@endif
@endforeach

{{ __('Showing :from to :to of :total results', ['from' => $products->firstItem() ?? 0, 'to' => $products->lastItem() ?? 0, 'total' => $products->total()]) }}

{{ $products->onEachSide(1)->links() }}
@endif
@if ($canViewRevenue)

{{ __('Top Selling Products') }}

{{ __('By revenue this month') }}

@forelse (collect($topSelling)->take(4) as $i => $row)
{{ $i + 1 }}

{{ $row['name'] }}

{{ $baseCode }} {{ number_format($row['revenue'], 0) }}

{{ trans_choice('{0} no invoices|{1} :count invoice|[2,*] :count invoices', $row['uses'], ['count' => $row['uses']]) }}

@empty

{{ __('No sales recorded this month yet.') }}

@endforelse

{{ __('Revenue Trend') }}

{{ __('Last 6 months') }}

{{ __('Monthly') }}
@endif

{{ __('Products by Category') }}

@if (! empty($categoryBreakdown['segments']))
@else

{{ __('No categorised products yet.') }}

@endif
{{ __('Add to a quote') }} @if ($addProduct)

{{ $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

@if ($addQuoteOptions->isEmpty())

{{ __('No editable quotes yet, create one on the Quotes page first.') }}

@endif
@endif
{{ __('Cancel') }} @if ($addQuoteOptions->isNotEmpty()) {{ __('Add to quote') }} @endif
{{ $editingId ? __('Edit Product') : __('New Product') }}
@if ($pricing_mode !== 'fixed')

{{ __('Dimensional item, width × height captured on each quote/invoice line') }}

@endif

{{ __('Spreads per-job labour/overhead onto a unit.') }}

{{ __('Enable to price DTF vs Embroidery separately.') }}

{{ __('Recipe (Bill of Materials)') }}

{{ __('What this product consumes to produce, drives the computed cost.') }}

@php // Fast lookup of a variant's surfaced dimension metadata by id. $variantById = collect($materialVariants)->keyBy('id'); @endphp @if (empty($bom))

{{ __('No recipe lines yet. Add materials, machine time, or outsourced work above.') }}

@else
@foreach ($bom as $i => $line) @php $lineVariant = ($line['component_type'] ?? 'material') === 'material' && ! empty($line['material_variant_id']) ? ($variantById[(int) $line['material_variant_id']] ?? null) : null; @endphp
@if (($line['component_type'] ?? 'material') === 'material')
@if ($lineVariant && ($lineVariant['stock_dimension'] ?? 'unit') === 'roll' && ($lineVariant['roll_width'] ?? null))

{{ __('Roll media, :w m wide drives consumption (real linear-m yield from the piece size).', ['w' => rtrim(rtrim(number_format((float) $lineVariant['roll_width'], 4), '0'), '.')]) }} @if (($lineVariant['default_waste_pct'] ?? 0) > 0) · {{ __('+:p% default waste', ['p' => rtrim(rtrim(number_format((float) $lineVariant['default_waste_pct'], 2), '0'), '.')]) }} @endif

@elseif ($lineVariant && ($lineVariant['stock_dimension'] ?? 'unit') === 'sheet' && ($lineVariant['sheet_width'] ?? null) && ($lineVariant['sheet_height'] ?? null))

{{ __('Sheet media, :w × :h m per sheet.', ['w' => rtrim(rtrim(number_format((float) $lineVariant['sheet_width'], 4), '0'), '.'), 'h' => rtrim(rtrim(number_format((float) $lineVariant['sheet_height'], 4), '0'), '.')]) }}

@endif
@elseif (($line['component_type'] ?? '') === 'machine')
@elseif (($line['component_type'] ?? '') === 'outsourced')
@elseif (in_array($line['component_type'] ?? '', ['labour', 'overhead'], true))

{{ __('Auto-fills the unit cost from jobs/day; override below if needed.') }}

@elseif (($line['component_type'] ?? '') === 'ink')

{{ __('Valued per m² at the ink cost set in Costing.') }}

@endif
@if (! empty($methods))
@endif
@endforeach
@endif

{{ __('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) . '%' }}

@if ($pricing_mode !== 'fixed')
@endif @if ($recipe['hasRoll'])
@endif
@if ($recipe['hasRoll'])

{{ ($recipe['width'] && $recipe['height']) ? __('Roll line valued by real linear-metre yield from the piece size above.') : __('Enter a piece W × H to value the roll line by real linear-metre yield (otherwise it falls back to area/per-item).') }}

@endif @if (! empty($methods) && ! empty($recipe['perMethod']))
@foreach ($recipe['perMethod'] as $m => $fig)

{{ strtoupper($m) }}

{{ __('Cost') }} {{ number_format($fig['cost'], 2) }} {{ __('Price') }} {{ number_format($fig['price'], 2) }} {{ __('Margin') }} {{ number_format($fig['margin'], 1) }}%
@endforeach
@endif @if (! empty($recipe['breakdown'])) @php $bdLabels = ['material' => __('Materials'), 'ink' => __('Ink'), 'outsourced' => __('Hardware / outsourced'), 'machine' => __('Machine'), 'labour' => __('Labour'), 'overhead' => __('Overhead')]; @endphp

{{ __('Cost breakdown') }}

@foreach ($recipe['breakdown'] as $type => $amt) {{ $bdLabels[$type] ?? ucfirst($type) }} {{ $baseCodeModal }} {{ number_format($amt, 2) }} @endforeach

{{ __('Cost-plus uplift') }}: +{{ number_format($recipe['uplift'], 1) }}% {{ __('(materials buffer + markup + risk + misc, summed)') }}

@endif @if (! empty($recipe['competitor'])) @php $cdiff = $recipe['price'] - $recipe['competitor']; $cpct = $recipe['competitor'] > 0 ? ($cdiff / $recipe['competitor']) * 100 : 0; @endphp
{{ __('Competitor') }}: {{ $baseCodeModal }} {{ number_format($recipe['competitor'], 2) }} {{ $cdiff <= 0 ? '▼' : '▲' }} {{ $baseCodeModal }} {{ number_format(abs($cdiff), 2) }} ({{ number_format(abs($cpct), 0) }}% {{ $cdiff <= 0 ? __('below market') : __('above market') }})
@endif

{{ __('Saving writes the computed cost into the product. Suggested price = cost ×(1 + buffer + markup + risk + misc), matching the costing workbook.') }}

{{ __('Quantity Price Tiers') }}

{{ __('Optional break pricing by quantity (e.g. 1-24, 25-100). Applied automatically on quotes.') }}

@if (! empty($tiers))
@foreach ($tiers as $i => $tier)
@if (! empty($methods))
@endif
@endforeach
@endif
{{ __('Cancel') }} {{ __('Save') }}