{{-- Brand tabs (only show on vehicle grid) --}} @if(!$selectedVehicleId) @php $counts = $this->brandCounts; @endphp
@endif {{-- Progress bar is handled by a separate child Livewire component so its polling never causes this page's navigation state to reset. --}} @if($selectedVehicleId) @livewire('jlr-extraction-progress', ['vehicleId' => $selectedVehicleId], key('jlr-ep-' . $selectedVehicleId)) @endif @php $activeRun = $this->activeRun; @endphp {{-- Breadcrumb (only shown when inside a vehicle) --}} @if($selectedVehicleId)
All Vehicles @foreach($this->breadcrumb as $index => $crumb) / @if($index === count($this->breadcrumb) - 1) {{ $crumb['label'] }} @else {{ $crumb['label'] }} @endif @endforeach
@endif {{-- Vehicle grid --}} @if(!$selectedVehicleId) @php $vehicles = $this->vehicles; @endphp @if(empty($vehicles))
No vehicles found. Go to the JLR connector and run Sync Vehicles first.
@else
@foreach($vehicles as $v) @php // oldest_updated, not newest: touching even one of a vehicle's 1,000+ // nodes shouldn't make the whole vehicle claim it was "just updated" -- // see the comment on vehicles() in BrowseCatalogue.php. $vTitle = ($v['extract_status'] === 'done' ? 'Fully extracted' : 'Extraction incomplete') .($v['oldest_updated'] ? ' · data from '.$v['oldest_updated']->diffForHumans() : ''); @endphp
@if($v['thumb_url']) {{ $v['name'] }} @else
@endif
{{ $v['name'] }}
@if($v['brand'])
{{ $v['brand'] === 'LR' ? 'Land Rover' : 'Jaguar' }}
@endif
@endforeach
@endif @elseif($selectedVehicleId) {{-- Section/node level --}} @php $nodes = $this->currentNodes; $selectedNode = $selectedNodeId ? \Webkul\JlrEpc\Models\JlrNode::find($selectedNodeId) : null; $isLeaf = $selectedNode?->is_leaf; @endphp @if(!$isLeaf) @if(empty($nodes)) {{-- Empty state — offer extraction --}}
@if(!$selectedNodeId)
No sections extracted yet
Run extraction to pull the full catalogue hierarchy for this vehicle. Background jobs fetch each section in turn.
@if($activeRun === null) @endif
@else
No sub-sections extracted yet
Fetch this section to pull its contents from the JLR catalogue.
@endif
@else {{-- Action bar — shown when no run is active, or when the run is stalled --}} @if(!$activeRun || ($activeRun['stalled'] ?? false))
@php $pendingCount = collect($nodes)->where('fetch_status', 'pending')->count(); $fetchingCount = collect($nodes)->where('fetch_status', 'fetching')->count(); $totalFailedCount = $this->failedNodeCount; @endphp {{ count($nodes) }} section{{ count($nodes) !== 1 ? 's' : '' }} @if($fetchingCount > 0) · {{ $fetchingCount }} fetching @endif @if($pendingCount > 0) · {{ $pendingCount }} queued @endif @if($totalFailedCount > 0) · {{ $totalFailedCount }} failed @endif {{-- Retry all failed nodes (vehicle-wide) --}} @if($totalFailedCount > 0) @endif @if(!$selectedNodeId) {{-- Top-level vehicle view: resume (skips done) or full re-extract --}} @else {{-- Inside a section: re-fetch just this section's children --}} @endif
@endif
@foreach($nodes as $node) @php $nodeTitle = $node['fetch_status'].($node['updated_at'] ? ' · updated '.$node['updated_at']->diffForHumans() : ''); @endphp
{{-- Large image area --}}
@if($node['icon_url']) {{-- onerror hides the img and shows the fallback if the file is corrupt/missing --}} @endif {{-- Generic section placeholder (also fallback when img fails to load) --}} {{-- Status dot (absolute, bottom-right of thumb) --}}
{{-- Re-fetch button (shown on hover, for pending/failed) --}} @if(in_array($node['fetch_status'], ['pending', 'failed'])) @endif
{{-- Label strip --}}
@endforeach
@endif @endif @endif {{-- Leaf: parts table (left, scrollable) + diagram (right, sticky) --}} @php $leafNode = $selectedNodeId ? \Webkul\JlrEpc\Models\JlrNode::find($selectedNodeId) : null; @endphp @if($leafNode?->is_leaf) @php $leafData = $this->currentLeafData; @endphp
{{-- Left: scrollable parts table --}}
@php // True from the moment refreshLeafParts()/refreshNode() flips this node to // pending, through the queue worker actually processing FetchPartsJob, until // it lands on done/failed. The Livewire click round-trip itself is instant // (the fetch happens on the queue), so without this the button would look // like nothing happened while the real work was still running in the background. $isRefreshing = in_array($leafData['fetch_status'] ?? null, ['pending', 'fetching'], true); @endphp
Parts ({{ count($leafData['parts']) }}) @if($isRefreshing) Refreshing… @endif @if(empty($leafData['parts'])) @else @endif
@if(empty($leafData['parts']))
No parts data extracted yet.
@else
@foreach($leafData['parts'] as $pi => $part) @php $features = $part['features'] ?? []; $fromVins = is_array($part['cp_from_vin'] ?? null) ? $part['cp_from_vin'] : []; $toVins = is_array($part['cp_to_vin'] ?? null) ? $part['cp_to_vin'] : []; @endphp @endforeach
# Part No. OSI Feature Description Qty Price From VIN To VIN
@if($part['callout_code'] ?? null) {{ $part['callout_code'] }} @else — @endif {{ $part['apn'] }} @if($part['reman_ind'] ?? false) REMAN @endif @php // The stored column is the full JLR responseObject for // this part's own apn -- the actual chain of successor(s) // is its nested `supersession` array, which can hold more // than one entry at once and each entry can nest further. $supersessionEntries = $part['supersession']['supersession'] ?? []; @endphp @if(!empty($supersessionEntries)) @include('jlr-epc::filament.partials.supersession-tree', ['entries' => $supersessionEntries]) @endif @if($part['osi_ind'] ?? null) {{ $part['osi_ind'] }} @endif @if(!empty($features))
@foreach($features as $feat) @if(($feat['ftrFamilyDesc'] ?? null) && ($feat['sysDesc'] ?? null) && $feat['ftrFamilyDesc'] !== $feat['sysDesc']) {{ $feat['ftrFamilyDesc'] }} @endif {{ $feat['sysDesc'] ?? $feat['ftrFamilyDesc'] ?? '' }} @endforeach
@endif
{{ $part['description'] ?? '' }} {{ $part['quantity'] ?? '' }} @if($part['unit_price'] ?? null) {{ $part['currency'] ?? '' }} {{ number_format((float)$part['unit_price'], 2) }} @else — @endif @foreach($fromVins as $vin) {{ is_array($vin) ? ($vin['vin'] ?? json_encode($vin)) : $vin }} @endforeach @foreach($toVins as $vin) {{ is_array($vin) ? ($vin['vin'] ?? json_encode($vin)) : $vin }} @endforeach
@endif
{{-- Right: sticky diagram --}} {{-- wire:ignore on the inner div prevents Livewire morphdom from wiping the injected SVG --}}
@if($leafData['svg_content']) {{-- SVG embedded server-side — no cross-origin fetch needed --}}
{!! $leafData['svg_content'] !!}
@elseif($leafData['diagram_url'])
{{-- Fallback: non-SVG diagram (PNG) --}} Exploded diagram
@else
No diagram downloaded
@endif
@endif