add custom contrat templates

This commit is contained in:
Marshal
2026-08-04 09:48:49 +00:00
parent aac1175964
commit 7c78a815eb
10 changed files with 366 additions and 58 deletions

View File

@@ -43,8 +43,19 @@ function templateDirection(code: ContractTemplate["code"]): string {
return code.split("_")[0];
}
// Codes are DIRECTION_FREIGHT_{CUSTOMS,NO_CUSTOMS}, so the freight segment is
// the second one — never the suffix.
function isBulk(code: ContractTemplate["code"]): boolean {
return code.endsWith("_BULK");
return code.split("_")[1] === "BULK";
}
// Intercity is domestic and crosses no border, so it has no customs variant at
// all — hence null rather than false, which would wrongly read as a deliberate
// "client clears its own customs" choice.
function customsVariant(code: ContractTemplate["code"]): boolean | null {
if (code.endsWith("_NO_CUSTOMS")) return false;
if (code.endsWith("_CUSTOMS")) return true;
return null;
}
function formatUpdated(value: string): string {
@@ -66,12 +77,12 @@ export default function ContractTemplatesPage() {
<PageContainer>
<PageHeader
title="Contract templates"
subtitle="The six contract documents generated when a contract is approved — one per trade direction and freight type. Articles are fully editable."
subtitle="The ten contract documents generated when a contract is approved — one per trade direction, freight type, and customs-clearing option. Intercity is domestic, so it has no customs variant. Articles are fully editable."
/>
<SimpleGrid cols={{ base: 1, md: 2, xl: 3 }} spacing="lg">
{isLoading
? Array.from({ length: 6 }, (_, i) => <TemplateCardSkeleton key={i} />)
? Array.from({ length: 10 }, (_, i) => <TemplateCardSkeleton key={i} />)
: (templates ?? []).map((template) => (
<TemplateCard
key={template.code}
@@ -104,6 +115,7 @@ function TemplateCard({
}) {
const direction = templateDirection(template.code);
const bulk = isBulk(template.code);
const customs = customsVariant(template.code);
return (
<Card
@@ -139,13 +151,29 @@ function TemplateCard({
</Text>
</Group>
</Group>
{!template.isActive && (
<Tooltip label="Not used for new contracts" withArrow>
<Badge size="sm" variant="light" color="red">
Inactive
</Badge>
</Tooltip>
)}
<Group gap={6} wrap="nowrap">
{customs !== null && (
<Tooltip
label={
customs
? "Used when the contract has customs clearing enabled"
: "Used when the client handles its own customs clearing"
}
withArrow
>
<Badge size="sm" variant="light" color={customs ? "teal" : "gray"}>
{customs ? "With customs" : "No customs"}
</Badge>
</Tooltip>
)}
{!template.isActive && (
<Tooltip label="Not used for new contracts" withArrow>
<Badge size="sm" variant="light" color="red">
Inactive
</Badge>
</Tooltip>
)}
</Group>
</Group>
{/* Name + description */}

View File

@@ -11,12 +11,18 @@ export interface ContractTemplateArticle {
export interface ContractTemplate {
id: string;
// Import/export split by customs clearing; intercity is domestic, crosses no
// border, and so has a single template.
code:
| "IMPORT_BULK"
| "EXPORT_BULK"
| "IMPORT_BULK_CUSTOMS"
| "IMPORT_BULK_NO_CUSTOMS"
| "EXPORT_BULK_CUSTOMS"
| "EXPORT_BULK_NO_CUSTOMS"
| "INTERCITY_BULK"
| "IMPORT_CONTAINER"
| "EXPORT_CONTAINER"
| "IMPORT_CONTAINER_CUSTOMS"
| "IMPORT_CONTAINER_NO_CUSTOMS"
| "EXPORT_CONTAINER_CUSTOMS"
| "EXPORT_CONTAINER_NO_CUSTOMS"
| "INTERCITY_CONTAINER";
name: string;
description?: string | null;