Files
whatsapp-inbox-platform/app/super-admin/billing/invoices/[invoiceId]/page.tsx
Wira Basalamah adde003fba
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
chore: initial project import
2026-04-21 09:29:29 +07:00

116 lines
3.6 KiB
TypeScript

import Link from "next/link";
import { ShellPage } from "@/components/page-templates";
import { Badge, SectionCard } from "@/components/ui";
import { getSession } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { redirect } from "next/navigation";
function formatDate(value: Date | null) {
if (!value) {
return "-";
}
return new Intl.DateTimeFormat("id-ID", {
day: "2-digit",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit"
}).format(value);
}
function formatMoney(value: number) {
return new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
maximumFractionDigits: 0
}).format(value);
}
function statusTone(status: string) {
if (status === "PAID") {
return "success";
}
if (status === "OVERDUE") {
return "danger";
}
return "warning";
}
export default async function SuperAdminInvoiceDetailPage({
params
}: {
params: Promise<{ invoiceId: string }>;
}) {
const session = await getSession();
if (!session || session.role !== "super_admin") {
redirect("/unauthorized");
}
const { invoiceId } = await params;
const invoice = await prisma.billingInvoice.findUnique({
where: { id: invoiceId },
include: {
tenant: { select: { name: true, slug: true } },
plan: { select: { name: true, code: true } }
}
});
if (!invoice) {
redirect("/super-admin/billing/invoices?error=invoice_not_found");
}
return (
<ShellPage shell="super-admin" title="Invoice Detail" description="Invoice view untuk super admin.">
<div className="grid gap-6 xl:grid-cols-2">
<SectionCard title="Summary">
<div className="space-y-2 text-sm text-on-surface-variant">
<p>
<strong className="text-on-surface">Invoice:</strong> {invoice.invoiceNumber}
</p>
<p>
<strong className="text-on-surface">Tenant:</strong>{" "}
<Link href={`/super-admin/tenants/${invoice.tenantId}`} className="text-brand hover:underline">
{invoice.tenant.name} ({invoice.tenant.slug})
</Link>
</p>
<p>
<strong className="text-on-surface">Plan:</strong> {invoice.plan.name} ({invoice.plan.code})
</p>
<p>
<strong className="text-on-surface">Period:</strong> {formatDate(invoice.periodStart)} - {formatDate(invoice.periodEnd)}
</p>
<p>
<strong className="text-on-surface">Total amount:</strong> {formatMoney(invoice.totalAmount)}
</p>
<p>
<strong className="text-on-surface">Subtotal:</strong> {formatMoney(invoice.subtotal)} | Tax: {formatMoney(invoice.taxAmount)}
</p>
<p>
<strong className="text-on-surface">Status:</strong> <Badge tone={statusTone(invoice.paymentStatus)}>{invoice.paymentStatus}</Badge>
</p>
</div>
</SectionCard>
<SectionCard title="Timeline">
<div className="space-y-2 text-sm text-on-surface-variant">
<p>
<strong className="text-on-surface">Issued:</strong> {formatDate(invoice.createdAt)}
</p>
<p>
<strong className="text-on-surface">Due date:</strong> {formatDate(invoice.dueDate)}
</p>
<p>
<strong className="text-on-surface">Paid at:</strong> {formatDate(invoice.paidAt)}
</p>
<p>
<strong className="text-on-surface">Updated:</strong> {formatDate(invoice.updatedAt)}
</p>
</div>
</SectionCard>
</div>
</ShellPage>
);
}