@medicus.ai/medicus-report-pdf-generator 1.3.13 → 1.3.15
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.
- package/.vscode/settings.json +3 -3
- package/HANDOVER.md +146 -0
- package/README.md +26 -26
- package/app/i18n.config.js +20 -20
- package/app/i18n_wellbeing.config.js +20 -20
- package/app/services/localeService.js +28 -28
- package/assets/Mediclinic/css/nasco_report.css +6297 -6297
- package/assets/Mediclinic/css/nasco_report_rtl.css +851 -851
- package/assets/Mediclinic/css/style.css +33 -33
- package/assets/Mediclinic/js/js.js +112 -112
- package/assets/Mediclinic/js/raphael.js +8437 -8437
- package/assets/arabic.css +275 -275
- package/assets/charts.min.js +7 -7
- package/assets/corporate_report/css/report.css +582 -582
- package/assets/corporate_report/css/report_rtl.css +144 -144
- package/assets/corporate_report/js/js.js +14 -14
- package/assets/data/data.json +124 -124
- package/assets/data/data2.json +675 -675
- package/assets/data/pdf_data_with_history.json +1255 -1255
- package/assets/doctor-note-insight-icon.svg +11 -11
- package/assets/imc/css/style.css +61 -61
- package/assets/jquery-1.4.min.js +151 -151
- package/assets/jquery-2.1.0.min.js +3 -3
- package/assets/js.js +201 -201
- package/assets/medicus_pdf/arabic.css +444 -444
- package/assets/medicus_pdf/styles.css +2427 -2427
- package/assets/print.css +4 -4
- package/assets/qr-code-style.css +68 -68
- package/assets/qrcode.min.js +3 -3
- package/assets/raphael-min.js +10 -10
- package/assets/sanusx/css/sanusx_report.css +707 -707
- package/assets/sizing.js +2 -2
- package/assets/styles.css +1545 -1545
- package/assets/styles.min.css +1358 -1358
- package/assets/translation.js +30 -30
- package/assets/wellbeing/css/nasco_report.css +3472 -3472
- package/assets/wellbeing/css/nasco_report_rtl.css +679 -679
- package/assets/wellbeing/js/js.js +111 -111
- package/assets/wellbeing/js/raphael.js +8437 -8437
- package/base64/cairo-font.js +2 -2
- package/config/Mediclinic.json +37 -37
- package/config/Najeeb.ai.json +35 -35
- package/config/Pha.json +64 -64
- package/config/bionext.json +37 -37
- package/config/default.json +35 -35
- package/config/diagnostikare.json +34 -34
- package/config/maisonsante.json +39 -39
- package/config/nasco.json +35 -35
- package/config/sanitas.json +35 -35
- package/config/sanusx.json +34 -34
- package/config/test.txt +4 -4
- package/data converter.js +53 -53
- package/docs/01-architecture-overview.md +149 -0
- package/docs/02-configuration-and-whitelabelling.md +111 -0
- package/docs/03-templating-and-rendering.md +71 -0
- package/docs/04-report-pipelines.md +128 -0
- package/docs/05-questions-and-data-model.md +67 -0
- package/docs/06-internationalization.md +59 -0
- package/docs/07-email-notifications.md +59 -0
- package/docs/08-known-issues-and-technical-debt.md +41 -0
- package/docs/README.md +22 -0
- package/downloadfile.js +6 -6
- package/example.txt +1188 -1188
- package/index.js +481 -480
- package/lib/big_integral_questionnaire.js +370 -298
- package/lib/corporate_report_generator.js +399 -399
- package/lib/pdf_generator.min.js +525 -525
- package/lib/sanusx_report_generator.js +533 -533
- package/lib/sendEmail.js +343 -343
- package/lib/template.js +2227 -2227
- package/lib/wellbeing_report_generator.js +2164 -2134
- package/locales/ar-AE.json +169 -169
- package/locales/ar-SA.json +169 -169
- package/locales/ar.json +397 -397
- package/locales/de.json +169 -169
- package/locales/en.json +229 -229
- package/locales/fr.json +168 -168
- package/locales/pt.json +165 -165
- package/locales/wellbeing/ar-AE.json +149 -149
- package/locales/wellbeing/ar-SA.json +257 -257
- package/locales/wellbeing/de.json +301 -301
- package/locales/wellbeing/en.json +423 -423
- package/locales/wellbeing/es.json +133 -133
- package/locales/wellbeing/fr.json +121 -121
- package/locales/wellbeing/it-IT.json +121 -121
- package/locales/wellbeing/pt.json +121 -121
- package/locales/wellbeing/tr.json +121 -121
- package/locales/wellbeing/zh-CN.json +121 -121
- package/locales/zh-CN.json +165 -165
- package/package.json +47 -47
- package/preview-big-integral.js +75 -75
- package/run.js +15 -15
- package/templates/Mediclinic/blocks/first-section.html +205 -205
- package/templates/Mediclinic/blocks/footer.html +7 -7
- package/templates/Mediclinic/blocks/header-template.html +5 -5
- package/templates/Mediclinic/blocks/header.html +20 -20
- package/templates/Mediclinic/blocks/patient-table.html +38 -38
- package/templates/Mediclinic/blocks/pdf-header.html +9 -9
- package/templates/Mediclinic/blocks/tips.html +8 -8
- package/templates/Mediclinic/ltr_no_pages.html +37 -37
- package/templates/Mediclinic/rtl_no_pages.html +42 -42
- package/templates/Mediclinic/wellbeing_template.html +41 -41
- package/templates/Pha/blocks/first-section.html +207 -207
- package/templates/Pha/blocks/footer.html +7 -7
- package/templates/Pha/blocks/header-template.html +5 -5
- package/templates/Pha/blocks/header.html +20 -20
- package/templates/Pha/blocks/patient-table.html +38 -38
- package/templates/Pha/blocks/pdf-header.html +8 -8
- package/templates/Pha/blocks/tips.html +8 -8
- package/templates/Pha/ltr_no_pages.html +37 -37
- package/templates/Pha/rtl_no_pages.html +42 -42
- package/templates/Pha/wellbeing_template.html +41 -41
- package/templates/base.html +23 -23
- package/templates/blocks/ar-first-page-header.html +68 -68
- package/templates/blocks/ar-footer.html +54 -54
- package/templates/blocks/ar-header.html +67 -67
- package/templates/blocks/biomarker-compact-ar.html +27 -27
- package/templates/blocks/biomarker-compact.html +27 -27
- package/templates/blocks/biomarker-details.html +41 -41
- package/templates/blocks/biomarker-insight.html +22 -22
- package/templates/blocks/biomarker-min.html +9 -9
- package/templates/blocks/doctor-note.html +11 -11
- package/templates/blocks/footer.html +35 -35
- package/templates/blocks/header-first-page.html +46 -46
- package/templates/blocks/header.html +49 -49
- package/templates/blocks/panel-compact.html +38 -38
- package/templates/blocks/panel-details.html +16 -16
- package/templates/blocks/report-summary.html +16 -16
- package/templates/blocks/section-title.html +4 -4
- package/templates/blocks/signature.html +16 -16
- package/templates/blocks/summary-insight.html +15 -15
- package/templates/blocks/summary.html +9 -9
- package/templates/corporate_report/cover_page.html +23 -23
- package/templates/corporate_report/footer.html +9 -9
- package/templates/corporate_report/ltr_no_pages.html +20 -20
- package/templates/corporate_report/page.html +24 -24
- package/templates/corporate_report/participant_analytics.html +5 -5
- package/templates/corporate_report/rtl_no_pages.html +20 -20
- package/templates/empty.html +10 -10
- package/templates/first_page_head.html +157 -157
- package/templates/imc/first-header-template.html +53 -53
- package/templates/ltr.html +1368 -1368
- package/templates/ltr_no_pages.html +89 -89
- package/templates/maisonsante/blocks/first-section.html +151 -207
- package/templates/maisonsante/blocks/footer.html +7 -7
- package/templates/maisonsante/blocks/header-template.html +5 -5
- package/templates/maisonsante/blocks/header.html +20 -20
- package/templates/maisonsante/blocks/patient-table.html +38 -38
- package/templates/maisonsante/blocks/pdf-header.html +8 -8
- package/templates/maisonsante/blocks/tips.html +8 -8
- package/templates/maisonsante/ltr_no_pages.html +38 -38
- package/templates/maisonsante/rtl_no_pages.html +43 -43
- package/templates/maisonsante/wellbeing_template.html +41 -41
- package/templates/no_pages.html +1224 -1224
- package/templates/popup/popup-template.html +129 -129
- package/templates/rtl_no_pages.html +92 -92
- package/templates/sanusx/blocks/footer.html +6 -6
- package/templates/sanusx/blocks/header.html +20 -20
- package/templates/sanusx/blocks/personal-details.html +123 -123
- package/templates/sanusx/blocks/predictions-section.html +57 -57
- package/templates/sanusx/blocks/recommendations-section.html +6 -6
- package/templates/sanusx/blocks/super-power-section.html +29 -29
- package/templates/sanusx/blocks/tips.html +8 -8
- package/templates/sanusx/ltr_no_pages.html +26 -26
- package/templates/sanusx/rtl_no_pages.html +36 -36
- package/templates/template.html +1377 -1377
- package/templates/wellbeing/blocks/first-section.html +106 -106
- package/templates/wellbeing/blocks/footer.html +7 -7
- package/templates/wellbeing/blocks/header.html +37 -37
- package/templates/wellbeing/blocks/tips.html +8 -8
- package/templates/wellbeing/ltr_no_pages.html +35 -35
- package/templates/wellbeing/rtl_no_pages.html +36 -36
- package/templates/wellbeing/wellbeing_template.html +43 -43
- package/test.js +74 -74
- package/testing-reports/mediclinic/arabic-data.js +0 -439
- package/testing-reports/mediclinic/doctor-data.js +0 -523
- package/testing-reports/mediclinic/with-wellbeing.js +0 -415
- package/testing-reports/pha/notes-example.js +0 -397
|
@@ -1,298 +1,370 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// Dummy data — structure mirrors the Excel RESULTS KEY sheet.
|
|
5
|
-
// result = 0 for all rows (questionnaire not yet filled in).
|
|
6
|
-
// Hormonal Health rows have no High/Questions/Maximum — rendered as "—".
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
function getDummyData() {
|
|
9
|
-
return {
|
|
10
|
-
rows: [
|
|
11
|
-
{
|
|
12
|
-
system: "STOMACH and GI (Gastrointestinal) Tract",
|
|
13
|
-
high: ">8",
|
|
14
|
-
questions: 9,
|
|
15
|
-
result: 0,
|
|
16
|
-
maximum: 27,
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
system: "Small Intestine",
|
|
20
|
-
high: ">7",
|
|
21
|
-
questions: 8,
|
|
22
|
-
result: 0,
|
|
23
|
-
maximum: 24,
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
system: "Infections/Invasions of the GI (Gastrointestinal Tract)",
|
|
27
|
-
high: ">3",
|
|
28
|
-
questions: 4,
|
|
29
|
-
result: 0,
|
|
30
|
-
maximum: 12,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
system: "Large Intestine",
|
|
34
|
-
high: ">2",
|
|
35
|
-
questions: 3,
|
|
36
|
-
result: 0,
|
|
37
|
-
maximum: 9,
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
system: "Liver and Gallbladder",
|
|
41
|
-
high: ">7",
|
|
42
|
-
questions: 8,
|
|
43
|
-
result: 0,
|
|
44
|
-
maximum: 22,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
system: "Essential Nutrients",
|
|
48
|
-
high: ">25",
|
|
49
|
-
questions: 26,
|
|
50
|
-
result: 0,
|
|
51
|
-
maximum: 68,
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
system: "Carbohydrate Metabolism",
|
|
55
|
-
high: ">4",
|
|
56
|
-
questions: 4,
|
|
57
|
-
result: 0,
|
|
58
|
-
maximum: 12,
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
system: "Immune System",
|
|
62
|
-
high: ">3",
|
|
63
|
-
questions: 4,
|
|
64
|
-
result: 0,
|
|
65
|
-
maximum: 10,
|
|
66
|
-
},
|
|
67
|
-
{ system: "Lifestyle", high: ">7", questions: 7, result: 0, maximum: 7 },
|
|
68
|
-
{
|
|
69
|
-
system: "Hormonal Health Female",
|
|
70
|
-
high: null,
|
|
71
|
-
questions: null,
|
|
72
|
-
result: 0,
|
|
73
|
-
maximum: null,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
system: "Hormonal Health Male",
|
|
77
|
-
high: null,
|
|
78
|
-
questions: null,
|
|
79
|
-
result: 0,
|
|
80
|
-
maximum: null,
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
meta: {
|
|
84
|
-
patientName: "Final Test 3",
|
|
85
|
-
age: "37",
|
|
86
|
-
bmi: "23.67",
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// ---------------------------------------------------------------------------
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
// ──
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
${
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
// ──
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
"
|
|
243
|
-
|
|
244
|
-
${
|
|
245
|
-
${
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
</
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Dummy data — structure mirrors the Excel RESULTS KEY sheet.
|
|
5
|
+
// result = 0 for all rows (questionnaire not yet filled in).
|
|
6
|
+
// Hormonal Health rows have no High/Questions/Maximum — rendered as "—".
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function getDummyData() {
|
|
9
|
+
return {
|
|
10
|
+
rows: [
|
|
11
|
+
{
|
|
12
|
+
system: "STOMACH and GI (Gastrointestinal) Tract",
|
|
13
|
+
high: ">8",
|
|
14
|
+
questions: 9,
|
|
15
|
+
result: 0,
|
|
16
|
+
maximum: 27,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
system: "Small Intestine",
|
|
20
|
+
high: ">7",
|
|
21
|
+
questions: 8,
|
|
22
|
+
result: 0,
|
|
23
|
+
maximum: 24,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
system: "Infections/Invasions of the GI (Gastrointestinal Tract)",
|
|
27
|
+
high: ">3",
|
|
28
|
+
questions: 4,
|
|
29
|
+
result: 0,
|
|
30
|
+
maximum: 12,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
system: "Large Intestine",
|
|
34
|
+
high: ">2",
|
|
35
|
+
questions: 3,
|
|
36
|
+
result: 0,
|
|
37
|
+
maximum: 9,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
system: "Liver and Gallbladder",
|
|
41
|
+
high: ">7",
|
|
42
|
+
questions: 8,
|
|
43
|
+
result: 0,
|
|
44
|
+
maximum: 22,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
system: "Essential Nutrients",
|
|
48
|
+
high: ">25",
|
|
49
|
+
questions: 26,
|
|
50
|
+
result: 0,
|
|
51
|
+
maximum: 68,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
system: "Carbohydrate Metabolism",
|
|
55
|
+
high: ">4",
|
|
56
|
+
questions: 4,
|
|
57
|
+
result: 0,
|
|
58
|
+
maximum: 12,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
system: "Immune System",
|
|
62
|
+
high: ">3",
|
|
63
|
+
questions: 4,
|
|
64
|
+
result: 0,
|
|
65
|
+
maximum: 10,
|
|
66
|
+
},
|
|
67
|
+
{ system: "Lifestyle", high: ">7", questions: 7, result: 0, maximum: 7 },
|
|
68
|
+
{
|
|
69
|
+
system: "Hormonal Health Female",
|
|
70
|
+
high: null,
|
|
71
|
+
questions: null,
|
|
72
|
+
result: 0,
|
|
73
|
+
maximum: null,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
system: "Hormonal Health Male",
|
|
77
|
+
high: null,
|
|
78
|
+
questions: null,
|
|
79
|
+
result: 0,
|
|
80
|
+
maximum: null,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
meta: {
|
|
84
|
+
patientName: "Final Test 3",
|
|
85
|
+
age: "37",
|
|
86
|
+
bmi: "23.67",
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Real data — maps the `doctorScores` payload cerbo sends for a Maison Sante
|
|
93
|
+
// doctor PDF onto the row shape above. Cerbo computes each section's total and
|
|
94
|
+
// maximum from the live wellbeing metadata, so its numbers win over the dummy
|
|
95
|
+
// maxima here (which came from the RESULTS KEY sheet and disagree with the
|
|
96
|
+
// doctor questionnaire spreadsheet on Essential Nutrients and Lifestyle).
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
// Section labels of the reference design, keyed by cerbo's stable symbol. Keeps
|
|
100
|
+
// the table wording stable even though the metadata element names differ
|
|
101
|
+
// (e.g. "IMMUNE SYSTEM", "LIFESTYLE").
|
|
102
|
+
var SYSTEM_LABELS = {
|
|
103
|
+
"stomach-and-gi-tract": "STOMACH and GI (Gastrointestinal) Tract",
|
|
104
|
+
"small-intestine": "Small Intestine",
|
|
105
|
+
"infections-invasions-of-the-gi":
|
|
106
|
+
"Infections/Invasions of the GI (Gastrointestinal Tract)",
|
|
107
|
+
"large-intestine": "Large Intestine",
|
|
108
|
+
"liver-and-gallbladder": "Liver and Gallbladder",
|
|
109
|
+
"essential-nutrients": "Essential Nutrients",
|
|
110
|
+
"carbohydrate-metabolism": "Carbohydrate Metabolism",
|
|
111
|
+
"immune-system": "Immune System",
|
|
112
|
+
"lifestyle": "Lifestyle",
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
function buildRowsFromDoctorScores(doctorScores, meta) {
|
|
116
|
+
var groups = (doctorScores && doctorScores.groups) || [];
|
|
117
|
+
if (!groups.length) return null;
|
|
118
|
+
|
|
119
|
+
var rows = groups.map(function (group) {
|
|
120
|
+
return {
|
|
121
|
+
system: SYSTEM_LABELS[group.symbol] || group.name || group.symbol,
|
|
122
|
+
high:
|
|
123
|
+
group.highThreshold === null || group.highThreshold === undefined
|
|
124
|
+
? null
|
|
125
|
+
: ">" + group.highThreshold,
|
|
126
|
+
questions:
|
|
127
|
+
group.questionCount === undefined ? null : group.questionCount,
|
|
128
|
+
result: group.total || 0,
|
|
129
|
+
maximum: group.max === undefined ? null : group.max,
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Hormonal Health is never scored (no maximum in the spreadsheet either); the
|
|
134
|
+
// rows exist so the doctor can see the questions were asked.
|
|
135
|
+
rows.push({
|
|
136
|
+
system: "Hormonal Health Female",
|
|
137
|
+
high: null,
|
|
138
|
+
questions: null,
|
|
139
|
+
result: 0,
|
|
140
|
+
maximum: null,
|
|
141
|
+
});
|
|
142
|
+
rows.push({
|
|
143
|
+
system: "Hormonal Health Male",
|
|
144
|
+
high: null,
|
|
145
|
+
questions: null,
|
|
146
|
+
result: 0,
|
|
147
|
+
maximum: null,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return { rows: rows, meta: meta || {} };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Helpers
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
function fmt(val, decimals) {
|
|
157
|
+
if (val === null || val === undefined) return "\u2014"; // em dash
|
|
158
|
+
if (typeof val === "number") {
|
|
159
|
+
const rounded =
|
|
160
|
+
Math.round(val * Math.pow(10, decimals)) / Math.pow(10, decimals);
|
|
161
|
+
return String(rounded);
|
|
162
|
+
}
|
|
163
|
+
return String(val);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Single render — faithful to the reference image, Maison Sante colours.
|
|
168
|
+
//
|
|
169
|
+
// Structure:
|
|
170
|
+
// ┌──────────────────────────────────────────────────────┐
|
|
171
|
+
// │ Key to the Big Integral Questionnaire [title bar] │
|
|
172
|
+
// ├──────────────────────────────────────────────────────┤
|
|
173
|
+
// │ Client/Patient Full Name … Age … BMI … │
|
|
174
|
+
// ├──────────────────────────────────────────────────────┤
|
|
175
|
+
// │ SYSTEM SCORES [subheader band] │
|
|
176
|
+
// ├────────────┬──────┬─────────┬────────┬───────┬──┬───┤
|
|
177
|
+
// │ SYSTEM │ High │Questions│ Result │Maximum│/3 │Cmt│
|
|
178
|
+
// ├────────────┼──────┼─────────┼────────┼───────┼──┼───┤
|
|
179
|
+
// │ ...rows... │
|
|
180
|
+
// └──────────────────────────────────────────────────────┘
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
function renderTable(rows, meta, cfg) {
|
|
183
|
+
const c = cfg.colors;
|
|
184
|
+
const title = cfg.title || "Key to the Big Integral Questionnaire";
|
|
185
|
+
|
|
186
|
+
// ── patient info bar ──────────────────────────────────────────────────
|
|
187
|
+
const infoBar = `
|
|
188
|
+
<div style="background:#ffffff;border-bottom:1px solid ${c.rowBorder};">
|
|
189
|
+
<table style="border-collapse:collapse;">
|
|
190
|
+
<tr>
|
|
191
|
+
<td style="padding:7px 40px 7px 14px;font-size:9px;color:#1a1a1a;white-space:nowrap;vertical-align:middle;">
|
|
192
|
+
<span style="color:${c.headerBg};font-weight:700;">Client/Patient Full Name</span>
|
|
193
|
+
${meta.patientName}
|
|
194
|
+
</td>
|
|
195
|
+
<td style="padding:7px 40px 7px 0;font-size:9px;color:#1a1a1a;white-space:nowrap;vertical-align:middle;">
|
|
196
|
+
<span style="color:${c.headerBg};font-weight:700;">Age</span>
|
|
197
|
+
${meta.age}
|
|
198
|
+
</td>
|
|
199
|
+
<td style="padding:7px 0 7px 0;font-size:9px;color:#1a1a1a;white-space:nowrap;vertical-align:middle;">
|
|
200
|
+
<span style="color:${c.headerBg};font-weight:700;">BMI</span>
|
|
201
|
+
${meta.bmi}
|
|
202
|
+
</td>
|
|
203
|
+
</tr>
|
|
204
|
+
</table>
|
|
205
|
+
</div>`;
|
|
206
|
+
|
|
207
|
+
// ── "SYSTEM SCORES" subheader band ───────────────────────────────────
|
|
208
|
+
const subheaderBg = c.subheaderBg || "#1B4080";
|
|
209
|
+
const subheaderBand = `
|
|
210
|
+
<div style="
|
|
211
|
+
padding: 5px 14px;
|
|
212
|
+
background: ${subheaderBg};
|
|
213
|
+
color: #ffffff;
|
|
214
|
+
font-size: 8.5px;
|
|
215
|
+
font-weight: 700;
|
|
216
|
+
letter-spacing: 0.8px;
|
|
217
|
+
text-transform: uppercase;
|
|
218
|
+
">SYSTEM SCORES</div>`;
|
|
219
|
+
|
|
220
|
+
// ── column widths ──────────────────────────────────────────────────────
|
|
221
|
+
const COL = {
|
|
222
|
+
system: "width:34%;text-align:left;",
|
|
223
|
+
high: "width:8%; text-align:center;",
|
|
224
|
+
questions: "width:9%; text-align:center;",
|
|
225
|
+
result: "width:8%; text-align:center;",
|
|
226
|
+
maximum: "width:9%; text-align:center;",
|
|
227
|
+
perThree: "width:8%; text-align:center;",
|
|
228
|
+
comments: "width:24%;text-align:left;",
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const TH_BASE = `padding:6px 14px;font-size:8px;font-weight:700;color:#ffffff;
|
|
232
|
+
background:${c.headerBg};border-bottom:2px solid rgba(255,255,255,0.2);`;
|
|
233
|
+
|
|
234
|
+
const th = (label, colKey) =>
|
|
235
|
+
`<th style="${TH_BASE}${COL[colKey]}">${label}</th>`;
|
|
236
|
+
|
|
237
|
+
const tableHeader = `
|
|
238
|
+
<tr>
|
|
239
|
+
${th("SYSTEM", "system")}
|
|
240
|
+
${th("High", "high")}
|
|
241
|
+
${th("Questions", "questions")}
|
|
242
|
+
${th("Result", "result")}
|
|
243
|
+
${th("Maximum", "maximum")}
|
|
244
|
+
${th("/3", "perThree")}
|
|
245
|
+
${th("Comments", "comments")}
|
|
246
|
+
</tr>`;
|
|
247
|
+
|
|
248
|
+
// ── data rows ─────────────────────────────────────────────────────────
|
|
249
|
+
const TD = (content, colKey, extra = "") =>
|
|
250
|
+
`<td style="padding:5px 14px;font-size:8.5px;${COL[colKey]}border-bottom:1px solid ${c.rowBorder};${extra}">${content}</td>`;
|
|
251
|
+
|
|
252
|
+
const dataRows = rows
|
|
253
|
+
.map((row, i) => {
|
|
254
|
+
const hasThreshold = row.high !== null && row.high !== undefined;
|
|
255
|
+
const hasQuantity = row.questions !== null && row.questions !== undefined;
|
|
256
|
+
const hasMaximum = row.maximum !== null && row.maximum !== undefined;
|
|
257
|
+
|
|
258
|
+
const highDisplay = hasThreshold
|
|
259
|
+
? `<span style="color:${c.elevatedText};font-weight:600;">${row.high}</span>`
|
|
260
|
+
: "\u2014";
|
|
261
|
+
|
|
262
|
+
const questionsDisplay = hasQuantity ? String(row.questions) : "\u2014";
|
|
263
|
+
const resultDisplay = String(row.result);
|
|
264
|
+
const maximumDisplay = hasMaximum ? fmt(row.maximum, 0) : "\u2014";
|
|
265
|
+
const perThreeDisplay = hasMaximum ? fmt(row.maximum / 3, 2) : "\u2014";
|
|
266
|
+
|
|
267
|
+
const rowBg = i % 2 === 0 ? "#ffffff" : "rgba(15, 32, 98, 0.03)";
|
|
268
|
+
|
|
269
|
+
return `
|
|
270
|
+
<tr style="background:${rowBg};">
|
|
271
|
+
${TD(`<span style="color:#1a1a1a;">${row.system}</span>`, "system")}
|
|
272
|
+
${TD(highDisplay, "high")}
|
|
273
|
+
${TD(questionsDisplay, "questions")}
|
|
274
|
+
${TD(resultDisplay, "result", `font-weight:600;color:#444;`)}
|
|
275
|
+
${TD(maximumDisplay, "maximum")}
|
|
276
|
+
${TD(perThreeDisplay, "perThree")}
|
|
277
|
+
${TD("", "comments")}
|
|
278
|
+
</tr>`;
|
|
279
|
+
})
|
|
280
|
+
.join("");
|
|
281
|
+
|
|
282
|
+
// ── assemble ──────────────────────────────────────────────────────────
|
|
283
|
+
// Outer wrapper uses padding-top instead of an empty spacer div, because
|
|
284
|
+
// empty divs with only `height` get collapsed by wkhtmltopdf. Padding
|
|
285
|
+
// always renders.
|
|
286
|
+
return `
|
|
287
|
+
<div style="padding-top:25px;font-family:'Open Sans',sans-serif;">
|
|
288
|
+
<div class="biq-wrapper" style="
|
|
289
|
+
font-family: 'Open Sans', sans-serif;
|
|
290
|
+
page-break-inside: avoid;
|
|
291
|
+
border-radius: 4px;
|
|
292
|
+
overflow: hidden;
|
|
293
|
+
border: 1px solid ${c.rowBorder};
|
|
294
|
+
">
|
|
295
|
+
<!-- title bar -->
|
|
296
|
+
<div style="
|
|
297
|
+
background: ${c.headerBg};
|
|
298
|
+
border-radius: 4px 4px 0 0;
|
|
299
|
+
color: #ffffff;
|
|
300
|
+
padding: 9px 14px;
|
|
301
|
+
font-size: 11px;
|
|
302
|
+
font-weight: 700;
|
|
303
|
+
letter-spacing: 0.3px;
|
|
304
|
+
">${title}</div>
|
|
305
|
+
|
|
306
|
+
${infoBar}
|
|
307
|
+
${subheaderBand}
|
|
308
|
+
|
|
309
|
+
<table style="width:100%;border-collapse:collapse;table-layout:fixed;">
|
|
310
|
+
<thead>${tableHeader}</thead>
|
|
311
|
+
<tbody>${dataRows}</tbody>
|
|
312
|
+
</table>
|
|
313
|
+
</div>
|
|
314
|
+
</div>`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// Public entry
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
/**
|
|
321
|
+
* Render the Big Integral Questionnaire section for Maison Sante doctor PDFs.
|
|
322
|
+
*
|
|
323
|
+
* @param {object} opts
|
|
324
|
+
* @param {object} opts.clientConfig - The full maisonsante.json config object
|
|
325
|
+
* @param {object} opts.localeService - localeService with .t(key) method (unused for now; strings are hardcoded to match the reference design)
|
|
326
|
+
* @returns {string} HTML string to inject into #big-integral-section
|
|
327
|
+
*/
|
|
328
|
+
function renderBigIntegralQuestionnaire({ clientConfig, doctorScores, meta }) {
|
|
329
|
+
const cfg = (clientConfig && clientConfig.big_integral_questionnaire) || {};
|
|
330
|
+
|
|
331
|
+
const colors = Object.assign(
|
|
332
|
+
{
|
|
333
|
+
headerBg: "#0F2062",
|
|
334
|
+
subheaderBg: "#1B4080",
|
|
335
|
+
headerText: "#FFFFFF",
|
|
336
|
+
rowBorder: "rgba(15, 32, 98, 0.12)",
|
|
337
|
+
elevatedText: "#A43B2F",
|
|
338
|
+
},
|
|
339
|
+
cfg.colors || {},
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
const resolvedCfg = Object.assign({}, cfg, { colors });
|
|
343
|
+
|
|
344
|
+
// Real payload when cerbo sent one; the dummy table is the design placeholder
|
|
345
|
+
// and is only reached when doctorScores is absent (e.g. an unfinished quiz).
|
|
346
|
+
const real = buildRowsFromDoctorScores(doctorScores, meta);
|
|
347
|
+
if (!real) {
|
|
348
|
+
console.log("[BigIntegral] no doctorScores in payload — rendering placeholder table");
|
|
349
|
+
} else {
|
|
350
|
+
console.log("[BigIntegral] rendering " + real.rows.length + " rows from doctorScores");
|
|
351
|
+
}
|
|
352
|
+
const source = real || getDummyData();
|
|
353
|
+
const allRows = source.rows;
|
|
354
|
+
const resolvedMeta = source.meta;
|
|
355
|
+
|
|
356
|
+
const rows = allRows.filter((row) => {
|
|
357
|
+
if (
|
|
358
|
+
row.system === "Hormonal Health Female" &&
|
|
359
|
+
cfg.showHormonalFemale === false
|
|
360
|
+
)
|
|
361
|
+
return false;
|
|
362
|
+
if (row.system === "Hormonal Health Male" && cfg.showHormonalMale === false)
|
|
363
|
+
return false;
|
|
364
|
+
return true;
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
return renderTable(rows, resolvedMeta, resolvedCfg);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
module.exports = { renderBigIntegralQuestionnaire };
|