40 lines
1.5 KiB
TypeScript
40 lines
1.5 KiB
TypeScript
import { redirect } from "next/navigation";
|
|
import { getTranslations } from "next-intl/server";
|
|
import { getSessionUser } from "@/lib/session";
|
|
import { listInvoices, syncOverdueInvoices } from "@/lib/db";
|
|
import { BackLink } from "@/components/ui/back-link";
|
|
import { InvoicesTable } from "@/components/admin/billing/invoices-table";
|
|
|
|
/**
|
|
* /admin/billing/invoices — list of all issued invoices, filterable
|
|
* by status and month. Click a row to drill into detail.
|
|
*
|
|
* Server-renders the initial table with no filters applied (showing
|
|
* the most recent 200). Client filters trigger a fetch with query
|
|
* params and re-render in place.
|
|
*/
|
|
export default async function AdminInvoicesListPage() {
|
|
const user = await getSessionUser();
|
|
if (!user) redirect("/login");
|
|
if (!user.isPlatform) redirect("/dashboard");
|
|
const t = await getTranslations("adminBilling");
|
|
|
|
await syncOverdueInvoices().catch((e) =>
|
|
console.error("syncOverdueInvoices failed:", e)
|
|
);
|
|
const invoices = await listInvoices({ limit: 200 });
|
|
|
|
return (
|
|
<main className="max-w-5xl mx-auto px-6 py-8">
|
|
<BackLink href="/admin/billing" label={t("backToBilling")} />
|
|
<div className="mb-8 animate-in">
|
|
<h1 className="font-display text-2xl font-semibold accent-rule">
|
|
{t("invoicesTitle")}
|
|
</h1>
|
|
<p className="text-sm text-text-secondary mt-3">{t("invoicesPageDesc")}</p>
|
|
</div>
|
|
<InvoicesTable initialInvoices={invoices} />
|
|
</main>
|
|
);
|
|
}
|