41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
import { notFound, redirect } from "next/navigation";
|
|
import { getTranslations } from "next-intl/server";
|
|
import { getSessionUser } from "@/lib/session";
|
|
import { getInvoiceDetail, listCreditNotesForInvoice } from "@/lib/db";
|
|
import { BackLink } from "@/components/ui/back-link";
|
|
import { InvoiceDetailView } from "@/components/admin/billing/invoice-detail-view";
|
|
|
|
/**
|
|
* /admin/billing/invoices/[id] — full detail of one invoice.
|
|
*
|
|
* Server-renders the static body (header, lines, totals, billing
|
|
* snapshot); the action bar (mark-paid, void, refund, delete, PDF
|
|
* download) is a client component for the interactive bits.
|
|
*
|
|
* Phase 7: also passes any linked credit notes so the detail view
|
|
* can show the "this invoice was voided / partially refunded" panel
|
|
* without an extra round-trip.
|
|
*/
|
|
export default async function AdminInvoiceDetailPage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ id: string }>;
|
|
}) {
|
|
const user = await getSessionUser();
|
|
if (!user) redirect("/login");
|
|
if (!user.isPlatform) redirect("/dashboard");
|
|
const t = await getTranslations("adminBilling");
|
|
|
|
const { id } = await params;
|
|
const detail = await getInvoiceDetail(id);
|
|
if (!detail) notFound();
|
|
const creditNotes = await listCreditNotesForInvoice(id);
|
|
|
|
return (
|
|
<main className="max-w-4xl mx-auto px-6 py-8">
|
|
<BackLink href="/admin/billing/invoices" label={t("backToInvoices")} />
|
|
<InvoiceDetailView detail={detail} creditNotes={creditNotes} />
|
|
</main>
|
|
);
|
|
}
|