{{-- ══════════════════════════════════════════════════════════════ Channel Bar – Call · Email · SMS ══════════════════════════════════════════════════════════════ --}}
{{-- ── Call ── --}}
{{-- ── Call Institute ── --}} @if($model instanceof \App\Models\Client\Client)
@endif {{-- ── Email ── --}}
Email @if($emailValidity) {{ $emailValidity->validity->label() }} @endif
@if($emailValidity?->email)
{{ $emailValidity->email }}
@elseif($emailValidity?->reason)
{{ $emailValidity->reason }}
@else
@endif
@can('communications.send.email') @else @endcan
{{-- ── SMS ── --}}
SMS @if($smsValidity) {{ $smsValidity->validity->label() }} @endif
@if($smsValidity?->phone)
{{ $smsValidity->phone }}
@elseif($smsValidity?->reason)
{{ $smsValidity->reason }}
@else
@endif
@can('communications.send.sms') @else @endcan
{{-- /row --}} {{-- ══════════════════════════════════════════════════════════════ Inline Compose Panel ══════════════════════════════════════════════════════════════ --}} {{-- ══════════════════════════════════════════════════════════════ Activity Feed ══════════════════════════════════════════════════════════════ --}}
Activity
@if($activities->isEmpty())
No activity yet.
@else
@foreach($activities as $activity) @if($activity['kind'] === 'call') @php $comm = $activity['data']; @endphp
{{ $comm->preview ?? 'Voice call' }}
{{ $comm->started_at?->diffForHumans() }} @if($comm->agent)  · {{ $comm->agent->name_first }} @endif @if($comm->recordings->isNotEmpty())  ·  Recorded @endif
$comm->status === 'completed', 'text-bg-danger' => in_array($comm->status, ['failed', 'missed']), 'text-bg-warning' => $comm->status === 'voicemail', 'text-bg-secondary' => !in_array($comm->status, ['completed', 'failed', 'missed', 'voicemail']), ])>{{ ucfirst(str_replace('_', ' ', $comm->status)) }}
{{-- Waveform player for completed recordings --}} @foreach($comm->recordings->where('status', 'completed') as $rec)
Could not load audio.
00:00 / {{ $rec->duration_label }}
{{-- ── Synapse Panel ── --}} @php $st = $rec->synapseTranscript; $cs = $rec->callScore; $dp = $rec->dataProtectionAudit; $hasT = $st && $st->status === 'completed'; $hasS = $cs && $cs->status === 'completed' && $cs->is_initial_call === true; $hasDp = $dp && $dp->status === 'completed'; $canExpand = $hasT || $hasS || $hasDp; $autoOpen = ($hasS && ($cs->auto_fail || !$cs->passed)) || ($hasDp && $dp->result === 'FAIL'); $secLabels = [ 'introduction_identity_recording' => 'Intro & ID', 'vulnerability_wellbeing' => 'Wellbeing', 'purpose_of_call' => 'Purpose', 'client_options_funding' => 'Options & Funding', 'private_funding_fees' => 'Fees', 'no_guarantee_scope_waiver' => 'Scope & Waiver', 'cancellation_pressure' => 'Cancellation', 'aml_identity_verification' => 'AML / ID', 'information_gathering' => 'Info & Statements', 'payment_types_next_steps' => 'Payments & Next Steps', ]; @endphp
{{-- Header bar --}}
{{-- Left: status / badges --}}
@if(!$st) No transcript yet @elseif($st->status === 'processing') Transcribing… @elseif($st->status === 'failed') Transcription failed @elseif(!$cs) Transcript ready @elseif($cs->status === 'processing') Analysing… @elseif($cs->status === 'failed') Analysis failed @elseif($cs->status === 'completed' && $cs->is_initial_call === null) Not Applicable No scoreable content @elseif($cs->status === 'completed' && $cs->is_initial_call === false) Follow-up @if($dp?->status === 'processing') Checking data protection… @elseif($dp?->status === 'failed') Data-protection analysis failed @elseif($hasDp) DP {{ $dp->result }} {{ $dp->number_of_checks_confirmed }} checks confirmed @else Compliance scoring not applicable @endif @elseif($cs->status === 'completed' && $cs->is_initial_call === true) Initial Call @if($cs->auto_fail) AUTO FAIL @elseif($cs->passed) PASS @else FAIL @endif {{ $cs->risk_rating_label }} {{ $cs->total_score }}/100 @endif
{{-- Right: icon action buttons + expand toggle --}}
{{-- Re-fetch audio --}} @if($hasT) @endif @if($canExpand) @endif
{{-- Expandable body --}} @if($canExpand)
@if($hasDp)
Data-protection check: {{ $dp->result }} · {{ $dp->number_of_checks_confirmed }} confirmed · {{ $dp->completed_at_outset ? 'completed at outset' : 'not completed at outset' }}
@if($dp->reasoning)
{{ $dp->reasoning }}
@endif @if(!empty($dp->evidence))
Evidence: {{ implode(' · ', $dp->evidence) }}
@endif
@endif {{-- Score breakdown --}} @if($hasS) {{-- Auto-fail triggers --}} @if($cs->auto_fail && !empty($cs->auto_fail_triggers))
Automatic fail triggers
@foreach($cs->auto_fail_triggers as $trigger) @php $trigger = is_array($trigger) ? $trigger : ['trigger' => (string) $trigger]; @endphp
{{ $trigger['trigger'] ?? 'Automatic fail' }}
@if(!empty($trigger['reason']))
{{ $trigger['reason'] }}
@endif @if(!empty($trigger['evidence']))
Evidence: {{ $trigger['evidence'] }}
@endif @if(!empty($trigger['related_wording_considered']))
Related wording considered: {{ $trigger['related_wording_considered'] }}
@endif
@endforeach
@endif @php $sectionScores = $cs->section_scores; if (is_string($sectionScores)) { $decoded = json_decode($sectionScores, true); $sectionScores = is_array($decoded) ? $decoded : []; } $sectionScores = is_array($sectionScores) ? $sectionScores : []; @endphp {{-- Coloured section score cards --}} @if(!empty($sectionScores))
@foreach($cs->section_scores as $key => $sec) @php $score = (int) ($sec['score'] ?? 0); $max = max((int) ($sec['max'] ?? 0), 1); $percentage = round(($score / $max) * 100); $colour = match (true) { $percentage >= 80 => [ 'background' => '#ecfdf5', 'border' => '#a7f3d0', 'text' => '#047857', 'bar' => '#10b981', 'icon' => 'mdi-check-circle', ], $percentage >= 50 => [ 'background' => '#fffbeb', 'border' => '#fde68a', 'text' => '#b45309', 'bar' => '#f59e0b', 'icon' => 'mdi-alert-circle', ], default => [ 'background' => '#fef2f2', 'border' => '#fecaca', 'text' => '#b91c1c', 'bar' => '#ef4444', 'icon' => 'mdi-close-circle', ], }; @endphp
{{ $secLabels[$key] ?? \Illuminate\Support\Str::headline($key) }}
{{ $score }}/{{ $max }}
{{ $percentage }}% @if($percentage >= 80) Good @elseif($percentage >= 50) Partial @else Missing @endif
@if(!empty($sec['notes']))
{{ $sec['notes'] }}
@endif
@endforeach
@endif {{-- Compliance summary --}} @if($cs->summary)
Compliance summary
{{ $cs->summary }}
@endif @if($hasT)
@endif @endif {{-- AI Summary + Sentiment (AssemblyAI) --}} @if($hasT && ($st->ai_summary || $st->sentiment_data))
@if($st->ai_summary)
Summary  {{ $st->ai_summary }}
@endif @if($st->sentiment_data) @php $sb = $st->sentiment_breakdown; @endphp
Sentiment
{{ $sb['positive'] ?? 0 }}% {{ $sb['neutral'] ?? 0 }}% {{ $sb['negative'] ?? 0 }}%
@endif
@endif {{-- Transcript --}} @if($hasT)
{{ $st->formatted_transcript }}
@endif
@endif
{{-- /synapse panel --}} @endforeach @if($comm->call?->notes)
{{ $comm->call->notes }}
@endif
@else @php $msg = $activity['data']; @endphp
{{ $msg->subject ?: Str::limit($msg->body_text, 80) }}
@if($msg->channel->value === 'email') {{ $msg->recipient_email ?? '—' }} @else {{ $msg->recipient_phone ?? '—' }} @endif @if($msg->template)  · via {{ $msg->template->name }} @endif @if($msg->creator)  · {{ $msg->creator->name }} @elseif($msg->origin_type)  · {{ $msg->origin_type->label() }} @endif  · {{ $msg->created_at?->diffForHumans() }}
@if($msg->failure_reason)
{{ $msg->failure_reason }}
@endif
{{ $msg->status->label() }}
@endif @endforeach @if($activities->hasPages())
{{ $activities->links() }}
@endif
@endif
{{-- /activity feed --}}
{{-- ══════════════════════════════════════════════════════════════ Message Detail Offcanvas ══════════════════════════════════════════════════════════════ --}}
@if($selectedMessage)
{{ $selectedMessage->subject ?: ucfirst($selectedMessage->channel->value) . ' Message' }}
{{ $selectedMessage->status->label() }}
@else
Message Detail
@endif
@if($selectedMessage) @php $m = $selectedMessage; @endphp {{-- ── Tabs ── --}}
{{-- ── Tab content ── --}}
{{-- Message tab --}}
{{-- Metadata --}}
From
@if($m->sender_display && $m->sender_value && $m->sender_display !== $m->sender_value) {{ $m->sender_display }} <{{ $m->sender_value }}> @else {{ $m->sender_display ?: $m->sender_value ?: '—' }} @endif
To
@if($m->recipient_name && ($m->recipient_email ?: $m->recipient_phone)) {{ $m->recipient_name }} <{{ $m->recipient_email ?: $m->recipient_phone }}> @else {{ $m->recipient_name ?: ($m->recipient_email ?: ($m->recipient_phone ?: '—')) }} @endif
@if($m->reply_to)
Reply-to
{{ $m->reply_to }}
@endif @if($m->template)
Template
{{ $m->template->name }}
@endif
Sent by
@if($m->creator){{ $m->creator->name }} @elseif($m->origin_type){{ $m->origin_type->label() }} @else—@endif
@php $stamps = array_filter([ 'Queued' => $m->queued_at, 'Sent' => $m->sent_at, 'Delivered' => $m->delivered_at, 'Opened' => $m->opened_at, 'Failed' => $m->failed_at, ]); @endphp @foreach($stamps as $label => $ts)
{{ $label }}
{{ $ts->format('d M Y, H:i') }} ({{ $ts->diffForHumans() }})
@endforeach @if($m->failure_reason)
Reason
{{ $m->failure_reason }}
@endif
{{-- Body --}}
@if($m->channel->value === 'email') @if($m->body_html)
{!! $m->body_html !!}
@elseif($m->body_text)
{{ $m->body_text }}
@else

No message body.

@endif @else
{{ $m->body_text ?: '—' }}
@endif
{{-- Attachments --}} @if($m->attachments->isNotEmpty())
Attachments ({{ $m->attachments->count() }})
@foreach($m->attachments as $att)
{{ $att->filename }}
@if($att->size)
{{ number_format($att->size / 1024, 1) }} KB
@endif
@endforeach
@endif
{{-- /message tab --}} {{-- Events tab --}}
@if($m->events->isEmpty())
No events recorded yet.
@else
    @foreach($m->events->sortBy('occurred_at') as $event)
  • {{ $event->event_type->label() }} @if($event->mapped_status) {{ $event->mapped_status->label() }} @endif
    {{ $event->occurred_at?->format('d M Y, H:i:s') }} ({{ $event->occurred_at?->diffForHumans() }})
    @if($event->provider_event_id)
    {{ $event->provider_event_id }}
    @endif
  • @endforeach
@endif
{{-- /events tab --}}
{{-- /tab-content --}} @else
No message selected.
@endif
@script @endscript