make a contrat template

This commit is contained in:
Marshal
2026-07-09 21:13:07 +00:00
parent e4429592d3
commit 3443b79644
26 changed files with 3243 additions and 41 deletions

View File

@@ -0,0 +1,63 @@
import Handlebars from 'handlebars';
/** One numbered clause of a dynamic article, with optional nested bullets. */
export interface RenderedClause {
text: string;
bullets: string[];
}
/** A dynamic article ready for the Handlebars template. */
export interface RenderedArticle {
number: number;
title: string;
/** Set (instead of clauses) when the body is a single plain paragraph. */
paragraph?: string;
clauses: RenderedClause[];
}
/**
* Parse a template article body into clauses. Format: one clause per line;
* lines prefixed with "- " become bullets nested under the preceding clause.
* A body that reduces to a single clause without bullets renders as a plain
* paragraph rather than a numbered list of one.
*/
export function parseArticleBody(body: string): Pick<RenderedArticle, 'paragraph' | 'clauses'> {
const lines = (body ?? '')
.split('\n')
.map((line) => line.trim())
.filter((line) => line.length > 0);
const clauses: RenderedClause[] = [];
for (const line of lines) {
if (line.startsWith('- ')) {
const bullet = line.slice(2).trim();
if (clauses.length === 0) {
clauses.push({ text: bullet, bullets: [] });
} else {
clauses[clauses.length - 1].bullets.push(bullet);
}
} else {
clauses.push({ text: line, bullets: [] });
}
}
if (clauses.length === 1 && clauses[0].bullets.length === 0) {
return { paragraph: clauses[0].text, clauses: [] };
}
return { clauses };
}
/**
* Interpolate Handlebars placeholders ({{client.companyName}}, {{contractDate}},
* …) inside admin-authored template text against the contract view model.
* Malformed placeholders must never break document generation — fall back to
* the raw text.
*/
export function interpolateTemplateText(text: string, context: unknown): string {
if (!text || !text.includes('{{')) return text ?? '';
try {
return Handlebars.compile(text)(context);
} catch {
return text;
}
}