mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
import { Injectable, OnModuleInit } from '@nestjs/common';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import Handlebars from 'handlebars';
|
|
|
|
import {
|
|
interpolateTemplateText,
|
|
parseArticleBody,
|
|
RenderedArticle,
|
|
} from './contract-article.util';
|
|
import { ContractViewModel } from './contract-view-model.builder';
|
|
|
|
@Injectable()
|
|
export class ContractRendererService implements OnModuleInit {
|
|
private readonly templatesDir = path.join(__dirname, 'templates');
|
|
private readonly compiled = new Map<string, Handlebars.TemplateDelegate>();
|
|
|
|
onModuleInit(): void {
|
|
Handlebars.registerHelper('eq', (a: unknown, b: unknown) => a === b);
|
|
|
|
const partialsDir = path.join(this.templatesDir, '_partials');
|
|
if (fs.existsSync(partialsDir)) {
|
|
for (const file of fs.readdirSync(partialsDir)) {
|
|
if (!file.endsWith('.hbs')) continue;
|
|
const name = file.replace(/\.hbs$/, '');
|
|
const content = fs.readFileSync(path.join(partialsDir, file), 'utf-8');
|
|
Handlebars.registerPartial(name, content);
|
|
}
|
|
}
|
|
}
|
|
|
|
render(view: ContractViewModel): string {
|
|
const fileName =
|
|
view.template.templateFile ?? 'generic.hbs';
|
|
const template = this.getCompiled(fileName);
|
|
return template({
|
|
...view,
|
|
paymentArticle: view.pricing.currency === 'ETB' ? 'ETB' : 'USD',
|
|
...this.buildDynamicSections(view),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Turn the DB-backed dynamic template (when present) into render-ready data:
|
|
* interpolate placeholders against the view model, then parse each article
|
|
* body into numbered clauses with nested bullets.
|
|
*/
|
|
private buildDynamicSections(view: ContractViewModel): {
|
|
dynamicDocumentTitle?: string;
|
|
dynamicWhereas?: string[];
|
|
dynamicArticles?: RenderedArticle[];
|
|
} {
|
|
const dyn = view.dynamicTemplate;
|
|
if (!dyn || dyn.articles.length === 0) return {};
|
|
|
|
const articles = [...dyn.articles]
|
|
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
|
.map((article, index) => ({
|
|
number: index + 1,
|
|
id: article.id,
|
|
title: interpolateTemplateText(article.title, view),
|
|
...parseArticleBody(interpolateTemplateText(article.body, view)),
|
|
}));
|
|
|
|
return {
|
|
dynamicDocumentTitle: interpolateTemplateText(dyn.documentTitle, view),
|
|
dynamicWhereas: dyn.whereasClauses.map((clause) =>
|
|
interpolateTemplateText(clause, view),
|
|
),
|
|
dynamicArticles: articles,
|
|
};
|
|
}
|
|
|
|
private getCompiled(fileName: string): Handlebars.TemplateDelegate {
|
|
const cached = this.compiled.get(fileName);
|
|
if (cached) return cached;
|
|
|
|
const filePath = path.join(this.templatesDir, fileName);
|
|
const fallbackPath = path.join(this.templatesDir, 'generic.hbs');
|
|
const source = fs.existsSync(filePath)
|
|
? fs.readFileSync(filePath, 'utf-8')
|
|
: fs.readFileSync(fallbackPath, 'utf-8');
|
|
|
|
const compiled = Handlebars.compile(source);
|
|
this.compiled.set(fileName, compiled);
|
|
return compiled;
|
|
}
|
|
}
|