{{-- Unified CRM inbox — a polished Chatwoot/Intercom-style omnichannel shell: channel rail + conversation list + thread + contact panel. The Livewire logic (selection, send, notes, assign, status, tabs) is unchanged; this view only elevates the presentation. Alpine owns the client-only affordances (collapse state persisted in localStorage, mobile pane switching, emoji / quick-reply popovers); every server interaction still flows through the same wire: calls. --}}
{{-- Page header lives in the topbar (see partials/topbar) so the shell can claim the full viewport height. --}}
{{-- ========================================================== 1 · Channel rail (vertical, brand icons) ========================================================== --}}
@php $allUnread = array_sum($channelUnread); @endphp
@foreach ($channels as $type => $meta) @php $u = $channelUnread[$type] ?? 0; @endphp @endforeach
{{-- Re-open strip (desktop) — visible only when the list is collapsed. --}}
{{-- ========================================================== 2 · Conversation list ========================================================== --}}
{{ __('Conversations') }}
@foreach (['open' => __('Open'), 'pending' => __('Pending'), 'resolved' => __('Resolved')] as $key => $label) @endforeach
{{-- Scroll-event based infinite load (Linear/Chatwoot pattern). Triggers loadMore() only when the user actually scrolls near the bottom. Uses $wire.hasMore (Livewire 3 reactive property) + a local `busy` flag to prevent concurrent calls. No IntersectionObserver → no re-trigger loop. --}}
@forelse ($conversations as $c) @php $ctype = $c->channel?->type; $cmeta = $channels[$ctype] ?? null; @endphp @empty

{{ __('No conversations in this view.') }}

@endforelse @if ($hasMore) {{-- Spinner at the bottom of the scroll container (visual only — the scroll event above drives the actual loadMore call). --}}
@endif
{{-- ========================================================== 3 · Thread ========================================================== --}}
@if ($active) @php $atype = $active->channel?->type; @endphp {{-- Header --}}

{{ $active->contact?->name ?? __('Unknown') }}

{{ __('via :channel', ['channel' => __($channels[$atype]['label'] ?? ($atype ?? ''))]) }} @if ($active->supportTicket) · {{ __('Ticket #:id', ['id' => $active->supportTicket->id]) }} @endif

{{-- Snooze (design affordance) --}} {{-- Assign menu --}}
@foreach ($staff as $s) @endforeach
{{-- AI agent + bot state (in-thread controls) --}} @php $botAgent = $active->aiAgent; @endphp @if ($active->is_bot && $active->is_bot_paused) {{ __('Bot paused') }} @elseif ($active->is_bot) {{ $botAgent?->name ?? __('Bot on') }} @endif {{-- Assign AI agent --}}
@forelse ($aiAgents as $agent) @empty

{{ __('No AI agents configured.') }}

