@include('festool-product-data::partials.tabs', ['active' => 'browse'])
{{-- LEFT: List panel --}}
{{ number_format($this->totalCount) }} products Loading…
@forelse($this->products as $product) @php $isSelected = $selectedSku === $product['product_number']; $typClass = match($product['product_typ'] ?? '') { 'Tool' => 'fpd-tag fpd-tag-tool', 'Accessory' => 'fpd-tag fpd-tag-acc', default => 'fpd-tag', }; $statusClass = match($product['sap_status'] ?? '') { 'Current' => 'fpd-tag fpd-tag-current', 'Discontinued' => 'fpd-tag fpd-tag-disc', default => 'fpd-tag', }; @endphp
{{ $product['product_number'] }} @if($product['price_inc_vat_gbp']) £{{ number_format($product['price_inc_vat_gbp'], 2) }} @elseif($product['rrp_gbp']) £{{ number_format($product['rrp_gbp'], 2) }} @endif
{{ $product['name'] ?? $product['product_name'] }}
@if($product['product_typ']) {{ $product['product_typ'] }} @endif @if($product['sap_status']) {{ $product['sap_status'] }} @endif @if($product['type']) {{ Str::limit($product['type'], 30) }} @endif @if($product['web_scrape_status'] === 'done') scraped @endif @if($product['is_spare_part']) spare part @endif
@empty
No products match your filters.
@endforelse
Page {{ $page }} / {{ $this->totalPages }}
{{-- RIGHT: Detail panel --}} {{-- wire:key forces morphdom to fully replace this panel when the selected SKU changes, preventing stale image elements or Alpine-initialized nodes from the previous product from persisting in the DOM during the patch. --}}
@if(! $selectedSku)
📦
Select a product
Click any item in the list to view its details.
@elseif(! $this->selectedProduct && ! $this->selectedSparePart)
🔍
No data found
SKU {{ $selectedSku }} has no records.
@elseif(! $this->selectedProduct && $this->selectedSparePart) {{-- Spare-part-only detail (no catalogue entry) --}} @php $sp = $this->selectedSparePart; @endphp
SKU {{ $sp->item_no }} @if($sp->ean)  ·  EAN {{ $sp->ean }} @endif
{{ $sp->description ?? '(no description)' }}
@if($sp->type)
{{ $sp->type }}
@endif
Spare Part @if($sp->status) @php $stClass = match($sp->status) { 'Current' => 'fpd-tag fpd-tag-current', 'Discontinued' => 'fpd-tag fpd-tag-disc', default => 'fpd-tag' }; @endphp {{ $sp->status }} @endif @if($sp->country_of_origin) Made in {{ $sp->country_of_origin }} @endif
@if($sp->price_gbp)
Trade Price GBP £{{ number_format($sp->price_gbp, 2) }}
@endif @if($sp->net_cost_price_gbp)
Net Cost GBP £{{ number_format($sp->net_cost_price_gbp, 2) }}
@endif @if($sp->partner_discount)
Partner Discount {{ $sp->partner_discount }}
@endif
Details
@if($sp->product_hierarchy)
Product Hierarchy
{{ $sp->product_hierarchy }}
@endif @if($sp->delivery_date)
Delivery Date
{{ $sp->delivery_date->format('d M Y') }}
@endif @if($sp->height_mm || $sp->width_mm || $sp->length_mm || $sp->weight_g)
Dimensions / Weight
@if($sp->length_mm) L {{ $sp->length_mm }}mm @endif @if($sp->width_mm) W {{ $sp->width_mm }}mm @endif @if($sp->height_mm) H {{ $sp->height_mm }}mm @endif @if($sp->weight_g) · {{ $sp->weight_g }}g @endif
@endif
@else @php $p = $this->selectedProduct; @endphp {{-- Header --}} @php $firstR2 = collect($this->selectedProductImages)->whereNotNull('r2_key')->first(); $heroUrl = $firstR2 ? \Illuminate\Support\Facades\Storage::disk('r2')->url($firstR2['r2_key']) : ($p->web_image_url ?? null); @endphp
@if($heroUrl) {{ $p->product_name ?? $p->name }} @endif
SKU {{ $p->product_number }} @if($p->ean)  ·  EAN {{ $p->ean }} @endif
{{ $p->product_name ?? $p->name }}
@if($p->type)
{{ $p->type }} @if($p->version) — {{ $p->version }} @endif
@endif
@if($this->selectedSparePart?->type === 'Spare Part') Spare Part @endif @if($p->product_typ) @php $typClass = match($p->product_typ) { 'Tool' => 'fpd-tag fpd-tag-tool', 'Accessory' => 'fpd-tag fpd-tag-acc', default => 'fpd-tag' }; @endphp {{ $p->product_typ }} @endif @if($p->sap_status) @php $stClass = match($p->sap_status) { 'Current' => 'fpd-tag fpd-tag-current', 'Discontinued' => 'fpd-tag fpd-tag-disc', default => 'fpd-tag' }; @endphp {{ $p->sap_status }} @endif @if($p->country_of_origin) Made in {{ $p->country_of_origin }} @endif @if($p->toolpoints) {{ $p->toolpoints }} toolpoints @endif @if($p->promotion) On Promotion @endif
@if($p->price_inc_vat_gbp)
Inc VAT £{{ number_format($p->price_inc_vat_gbp, 2) }}
@endif @if($p->price_ex_vat_gbp)
Ex VAT £{{ number_format($p->price_ex_vat_gbp, 2) }}
@endif @if($p->dealer_net_price_gbp)
Dealer Net £{{ number_format($p->dealer_net_price_gbp, 2) }}
@endif @if($p->rrp_gbp)
RRP £{{ number_format($p->rrp_gbp, 2) }}
@endif @if($p->discount_group)
Discount Group {{ $p->discount_group }}
@endif
{{-- /fpd-header-text --}}
{{-- Marketing copy --}} @if($p->claim || $p->competence_text || $p->description_text)
Marketing Copy
@if($p->claim)
Claim
{{ $p->claim }}
@endif @if($p->competence_text)
Competence Text
{{ $p->competence_text }}
@endif @if($p->description_text)
Description
{{ $p->description_text }}
@endif
@endif {{-- Strengths & Application --}} @if($p->strengths_benefits || $p->application)
Strengths & Application
@if($p->strengths_benefits)
Strengths & Benefits
@foreach(explode('|', $p->strengths_benefits) as $item) @if(trim($item))
{{ trim($item) }}
@endif @endforeach
@endif @if($p->application)
Applications
@foreach(explode('|', $p->application) as $item) @if(trim($item))
{{ trim($item) }}
@endif @endforeach
@endif
@endif {{-- Highlights --}} @if($p->highlights_headline || $p->highlights_text)
Highlights
@if($p->highlights_headline)
Headlines
@foreach(explode('|', $p->highlights_headline) as $item) @if(trim($item))
{{ trim($item) }}
@endif @endforeach
@endif @if($p->highlights_text)
Detail
@foreach(explode('|', $p->highlights_text) as $item) @if(trim($item))
{{ trim($item) }}
@endif @endforeach
@endif
@endif {{-- Technical details --}} @if($p->technical_details || $p->technical_details_2 || $p->scope_of_delivery)
Technical Details
@if($p->technical_details)
Specifications
{{ $p->technical_details }}
@endif @if($p->technical_details_2)
Additional Specifications
{{ $p->technical_details_2 }}
@endif @if($p->scope_of_delivery)
Scope of Delivery
{{ $p->scope_of_delivery }}
@endif
@endif {{-- Dimensions --}}
Dimensions & Weight
@if($p->prod_length_cm || $p->prod_width_cm || $p->prod_height_cm || $p->prod_weight_kg)
Product
@if($p->prod_length_cm)
{{ $p->prod_length_cm }}
Length cm
@endif @if($p->prod_width_cm)
{{ $p->prod_width_cm }}
Width cm
@endif @if($p->prod_height_cm)
{{ $p->prod_height_cm }}
Height cm
@endif @if($p->prod_weight_kg)
{{ $p->prod_weight_kg }}
Weight kg
@endif
@endif @if($p->pkg_length_cm || $p->pkg_width_cm || $p->pkg_height_cm || $p->logistic_weight_kg)
Packaging
@if($p->pkg_length_cm)
{{ $p->pkg_length_cm }}
Length cm
@endif @if($p->pkg_width_cm)
{{ $p->pkg_width_cm }}
Width cm
@endif @if($p->pkg_height_cm)
{{ $p->pkg_height_cm }}
Height cm
@endif @if($p->logistic_weight_kg)
{{ $p->logistic_weight_kg }}
Log. weight kg
@endif
@endif
{{-- Logistics --}}
Logistics & Classification
@if($p->pack_unit || $p->sales_unit)
Pack / Sales Unit
{{ $p->pack_unit }} / {{ $p->sales_unit }}
@endif @if($p->customs_tariff_number)
Customs Tariff No.
{{ $p->customs_tariff_number }}
@endif @if($p->product_hierarchy)
Product Hierarchy
{{ $p->product_hierarchy }}
@endif @if($p->market_launch_date)
Market Launch
{{ $p->market_launch_date->format('d M Y') }}
@endif @if($p->expiry_date)
Expiry Date
{{ $p->expiry_date->format('d M Y') }}
@endif @if($p->substitution_number)
Substitution No.
{{ $p->substitution_number }}
@endif @if($p->valid_for)
Compatible With
{{ $p->valid_for }}
@endif
{{-- Images (Excel-imported filenames only) --}} @php $webImgs = collect($this->selectedProductImages)->filter(fn($i) => str_starts_with($i['filename'] ?? '', 'http')); $excelImgs = collect($this->selectedProductImages)->reject(fn($i) => str_starts_with($i['filename'] ?? '', 'http')); @endphp @if($excelImgs->count())
Images ({{ $excelImgs->count() }})
@foreach($excelImgs as $img)
{{ $img['filename'] }} @if($img['r2_key']) ✓ R2@endif
@endforeach
@endif {{-- Spare part data --}} @if($this->selectedSparePart) @php $sp = $this->selectedSparePart; @endphp
Spare Part Data
@if($sp->description)
Description
{{ $sp->description }}
@endif @if($sp->price_gbp)
Price GBP
£{{ number_format($sp->price_gbp, 2) }}
@endif @if($sp->net_cost_price_gbp)
Net Cost GBP
£{{ number_format($sp->net_cost_price_gbp, 2) }}
@endif @if($sp->type)
Type
{{ $sp->type }}
@endif @if($sp->status)
Status
{{ $sp->status }}
@endif @if($sp->ean)
EAN
{{ $sp->ean }}
@endif @if($sp->country_of_origin)
Country of Origin
{{ $sp->country_of_origin }}
@endif @if($sp->partner_discount)
Partner Discount
{{ $sp->partner_discount }}
@endif @if($sp->delivery_date)
Delivery Date
{{ $sp->delivery_date->format('d M Y') }}
@endif @if($sp->height_mm || $sp->width_mm || $sp->length_mm || $sp->weight_g)
Dimensions / Weight
@if($sp->length_mm) L {{ $sp->length_mm }}mm @endif @if($sp->width_mm) W {{ $sp->width_mm }}mm @endif @if($sp->height_mm) H {{ $sp->height_mm }}mm @endif @if($sp->weight_g) · {{ $sp->weight_g }}g @endif
@endif
@endif {{-- Web scrape status --}}
Web Scrape
{{-- Confirmation modal --}}
Status
{{ $p->web_scrape_status ?? 'Not scraped' }}
@if($p->web_scraped_at)
Scraped At
{{ $p->web_scraped_at->format('d M Y H:i') }}
@endif @if($p->web_product_url) @endif @if($webImgs->count()) @php $lightboxImages = $webImgs->values()->map(function ($i) { $url = $i['r2_key'] ? \Illuminate\Support\Facades\Storage::disk('r2')->url($i['r2_key']) : $i['filename']; return ['url' => $url, 'seq' => $i['sequence'], 'onR2' => (bool) $i['r2_key']]; })->toJson(); $r2Count = $webImgs->whereNotNull('r2_key')->count(); @endphp
Images ({{ $webImgs->count() }}){{ $r2Count ? ' · ' . $r2Count . ' on R2' : '' }}
@foreach($webImgs->values() as $i => $img) @php $thumbSrc = $img['r2_key'] ? \Illuminate\Support\Facades\Storage::disk('r2')->url($img['r2_key']) : $img['filename']; @endphp @endforeach
{{-- Lightbox overlay --}}
@endif @if($p->web_scrape_error)
Scrape Error
{{ $p->web_scrape_error }}
@endif @if($p->web_raw_json) @php $wd = is_array($p->web_raw_json) ? $p->web_raw_json : (json_decode($p->web_raw_json, true) ?? []); @endphp {{-- ── Product Page ── --}} @if(!empty($wd['product_type']) || !empty($wd['subtitle']) || !empty($wd['breadcrumb']) || !empty($wd['compatibility']))
Product Page
@if(!empty($wd['product_type']))
Product Type
{{ ucfirst($wd['product_type']) }}
@endif @if(!empty($wd['subtitle']))
Subtitle
{{ $wd['subtitle'] }}
@endif @if(!empty($wd['breadcrumb']))
Breadcrumb
{{ implode(' › ', $wd['breadcrumb']) }}
@endif @if(!empty($wd['compatibility']))
Compatible With
{{ implode(', ', $wd['compatibility']) }}
@endif @endif {{-- ── Functions ── --}} @if(!empty($wd['description']) || !empty($wd['features']))
Functions
@if(!empty($wd['description']))
Description
{{ $wd['description'] }}
@endif @if(!empty($wd['features']))
Features
    @foreach($wd['features'] as $feat)
  • {{ $feat }}
  • @endforeach
