@extends('setting.nav') @section('title', 'Follow-ups') @section('content')
@include('follow_ups._tabs') @php // Tab labels in the order the controller lists its views. Overdue // first because it is the only one that is actually a problem. $tabs = [ // The tone is a chip modifier from the design system, so a // follow-up filter and a mail filter are the same object. 'overdue' => ['label' => 'Overdue', 'icon' => 'bi-exclamation-triangle', 'tone' => 'action'], 'today' => ['label' => 'Due today', 'icon' => 'bi-clock', 'tone' => 'reply'], 'upcoming' => ['label' => 'Upcoming', 'icon' => 'bi-calendar3', 'tone' => 'group'], 'open' => ['label' => 'All open', 'icon' => 'bi-list-task', 'tone' => 'all'], 'done' => ['label' => 'Done', 'icon' => 'bi-check2-circle', 'tone' => 'read'], 'dismissed' => ['label' => 'Dismissed', 'icon' => 'bi-slash-circle', 'tone' => 'all'], 'all' => ['label' => 'Everything', 'icon' => 'bi-archive', 'tone' => 'all'], ]; $query = fn(array $extra = []) => array_merge( array_filter([ 'view' => $view, 'q' => $search ?: null, 'mailbox' => $mailboxFilter ?: null, ]), $extra, ); @endphp @unless ($configured) {{-- Opt-in, like auto-reply. Say so plainly rather than showing an empty queue that looks like a broken feature. --}}
Follow-ups are not switched on for this account. No tasks are being raised and no reminders are being sent. Nothing has been missed in the meantime — mail is read from the messages table when you turn it on, not from the moment you do.
@if ($isAdmin) {{-- Turns it on and sweeps in the same request. It used to link to the settings form, where the switch still had to be ticked and saved — so the banner stayed up and looked like it had ignored the click. --}}
@csrf
or set the windows first
@endif
@endunless {{-- The same chip toolbar the mail list filters with. A segmented button group broke into stretched half-rows as soon as it wrapped; chips wrap as chips. --}}
@foreach ($tabs as $key => $tab) {{ $tab['label'] }} {{ $counts[$key] ?? 0 }} @endforeach
@if ($isAdmin && count($mailboxNames) > 1) {{-- An admin sees the whole account by default; this narrows it to one person's queue. --}}
@if ($search) @endif
@endif @if ($isAdmin && $configured) {{-- The scheduled pass is the normal path; this one exists so a queue can be filled while the admin is still looking at it, rather than on the next cron tick. --}}
@csrf
@endif
@php // Bootstrap contextual names come off the model; the cards speak in // design-system tones, so translate once here rather than in six // places below. $statusTone = [ 'success' => 'green', 'danger' => 'red', 'warning' => 'amber', 'secondary' => 'slate', ]; @endphp
@forelse ($tasks as $task) @php $late = $task->isOverdue(); $senderEmail = trim((string) $task->sender_email); $displayName = trim((string) $task->sender_name) ?: $senderEmail; $showAddress = $senderEmail !== '' && strcasecmp($displayName, $senderEmail) !== 0; // Drives the stripe down the card's edge: a problem, work in // hand, or finished with. $state = $late ? 'late' : ($task->isOpen() ? 'open' : ($task->status === \App\Models\FollowUp::STATUS_DONE ? 'done' : 'dismissed')); $statusLabel = $late && $task->isOpen() ? 'overdue' : $task->status; @endphp
{{-- Who it is from, and when it is owed. --}}
{{ $displayName ?: 'Unknown sender' }} @if ($showAddress) {{ $senderEmail }} @endif @if ($task->isOpen()) {{-- "3 weeks late", and under it the time it was actually owed. --}} {{ $task->dueLabel() }} @if ($task->due_at) {{ $task->due_at->format('d M, H:i') }} @endif @else {{-- A closed task's label is already the absolute time, so naming it beats printing it twice. --}} was due {{ $task->dueLabel() }} @endif
{{-- The mail itself, and why it is in the queue at all. A queue nobody trusts is a queue nobody works. --}}

{{ \Illuminate\Support\Str::limit($task->subject, 120) ?: '(no subject)' }}

@if ($task->reason)

{{ $task->reason }}

@endif @if ($task->note)

{{ $task->note }}

@endif
{{-- What it needs on the left, what you can do about it on the right — the columns that used to fall off the edge. --}}
{{-- Same kind marks as the mail cards: two pills side by side gave no way to tell the action from the status. --}} @if ($task->actionName()) Action{{ $task->actionName() }} @else ActionReply @endif Status{{ ucfirst($statusLabel) }} @if ($task->escalated) escalated @endif {{ $task->reminder_count }} {{ \Illuminate\Support\Str::plural('reminder', $task->reminder_count) }} @if ($task->isOpen() && !$task->remind_at && $task->reminder_count > 0) {{-- Chasing stopped, the work did not. --}} no more due @endif @if ($task->completion_source === \App\Models\FollowUp::COMPLETION_REPLY) reply seen @endif @if ($task->trigger_type === \App\Models\FollowUp::TRIGGER_MANUAL) raised by hand @endif @if (count($mailboxNames) > 1 && !$mailboxFilter) {{ $mailboxNames[$task->user_id] ?? '#' . $task->user_id }} @endif
@if ($task->message_ref) Reply @endif @if ($task->isOpen())
@csrf
@csrf
@else
@csrf
@endif
@empty
@if ($view === 'overdue')

Nothing is overdue

That is the whole point.

@elseif ($view === 'today')

Nothing else is due today

Anything raised later will appear here.

@else

No follow-ups here yet

Mail that is owed an answer turns up on this queue on its own.

@endif
@endforelse
@if ($tasks->hasPages())
{{ $tasks->links() }}
@endif

A follow-up closes itself once the sync sees a reply on the conversation, so anything answered from a mail client drops off this queue on its own. Dismissing one is permanent in the sense that matters: the mail is never raised again.

@endsection