120 lines
4.3 KiB
TypeScript
120 lines
4.3 KiB
TypeScript
import Link from "next/link";
|
|
import { redirect } from "next/navigation";
|
|
|
|
import { ShellPage } from "@/components/page-templates";
|
|
import { Button, SectionCard } from "@/components/ui";
|
|
import { getSession } from "@/lib/auth";
|
|
import { prisma } from "@/lib/prisma";
|
|
|
|
function formatDate(date: Date | null | undefined) {
|
|
if (!date) {
|
|
return "-";
|
|
}
|
|
|
|
return new Intl.DateTimeFormat("id-ID", {
|
|
day: "2-digit",
|
|
month: "short",
|
|
year: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit"
|
|
}).format(date);
|
|
}
|
|
|
|
export default async function CampaignReviewPage({
|
|
searchParams
|
|
}: {
|
|
searchParams?: Promise<{ campaignId?: string; error?: string }>;
|
|
}) {
|
|
const session = await getSession();
|
|
if (!session) {
|
|
redirect("/login");
|
|
}
|
|
|
|
const query = await (searchParams ?? Promise.resolve<{ campaignId?: string; error?: string }>({}));
|
|
const campaignId = query.campaignId;
|
|
|
|
const campaign = campaignId
|
|
? await prisma.broadcastCampaign.findFirst({
|
|
where: {
|
|
id: campaignId,
|
|
tenantId: session.tenantId
|
|
},
|
|
include: {
|
|
template: { select: { name: true, category: true, approvalStatus: true } },
|
|
channel: { select: { channelName: true } }
|
|
}
|
|
})
|
|
: null;
|
|
|
|
if (campaignId && !campaign) {
|
|
redirect("/campaigns/review?error=campaign_not_found");
|
|
}
|
|
|
|
return (
|
|
<ShellPage
|
|
shell="admin"
|
|
title="Campaign Review"
|
|
description={campaign ? "Review draft campaign sebelum di-queue untuk pengiriman." : "Belum ada campaign yang dipilih untuk review."}
|
|
>
|
|
<div className="grid gap-6 xl:grid-cols-2">
|
|
<SectionCard title="Campaign summary">
|
|
{campaign ? (
|
|
<div className="space-y-2 text-sm text-on-surface-variant">
|
|
<p>
|
|
<strong className="text-on-surface">Nama:</strong> {campaign.name}
|
|
</p>
|
|
<p>
|
|
<strong className="text-on-surface">Template:</strong> {campaign.template.name} ({campaign.template.category}) •{" "}
|
|
{campaign.template.approvalStatus}
|
|
</p>
|
|
<p>
|
|
<strong className="text-on-surface">Channel:</strong> {campaign.channel.channelName}
|
|
</p>
|
|
<p>
|
|
<strong className="text-on-surface">Audience:</strong> {campaign.audienceType}
|
|
</p>
|
|
<p>
|
|
<strong className="text-on-surface">Scheduled:</strong> {formatDate(campaign.scheduledAt)}
|
|
</p>
|
|
<p>
|
|
<strong className="text-on-surface">Status:</strong> {campaign.status}
|
|
</p>
|
|
<p>
|
|
<strong className="text-on-surface">Recipient estimate:</strong> {campaign.totalRecipients}
|
|
</p>
|
|
<p className="mt-2">Estimasi sukses: {(campaign.totalRecipients * 0.82).toFixed(0)} kontak</p>
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-on-surface-variant">Pilih campaign dari halaman campaign list untuk menampilkan detail review.</p>
|
|
)}
|
|
</SectionCard>
|
|
<SectionCard title="Review checks">
|
|
{campaign ? (
|
|
<div className="space-y-2 text-sm text-on-surface-variant">
|
|
<p>Template approval: {campaign.template.approvalStatus}</p>
|
|
<p>Audience validation: OK</p>
|
|
<p>Recipient validation: {campaign.totalRecipients > 0 ? "PASS" : "No recipients"}</p>
|
|
<p>Channel availability: Available</p>
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-on-surface-variant">Tidak ada pemeriksaan yang berjalan karena campaign belum dipilih.</p>
|
|
)}
|
|
</SectionCard>
|
|
</div>
|
|
<div className="flex gap-3">
|
|
{campaign ? (
|
|
<Button href={`/campaigns/${campaign.id}`}>Go to campaign detail</Button>
|
|
) : (
|
|
<Button href="/campaigns">Open campaigns</Button>
|
|
)}
|
|
<Button href="/campaigns/new" variant="secondary">
|
|
Create another campaign
|
|
</Button>
|
|
<Link href="/campaigns" className="inline-flex items-center justify-center rounded-full border border-outline-variant/70 px-4 py-2.5 text-sm font-semibold font-headline text-on-surface transition hover:bg-surface-container-low">
|
|
Back
|
|
</Link>
|
|
</div>
|
|
</ShellPage>
|
|
);
|
|
}
|