@hed-hog/core 0.0.328 → 0.0.330

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/dist/ai/ai.service.d.ts +1 -1
  2. package/dist/auth/auth.controller.d.ts +23 -23
  3. package/dist/auth/auth.service.d.ts +41 -41
  4. package/dist/challenge/challenge.service.d.ts +14 -14
  5. package/dist/core/system.controller.d.ts +1 -1
  6. package/dist/core/system.service.d.ts +1 -1
  7. package/dist/core.module.d.ts.map +1 -1
  8. package/dist/core.module.js +9 -2
  9. package/dist/core.module.js.map +1 -1
  10. package/dist/dashboard/dashboard/dashboard.controller.d.ts +84 -84
  11. package/dist/dashboard/dashboard/dashboard.service.d.ts +84 -84
  12. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +29 -29
  13. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +29 -29
  14. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +42 -42
  15. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +42 -42
  16. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +11 -11
  17. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +11 -11
  18. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +6 -6
  19. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +6 -6
  20. package/dist/dashboard/dashboard-role/dashboard-role.controller.d.ts +33 -33
  21. package/dist/dashboard/dashboard-role/dashboard-role.service.d.ts +33 -33
  22. package/dist/dashboard/dashboard-user/dashboard-user.controller.d.ts +3 -3
  23. package/dist/dashboard/dashboard-user/dashboard-user.service.d.ts +3 -3
  24. package/dist/file/file.controller.d.ts +10 -10
  25. package/dist/file/file.service.d.ts +22 -22
  26. package/dist/index.d.ts +11 -4
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +12 -4
  29. package/dist/index.js.map +1 -1
  30. package/dist/mail/mail.controller.d.ts +15 -15
  31. package/dist/mail/mail.service.d.ts +15 -15
  32. package/dist/mail-profile/mail-profile.controller.d.ts +9 -9
  33. package/dist/mail-profile/mail-profile.service.d.ts +9 -9
  34. package/dist/mail-sent/mail-sent.controller.d.ts +17 -17
  35. package/dist/mail-sent/mail-sent.service.d.ts +17 -17
  36. package/dist/mcp/decorators/mcp-tool.decorator.d.ts +4 -0
  37. package/dist/mcp/decorators/mcp-tool.decorator.d.ts.map +1 -0
  38. package/dist/mcp/decorators/mcp-tool.decorator.js +8 -0
  39. package/dist/mcp/decorators/mcp-tool.decorator.js.map +1 -0
  40. package/dist/mcp/guards/mcp-auth.guard.d.ts +9 -0
  41. package/dist/mcp/guards/mcp-auth.guard.d.ts.map +1 -0
  42. package/dist/mcp/guards/mcp-auth.guard.js +41 -0
  43. package/dist/mcp/guards/mcp-auth.guard.js.map +1 -0
  44. package/dist/mcp/guards/mcp-permission.guard.d.ts +8 -0
  45. package/dist/mcp/guards/mcp-permission.guard.d.ts.map +1 -0
  46. package/dist/mcp/guards/mcp-permission.guard.js +57 -0
  47. package/dist/mcp/guards/mcp-permission.guard.js.map +1 -0
  48. package/dist/mcp/interfaces/mcp-tool-definition.interface.d.ts +7 -0
  49. package/dist/mcp/interfaces/mcp-tool-definition.interface.d.ts.map +1 -0
  50. package/dist/mcp/interfaces/mcp-tool-definition.interface.js +3 -0
  51. package/dist/mcp/interfaces/mcp-tool-definition.interface.js.map +1 -0
  52. package/dist/mcp/mcp-audit.service.d.ts +17 -0
  53. package/dist/mcp/mcp-audit.service.d.ts.map +1 -0
  54. package/dist/mcp/mcp-audit.service.js +46 -0
  55. package/dist/mcp/mcp-audit.service.js.map +1 -0
  56. package/dist/mcp/mcp-registry.service.d.ts +21 -0
  57. package/dist/mcp/mcp-registry.service.d.ts.map +1 -0
  58. package/dist/mcp/mcp-registry.service.js +69 -0
  59. package/dist/mcp/mcp-registry.service.js.map +1 -0
  60. package/dist/mcp/mcp-server.service.d.ts +12 -0
  61. package/dist/mcp/mcp-server.service.d.ts.map +1 -0
  62. package/dist/mcp/mcp-server.service.js +94 -0
  63. package/dist/mcp/mcp-server.service.js.map +1 -0
  64. package/dist/mcp/mcp.controller.d.ts +8 -0
  65. package/dist/mcp/mcp.controller.d.ts.map +1 -0
  66. package/dist/mcp/mcp.controller.js +54 -0
  67. package/dist/mcp/mcp.controller.js.map +1 -0
  68. package/dist/mcp/mcp.module.d.ts +3 -0
  69. package/dist/mcp/mcp.module.d.ts.map +1 -0
  70. package/dist/mcp/mcp.module.js +49 -0
  71. package/dist/mcp/mcp.module.js.map +1 -0
  72. package/dist/mcp/tools/core-settings.mcp-tools.d.ts +12 -0
  73. package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -0
  74. package/dist/mcp/tools/core-settings.mcp-tools.js +66 -0
  75. package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -0
  76. package/dist/mcp/tools/core-users.mcp-tools.d.ts +55 -0
  77. package/dist/mcp/tools/core-users.mcp-tools.d.ts.map +1 -0
  78. package/dist/mcp/tools/core-users.mcp-tools.js +295 -0
  79. package/dist/mcp/tools/core-users.mcp-tools.js.map +1 -0
  80. package/dist/mcp/types/mcp-context.type.d.ts +7 -0
  81. package/dist/mcp/types/mcp-context.type.d.ts.map +1 -0
  82. package/dist/mcp/types/mcp-context.type.js +3 -0
  83. package/dist/mcp/types/mcp-context.type.js.map +1 -0
  84. package/dist/mcp-chat/dto/create-conversation.dto.d.ts +4 -0
  85. package/dist/mcp-chat/dto/create-conversation.dto.d.ts.map +1 -0
  86. package/dist/mcp-chat/dto/create-conversation.dto.js +22 -0
  87. package/dist/mcp-chat/dto/create-conversation.dto.js.map +1 -0
  88. package/dist/mcp-chat/dto/send-message.dto.d.ts +4 -0
  89. package/dist/mcp-chat/dto/send-message.dto.d.ts.map +1 -0
  90. package/dist/mcp-chat/dto/send-message.dto.js +22 -0
  91. package/dist/mcp-chat/dto/send-message.dto.js.map +1 -0
  92. package/dist/mcp-chat/mcp-chat.controller.d.ts +55 -0
  93. package/dist/mcp-chat/mcp-chat.controller.d.ts.map +1 -0
  94. package/dist/mcp-chat/mcp-chat.controller.js +90 -0
  95. package/dist/mcp-chat/mcp-chat.controller.js.map +1 -0
  96. package/dist/mcp-chat/mcp-chat.module.d.ts +3 -0
  97. package/dist/mcp-chat/mcp-chat.module.d.ts.map +1 -0
  98. package/dist/mcp-chat/mcp-chat.module.js +30 -0
  99. package/dist/mcp-chat/mcp-chat.module.js.map +1 -0
  100. package/dist/mcp-chat/mcp-chat.service.d.ts +50 -0
  101. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -0
  102. package/dist/mcp-chat/mcp-chat.service.js +290 -0
  103. package/dist/mcp-chat/mcp-chat.service.js.map +1 -0
  104. package/dist/menu/menu.controller.d.ts +8 -8
  105. package/dist/menu/menu.service.d.ts +8 -8
  106. package/dist/oauth/oauth.service.js +1 -1
  107. package/dist/oauth/oauth.service.js.map +1 -1
  108. package/dist/pdf/components/index.d.ts +57 -0
  109. package/dist/pdf/components/index.d.ts.map +1 -0
  110. package/dist/pdf/components/index.js +258 -0
  111. package/dist/pdf/components/index.js.map +1 -0
  112. package/dist/pdf/dto/generate-pdf.dto.d.ts +5 -0
  113. package/dist/pdf/dto/generate-pdf.dto.d.ts.map +1 -0
  114. package/dist/pdf/dto/generate-pdf.dto.js +27 -0
  115. package/dist/pdf/dto/generate-pdf.dto.js.map +1 -0
  116. package/dist/pdf/interfaces/pdf-theme-config.interface.d.ts +10 -0
  117. package/dist/pdf/interfaces/pdf-theme-config.interface.d.ts.map +1 -0
  118. package/dist/pdf/interfaces/pdf-theme-config.interface.js +3 -0
  119. package/dist/pdf/interfaces/pdf-theme-config.interface.js.map +1 -0
  120. package/dist/pdf/pdf.controller.d.ts +16 -0
  121. package/dist/pdf/pdf.controller.d.ts.map +1 -0
  122. package/dist/pdf/pdf.controller.js +100 -0
  123. package/dist/pdf/pdf.controller.js.map +1 -0
  124. package/dist/pdf/pdf.module.d.ts +3 -0
  125. package/dist/pdf/pdf.module.d.ts.map +1 -0
  126. package/dist/pdf/pdf.module.js +29 -0
  127. package/dist/pdf/pdf.module.js.map +1 -0
  128. package/dist/pdf/pdf.service.d.ts +44 -0
  129. package/dist/pdf/pdf.service.d.ts.map +1 -0
  130. package/dist/pdf/pdf.service.js +185 -0
  131. package/dist/pdf/pdf.service.js.map +1 -0
  132. package/dist/pdf/templates/hcode-institutional.template.d.ts +36 -0
  133. package/dist/pdf/templates/hcode-institutional.template.d.ts.map +1 -0
  134. package/dist/pdf/templates/hcode-institutional.template.js +67 -0
  135. package/dist/pdf/templates/hcode-institutional.template.js.map +1 -0
  136. package/dist/pdf/themes/hcode.theme.d.ts +25 -0
  137. package/dist/pdf/themes/hcode.theme.d.ts.map +1 -0
  138. package/dist/pdf/themes/hcode.theme.js +504 -0
  139. package/dist/pdf/themes/hcode.theme.js.map +1 -0
  140. package/dist/profile/profile.controller.d.ts +16 -16
  141. package/dist/profile/profile.service.d.ts +16 -16
  142. package/dist/role/role.controller.d.ts +12 -12
  143. package/dist/role/role.service.d.ts +12 -12
  144. package/dist/route/dto/create.dto.d.ts +6 -2
  145. package/dist/route/dto/create.dto.d.ts.map +1 -1
  146. package/dist/route/dto/create.dto.js +19 -1
  147. package/dist/route/dto/create.dto.js.map +1 -1
  148. package/dist/route/dto/update.dto.d.ts +4 -0
  149. package/dist/route/dto/update.dto.d.ts.map +1 -1
  150. package/dist/route/dto/update.dto.js +19 -3
  151. package/dist/route/dto/update.dto.js.map +1 -1
  152. package/dist/route/route.service.d.ts +1 -1
  153. package/dist/route/route.service.d.ts.map +1 -1
  154. package/dist/route/route.service.js +18 -11
  155. package/dist/route/route.service.js.map +1 -1
  156. package/dist/screen/screen.controller.d.ts +3 -3
  157. package/dist/screen/screen.service.d.ts +3 -3
  158. package/dist/session/session.controller.d.ts +4 -4
  159. package/dist/session/session.service.d.ts +12 -12
  160. package/dist/setting/setting.controller.d.ts +13 -13
  161. package/dist/setting/setting.service.d.ts +13 -13
  162. package/dist/user/user.controller.d.ts +24 -24
  163. package/dist/user/user.service.d.ts +55 -55
  164. package/hedhog/data/menu.yaml +40 -26
  165. package/hedhog/data/route.yaml +583 -152
  166. package/hedhog/data/setting_group.yaml +60 -0
  167. package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +574 -0
  168. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +5 -9
  169. package/hedhog/frontend/messages/en.json +19 -0
  170. package/hedhog/frontend/messages/pt.json +19 -0
  171. package/hedhog/table/mcp_conversation.yaml +12 -0
  172. package/hedhog/table/mcp_conversation_message.yaml +23 -0
  173. package/hedhog/table/mcp_tool_call_log.yaml +22 -0
  174. package/hedhog/table/route.yaml +33 -22
  175. package/package.json +7 -6
  176. package/src/core.module.ts +18 -11
  177. package/src/index.ts +22 -12
  178. package/src/mcp/decorators/mcp-tool.decorator.ts +7 -0
  179. package/src/mcp/guards/mcp-auth.guard.ts +32 -0
  180. package/src/mcp/guards/mcp-permission.guard.ts +51 -0
  181. package/src/mcp/interfaces/mcp-tool-definition.interface.ts +6 -0
  182. package/src/mcp/mcp-audit.service.ts +36 -0
  183. package/src/mcp/mcp-registry.service.ts +69 -0
  184. package/src/mcp/mcp-server.service.ts +87 -0
  185. package/src/mcp/mcp.controller.ts +30 -0
  186. package/src/mcp/mcp.module.ts +36 -0
  187. package/src/mcp/tools/core-settings.mcp-tools.ts +53 -0
  188. package/src/mcp/tools/core-users.mcp-tools.ts +267 -0
  189. package/src/mcp/types/mcp-context.type.ts +6 -0
  190. package/src/mcp-chat/dto/create-conversation.dto.ts +7 -0
  191. package/src/mcp-chat/dto/send-message.dto.ts +7 -0
  192. package/src/mcp-chat/mcp-chat.controller.ts +60 -0
  193. package/src/mcp-chat/mcp-chat.module.ts +17 -0
  194. package/src/mcp-chat/mcp-chat.service.ts +332 -0
  195. package/src/oauth/oauth.service.ts +12 -12
  196. package/src/pdf/components/index.ts +316 -0
  197. package/src/pdf/dto/generate-pdf.dto.ts +11 -0
  198. package/src/pdf/interfaces/pdf-theme-config.interface.ts +10 -0
  199. package/src/pdf/pdf.controller.ts +86 -0
  200. package/src/pdf/pdf.module.ts +17 -0
  201. package/src/pdf/pdf.service.ts +229 -0
  202. package/src/pdf/templates/hcode-institutional.template.ts +122 -0
  203. package/src/pdf/themes/hcode.theme.ts +515 -0
  204. package/src/route/dto/create.dto.ts +32 -14
  205. package/src/route/dto/update.dto.ts +31 -16
  206. package/src/route/route.service.ts +23 -12
