{{-- Full-costing controls: pricing/uplift, the recipe (BOM) grouped & colour-coded by category with a tick-many item picker, live readout, and price tiers. Expects the Editor component's state ($bom, $tiers, $methods, $picker, …) and the view data $recipe, $materialVariants, $assetOptions, $supplierOptions, $operatingCostOptions, $inkModeOptions, $baseCodeModal. --}} @php // One place for every category's colour + icon. Full literal Tailwind class // strings so the JIT compiler picks them up. $typeMeta = [ 'material' => ['label' => __('Material'), 'icon' => 'cube', 'head' => 'bg-emerald-50 text-emerald-700 ring-emerald-200', 'border' => 'border-emerald-200', 'zebra' => 'bg-emerald-50/50', 'num' => 'bg-emerald-100 text-emerald-700', 'btn' => 'border-emerald-200 bg-emerald-50 text-emerald-700 hover:bg-emerald-100', 'dot' => 'bg-emerald-400'], 'ink' => ['label' => __('Ink'), 'icon' => 'swatch', 'head' => 'bg-violet-50 text-violet-700 ring-violet-200', 'border' => 'border-violet-200', 'zebra' => 'bg-violet-50/50', 'num' => 'bg-violet-100 text-violet-700', 'btn' => 'border-violet-200 bg-violet-50 text-violet-700 hover:bg-violet-100', 'dot' => 'bg-violet-400'], 'machine' => ['label' => __('Machine'), 'icon' => 'wrench-screwdriver', 'head' => 'bg-sky-50 text-sky-700 ring-sky-200', 'border' => 'border-sky-200', 'zebra' => 'bg-sky-50/50', 'num' => 'bg-sky-100 text-sky-700', 'btn' => 'border-sky-200 bg-sky-50 text-sky-700 hover:bg-sky-100', 'dot' => 'bg-sky-400'], 'outsourced' => ['label' => __('Outsourced'), 'icon' => 'truck', 'head' => 'bg-amber-50 text-amber-700 ring-amber-200', 'border' => 'border-amber-200', 'zebra' => 'bg-amber-50/50', 'num' => 'bg-amber-100 text-amber-700', 'btn' => 'border-amber-200 bg-amber-50 text-amber-700 hover:bg-amber-100', 'dot' => 'bg-amber-400'], 'labour' => ['label' => __('Labour'), 'icon' => 'users', 'head' => 'bg-rose-50 text-rose-700 ring-rose-200', 'border' => 'border-rose-200', 'zebra' => 'bg-rose-50/50', 'num' => 'bg-rose-100 text-rose-700', 'btn' => 'border-rose-200 bg-rose-50 text-rose-700 hover:bg-rose-100', 'dot' => 'bg-rose-400'], 'overhead' => ['label' => __('Overhead'), 'icon' => 'building-office', 'head' => 'bg-teal-50 text-teal-700 ring-teal-200', 'border' => 'border-teal-200', 'zebra' => 'bg-teal-50/50', 'num' => 'bg-teal-100 text-teal-700', 'btn' => 'border-teal-200 bg-teal-50 text-teal-700 hover:bg-teal-100', 'dot' => 'bg-teal-400'], ]; $variantById = collect($materialVariants)->keyBy('id'); @endphp {{-- ===================== TICK-MANY PICKER ===================== --}} {{-- Kept at the partial's top level (outside every ) so `fixed` centres against the viewport, the cards use a hover transform that would otherwise become the containing block and knock the overlay off-centre. --}} @if ($picker !== '') @php $pm = $typeMeta[$picker]; $pickItems = match ($picker) { 'material' => collect($materialVariants)->map(fn ($v) => ['id' => $v['id'], 'title' => $v['material'] . ($v['label'] ? ', ' . $v['label'] : ''), 'meta' => $v['base_unit'] ?: null])->all(), 'machine' => $assetOptions->map(fn ($a) => ['id' => $a->id, 'title' => $a->name, 'meta' => $a->run_rate !== null ? $baseCodeModal . ' ' . number_format((float) $a->run_rate, 2) . '/' . ($a->rate_unit ?: __('unit')) : null])->all(), 'outsourced' => $supplierOptions->map(fn ($s) => ['id' => $s->id, 'title' => $s->name, 'meta' => null])->all(), 'ink' => $inkModeOptions->map(fn ($im) => ['id' => $im->id, 'title' => $im->name, 'meta' => rtrim(rtrim(number_format((float) $im->ml_per_sqm, 2), '0'), '.') . ' ml/m²'])->all(), 'labour', 'overhead' => $operatingCostOptions->where('type', $picker)->map(fn ($oc) => ['id' => $oc->id, 'title' => $oc->name, 'meta' => str_replace('_', ' ', $oc->allocation_basis)])->values()->all(), default => [], }; @endphp
{{ __('Add :type', ['type' => $pm['label']]) }}
@if (empty($pickItems))

{{ __('Nothing set up yet in this category. Add a manual line instead.') }}

@else

{{ __('Tick everything this product uses.') }}

@foreach ($pickItems as $item) @endforeach
@endif
{{ __('Cancel') }} {{ __('Add selected') }}
@endif {{-- Pricing & cost-plus uplift --}}

{{ __('Pricing & margin') }}

{{ __('The suggested price = computed cost × (1 + buffer + markup + risk + misc), matching the costing workbook.') }}

@if ($pricing_mode !== 'fixed')

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

@endif

{{ __('Cost is computed from the recipe on save.') }}

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

{{-- Recipe (bill of materials) --}}

{{ __('Bill of materials') }}

{{ __('What this product consumes to produce, drives the computed cost. Add a category, tick the items you use, then fill in the details.') }}

{{-- Category add buttons (open the tick-many picker) --}}
@foreach ($typeMeta as $t => $m) @endforeach
@if (empty($bom))

{{ __('No recipe lines yet. Pick a category above to add materials, machine time, labour, or outsourced work.') }}

@else @php // Compact per-field label, kept tiny so a whole line fits on one row. $lbl = 'mb-0.5 block text-[10px] font-semibold uppercase tracking-wide text-slate-400'; $fld = 'block w-full rounded-lg border-slate-300 py-1.5 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500'; @endphp
@foreach ($typeMeta as $type => $meta) @php $groupIdx = collect($bom)->keys()->filter(fn ($k) => ($bom[$k]['component_type'] ?? 'material') === $type)->values(); @endphp @if ($groupIdx->isNotEmpty())
{{-- Group header, click the label to collapse/expand the section --}}
{{-- Rows, collapse via a CSS grid-rows height transition (no plugin needed) --}}
{{-- Zebra-striped in the category hue + numbered so no two read alike --}}
@foreach ($groupIdx as $i) @php $line = $bom[$i]; $ctype = $line['component_type'] ?? 'material'; $lineVariant = $ctype === 'material' && ! empty($line['material_variant_id']) ? ($variantById[(int) $line['material_variant_id']] ?? null) : null; $fromStock = ($line['source'] ?? (empty($line['material_variant_id']) ? 'bought' : 'stock')) === 'stock'; @endphp
{{ $loop->iteration }}
{{-- Identity (per type) --}} @if ($ctype === 'material')
@if ($fromStock) @else
@endif
@elseif ($ctype === 'machine')
{{ __('Machine') }}

{{ __('Cost auto, from the machine run-rate.') }}

@elseif ($ctype === 'outsourced')
{{ __('Item / service') }}
{{ __('Supplier') }}
{{ __('Unit cost') }}
@elseif (in_array($ctype, ['labour', 'overhead'], true))
{{ __('Rate') }}
{{ __('Unit cost') }}
@elseif ($ctype === 'ink')
{{ __('Ink pass-mode') }}

{{ __('Cost auto, per m² at the ink rate in Costing.') }}

@endif {{-- Detail (shared, compact) --}}
@if (! empty($methods))
{{ __('Method') }}
@endif
{{ __('Basis') }}
{{ __('Qty') }}
{{ __('Unit') }}
{{ __('Waste') }}
{{-- Contextual notes (material only) --}} @if ($ctype === 'material')
@if ($fromStock) {{ __('Cost auto, live stock price.') }} @if ($lineVariant && ($lineVariant['stock_dimension'] ?? 'unit') === 'roll' && ($lineVariant['roll_width'] ?? null)) {{ __('Roll, :w m wide', ['w' => rtrim(rtrim(number_format((float) $lineVariant['roll_width'], 4), '0'), '.')]) }} @elseif ($lineVariant && ($lineVariant['stock_dimension'] ?? 'unit') === 'sheet' && ($lineVariant['sheet_width'] ?? null) && ($lineVariant['sheet_height'] ?? null)) {{ __('Sheet, :w × :h m', ['w' => rtrim(rtrim(number_format((float) $lineVariant['sheet_width'], 4), '0'), '.'), 'h' => rtrim(rtrim(number_format((float) $lineVariant['sheet_height'], 4), '0'), '.')]) }} @endif @else @endif
@endif
@endforeach
{{-- divide-y --}}
{{-- overflow-hidden --}}
{{-- grid collapse --}}
{{-- group --}} @endif @endforeach
@endif {{-- Live cost readout --}}

{{ __('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 (! empty($recipe['perMethod']) && ! empty($methods))
@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) }}% {{ __('(buffer + markup + risk + misc)') }}

@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
{{-- Quantity price tiers --}}

{{ __('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