177 lines
11 KiB
HTML
177 lines
11 KiB
HTML
{% extends "ui/templates/base/layout.html" %}
|
|
{% block content %}
|
|
<div class="space-y-6">
|
|
<div class="rounded-2xl bg-white p-6 shadow-card ring-1 ring-slate-200">
|
|
<div class="flex flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
|
|
<div>
|
|
<h2 class="text-xl font-semibold text-slate-900">Email Audit Dashboard</h2>
|
|
<p class="mt-1 text-sm text-slate-500">Monitor SMTP delivery, retry queue, incoming IMAP messages, template activity and email-to-work mapping health.</p>
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
<a href="/email/settings" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Settings</a>
|
|
<a href="/email/logs" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Logs</a>
|
|
<a href="/email/queue" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Queue</a>
|
|
<a href="/email/inbox" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Inbox</a>
|
|
<a href="/email/templates" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Templates</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
|
<div class="rounded-2xl bg-white p-5 shadow-card ring-1 ring-slate-200">
|
|
<div class="text-sm font-medium text-slate-500">SMTP Status</div>
|
|
<div class="mt-3 flex items-center justify-between gap-3">
|
|
<div class="text-2xl font-semibold text-slate-900">{{ 'Ready' if smtp_configured else 'Pending' }}</div>
|
|
{% if smtp_configured %}
|
|
<span class="rounded-full bg-emerald-50 px-3 py-1 text-xs font-semibold text-emerald-700">Configured</span>
|
|
{% else %}
|
|
<span class="rounded-full bg-amber-50 px-3 py-1 text-xs font-semibold text-amber-700">Needs setup</span>
|
|
{% endif %}
|
|
</div>
|
|
<p class="mt-2 text-xs text-slate-500">{{ setting.smtp_host if setting and setting.smtp_host else 'SMTP host not configured' }}</p>
|
|
</div>
|
|
|
|
<div class="rounded-2xl bg-white p-5 shadow-card ring-1 ring-slate-200">
|
|
<div class="text-sm font-medium text-slate-500">Sent in 24 Hours</div>
|
|
<div class="mt-3 text-3xl font-semibold text-slate-900">{{ summary.sent_24h }}</div>
|
|
<p class="mt-2 text-xs text-slate-500">Last 7 days sent: {{ summary.sent_7d }}</p>
|
|
</div>
|
|
|
|
<div class="rounded-2xl bg-white p-5 shadow-card ring-1 ring-slate-200">
|
|
<div class="text-sm font-medium text-slate-500">Failed in 24 Hours</div>
|
|
<div class="mt-3 text-3xl font-semibold {% if summary.failed_24h %}text-red-700{% else %}text-slate-900{% endif %}">{{ summary.failed_24h }}</div>
|
|
<p class="mt-2 text-xs text-slate-500">Last 7 days failed: {{ summary.failed_7d }}</p>
|
|
</div>
|
|
|
|
<div class="rounded-2xl bg-white p-5 shadow-card ring-1 ring-slate-200">
|
|
<div class="text-sm font-medium text-slate-500">Queue Pending</div>
|
|
<div class="mt-3 text-3xl font-semibold {% if summary.pending_now %}text-amber-700{% else %}text-slate-900{% endif %}">{{ summary.pending_now }}</div>
|
|
<p class="mt-2 text-xs text-slate-500">Retryable: {{ summary.queue_retryable }} | Exhausted: {{ summary.queue_exhausted }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
|
<div class="rounded-2xl bg-white p-5 shadow-card ring-1 ring-slate-200">
|
|
<div class="text-sm font-medium text-slate-500">IMAP Status</div>
|
|
<div class="mt-3 flex items-center justify-between gap-3">
|
|
<div class="text-2xl font-semibold text-slate-900">{{ 'Ready' if imap_configured else 'Pending' }}</div>
|
|
{% if imap_configured %}
|
|
<span class="rounded-full bg-emerald-50 px-3 py-1 text-xs font-semibold text-emerald-700">Configured</span>
|
|
{% else %}
|
|
<span class="rounded-full bg-amber-50 px-3 py-1 text-xs font-semibold text-amber-700">Needs setup</span>
|
|
{% endif %}
|
|
</div>
|
|
<p class="mt-2 text-xs text-slate-500">{{ setting.imap_host if setting and setting.imap_host else 'IMAP host not configured' }}</p>
|
|
</div>
|
|
|
|
<div class="rounded-2xl bg-white p-5 shadow-card ring-1 ring-slate-200">
|
|
<div class="text-sm font-medium text-slate-500">Incoming Emails</div>
|
|
<div class="mt-3 text-3xl font-semibold text-slate-900">{{ summary.incoming_7d }}</div>
|
|
<p class="mt-2 text-xs text-slate-500">Fetched in last 7 days</p>
|
|
</div>
|
|
|
|
<div class="rounded-2xl bg-white p-5 shadow-card ring-1 ring-slate-200">
|
|
<div class="text-sm font-medium text-slate-500">Unmapped Incoming</div>
|
|
<div class="mt-3 text-3xl font-semibold {% if summary.incoming_unmapped %}text-amber-700{% else %}text-slate-900{% endif %}">{{ summary.incoming_unmapped }}</div>
|
|
<p class="mt-2 text-xs text-slate-500">Mapped: {{ summary.incoming_mapped }}</p>
|
|
</div>
|
|
|
|
<div class="rounded-2xl bg-white p-5 shadow-card ring-1 ring-slate-200">
|
|
<div class="text-sm font-medium text-slate-500">Templates</div>
|
|
<div class="mt-3 text-3xl font-semibold text-slate-900">{{ summary.active_templates }}</div>
|
|
<p class="mt-2 text-xs text-slate-500">Inactive: {{ summary.inactive_templates }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid gap-6 xl:grid-cols-2">
|
|
<section class="rounded-2xl bg-white p-6 shadow-card ring-1 ring-slate-200">
|
|
<div class="flex items-center justify-between gap-4">
|
|
<h3 class="text-base font-semibold text-slate-900">7-Day Delivery Summary</h3>
|
|
<span class="text-xs text-slate-500">Generated: {{ generated_at }}</span>
|
|
</div>
|
|
<div class="mt-4 overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-slate-200 text-sm">
|
|
<thead><tr class="text-left text-xs uppercase tracking-wide text-slate-500"><th class="py-2 pr-4">Status</th><th class="py-2 pr-4 text-right">Count</th></tr></thead>
|
|
<tbody class="divide-y divide-slate-100">
|
|
{% for row in status_summary %}
|
|
<tr><td class="py-3 pr-4"><span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700">{{ row.status }}</span></td><td class="py-3 pr-4 text-right font-semibold">{{ row.count }}</td></tr>
|
|
{% else %}
|
|
<tr><td colspan="2" class="py-6 text-center text-slate-500">No email activity in the last 7 days.</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="rounded-2xl bg-white p-6 shadow-card ring-1 ring-slate-200">
|
|
<h3 class="text-base font-semibold text-slate-900">Template Activity</h3>
|
|
<div class="mt-4 overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-slate-200 text-sm">
|
|
<thead><tr class="text-left text-xs uppercase tracking-wide text-slate-500"><th class="py-2 pr-4">Template</th><th class="py-2 pr-4 text-right">Sent</th><th class="py-2 pr-4 text-right">Failed</th><th class="py-2 pr-4 text-right">Pending</th><th class="py-2 pr-4 text-right">Total</th></tr></thead>
|
|
<tbody class="divide-y divide-slate-100">
|
|
{% for row in template_summary_rows %}
|
|
<tr>
|
|
<td class="py-3 pr-4 font-medium text-slate-800">{{ row.template_code }}</td>
|
|
<td class="py-3 pr-4 text-right text-emerald-700">{{ row.SENT }}</td>
|
|
<td class="py-3 pr-4 text-right text-red-700">{{ row.FAILED }}</td>
|
|
<td class="py-3 pr-4 text-right text-amber-700">{{ row.PENDING }}</td>
|
|
<td class="py-3 pr-4 text-right font-semibold">{{ row.total }}</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr><td colspan="5" class="py-6 text-center text-slate-500">No template-wise activity found.</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<div class="grid gap-6 xl:grid-cols-3">
|
|
<section class="rounded-2xl bg-white p-6 shadow-card ring-1 ring-slate-200">
|
|
<div class="flex items-center justify-between gap-2"><h3 class="text-base font-semibold text-slate-900">Recent Failed Emails</h3><a href="/email/logs" class="text-xs font-semibold text-brand-700 hover:underline">View logs</a></div>
|
|
<div class="mt-4 space-y-3">
|
|
{% for log in recent_failed %}
|
|
<div class="rounded-xl border border-red-100 bg-red-50 p-3">
|
|
<div class="text-sm font-semibold text-slate-900">{{ log.recipient_email }}</div>
|
|
<div class="mt-1 truncate text-xs text-slate-700">{{ log.subject }}</div>
|
|
<div class="mt-1 line-clamp-2 text-xs text-red-700">{{ log.error_message or 'No error message stored.' }}</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="rounded-xl border border-emerald-100 bg-emerald-50 p-4 text-sm text-emerald-800">No recent failed emails.</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
|
|
<section class="rounded-2xl bg-white p-6 shadow-card ring-1 ring-slate-200">
|
|
<div class="flex items-center justify-between gap-2"><h3 class="text-base font-semibold text-slate-900">Queue Attention</h3><a href="/email/queue" class="text-xs font-semibold text-brand-700 hover:underline">Process queue</a></div>
|
|
<div class="mt-4 space-y-3">
|
|
{% for log in queue_due %}
|
|
<div class="rounded-xl border border-amber-100 bg-amber-50 p-3">
|
|
<div class="flex justify-between gap-2"><span class="text-sm font-semibold text-slate-900">{{ log.status }}</span><span class="text-xs text-slate-500">{{ log.attempt_count }}/{{ log.max_attempts }}</span></div>
|
|
<div class="mt-1 truncate text-xs text-slate-700">{{ log.recipient_email }}</div>
|
|
<div class="mt-1 truncate text-xs text-slate-500">{{ log.subject }}</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="rounded-xl border border-emerald-100 bg-emerald-50 p-4 text-sm text-emerald-800">No pending retry items.</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
|
|
<section class="rounded-2xl bg-white p-6 shadow-card ring-1 ring-slate-200">
|
|
<div class="flex items-center justify-between gap-2"><h3 class="text-base font-semibold text-slate-900">Incoming Mapping Attention</h3><a href="/email/inbox" class="text-xs font-semibold text-brand-700 hover:underline">Open inbox</a></div>
|
|
<div class="mt-4 space-y-3">
|
|
{% for mail in incoming_attention %}
|
|
<a href="/email/inbox/{{ mail.id }}" class="block rounded-xl border border-slate-200 p-3 hover:bg-slate-50">
|
|
<div class="flex justify-between gap-2"><span class="truncate text-sm font-semibold text-slate-900">{{ mail.sender_email or '-' }}</span><span class="text-xs text-amber-700">{{ mail.mapping_status }}</span></div>
|
|
<div class="mt-1 truncate text-xs text-slate-700">{{ mail.subject or '(No subject)' }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">Attachments: {{ mail.attachment_count }}</div>
|
|
</a>
|
|
{% else %}
|
|
<div class="rounded-xl border border-emerald-100 bg-emerald-50 p-4 text-sm text-emerald-800">No unmapped incoming emails needing attention.</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|