Reworks the T&C feature per review:
- categories are user-defined DATA, not a fixed enum — admins add new ones;
- ALL PO T&Cs are catalogued, incl. the previously-fixed boilerplate (seeded
under a "General" category) and an "Others" bucket;
- the PO form is a dynamic editor: "+ Add term", pick a category, type/pick a
clause.
- schema: TermsCategory (name/sortOrder/isActive) + TermsCondition (categoryId
FK + text + isDefault + isActive). PurchaseOrder.terms Json snapshot. Migration
seeds every standard line as a clause (named slots, the two fixed lines under
General, empty Others); isDefault rows pre-fill new POs.
- admin /admin/terms: Add/Edit clause form's category is a combobox — typing a
new name creates the category; isDefault checkbox.
- PO editor components/po/po-terms-editor.tsx: dynamic rows (category + clause
comboboxes), used by new/edit/manager-edit forms; new POs pre-fill from
getDefaultPoTerms, edits load po.terms or legacyPoTerms (old tc* + fixed lines).
- storage: PurchaseOrder.terms ([{category,text}]) supersedes tc* for export +
detail; null on old POs falls back to tc* + fixed lines. parsePoTerms validates.
- export route + po-detail render from terms when present.
- tests rewritten for category creation + catalogue/default helpers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
35 lines
1.1 KiB
TypeScript
35 lines
1.1 KiB
TypeScript
import { auth } from "@/auth";
|
|
import { db } from "@/lib/db";
|
|
import { hasPermission } from "@/lib/permissions";
|
|
import { redirect } from "next/navigation";
|
|
import { TermsTable } from "./terms-table";
|
|
import type { Metadata } from "next";
|
|
|
|
export const metadata: Metadata = { title: "Terms & Conditions" };
|
|
|
|
export default async function TermsPage() {
|
|
const session = await auth();
|
|
if (!session?.user) redirect("/login");
|
|
if (!hasPermission(session.user.role, "manage_terms")) redirect("/dashboard");
|
|
|
|
const [terms, categories] = await Promise.all([
|
|
db.termsCondition.findMany({
|
|
orderBy: [{ category: { sortOrder: "asc" } }, { isActive: "desc" }, { sortOrder: "asc" }, { createdAt: "asc" }],
|
|
include: { category: { select: { name: true } } },
|
|
}),
|
|
db.termsCategory.findMany({ orderBy: [{ sortOrder: "asc" }, { name: "asc" }], select: { name: true } }),
|
|
]);
|
|
|
|
return (
|
|
<TermsTable
|
|
categoryNames={categories.map((c) => c.name)}
|
|
terms={terms.map((t) => ({
|
|
id: t.id,
|
|
categoryName: t.category.name,
|
|
text: t.text,
|
|
isDefault: t.isDefault,
|
|
isActive: t.isActive,
|
|
}))}
|
|
/>
|
|
);
|
|
}
|