stage, ['fetching', 'importing'])) wire:poll.3000ms="pollStatus" @endif> {{-- ── STAGE: IDLE ──────────────────────────────────────── --}} @if($this->stage === 'idle')

Import Products from Shopify

Import all products immediately, or fetch first to preview and choose which ones to import.

Import All runs immediately in the background — best for large catalogues.
Fetch & Select previews the first 500 products.

Search for specific products

Searches Shopify by SKU — returns exact matches.

{{-- ── STAGE: FETCHING (background job running) ─────────── --}} @elseif($this->stage === 'fetching') @php $fetched = $this->importData['fetched_count'] ?? 0; @endphp

Fetching products from Shopify…

@if($fetched > 0)

{{ number_format($fetched) }}

products fetched so far

@else

Connecting to Shopify in the background. This page will update automatically.

@endif
{{-- ── STAGE: PREVIEW / SELECT ───────────────────────────── --}} @elseif($this->stage === 'preview') @php $products = $this->filteredPreview; $meta = $this->previewMeta; $totalPages = $meta['total_pages'] ?? 1; $totalProducts = $meta['total_products'] ?? count($this->previewProducts); $currentPage = $this->previewPage; $searchedFor = $this->importData['search_query'] ?? null; @endphp

@if($searchedFor) {{ $totalProducts }} product{{ $totalProducts !== 1 ? 's' : '' }} matching "{{ $searchedFor }}" @else {{ number_format($totalProducts) }} product{{ $totalProducts !== 1 ? 's' : '' }} fetched from Shopify @endif

{{ count($this->selectedVariantIds) }} variant{{ count($this->selectedVariantIds) !== 1 ? 's' : '' }} selected across all pages

{{-- Search + select all + pagination --}}
@if($totalPages > 1)
Page {{ $currentPage + 1 }} of {{ $totalPages }}
@endif
@forelse($products as $product) @php $thumb = $product['images'][0]['src'] ?? null; $variantCount = count($product['variants'] ?? []); @endphp @foreach($product['variants'] ?? [] as $variant) @php $vid = (string) $variant['id']; $checked = in_array($vid, $this->selectedVariantIds, true); $varTitle = ($variant['title'] ?? 'Default Title') !== 'Default Title' ? $product['title'] . ' — ' . $variant['title'] : $product['title']; $existing = \Webkul\Product\Models\Product::where('shopify_variant_id', $vid)->first() ?? (($variant['sku'] ?? '') ? \Webkul\Product\Models\Product::where('sku', $variant['sku'])->whereNull('shopify_variant_id')->first() : null); $statusLabel = $existing ? 'Update' : 'New'; $statusColor = $existing ? '#3b82f6' : '#10b981'; @endphp @endforeach @empty @endforelse
Product / Variant SKU Price Status
@if($thumb) @else
@endif
{{ $varTitle }}
@if($variantCount > 1)
{{ $variantCount }} variants
@endif
{{ $variant['sku'] ?: '—' }} £{{ number_format((float)($variant['price'] ?? 0), 2) }} {{ $statusLabel }}
No products match your search.
{{-- ── STAGE: IMPORTING ─────────────────────────────────── --}} @elseif($this->stage === 'importing' || $this->isImporting()) @php $progress = $this->importProgress; $hasStarted = !empty($progress) && ($progress['total'] ?? 0) > 0; $elapsed = $this->importStartedAt ? \Carbon\Carbon::parse($this->importStartedAt)->diffForHumans(null, true) : null; @endphp

Importing products…

@if($hasStarted)

Running for {{ $elapsed }} — updating every 3 seconds

@foreach([ ['label' => 'Processed', 'value' => $progress['total'], 'color' => '#6b7280'], ['label' => 'Created', 'value' => $progress['created'], 'color' => '#10b981'], ['label' => 'Updated', 'value' => $progress['updated'], 'color' => '#3b82f6'], ['label' => 'Errors', 'value' => $progress['errors'], 'color' => '#ef4444'], ] as $card)
{{ $card['label'] }}
{{ number_format($card['value']) }}
@endforeach
@if(($progress['errors'] ?? 0) > 0)

{{ $progress['errors'] }} error{{ $progress['errors'] !== 1 ? 's' : '' }} so far — full details shown when complete.

@endif @else

Connecting to Shopify and downloading products. @if($elapsed) Running for {{ $elapsed }}. @endif Counts will appear once the first batch is processed.

@endif
Live — page auto-refreshes every 3 seconds

You can safely navigate away — the import runs in the background and will finish on its own. Come back to this page to see the results.

{{ $this->cancelImportAction }}
{{-- ── STAGE: DONE ──────────────────────────────────────── --}} @elseif($this->stage === 'done' || ($this->importData['stage'] ?? '') === 'done')

@if($this->importData['cancelled'] ?? false) Import Cancelled @else Import Complete @endif

@if($this->importData['finished_at'])

Finished {{ \Carbon\Carbon::parse($this->importData['finished_at'])->diffForHumans() }}

@endif
@if($this->importData['error'])
Error: {{ $this->importData['error'] }}
@endif
@foreach([ ['label' => 'Total', 'value' => $this->stats['total'], 'color' => '#6b7280'], ['label' => 'Created', 'value' => $this->stats['created'], 'color' => '#10b981'], ['label' => 'Updated', 'value' => $this->stats['updated'], 'color' => '#3b82f6'], ['label' => 'Errors', 'value' => $this->stats['errors'], 'color' => '#ef4444'], ] as $card)
{{ $card['label'] }}
{{ $card['value'] }}
@endforeach
@if($this->stats['total'] > 500)

Large import — showing errors only. All {{ number_format($this->stats['total']) }} products are saved to the database.

@endif
@forelse($this->filteredResults as $row) @php $statusColor = match($row['status']) { 'created' => '#10b981', 'updated' => '#3b82f6', 'error' => '#ef4444', default => '#9ca3af' }; $thumb = null; if (!empty($row['images'][0])) { $filename = $row['images'][0]; if (config('filesystems.disks.r2')) { try { $settings = app(\App\Settings\R2StorageSettings::class); $base = rtrim($settings->public_url ?? '', '/'); if ($base) $thumb = $base . '/' . ltrim($filename, '/'); } catch (\Throwable) {} } if (!$thumb) { try { $thumb = \Illuminate\Support\Facades\Storage::disk('public')->url($filename); } catch (\Throwable) {} } } @endphp @empty @endforelse
Product SKU Status Message
@if($thumb) @else
@endif
{{ $row['name'] }} {{ $row['sku'] }} {{ ucfirst($row['status']) }} {{ $row['message'] ?? '' }}
No results.
@endif