@php $allChannels = $this->getAllChannels(); $isLanding = $this->channelId === ''; @endphp @if ($isLanding) {{-- ═══════════════════════════════════════════════════════════════ --}} {{-- LANDING: channel card overview --}} {{-- ═══════════════════════════════════════════════════════════════ --}} @php $channelStats = $this->getChannelStats(); $global = $this->getGlobalStats(); $palette = ['#4f46e5','#0891b2','#059669','#d97706','#dc2626','#7c3aed','#be185d','#065f46']; @endphp
{{-- Global summary bar --}}
@if ($global['listed'] > 0) {{ number_format($global['listed']) }} live listings @endif @if ($global['errors'] > 0) · {{ number_format($global['errors']) }} errors @endif
@foreach ($allChannels as $index => $channel) @php $connected = $channel->isConnected(); $stats = $channelStats[$channel->id] ?? ['mapped' => 0, 'listed' => 0, 'errors' => 0]; $bgColor = $palette[$index % count($palette)]; $iconSlug = file_exists(public_path("vendor/channels/platforms/small/{$channel->platform->value}.svg")) ? $channel->platform->value : null; $initial = mb_strtoupper(mb_substr($channel->name, 0, 1)); @endphp
{{-- Card header --}}
@if ($iconSlug) {{ $channel->platform->getLabel() }} @else {{ $initial }} @endif
{{ $channel->name }}
@if ($channel->shop_domain)
{{ $channel->shop_domain }}
@else
{{ $channel->platform->getLabel() }}
@endif
{{-- Connection status --}}
{{ $connected ? 'Connected' : 'Not connected' }}
{{-- Stats or not-connected message --}} @if ($connected)
{{ number_format($stats['mapped']) }}
Linked
{{ number_format($stats['listed']) }}
Listed
{{ number_format($stats['errors']) }}
Errors
@else

Connect this channel to start managing listings.

@endif {{-- Action button --}} @if ($connected) @else Connect Channel @endif
@endforeach
@else {{-- ═══════════════════════════════════════════════════════════════ --}} {{-- PRODUCT TABLE: per-channel listing management --}} {{-- ═══════════════════════════════════════════════════════════════ --}} @php $activeChannel = $this->getActiveChannel(); $statusCounts = $this->getStatusCounts(); $data = $this->getProductRows(); $rows = $data['rows']; $total = $data['total']; $totalPages = max(1, (int) ceil($total / $this->perPage)); @endphp
{{-- Back + channel name + action buttons --}}
› {{ $activeChannel?->name ?? 'Listings' }}
{{-- Toolbar --}}
@if ($this->search !== '')
Searching all statuses
@else
@foreach ([ 'listed' => 'Listed (' . number_format($statusCounts['listed']) . ')', 'needs_update' => 'Needs Update (' . number_format($statusCounts['needs_update']) . ')', 'not_synced' => 'Pending Sync (' . number_format($statusCounts['not_synced']) . ')', 'not_mapped' => 'Not Listed (' . number_format($statusCounts['not_mapped']) . ')', 'error' => 'Errors (' . number_format($statusCounts['error']) . ')', ] as $value => $label) @endforeach
@endif {{-- Quick action: push/update all in current filter without selecting --}} @if (($statusCounts['pending_sync'] ?? 0) > 0) @endif @if ($this->statusFilter === 'not_mapped' && ($statusCounts['not_mapped'] ?? 0) > 0) @elseif ($this->statusFilter === 'not_synced' && ($statusCounts['not_synced'] ?? 0) > 0) @elseif ($this->statusFilter === 'needs_update' && ($statusCounts['needs_update'] ?? 0) > 0) @endif
{{-- Table --}} @php $pageProductIds = collect($rows)->pluck('product_id')->map(fn($id) => (string) $id)->toArray(); $selectedStrings = array_map('strval', $this->selectedIds); $allPageSelected = ! empty($pageProductIds) && empty(array_diff($pageProductIds, $selectedStrings)); $selectedCount = $this->allFilteredSelected ? $total : count($this->selectedIds); @endphp
@if (count($this->selectedIds) > 0)
@if ($this->allFilteredSelected) All {{ number_format($total) }} {{ $this->statusFilter !== 'all' ? str_replace('_',' ',$this->statusFilter) . ' ' : '' }}products selected. @elseif ($allPageSelected && count($this->selectedIds) >= count($rows) && $total > count($rows)) {{ count($this->selectedIds) }} on this page selected. @else {{ count($this->selectedIds) }} product{{ count($this->selectedIds) !== 1 ? 's' : '' }} selected. @endif
@endif {{-- ══ BULK ACTION BAR (inline, visible when items are selected) ══ --}} @if ($selectedCount > 0)
{{ number_format($selectedCount) }} {{ $selectedCount === 1 ? 'product' : 'products' }} selected
@if ($this->search !== '') {{-- Searching across all statuses: show both Create and Update --}} @elseif ($this->statusFilter === 'not_mapped') {{-- Not Listed filter: Create only --}} @elseif ($this->statusFilter === 'not_synced') {{-- Pending Sync filter: Push only --}} @elseif (in_array($this->statusFilter, ['listed', 'needs_update'])) {{-- Listed / Needs Update filter: Update only --}} @elseif ($this->statusFilter === 'error') {{-- Errors filter: Retry only --}} @else {{-- Fallback (should not be reachable without All tab) --}} @endif
@endif @if (empty($rows))

No products found

@if ($this->search !== '') No products match "{{ $this->search }}" @elseif ($this->statusFilter !== 'all') No products with status "{{ str_replace('_', ' ', $this->statusFilter) }}" @else No active SKU mappings for this channel @endif

