fix email sender all flow
This commit is contained in:
@@ -5,6 +5,7 @@ import { authOptions } from "@/lib/auth";
|
||||
import { isAdminEmail } from "@/lib/admin";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { systemHealthService } from "@/server/services/system-health.service";
|
||||
import { emailRepo } from "@/server/repositories/email.repo";
|
||||
|
||||
interface JobSummary {
|
||||
jobName: string;
|
||||
@@ -80,20 +81,22 @@ export default async function AdminSystemPage() {
|
||||
);
|
||||
}
|
||||
|
||||
const [summaries, recentRuns, stale] = await Promise.all([
|
||||
const [summaries, recentRuns, stale, emailStats] = await Promise.all([
|
||||
Promise.all(TRACKED_JOBS.map(getJobSummary)),
|
||||
prisma.cronRun.findMany({
|
||||
orderBy: { startedAt: "desc" },
|
||||
take: 20,
|
||||
}),
|
||||
systemHealthService.detectStale(),
|
||||
emailRepo.stats(),
|
||||
]);
|
||||
|
||||
const hasAnyStale =
|
||||
stale.stalePaymentsCount > 0 ||
|
||||
stale.awaitingPayPastDepartureCount > 0 ||
|
||||
stale.overduePayoutsCount > 0 ||
|
||||
stale.stuckRefundsCount > 0;
|
||||
stale.stuckRefundsCount > 0 ||
|
||||
emailStats.deadLetter > 0;
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-5xl px-4 py-8 sm:py-12">
|
||||
@@ -152,6 +155,19 @@ export default async function AdminSystemPage() {
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
{emailStats.deadLetter > 0 && (
|
||||
<li>
|
||||
• <strong>{emailStats.deadLetter}</strong> email gagal kirim &
|
||||
sudah habis 5 attempt — cron berhenti retry, perlu retry
|
||||
manual.{" "}
|
||||
<Link
|
||||
href="/admin/emails?tab=failed"
|
||||
className="font-semibold text-amber-700 hover:underline"
|
||||
>
|
||||
Lihat email gagal →
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</section>
|
||||
)}
|
||||
@@ -236,6 +252,37 @@ export default async function AdminSystemPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mb-8">
|
||||
<h2 className="mb-3 text-xs font-bold uppercase tracking-wide text-neutral-500">
|
||||
Email
|
||||
</h2>
|
||||
<div className="grid gap-3 sm:grid-cols-3">
|
||||
<EmailStat
|
||||
label="Antri dikirim"
|
||||
value={emailStats.queued}
|
||||
tone={emailStats.queued > 0 ? "amber" : "ok"}
|
||||
/>
|
||||
<EmailStat
|
||||
label="Gagal 24 jam"
|
||||
value={emailStats.failed24h}
|
||||
tone={emailStats.failed24h > 0 ? "red" : "ok"}
|
||||
/>
|
||||
<EmailStat
|
||||
label="Perlu aksi manual"
|
||||
value={emailStats.deadLetter}
|
||||
tone={emailStats.deadLetter > 0 ? "red" : "ok"}
|
||||
/>
|
||||
</div>
|
||||
<p className="mt-2 text-xs text-neutral-500">
|
||||
<Link
|
||||
href="/admin/emails"
|
||||
className="font-semibold text-primary-600 hover:underline"
|
||||
>
|
||||
Buka Email Log →
|
||||
</Link>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="mb-3 text-xs font-bold uppercase tracking-wide text-neutral-500">
|
||||
Recent Runs (20 terakhir)
|
||||
@@ -301,6 +348,37 @@ function truncate(s: string, max: number): string {
|
||||
return s.length > max ? `${s.slice(0, max)}…` : s;
|
||||
}
|
||||
|
||||
function EmailStat({
|
||||
label,
|
||||
value,
|
||||
tone,
|
||||
}: {
|
||||
label: string;
|
||||
value: number;
|
||||
tone: "ok" | "amber" | "red";
|
||||
}) {
|
||||
const cls =
|
||||
tone === "red"
|
||||
? "border-red-200 bg-red-50/60"
|
||||
: tone === "amber"
|
||||
? "border-amber-200 bg-amber-50/60"
|
||||
: "border-emerald-200 bg-emerald-50/50";
|
||||
const valueCls =
|
||||
tone === "red"
|
||||
? "text-red-700"
|
||||
: tone === "amber"
|
||||
? "text-amber-700"
|
||||
: "text-emerald-700";
|
||||
return (
|
||||
<div className={`rounded-2xl border p-4 shadow-sm ${cls}`}>
|
||||
<p className="text-[10px] font-semibold uppercase tracking-wide text-neutral-500">
|
||||
{label}
|
||||
</p>
|
||||
<p className={`mt-1 text-2xl font-bold ${valueCls}`}>{value}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StatusBadge({ value }: { value: string }) {
|
||||
const cls =
|
||||
value === "SUCCESS"
|
||||
|
||||
Reference in New Issue
Block a user