@extends('layouts.app') @section('content')
{{--

{{ $email_id }} or {{ Auth::id() }}

--}} @php $spamLabels = [ 0 => 'Negative', 1 => 'Positive', 2 => 'Reply-Mail', 3 => 'Neutral', 4 => 'Promotion', 5 => 'Sales', 6 => 'AMC', 100 => 'Unknown', ]; @endphp

{{ $mail->action_required == 1 && $mail->sent_mail_id == '-1' ? 'Pending Reply / ' : '' }} {{ $mail->sent_mail_id != '-1' ? 'Replied Mail / ' : '' }} {{ $mail->is_promotion == 1 ? 'Promotion Mail / ' : '' }} {{ ucfirst($mail->is_spam_value) }} / Group By :- {{ ucfirst($mail->is_category_value) }}

Welcome to your single Mail Dashboard

{{-- AI Insights. Everything the one classification call worked out about this mail, in one place. It was previously scattered: the sentiment and category in the heading above as bare words, the next action only on the list, and the lead nowhere at all. The labels and colours are looked up in the same vocabularies the mail list uses, so a category an admin renames reads the same here as it does there. --}} @php $insightSentiment = collect($counts['sentiment_defs'] ?? [])->firstWhere( 'code', strtolower(trim((string) $mail->is_spam_value)), ); $insightAction = null; $actionWord = strtolower(trim((string) ($mail->next_action ?? ''))); foreach ($counts['next_action_defs'] ?? [] as $def) { if (strtolower($def['code']) === $actionWord || in_array($actionWord, $def['aliases'] ?? [], true)) { $insightAction = $def; break; } } $insightCategory = null; $categoryWord = strtolower(trim((string) ($mail->is_category_value ?? ''))); foreach ($counts['category_defs'] ?? [] as $def) { if (strtolower($def['code']) === $categoryWord || in_array($categoryWord, $def['aliases'] ?? [], true)) { $insightCategory = $def; break; } } $insightLeadIs = (int) ($mail->is_potential_lead ?? 0) === 1; $insightLead = null; $leadWord = strtolower(trim((string) ($mail->lead_type ?? ''))); foreach ($counts['lead_defs'] ?? [] as $def) { if (strtolower($def['code']) === $leadWord || in_array($leadWord, $def['aliases'] ?? [], true)) { $insightLead = $def; break; } } // How urgently it needs answering, worked out from the type and the // score rather than read off the row — so this card and the mail list // cannot drift, and both follow the bands as they are configured now. $insightBand = \App\Models\LeadType::priorityFor($mail); $insightBandDef = \App\Models\LeadType::priorityDefinitionFor($insightBand); $insightOwnBand = $insightLead['priority'] ?? null; $insightDemoted = $insightBand !== null && $insightOwnBand !== null && $insightOwnBand !== $insightBand; // Null is not zero: an inferred lead was never scored. $insightScore = $mail->lead_score ?? null; $insightScore = $insightScore === null || $insightScore === '' ? null : (int) $insightScore; $insightReason = trim((string) ($mail->lead_reason ?? '')); $insightSummary = trim((string) ($mail->mail_summary ?? '')); // Which of the four answers the classifier actually made for this mail, // as opposed to being derived from its other labels afterwards. $actionInferred = strtolower(trim((string) ($mail->next_action_source ?? ''))) !== 'ai'; $leadInferred = strtolower(trim((string) ($mail->lead_source ?? ''))) !== 'ai'; // Assignment. Not one of the classifier's answers — the AI is never // asked who owns a mail — so it sits in this card as the consequence of // the Category above it rather than as another judgement. $assignOwnerId = \App\Models\Department::ownerIdFor(auth()->id()); $assignDeptId = (int) ($mail->assigned_department_id ?? 0); $assignDept = $assignDeptId > 0 ? \App\Models\Department::find_for($assignDeptId, $assignOwnerId) : null; $assignToId = (int) ($mail->assigned_user_id ?? 0); $assignNames = \App\Models\Department::assigneeNamesFor(auth()->id()); $assignToName = $assignNames[$assignToId] ?? null; $assignManual = strtolower(trim((string) ($mail->assignment_source ?? ''))) === \App\Models\Department::SOURCE_MANUAL; $assignedBy = (int) ($mail->assigned_by ?? 0); $assignedByName = $assignNames[$assignedBy] ?? null; // Where the rule WOULD send it, which is only worth showing when that // differs from where it is — after a manual override, or while the mail // sits unassigned because nobody has mapped its category yet. $assignRouted = \App\Models\Department::routeFor($categoryWord, $assignOwnerId); // The teams and their members, for the reassignment picker. $assignTeams = \App\Models\Department::activeFor($assignOwnerId); $assignMembers = []; foreach ($assignTeams as $team) { $assignMembers[(int) $team->id] = \App\Models\Department::membersFor($team->id, $assignOwnerId); } @endphp