@@ -0,0 +1,229 @@
1
+ import { Inject, Injectable, InternalServerErrorException, Logger, forwardRef } from '@nestjs/common';
2
+ import { ConfigService } from '@nestjs/config';
3
+
4
+ import { SettingService } from '../setting/setting.service';
5
+ import { PdfAreaType, PdfThemeConfig } from './interfaces/pdf-theme-config.interface';
6
+ import { hcodeInstitutionalTemplate } from './templates/hcode-institutional.template';
7
+ import { AreaDefaults, HCODE_DEFAULTS } from './themes/hcode.theme';
8
+
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ // Types
11
+ // ─────────────────────────────────────────────────────────────────────────────
12
+
13
+ export interface PdfGenerateOptions {
14
+ format?: 'A4' | 'A3' | 'Letter';
15
+ margin?: { top?: string; right?: string; bottom?: string; left?: string };
16
+ printBackground?: boolean;
17
+ landscape?: boolean;
18
+ }
19
+
20
+ // ─────────────────────────────────────────────────────────────────────────────
21
+ // Service
22
+ // ─────────────────────────────────────────────────────────────────────────────
23
+
24
+ @Injectable()
25
+ export class PdfService {
26
+ private readonly logger = new Logger(PdfService.name);
27
+
28
+ constructor(
29
+ @Inject(forwardRef(() => SettingService))
30
+ private readonly settingService: SettingService,
31
+ private readonly configService: ConfigService,
32
+ ) {}
33
+
34
+ // ── Theme config ─────────────────────────────────────────────────────────
35
+
36
+ /**
37
+ * Resolves the active PdfThemeConfig for the given area.
38
+ * Reads admin-configured overrides from SettingService and falls back to
39
+ * the hard-coded Design System v1.0 defaults.
40
+ */
41
+ async loadThemeConfig(area: PdfAreaType = 'consulting'): Promise<PdfThemeConfig> {
42
+ const defaults: AreaDefaults = HCODE_DEFAULTS[area];
43
+
44
+ try {
45
+ const values = await this.settingService.getSettingValues([
46
+ 'pdf-company-name',
47
+ 'pdf-logo',
48
+ `pdf-${area}-color`,
49
+ 'theme-primary-light', // system-wide brand color
50
+ 'image-url', // system logo
51
+ 'system-name', // system display name fallback
52
+ ]);
53
+
54
+ // Color priority:
55
+ // 1. pdf-${area}-color if admin explicitly changed it from the DS default
56
+ // 2. theme-primary-light (global brand color configured in Settings)
57
+ // 3. DS hard-coded default for the area
58
+ const areaSpecificColor = values[`pdf-${area}-color`] as string;
59
+ const primaryColor: string =
60
+ (areaSpecificColor && areaSpecificColor !== defaults.primary)
61
+ ? areaSpecificColor
62
+ : (values['theme-primary-light'] as string) || defaults.primary;
63
+
64
+ const areaDefault =
65
+ primaryColor === defaults.primary
66
+ ? defaults
67
+ : { ...defaults, primary: primaryColor };
68
+
69
+ // Use pdf-logo if set, otherwise fall back to the system image-url setting.
70
+ // Relative paths (e.g. /logo.svg) are resolved to absolute so Playwright can fetch them.
71
+ const rawLogoUrl: string =
72
+ (values['pdf-logo'] as string) ||
73
+ (values['image-url'] as string) ||
74
+ '';
75
+ const logoOverride = rawLogoUrl ? this.resolveUrl(rawLogoUrl) : '';
76
+
77
+ return {
78
+ area,
79
+ companyName:
80
+ (values['pdf-company-name'] as string) ||
81
+ (values['system-name'] as string) ||
82
+ 'hcode',
83
+ logoOverride,
84
+ primaryColor: areaDefault.primary,
85
+ darkColor: areaDefault.dark,
86
+ lightColor: areaDefault.light,
87
+ };
88
+ } catch {
89
+ this.logger.warn(
90
+ `Could not load PDF settings from SettingService – using Design System defaults for area "${area}".`,
91
+ );
92
+ return {
93
+ area,
94
+ companyName: 'hcode',
95
+ logoOverride: '',
96
+ primaryColor: defaults.primary,
97
+ darkColor: defaults.dark,
98
+ lightColor: defaults.light,
99
+ };
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Resolves a potentially relative URL (e.g. /logo.svg) to an absolute URL
105
+ * using the API base URL derived from environment variables.
106
+ */
107
+ private resolveUrl(url: string): string {
108
+ if (!url) return '';
109
+ if (
110
+ url.startsWith('data:') ||
111
+ url.startsWith('http://') ||
112
+ url.startsWith('https://')
113
+ ) {
114
+ return url;
115
+ }
116
+ const port = this.configService.get<string | number>('PORT') ?? 3100;
117
+ const baseUrl =
118
+ this.configService.get<string>('API_URL') ??
119
+ `http://localhost:${port}`;
120
+ return (
121
+ baseUrl.replace(/\/$/, '') +
122
+ (url.startsWith('/') ? url : '/' + url)
123
+ );
124
+ }
125
+
126
+ // ── HTML → PDF ────────────────────────────────────────────────────────────
127
+
128
+ /**
129
+ * Renders an HTML string to a PDF buffer using Playwright/Chromium.
130
+ *
131
+ * Uses dynamic `import()` via `new Function` to remain compatible with
132
+ * CommonJS bundle output (same pattern as operations.service.ts).
133
+ */
134
+ async generatePdfFromHtml(html: string, options: PdfGenerateOptions = {}): Promise<Buffer> {
135
+ const executablePath = this.configService.get<string>('CHROMIUM_EXECUTABLE_PATH');
136
+ const noSandbox = this.configService.get<string>('CHROMIUM_NO_SANDBOX') === 'true';
137
+
138
+ const launchArgs = noSandbox
139
+ ? ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
140
+ : [];
141
+
142
+ let browser: any = null;
143
+
144
+ try {
145
+ const importPlaywright = new Function(
146
+ 'moduleName',
147
+ 'return import(moduleName);',
148
+ ) as (moduleName: string) => Promise<any>;
149
+
150
+ const playwright = await importPlaywright('playwright');
151
+
152
+ browser = await playwright.chromium.launch({
153
+ headless: true,
154
+ ...(executablePath ? { executablePath } : {}),
155
+ args: launchArgs,
156
+ });
157
+
158
+ const page = await browser.newPage();
159
+
160
+ // A4 viewport for proper page layout
161
+ await page.setViewportSize({ width: 794, height: 1123 });
162
+ await page.setContent(html, { waitUntil: 'networkidle' });
163
+
164
+ const pdfBuffer = await page.pdf({
165
+ format: options.format ?? 'A4',
166
+ printBackground: options.printBackground ?? true,
167
+ landscape: options.landscape ?? false,
168
+ margin: options.margin ?? { top: '0', right: '0', bottom: '0', left: '0' },
169
+ });
170
+
171
+ return Buffer.from(pdfBuffer);
172
+ } catch (error) {
173
+ const errorMessage = error instanceof Error ? error.message : String(error);
174
+ const errorStack = error instanceof Error ? (error.stack ?? errorMessage) : String(error);
175
+
176
+ const missingRuntime =
177
+ /Cannot find package ['"]playwright['"]|Cannot find module ['"]playwright['"]|Executable doesn't exist|browserType\.launch|Failed to launch|Please run the following command/i.test(
178
+ errorMessage,
179
+ );
180
+
181
+ this.logger.error(`PDF generation failed. ${errorMessage}`, errorStack);
182
+
183
+ if (missingRuntime) {
184
+ throw new InternalServerErrorException(
185
+ 'Playwright runtime is not available. Run `npx playwright install chromium` in apps/api.',
186
+ );
187
+ }
188
+
189
+ throw new InternalServerErrorException(`PDF generation failed: ${errorMessage}`);
190
+ } finally {
191
+ if (browser) {
192
+ try {
193
+ await browser.close();
194
+ } catch {
195
+ // Ignore close errors
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ // ── Template engine ───────────────────────────────────────────────────────
202
+
203
+ /**
204
+ * Generates a PDF from a named template and structured data.
205
+ * The theme is resolved automatically from `data.area` via SettingService.
206
+ */
207
+ async generatePdfFromTemplate(
208
+ templateName: string,
209
+ data: Record<string, any>,
210
+ options?: PdfGenerateOptions,
211
+ ): Promise<Buffer> {
212
+ const area = (data.area as PdfAreaType) ?? 'consulting';
213
+ const themeConfig = await this.loadThemeConfig(area);
214
+
215
+ let html: string;
216
+
217
+ switch (templateName) {
218
+ case 'hcode-institutional':
219
+ html = hcodeInstitutionalTemplate(data as any, themeConfig);
220
+ break;
221
+ default:
222
+ throw new InternalServerErrorException(
223
+ `Unknown PDF template: "${templateName}". Available: hcode-institutional`,
224
+ );
225
+ }
226
+
227
+ return this.generatePdfFromHtml(html, options);
228
+ }
229
+ }
@@ -0,0 +1,122 @@
1
+ import {
2
+ coverPage,
3
+ CoverPageData,
4
+ dataTable,
5
+ FeatureCardData,
6
+ featureCards,
7
+ metadataBlock,
8
+ pageAccentBar,
9
+ pageHeader,
10
+ pageShell,
11
+ pdfFooter,
12
+ purposeBlock,
13
+ sectionHeader,
14
+ signatureArea,
15
+ } from '../components';
16
+ import { PdfThemeConfig } from '../interfaces/pdf-theme-config.interface';
17
+
18
+ // ─────────────────────────────────────────────────────────────────────────────
19
+ // Data contract
20
+ // ─────────────────────────────────────────────────────────────────────────────
21
+
22
+ export interface HcodeInstitutionalData {
23
+ area?: 'consulting' | 'training' | 'research';
24
+ title: string;
25
+ subtitle?: string;
26
+ /** Long-form description rendered as purpose block (primary-color background) */
27
+ description?: string;
28
+ features?: FeatureCardData[];
29
+ tableHeaders?: string[];
30
+ tableRows?: string[][];
31
+ metadata?: Array<{ label: string; value: string }>;
32
+ signatories?: Array<{ label: string; name?: string }>;
33
+ /** Override the bottom meta line on the cover (default: "HCODE · CNPJ 24.700.731/0001-08") */
34
+ coverMeta?: string;
35
+ /** Override the page title shown in the repeating header */
36
+ pageTitle?: string;
37
+ }
38
+
39
+ // ─────────────────────────────────────────────────────────────────────────────
40
+ // Template
41
+ // ─────────────────────────────────────────────────────────────────────────────
42
+
43
+ /**
44
+ * hcode Institutional template — Design System v1.0
45
+ *
46
+ * Structure:
47
+ * Page 1 — Full-bleed cover (break-after)
48
+ * Page 2+ — accent bar + header + content sections
49
+ *
50
+ * All sections are optional; only present when the corresponding data
51
+ * field is provided and non-empty.
52
+ */
53
+ export function hcodeInstitutionalTemplate(
54
+ data: HcodeInstitutionalData,
55
+ config: PdfThemeConfig,
56
+ ): string {
57
+ const coverData: CoverPageData = {
58
+ title: data.title,
59
+ subtitle: data.subtitle,
60
+ bottomMeta: data.coverMeta,
61
+ };
62
+
63
+ const pageTitle = data.pageTitle ?? `${config.area.toUpperCase()} · ${data.title}`;
64
+
65
+ // ── Content sections ──────────────────────────────────────────────────────
66
+ const sections: string[] = [];
67
+
68
+ // Purpose block (description)
69
+ if (data.description) {
70
+ sections.push(
71
+ purposeBlock(
72
+ 'SOBRE O PROJETO',
73
+ config.companyName.toUpperCase(),
74
+ data.description,
75
+ ),
76
+ );
77
+ }
78
+
79
+ // Feature cards
80
+ if (data.features && data.features.length > 0) {
81
+ sections.push(sectionHeader('DIFERENCIAIS', 'O que entregamos'));
82
+ sections.push(featureCards(data.features));
83
+ }
84
+
85
+ // Data table
86
+ if (
87
+ data.tableHeaders &&
88
+ data.tableHeaders.length > 0 &&
89
+ data.tableRows &&
90
+ data.tableRows.length > 0
91
+ ) {
92
+ sections.push(sectionHeader('EXECUÇÃO', 'Plano de trabalho'));
93
+ sections.push(dataTable(data.tableHeaders, data.tableRows));
94
+ }
95
+
96
+ // Metadata block
97
+ if (data.metadata && data.metadata.length > 0) {
98
+ sections.push(metadataBlock(data.metadata));
99
+ }
100
+
101
+ // Signature area
102
+ if (data.signatories && data.signatories.length > 0) {
103
+ sections.push(sectionHeader('ACEITE', 'Assinaturas'));
104
+ sections.push(signatureArea(data.signatories));
105
+ }
106
+
107
+ // ── Assembly ──────────────────────────────────────────────────────────────
108
+ const body = [
109
+ // Page 1: cover
110
+ coverPage(config, coverData),
111
+
112
+ // Page 2+: content
113
+ pageAccentBar(),
114
+ pageHeader(config, pageTitle),
115
+ `<div class="page-content">${sections.join('\n')}</div>`,
116
+
117
+ // Fixed footer (appears on every page including cover)
118
+ pdfFooter(config.companyName),
119
+ ].join('\n');
120
+
121
+ return pageShell('', body, config);
122
+ }