@php $syncing = $syncingNow; $lastSynced = $this->lastSyncedAt(); $hasMappings = $this->hasMappings(); $hasLocations = $this->hasLocationMappings(); $pagedRows = $this->pagedRows; $totalPages = $this->totalPages; $stats = $this->stats; $totalFiltered = $this->totalFiltered; @endphp {{-- Alpine.js component wraps the entire page so selection state persists across re-renders --}}
{{-- ── Controls bar ──────────────────────────────────────────────── --}}
Stock & Price Sync
@if($syncing) Syncing… @else Push & Sync @endif Starting… @if($syncing) {{ $this->cancelSyncAction }} @endif
{{-- Progress bar --}}
{{-- Phase label row --}}
{{ $syncProgressData['phase'] ?? 'Starting…' }} Phase of
{{-- Bar tracks current phase only --}}
{{-- Product count --}}
of products · location of
{{-- Stale warning --}}
No update for s — job may be stalled — Shopify may be rate-limiting
@if(! $syncing && $lastSynced)
Last synced {{ $lastSynced }}
@elseif(! $syncing)
Never synced — click Push & Sync to start
@endif
{{-- /ctrl-bar --}} {{-- Fallback poll: if the Alpine $wire.call misses, this catches it within 5s --}} @if($syncing)
@endif {{-- ── Warning banners ──────────────────────────────────────────── --}} @if(! $hasMappings)
No SKU mappings configured. Go to SKU Mappings to link Cydekick products to Shopify variants first.
@elseif(! $hasLocations)
No location mappings configured. Go to Location Mappings to map Shopify locations to warehouses first.
@endif {{-- ── Stat cards ─────────────────────────────────────────────────── --}} @if($stats['total'] > 0) @php $mismatches = $stats['mismatches']; @endphp
Total Mappings
{{ $stats['total'] }}
Synced
{{ $stats['synced'] }}
Errors
{{ $stats['error'] }}
Pending
{{ $stats['pending'] }}
Data Mismatches
{{ $mismatches }}
Inventory, prices and costs as last pushed to / fetched from Shopify. Click Push & Sync to push Cydekick data to Shopify and refresh.
@endif {{-- ── Selection Alpine component wraps table + action bar ────────── --}}
{{-- Selection action bar --}}
· Syncs selected SKUs across all locations
{{-- ── Sync status table ──────────────────────────────────────────── --}}
{{-- Loading overlay — wire:loading element must NOT have inline display: style --}}
Loading…
@if($stats['total'] === 0)

No SKU mappings yet

Go to SKU Mappings to link Cydekick products to Shopify variants, then click Push & Sync.

@elseif(empty($pagedRows))

No results for "{{ $skuSearch }}"

Try a different SKU or product name.

@else
{{-- Cydekick --}} {{-- Link --}} {{-- Shopify --}} {{-- Meta --}} @foreach($pagedRows as $row) @php $productUrl = $row['product_id'] ? route('filament.admin.inventory.resources.products.view', ['record' => $row['product_id']]) : null; $priceDiff = $row['shopify_price'] !== null && $row['cydekick_price'] !== null && abs($row['shopify_price'] - $row['cydekick_price']) > 0.001; $costDiff = $row['shopify_cost'] !== null && $row['cydekick_cost'] !== null && abs($row['shopify_cost'] - $row['cydekick_cost']) > 0.001; $qtyDiff = $row['shopify_qty'] !== null && $row['warehouse_qty'] !== null && (int)$row['shopify_qty'] !== (int)$row['warehouse_qty']; $anyDiff = $priceDiff || $costDiff || $qtyDiff; $hasSyncData = $row['shopify_price'] !== null || $row['shopify_qty'] !== null; @endphp {{-- Cydekick SKU — clickable --}} {{-- Link columns --}} {{-- Shopify SKU — clickable (same product) --}} {{-- Data Match: comprehensive mismatch breakdown --}} {{-- Status / meta --}} @endforeach
Cydekick ⇄ Link Shopify
SKU Product Warehouse Qty Cost Channel PriceWarehouse LocationSKU Qty Cost Price Data MatchStatus Last Synced
@if($productUrl) {{ $row['cydekick_sku'] ?? '—' }} @else {{ $row['cydekick_sku'] ?? '—' }} @endif {{ $row['product_name'] ?? '—' }} @if($row['warehouse_qty'] !== null) {{ (int) $row['warehouse_qty'] }} @else — @endif @if($row['cydekick_cost'] !== null) £{{ number_format($row['cydekick_cost'], 2) }} @else — @endif @if($row['cydekick_price'] !== null) £{{ number_format($row['cydekick_price'], 2) }} @else — @endif {{ $row['warehouse'] ?? '—' }} {{ $row['shopify_location'] ?? '—' }} @if($productUrl) {{ $row['shopify_sku'] ?? '—' }} @else {{ $row['shopify_sku'] ?? '—' }} @endif @if($row['shopify_qty'] !== null) {{ $row['shopify_qty'] }} @else — @endif @if($row['shopify_cost'] !== null) £{{ number_format($row['shopify_cost'], 2) }} @else — @endif @if($row['shopify_price'] !== null) £{{ number_format($row['shopify_price'], 2) }} @else — @endif @if(! $hasSyncData) — @elseif(! $anyDiff) ✓ All match @else
@if($qtyDiff)
Qty: {{ (int)$row['warehouse_qty'] }} → {{ $row['shopify_qty'] }}
@endif @if($priceDiff)
Price: £{{ number_format($row['cydekick_price'], 2) }} → £{{ number_format($row['shopify_price'], 2) }}
@endif @if($costDiff)
Cost: £{{ number_format($row['cydekick_cost'], 2) }} → £{{ number_format($row['shopify_cost'], 2) }}
@endif
@endif
@if($row['sync_status'] === 'synced') ✓ Synced @elseif($row['sync_status'] === 'error') ✗ Error @else Pending @endif {{ $row['last_synced_at'] ?? '—' }}
{{-- ── Pagination ─────────────────────────────────────────── --}} @if($totalPages > 1 || $totalFiltered > $perPage)
Showing {{ (($page - 1) * $perPage) + 1 }}–{{ min($page * $perPage, $totalFiltered) }} of {{ $totalFiltered }} rows @if($skuSearch !== '') · filtered @endif
Page {{ $page }} of {{ $totalPages }}
@endif @endif
{{-- ── Legend ─────────────────────────────────────────────────────── --}} @if($stats['total'] > 0)
Cydekick data Shopify data SKU values are clickable — opens the product page. Data Match checks qty, price and cost between Cydekick and Shopify. Red values = mismatch between Cydekick and Shopify.
@endif
{{-- /selectionState Alpine component --}}