AI Insights

@if ($insightSummary !== '')

{{ $insightSummary }}

@endif
Category
@if ($insightCategory) {{ $insightCategory['label'] }} @elseif ($categoryWord !== '') {{ ucfirst(str_replace('_', ' ', $categoryWord)) }} @else — @endif
Sentiment
@if ($insightSentiment) {{ $insightSentiment['label'] }} @else — @endif
Next action
@if ($insightAction) {{ $insightAction['label'] }} @else — @endif
Lead
@if ($insightLeadIs) {{ $insightLead['label'] ?? 'Potential lead' }} @else Not a lead @endif
@if ($insightLeadIs)
Priority
@if ($insightBandDef) {{ $insightBandDef['label'] }} @else {{-- A lead typed with a word the vocabulary no longer claims. It is still an opportunity, but nothing can say how urgent. --}} — @endif
Lead score
@if ($insightScore !== null) {{ $insightScore }} / 100 @else Not scored @endif
@endif {{-- Assigned to. Outside the lead block on purpose: every mail has an owner or visibly lacks one, whether or not it is an opportunity. The pill links to the team's listing, so "who else is in this queue" is one click. --}}
Assigned to
@if ($assignDept) {{ $assignDept->displayName() }}{{ $assignToName ? ' / ' . $assignToName : '' }} @elseif ($assignDeptId > 0) {{-- The team was deleted after the mail was assigned to it. --}} Deleted team @else — @endif
{{-- Why it is where it is, but only when that is not obvious. Three cases are worth a sentence and the rest are not: a manual override (the rule disagrees), an unassigned mail whose category IS routed (something is wrong), and an unassigned mail whose category is not routed (nothing is wrong, but somebody has to act). --}} @if ($assignManual && $assignRouted && (int) $assignRouted->id !== $assignDeptId)

Assigned by hand: the routing rule sends {{ $insightCategory['label'] ?? 'this category' }} to {{ $assignRouted->displayName() }}, but {{ $assignedByName ?? 'somebody' }} assigned this one {{ $assignToName ? 'to ' . $assignToName : 'to ' . ($assignDept?->displayName() ?? 'another team') }}. A hand-assigned mail is never reassigned automatically.

@elseif ($assignDeptId === 0 && $assignRouted)

Not assigned yet: {{ $insightCategory['label'] ?? 'This category' }} is routed to {{ $assignRouted->displayName() }}, but this mail was classified before the rule existed or its assignment was cleared. php artisan mail:assign-existing gives mail like this its team.

@elseif ($assignDeptId === 0 && $categoryWord !== '')

Nobody owns this: {{ $insightCategory['label'] ?? 'this category' }} is not routed to a team. @if (auth()->check() && auth()->user()->auth_id == 0) Map it on the Mail Routing screen and new mail of this kind is assigned as it arrives. @endif

@endif {{-- Reassign. Anybody in the account may reassign, not just the admin: the person who knows a mail landed in the wrong queue is usually whoever it landed on. Changing the RULE stays an admin action — one mail is not a policy. --}} @if ($assignTeams->isNotEmpty())
@csrf
{{-- Every member of every team in one list, labelled with their team. Picking a person sets the team from their own membership server-side, so the two columns cannot disagree — which is why this does not need to filter itself as the team above changes. --}}
@if ($assignDeptId > 0) @endif
@endif @if ($insightLeadIs && $insightReason !== '')

Why this is a lead: {{ $insightReason }}

@endif @if ($insightDemoted) {{-- Only the surprising case is spelled out. A band that matches its type needs no explanation beyond the pill; a band one step below its type looks like a mistake until you know the score is what moved it. --}}

Why {{ strtolower($insightBandDef['label']) }} and not {{ strtolower(\App\Models\LeadType::priorityDefinitionFor($insightOwnBand)['label']) }}: {{ $insightLead['label'] }} is normally {{ strtolower(\App\Models\LeadType::priorityDefinitionFor($insightOwnBand)['label']) }} priority, but the classifier scored this mail {{ $insightScore }} against a floor of {{ $insightLead['min_score'] }} — inside the {{ \App\Models\LeadType::CONFIDENCE_MARGIN }}-point margin where it only just believed its own answer — so it is shown one band lower.

@endif