@else @foreach ($rows as $row) @php $isMapped = $row['listing_status'] !== 'not_mapped'; $isLinked = ! empty($row['shopify_variant_id']); $isListed = $row['listing_status'] === 'listed'; $isError = $row['listing_status'] === 'error'; $needsUpd = $row['listing_status'] === 'needs_update'; $shopifyHandle = $row['attributes']['handle'] ?? \Illuminate\Support\Str::slug($row['shopify_product_title'] ?? $row['product_name']); $storefrontUrl = ($isLinked && $shopifyHandle && $activeChannel?->shop_domain) ? 'https://' . $activeChannel->shop_domain . '/products/' . $shopifyHandle : null; @endphp {{-- Thumbnail --}} {{-- Product name --}} {{-- Shopify SKU (linked) or Not Linked badge --}} {{-- Price --}} {{-- Status badge --}} {{-- Last synced --}} {{-- Actions --}} @endforeach
Product Shopify SKU Price Status Last Synced
@if ($row['thumbnail']) @else
@endif
{{ $row['attributes']['title'] ?? $row['product_name'] }} @if (!empty($row['attributes']['title']) && $row['attributes']['title'] !== $row['product_name'])
{{ $row['product_name'] }} @endif @if ($row['product_sku'])
{{ $row['product_sku'] }} @endif
@if ($storefrontUrl) {{ $row['shopify_sku'] }} @elseif ($row['shopify_sku']) {{ $row['shopify_sku'] }} @else — @endif £{{ $row['price'] }} {{ match($row['listing_status']) { 'listed' => 'Listed', 'needs_update'=> 'Needs Update', 'error' => 'Error', 'not_mapped' => 'Not Listed', 'not_listed' => 'Pending Sync', default => 'Pending Sync', } }} @if ($isError && $row['last_error'])
{{ \Illuminate\Support\Str::limit($row['last_error'], 45) }} @endif
{{ $row['last_synced_at'] ?? '—' }}
@if (! $isMapped) {{-- Not listed — Link to existing Shopify variant --}} @else {{-- Mapped — Edit + optional Retry (errors only) + Unlink --}} @if ($isError) @endif @endif
@if ($totalPages > 1)
Showing {{ ($this->page - 1) * $this->perPage + 1 }}–{{ min($this->page * $this->perPage, $total) }} of {{ number_format($total) }}
@endif @endif
@include('channels::filament.partials.listing-modal') {{-- ═══════════════ LINK MODAL ══════════════════════════════════════ --}} @if ($linkModalProductId !== null)
Link to Shopify Variant
{{ $linkModalProductName }}
@if (mb_strlen($linkSearch) < 2)
Type at least 2 characters to search Shopify variants.
@elseif (empty($linkResults))
No Shopify variants found matching "{{ $linkSearch }}".
@else @foreach ($linkResults as $result)
{{ $result['shopify_sku'] }}
{{ $result['product_title'] }} @if ($result['variant_title'] && $result['variant_title'] !== 'Default Title') — {{ $result['variant_title'] }} @endif
@if ($result['already_mapped'])
Already linked to another product
@endif
@endforeach @endif
@endif @endif {{-- Cancel Sync confirmation modal --}}
{{-- Backdrop --}}
{{-- Flex centering layer (separate from x-show element so display:flex always applies) --}}
{{-- Panel --}}
{{-- Icon + title --}}

Stop sync jobs?

This will remove all queued sync jobs and reset those products back to Needs Update.

Jobs already being processed by the queue worker cannot be stopped and will complete normally.

{{-- Actions --}}
{{-- /centering layer --}}
{{-- ── Orphaned Collections Confirmation Modal ──────────────────────────── --}} @if ($showMarkAllModal)
{{-- Backdrop --}}
{{-- Panel --}}
{{-- Header --}}

Mark All for Update

This will mark all {{ number_format($statusCounts['listed'] ?? 0) }} listed {{ str('product')->plural($statusCounts['listed'] ?? 0) }} as Needs Update so they are pushed to Shopify on the next sync.

{{-- Warning note --}}

Switch to the Needs Update tab after confirming and use Sync Selected or run a bulk sync to push them all to Shopify.

{{-- Actions --}}
@endif {{-- Bulk sync confirmation modal --}} @if ($showBulkSyncModal)

@if ($this->statusFilter === 'needs_update') Update All {{ number_format($statusCounts['needs_update'] ?? 0) }} Products? @else Push All {{ number_format($statusCounts['not_synced'] ?? 0) }} Pending Products? @endif

@if ($this->statusFilter === 'needs_update') Queues every product marked Needs Update for a Shopify sync. Jobs run in the background — watch the console for progress. @else Queues all unlisted and pending products for Shopify sync. Jobs run in the background — watch the console for progress. @endif

@endif @if ($showOrphanModal)
{{-- Backdrop --}}
{{-- Panel --}}
{{-- Header --}}

Remove Orphaned Collections

{{ count($orphanList) }} Shopify {{ str('collection')->plural(count($orphanList)) }} found that no longer match the Cydekick category tree. This will permanently delete them from Shopify.

{{-- Collection list --}}
@foreach ($orphanList as $orphan)
{{ $orphan['title'] }} /collections/{{ $orphan['handle'] }}
@endforeach
{{-- Warning note --}}

Shopify URL redirects are not created automatically. If any of these collections are live and indexed by Google, set up manual redirects in Shopify before deleting.

{{-- Actions --}}
@endif