36 lines
1.3 KiB
TypeScript
36 lines
1.3 KiB
TypeScript
import { redirect, notFound } from "next/navigation";
|
|
import { getTranslations } from "next-intl/server";
|
|
import { getSessionUser } from "@/lib/session";
|
|
import { getInvoiceByNumberForOrg } from "@/lib/db";
|
|
import { BackLink } from "@/components/ui/back-link";
|
|
import { CustomerInvoiceDetail } from "@/components/billing/customer-invoice-detail";
|
|
|
|
/**
|
|
* /billing/[invoiceNumber] — single-invoice view.
|
|
*
|
|
* Lookup is by the human-readable invoice number (the YYYY-NNNNN
|
|
* format printed on the PDF and in the issuance email). Org
|
|
* filter is enforced in the DB query — a customer trying another
|
|
* org's number gets 404, not 403, to avoid leaking the existence
|
|
* of other orgs' invoices.
|
|
*/
|
|
export default async function CustomerInvoiceDetailPage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ invoiceNumber: string; locale: string }>;
|
|
}) {
|
|
const user = await getSessionUser();
|
|
if (!user) redirect("/login");
|
|
const { invoiceNumber } = await params;
|
|
const t = await getTranslations("customerBilling");
|
|
const detail = await getInvoiceByNumberForOrg(invoiceNumber, user.orgId);
|
|
if (!detail) notFound();
|
|
|
|
return (
|
|
<main className="max-w-3xl mx-auto px-6 py-8">
|
|
<BackLink href="/billing" label={t("backToBilling")} />
|
|
<CustomerInvoiceDetail invoice={detail.invoice} lines={detail.lines} />
|
|
</main>
|
|
);
|
|
}
|