@endif @if(!empty($wd['performance_features']))
Performance Features
@foreach($wd['performance_features'] as $pf)
{{ $pf['name'] ?? '' }} @if(!empty($pf['description'])) — {{ $pf['description'] }}@endif
@endforeach
@endif @endif {{-- ── Utilisation ── --}} @if(!empty($wd['applications']))
Utilisation
    @foreach($wd['applications'] as $app)
  • {{ $app }}
  • @endforeach
@endif {{-- ── Scope of Delivery ── --}} @if(!empty($wd['contains']))
Scope of Delivery
@foreach($wd['contains'] as $item) @php $containsImg = !empty($item['r2_key']) ? \Illuminate\Support\Facades\Storage::disk('r2')->url($item['r2_key']) : ($item['image_url'] ?? null); @endphp
@if($containsImg) {{ $item['name'] ?? '' }} @endif
{{ $item['name'] ?? '' }}
@if(!empty($item['subtitle']))
{{ $item['subtitle'] }}
@endif @if(!empty($item['r2_key']))
✓ R2
@endif
@endforeach
@endif {{-- ── Compare Sets ── --}} @if(!empty($wd['compare_sets']['variants'])) @php $cs = $wd['compare_sets']; $variants = $cs['variants']; $includes = $cs['includes'] ?? []; @endphp
Set Comparison
{{-- Variant header cards --}}
@foreach($variants as $v) @php $isCurrent = ($v['sku'] ?? '') === $p->product_number; @endphp
{{ $v['name'] ?? $v['sku'] }}
@if(!empty($v['price']))
{{ $v['price'] }}
@endif @if($isCurrent)
this product
@endif
@endforeach
{{-- Includes table --}} @if(count($includes)) @foreach($variants as $v) @endforeach @foreach($includes as $item) @foreach($variants as $v) @php $incl = in_array($v['sku'] ?? '', $item['included_in'] ?? []); @endphp @endforeach @endforeach
Item{{ $v['name'] ?? $v['sku'] }}
{{ $item['name'] }}
@if(!empty($item['subtitle']))
{{ $item['subtitle'] }}
@endif
@if($incl) ✓ @else – @endif
@endif
@endif {{-- ── Technical Data ── --}} @if(!empty($wd['technical_data']))
Technical Data
@php $techRows = $wd['technical_data']; $isAssoc = array_keys($techRows) !== range(0, count($techRows) - 1); @endphp @if($isAssoc) {{-- Legacy flat object format --}} @foreach($techRows as $key => $val) @endforeach
{{ $key }} {{ $val }}
@else {{-- New array-of-objects format with section grouping --}} @php $sections = collect($techRows)->groupBy('section'); @endphp @foreach($sections as $sectionName => $rows) @if($sectionName)
{{ $sectionName }}
@endif @foreach($rows as $row) @endforeach
{{ $row['label'] }} {{ $row['value'] }}
@endforeach @endif
@endif {{-- ── Downloads ── --}} @if(!empty($wd['downloads']))
Downloads
@endif {{-- ── Highlights ── --}} @if(!empty($wd['highlights']))
Highlights
@foreach($wd['highlights'] as $hl) @php $hlImg = !empty($hl['r2_key']) ? \Illuminate\Support\Facades\Storage::disk('r2')->url($hl['r2_key']) : ($hl['image_url'] ?? null); @endphp
@if($hlImg) @endif
{{ $hl['title'] }}
@if(!empty($hl['description']))
{{ $hl['description'] }}
@endif
@endforeach
@endif {{-- ── Videos ── --}} @if(!empty($wd['videos']))
Videos
@endif {{-- ── 360° View ── --}} @if(!empty($wd['orbitvu_360_url']))
360° View
🔄 View 360° spin {{ $wd['orbitvu_360_url'] }}
@endif {{-- ── Suitable Accessories ── --}} @if(!empty($wd['suitable_accessories']))
Suitable Accessories
@foreach($wd['suitable_accessories'] as $item) @php $skuVal = is_array($item) ? ($item['sku'] ?? null) : $item; @endphp @if($skuVal) {{ $skuVal }} @endif @endforeach
@endif {{-- ── This Might Also Interest You ── --}} @if(!empty($wd['related_products']))
This Might Also Interest You
@foreach($wd['related_products'] as $item) @php $skuVal = is_array($item) ? ($item['sku'] ?? null) : $item; @endphp @if($skuVal) {{ $skuVal }} @endif @endforeach
@endif @endif
@endif