@endforelse
{{-- Pause / resume bot --}} @if ($active->is_bot) @endif {{-- Status --}} @if ($active->status !== 'resolved') @else @endif {{-- More actions. The destructive item lives behind an overflow menu on purpose: beside Resolve it would be one mis-click from erasing a customer's history. --}} @if ($canSend)
@endif {{-- Toggle contact panel (desktop) --}} {{-- Contact info (mobile) --}}
{{-- Messages — no wire:poll here: Livewire allows a single poll per component (the root has it). A second poll stacks on every morph and floods /livewire/update. The root poll refreshes this pane too. --}}
@php $lastDate = null; $lastDir = null; @endphp @forelse ($messages as $msg) @php $msgDate = $msg->created_at?->toDateString(); @endphp @if ($msgDate !== $lastDate)
@if ($msg->created_at?->isToday()) {{ __('Today') }} @elseif ($msg->created_at?->isYesterday()) {{ __('Yesterday') }} @else {{ rf_date($msg->created_at) }} @endif
@php $lastDate = $msgDate; $lastDir = null; @endphp @endif @if ($msg->is_note)
{{ __('Internal note') }}
{{ $msg->content }}
{{ rf_date($msg->created_at, 'g:i A') }}
@php $lastDir = null; @endphp @else @php $grouped = $lastDir === $msg->direction; @endphp
@if ($msg->media_url) @if ($msg->type === 'image') {{ __('Image attachment') }} @elseif ($msg->type === 'video') @elseif ($msg->type === 'audio') @else {{ __('Attachment') }} @endif @endif @if (filled($msg->content))
{{ $msg->content }}
@endif
{{ rf_date($msg->created_at, 'g:i A') }} @if ($msg->direction === 'outbound') @endif
@php $lastDir = $msg->direction; @endphp @endif @empty

{{ __('No messages yet') }}

{{ __('Say hello — your first reply starts the conversation.') }}

@endforelse
{{-- Composer. Hidden entirely without crm.send: a viewer who may read the inbox but not write into it is never shown a Send button that the server would refuse. --}} @if (! $canSend)
{{ __('You have read-only access to this inbox.') }}
@elseif ($active->status !== 'resolved')
{{-- Reply / Note toggle --}}
@php $waWindowClosed = ! $asNote && ($active->channel?->type === 'whatsapp') && ! ($windowStatus['in_window'] ?? true); @endphp @if ($waWindowClosed) {{-- WhatsApp 24h window is closed: template picker replaces the free-form textarea. --}} @else
{{-- Emoji picker (anchored to bar left edge, never clips viewport) --}}
{{-- Emoji toggle --}} {{-- Quick replies --}}
@forelse ($quickReplies as $qr) @empty

{{ __('No quick replies yet.') }}

@endforelse
{{-- Attach (design affordance) --}} {{-- AI assist (design affordance) --}} ⏎
@error('reply')

{{ $message }}

@enderror @endif
@else
{{ __('This conversation is resolved.') }}
@endif @else {{-- Empty state --}}

{{ __('Select a conversation') }}

{{ __('Pick a conversation from the list to read the thread and reply across any channel.') }}

@endif
{{-- ========================================================== 4 · Contact / context panel ========================================================== --}}
@if ($active) @php $contact = $active->contact; @endphp {{-- Back to thread (mobile) --}}
{{ __('Contact') }}
{{-- Identity card --}}
{{ $contact?->name ?? __('Unknown') }}
{{ $contact?->email ?? __($channels[$active->channel?->type]['label'] ?? '') }}
{{-- Labels — tagging a thread here fires the label_added automation event (a human UI action), so a flow can react to it. --}}
{{ __('Labels') }} @if ($canSend && $allLabels->isNotEmpty()) @php $appliedLabelIds = $active->labels->pluck('id')->all(); @endphp
@forelse ($allLabels->whereNotIn('id', $appliedLabelIds) as $l) @empty

{{ __('All labels applied.') }}

@endforelse
@endif
@forelse ($active->labels as $label) {{ $label->name }} @if ($canSend) @endif @empty {{ __('No labels yet.') }} @endforelse
{{-- Contact details --}}
{{ __('Contact details') }}
{{ $contact?->name ?? '—' }}
{{ $contact?->email ?? '—' }}
{{ $contact?->phone ?? '—' }}
{{ __($channels[$active->channel?->type]['label'] ?? '—') }}
@if ($contact?->user_id)
{{ __('Client #:id', ['id' => $contact->user_id]) }}
@endif
{{-- Opt-in --}}
{{ __('Opt-in') }}
@if ($contact?->phone) {{ __('WhatsApp') }} @endif @if ($contact?->email) {{ __('Email') }} @endif @unless ($contact?->phone || $contact?->email) {{ __('No opt-in recorded.') }} @endunless
{{-- Internal notes --}}
{{ __('Internal notes') }}
@php $notes = $messages->where('is_note', true); @endphp @forelse ($notes->take(5) as $note)
{{ \Illuminate\Support\Str::limit($note->content, 160) }}
{{ $note->created_at?->diffForHumans() }}
@empty {{ __('No internal notes yet.') }} @endforelse
@if ($active->supportTicket)
{{ __('Support ticket — replies post to the ticket thread.') }}
@endif
@else

{{ __('Contact details appear here.') }}

@endif