@acorex-platform/module-assessment-management-client 0.0.20

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 (35) hide show
  1. package/fesm2022/acorex-platform-module-assessment-management-client-acorex-platform-module-assessment-management-client-jljmsqNu.mjs +10593 -0
  2. package/fesm2022/acorex-platform-module-assessment-management-client-acorex-platform-module-assessment-management-client-jljmsqNu.mjs.map +1 -0
  3. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-case-status.provider-i48XDX-I.mjs +83 -0
  4. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-case-status.provider-i48XDX-I.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-session-answers-view.util-42ZcOY2C.mjs +124 -0
  6. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-session-answers-view.util-42ZcOY2C.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-session-status.provider-Bk9hHj5V.mjs +97 -0
  8. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-session-status.provider-Bk9hHj5V.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-voice.commands-CduVfWQ-.mjs +134 -0
  10. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-voice.commands-CduVfWQ-.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-module-assessment-management-client-contracts.mjs +1171 -0
  12. package/fesm2022/acorex-platform-module-assessment-management-client-contracts.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-module-assessment-management-client-fill-assessment-session.command-VF80DWQf.mjs +270 -0
  14. package/fesm2022/acorex-platform-module-assessment-management-client-fill-assessment-session.command-VF80DWQf.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-module-assessment-management-client-list-question-difficulty-options.query-h3xT-MNd.mjs +59 -0
  16. package/fesm2022/acorex-platform-module-assessment-management-client-list-question-difficulty-options.query-h3xT-MNd.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-module-assessment-management-client-preview-question.command-C1ODQxDn.mjs +123 -0
  18. package/fesm2022/acorex-platform-module-assessment-management-client-preview-question.command-C1ODQxDn.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-module-assessment-management-client-preview-questionnaire.command-DBm7ve4w.mjs +165 -0
  20. package/fesm2022/acorex-platform-module-assessment-management-client-preview-questionnaire.command-DBm7ve4w.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-module-assessment-management-client-preview-session-pdf.command-BsiJU6ep.mjs +178 -0
  22. package/fesm2022/acorex-platform-module-assessment-management-client-preview-session-pdf.command-BsiJU6ep.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-module-assessment-management-client-question-bank-interface-editor-widget-edit.component-Bbl-efrl.mjs +489 -0
  24. package/fesm2022/acorex-platform-module-assessment-management-client-question-bank-interface-editor-widget-edit.component-Bbl-efrl.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-module-assessment-management-client-questionnaire-viewer-popup.component-Djcfg0dv.mjs +609 -0
  26. package/fesm2022/acorex-platform-module-assessment-management-client-questionnaire-viewer-popup.component-Djcfg0dv.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-module-assessment-management-client-review-assessment-session.command-CZ96KQ-l.mjs +228 -0
  28. package/fesm2022/acorex-platform-module-assessment-management-client-review-assessment-session.command-CZ96KQ-l.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-module-assessment-management-client-view-session-answers.command-CCzJoJ4p.mjs +280 -0
  30. package/fesm2022/acorex-platform-module-assessment-management-client-view-session-answers.command-CCzJoJ4p.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-module-assessment-management-client.mjs +2 -0
  32. package/fesm2022/acorex-platform-module-assessment-management-client.mjs.map +1 -0
  33. package/package.json +28 -0
  34. package/types/acorex-platform-module-assessment-management-client-contracts.d.ts +752 -0
  35. package/types/acorex-platform-module-assessment-management-client.d.ts +1279 -0
@@ -0,0 +1,1171 @@
1
+ const AXMAssessmentManagementFeatureKeys = {
2
+ QuestionnaireManagement: 'AssessmentManagement:Feature:Questionnaire.Management',
3
+ };
4
+
5
+ const AXPAssessmentManagementMenuKeys = {
6
+ Questionnaires: 'AssessmentManagement:Menu:Questionnaires',
7
+ Cases: 'AssessmentManagement:Menu:Cases',
8
+ QuestionBankItems: 'AssessmentManagement:Menu:QuestionBankItems',
9
+ };
10
+
11
+ /**
12
+ * Safety & Risk capabilities used by this module.
13
+ * Domain capability keys for this module. Root groups live in `AXPCommonPermissionGroupKeys` (common contracts).
14
+ */
15
+ const AXPSafetyRiskPermissionKeys = {
16
+ Access: 'SafetyRisk:Permission:Access',
17
+ MaintainSafety: 'SafetyRisk:Permission:MaintainSafety',
18
+ MaintainRisk: 'SafetyRisk:Permission:MaintainRisk',
19
+ MaintainHealth: 'SafetyRisk:Permission:MaintainHealth',
20
+ MaintainAssessments: 'SafetyRisk:Permission:MaintainAssessments',
21
+ Setup: 'SafetyRisk:Permission:Setup',
22
+ };
23
+ /**
24
+ * @deprecated Prefer {@link AXPSafetyRiskPermissionKeys}.
25
+ * Legacy nested shape kept so existing imports compile during migration.
26
+ */
27
+ const AXMAssessmentManagementPermissionKeys = {
28
+ AssessmentManagement: {
29
+ QuestionBank: AXPSafetyRiskPermissionKeys.MaintainAssessments,
30
+ Questionnaire: {
31
+ Management: AXPSafetyRiskPermissionKeys.MaintainAssessments,
32
+ Create: AXPSafetyRiskPermissionKeys.MaintainAssessments,
33
+ Edit: AXPSafetyRiskPermissionKeys.MaintainAssessments,
34
+ Delete: AXPSafetyRiskPermissionKeys.MaintainAssessments,
35
+ },
36
+ Case: {
37
+ Management: AXPSafetyRiskPermissionKeys.MaintainAssessments,
38
+ },
39
+ Session: {
40
+ Management: AXPSafetyRiskPermissionKeys.MaintainAssessments,
41
+ },
42
+ Automation: {
43
+ Management: AXPSafetyRiskPermissionKeys.MaintainAssessments,
44
+ },
45
+ },
46
+ };
47
+
48
+ /** Component key for the questionnaire Outcomes page tab. Register via AXP_PAGE_COMPONENT_PROVIDER. */
49
+ const QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY = 'questionnaire-outcomes-page';
50
+
51
+ /** Component key for the questionnaire Questions page tab. Register via AXP_PAGE_COMPONENT_PROVIDER. */
52
+ const QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY = 'questionnaire-questions-page';
53
+
54
+ //#endregion
55
+
56
+ //#region ---- Imports ----
57
+ //#endregion
58
+
59
+ //#region ---- Questionnaire print settings ----
60
+ const DEFAULT_QUESTIONNAIRE_PRINT_SETTINGS = {
61
+ paperSize: 'A4',
62
+ orientation: 'portrait',
63
+ watermark: '',
64
+ showQuestionNumbers: true,
65
+ showWelcomeScreen: false,
66
+ showSummaryAfterSubmit: false,
67
+ showOutcomeResults: false,
68
+ showUnansweredQuestions: true,
69
+ showComments: true,
70
+ showAttachments: true,
71
+ showHints: true,
72
+ showNotes: true,
73
+ };
74
+ const PRINT_PAPER_SIZE_VALUES = ['A4', 'Letter', 'Legal'];
75
+ const PRINT_ORIENTATION_VALUES = ['portrait', 'landscape'];
76
+ const PRINT_PAGE_SIZE_MM = {
77
+ A4: { width: 210, height: 297 },
78
+ Letter: { width: 215.9, height: 279.4 },
79
+ Legal: { width: 215.9, height: 355.6 },
80
+ };
81
+ /** Side margin used by the print document and Chromium PDF engine. */
82
+ const PRINT_PAGE_SIDE_MARGIN_MM = 14;
83
+ /**
84
+ * Playwright paints repeating letterhead in the top/bottom margin box.
85
+ * These must stay large enough for the company header/footer; measuring
86
+ * chrome height under-reported and overlapped the questionnaire title.
87
+ */
88
+ const PRINT_PAGE_LETTERHEAD_TOP_MARGIN_MM = 36;
89
+ const PRINT_PAGE_LETTERHEAD_BOTTOM_MARGIN_MM = 28;
90
+ function resolvePrintLetterheadPdfMargin() {
91
+ return {
92
+ top: `${PRINT_PAGE_LETTERHEAD_TOP_MARGIN_MM}mm`,
93
+ right: `${PRINT_PAGE_SIDE_MARGIN_MM}mm`,
94
+ bottom: `${PRINT_PAGE_LETTERHEAD_BOTTOM_MARGIN_MM}mm`,
95
+ left: `${PRINT_PAGE_SIDE_MARGIN_MM}mm`,
96
+ };
97
+ }
98
+ function definedPartial(value) {
99
+ if (!value) {
100
+ return {};
101
+ }
102
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
103
+ }
104
+ function coerceSelectId(value) {
105
+ if (value != null && typeof value === 'object' && 'id' in value) {
106
+ return value.id;
107
+ }
108
+ return value;
109
+ }
110
+ function normalizePrintPaperSize(value) {
111
+ const raw = coerceSelectId(value);
112
+ if (typeof raw === 'string' && PRINT_PAPER_SIZE_VALUES.includes(raw)) {
113
+ return raw;
114
+ }
115
+ return DEFAULT_QUESTIONNAIRE_PRINT_SETTINGS.paperSize;
116
+ }
117
+ function normalizePrintOrientation(value) {
118
+ const raw = coerceSelectId(value);
119
+ if (typeof raw === 'string' && PRINT_ORIENTATION_VALUES.includes(raw)) {
120
+ return raw;
121
+ }
122
+ return DEFAULT_QUESTIONNAIRE_PRINT_SETTINGS.orientation;
123
+ }
124
+ function normalizePrintWatermark(value) {
125
+ return typeof value === 'string' ? value.trim() : '';
126
+ }
127
+ /**
128
+ * Merges questionnaire defaults with an optional per-call override.
129
+ * Undefined override fields keep the stored / built-in default.
130
+ */
131
+ function resolvePrintSettings(stored, override) {
132
+ const merged = {
133
+ ...DEFAULT_QUESTIONNAIRE_PRINT_SETTINGS,
134
+ ...definedPartial(stored),
135
+ ...definedPartial(override),
136
+ };
137
+ return {
138
+ ...merged,
139
+ paperSize: normalizePrintPaperSize(merged.paperSize),
140
+ orientation: normalizePrintOrientation(merged.orientation),
141
+ watermark: normalizePrintWatermark(merged.watermark),
142
+ };
143
+ }
144
+ /**
145
+ * Resolves print page breaks from the Display view mode.
146
+ * `side-menu` prints as page-per-group (no sidebar).
147
+ */
148
+ function resolvePrintPaging(displayViewMode) {
149
+ if (displayViewMode === 'page-per-question') {
150
+ return 'page-per-question';
151
+ }
152
+ if (displayViewMode === 'page-per-group' || displayViewMode === 'side-menu') {
153
+ return 'page-per-group';
154
+ }
155
+ return 'single-page';
156
+ }
157
+ /** Content width after side margins, for the off-screen print host and wrapped HTML. */
158
+ function resolvePrintContentWidthMm(print) {
159
+ const size = PRINT_PAGE_SIZE_MM[normalizePrintPaperSize(print.paperSize)];
160
+ const pageWidth = normalizePrintOrientation(print.orientation) === 'landscape' ? size.height : size.width;
161
+ return Math.round((pageWidth - PRINT_PAGE_SIDE_MARGIN_MM * 2) * 10) / 10;
162
+ }
163
+ function resolvePrintPdfPageOptions(print) {
164
+ return {
165
+ format: normalizePrintPaperSize(print.paperSize),
166
+ landscape: normalizePrintOrientation(print.orientation) === 'landscape',
167
+ };
168
+ }
169
+ //#endregion
170
+
171
+ //#region ---- Root Configuration ----
172
+ const config = { i18n: 'assessment-management', module: 'AssessmentManagement' };
173
+ const RootConfig = {
174
+ config,
175
+ module: {
176
+ module: 'assessment-management',
177
+ route: 'assessment-management',
178
+ name: 'AssessmentManagement',
179
+ title: '@assessment-management:module.title',
180
+ icon: 'fa-light fa-clipboard-list-check',
181
+ },
182
+ entities: {
183
+ questionBankItem: {
184
+ name: 'QuestionBankItem',
185
+ title: '@assessment-management:question-bank.entities.question-bank-item.title',
186
+ titlePlural: '@assessment-management:question-bank.entities.question-bank-item.plural',
187
+ icon: 'fa-light fa-question-circle',
188
+ },
189
+ questionnaire: {
190
+ name: 'Questionnaire',
191
+ title: '@assessment-management:questionnaires.entities.questionnaire.title',
192
+ titlePlural: '@assessment-management:questionnaires.entities.questionnaire.plural',
193
+ icon: 'fa-light fa-clipboard-list',
194
+ },
195
+ assessmentCase: {
196
+ name: 'AssessmentCase',
197
+ title: '@assessment-management:cases.entities.assessment-case.title',
198
+ titlePlural: '@assessment-management:cases.entities.assessment-case.plural',
199
+ icon: 'fa-light fa-briefcase',
200
+ },
201
+ assessmentSession: {
202
+ name: 'AssessmentSession',
203
+ title: '@assessment-management:sessions.entities.assessment-session.title',
204
+ titlePlural: '@assessment-management:sessions.entities.assessment-session.plural',
205
+ icon: 'fa-light fa-clipboard-check',
206
+ },
207
+ },
208
+ queries: {
209
+ dashboard: {
210
+ overdueAssessmentsKpi: `${config.module}:Dashboard:OverdueAssessmentsKpi`,
211
+ pendingAssessmentsKpi: `${config.module}:Dashboard:PendingAssessmentsKpi`,
212
+ myAssessmentsKpi: `${config.module}:Dashboard:MyAssessmentsKpi`,
213
+ assessmentsWeekLine: `${config.module}:Dashboard:AssessmentsWeekLine`,
214
+ },
215
+ },
216
+ };
217
+ //#endregion
218
+
219
+ //#region ---- Assessment session PDF preview contracts ----
220
+ /** Server command that renders a filled assessment session to PDF. */
221
+ const AXM_ASSESSMENT_SESSION_PREVIEW_PDF_COMMAND_KEY = 'AssessmentManagement:AssessmentSession:PreviewPdf';
222
+ /** Client / entity-menu command that opens the PDF preview dialog. */
223
+ const AXM_ASSESSMENT_SESSION_PREVIEW_PDF_CLIENT_COMMAND_KEY = 'AssessmentManagement.AssessmentSession:PreviewPdf';
224
+ //#endregion
225
+
226
+ //#region ---- Assessment print merge keys ----
227
+ /**
228
+ * Event merge keys passed to `ContentManagement:TextTemplate:Render` (`channel: print`).
229
+ * Names must match `PlatformManagement.Tokens` Event rows.
230
+ */
231
+ const AXM_ASSESSMENT_PRINT_MERGE_KEYS = {
232
+ questionnaireTitle: 'questionnaireTitle',
233
+ caseTitle: 'caseTitle',
234
+ sessionDate: 'sessionDate',
235
+ generatedAt: 'generatedAt',
236
+ status: 'status',
237
+ statusLabel: 'statusLabel',
238
+ sessionBody: 'sessionBody',
239
+ };
240
+ //#endregion
241
+
242
+ //#region ---- Print HTML snapshot helpers ----
243
+ const ASSESSMENT_PRINT_PAGE_FORMAT = DEFAULT_QUESTIONNAIRE_PRINT_SETTINGS.paperSize;
244
+ /** A4 portrait content width after 14mm side margins (210 - 14 - 14). */
245
+ const ASSESSMENT_PRINT_CONTENT_WIDTH_MM = resolvePrintContentWidthMm(DEFAULT_QUESTIONNAIRE_PRINT_SETTINGS);
246
+ const ASSESSMENT_PRINT_HTML_GZIP_ENCODING = 'gzip-base64';
247
+ function resolveWatermarkFontMm(watermark, contentWidthMm) {
248
+ const chars = Math.max(watermark.trim().length, 1);
249
+ const fitted = (contentWidthMm * 0.78) / (chars * 0.62);
250
+ return Math.round(Math.max(5, Math.min(14, fitted)) * 10) / 10;
251
+ }
252
+ function buildPrintDocumentCss(print) {
253
+ const width = resolvePrintContentWidthMm(print);
254
+ const watermark = print.watermark
255
+ ? `.__print-watermark{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-32deg);transform-origin:center center;pointer-events:none;z-index:0;color:rgba(15,23,42,.12);font-size:${resolveWatermarkFontMm(print.watermark, width)}mm;font-weight:700;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;line-height:1;}`
256
+ : '';
257
+ return `
258
+ @page { size: ${print.paperSize} ${print.orientation}; }
259
+ html, body { width: ${width}mm; max-width: ${width}mm; margin: 0; }
260
+ body { background: #fff; color: #0f172a; font-family: Arial, Helvetica, sans-serif; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
261
+ ${watermark}
262
+ .__print-letterhead { width: 100%; border-collapse: collapse; table-layout: fixed; border: 1.25pt solid #1e3a5f; }
263
+ .__print-letterhead__accent { height: 5px; background: #1e3a5f; font-size: 0; line-height: 0; }
264
+ .__print-letterhead__header { padding: 7mm 8mm 5mm; }
265
+ .__print-letterhead__brand { width: 100%; border-collapse: collapse; }
266
+ .__print-letterhead__logo { width: 42px; vertical-align: middle; }
267
+ .__print-letterhead__logo img, .__print-letterhead__logo svg { display: block; max-width: 36px; max-height: 36px; }
268
+ .__print-letterhead__name { font-size: 13.5pt; line-height: 1.2; font-weight: 700; color: #0f172a; letter-spacing: 0.01em; }
269
+ .__print-letterhead__email { font-size: 8.5pt; line-height: 1.4; color: #64748b; margin-top: 2px; }
270
+ .__print-letterhead__contact { text-align: right; vertical-align: middle; font-size: 8pt; line-height: 1.45; color: #64748b; }
271
+ .__print-letterhead__body { padding: 2mm 8mm 8mm; vertical-align: top; min-height: 165mm; }
272
+ .__print-letterhead__footer { padding: 4.5mm 8mm; background: #f1f5f9; border-top: 1px solid #cbd5e1; font-size: 8pt; line-height: 1.45; color: #475569; }
273
+ .__print-letterhead__footer-table { width: 100%; border-collapse: collapse; }
274
+ .__print-letterhead__copy { text-align: right; color: #64748b; }
275
+ .__print-doc-head { margin: 0 0 12px; padding: 0 0 10px; border-bottom: 1px solid #e2e8f0; }
276
+ .__print-doc-title { margin: 0 0 6px; font-size: 15pt; line-height: 1.25; font-weight: 700; color: #0f172a; }
277
+ .__print-doc-meta { display: flex; flex-wrap: wrap; gap: 6px 10px; font-size: 8.5pt; line-height: 1.4; color: #64748b; }
278
+ .__print-doc-meta > span:empty { display: none; }
279
+ .__print-doc-meta > span:not(:empty) { padding: 2px 8px; background: #f1f5f9; border-radius: 999px; color: #334155; }
280
+ .__print-document {
281
+ display: block !important; width: 100%; position: relative; z-index: 1;
282
+ break-inside: auto !important; page-break-inside: auto !important;
283
+ }
284
+ axm-questionnaire-print-host,
285
+ .axm-questionnaire-print-surface,
286
+ axm-questionnaire-viewer,
287
+ axm-questionnaire-viewer-print-view,
288
+ axp-widgets-container,
289
+ ax-form,
290
+ .__questionnaire-container {
291
+ display: block !important; break-inside: auto !important; page-break-inside: auto !important;
292
+ }
293
+ .__print-section {
294
+ display: block !important; break-inside: auto !important; page-break-inside: auto !important;
295
+ margin: 0 0 10px !important; padding: 0 !important; border: 1px solid #d0d9e6 !important; border-radius: 4px !important;
296
+ background: #fff !important; overflow: visible;
297
+ }
298
+ .__print-section:last-child { margin-bottom: 0 !important; }
299
+ axm-questionnaire-viewer-section-header {
300
+ display: block !important; width: 100% !important; margin: 0 !important; padding: 0 !important;
301
+ background: #eef3f8 !important; border-bottom: 1px solid #d0d9e6 !important;
302
+ }
303
+ axm-questionnaire-viewer-section-header .__mb-sm,
304
+ axm-questionnaire-viewer-section-header .__mb-md { margin-bottom: 0 !important; }
305
+ .__print-questions { display: block !important; padding: 2px 12px 8px !important; }
306
+ .__section-header { margin: 0 !important; padding: 8px 12px 7px !important; background: #eef3f8 !important; }
307
+ .__section-title-md, .__section-title-lg, .__print-section h2, .__print-section .__section-title-md, .__print-section .__section-title-lg {
308
+ margin: 0 !important; padding: 0 !important; padding-inline-start: 0 !important; border-inline-start: none !important;
309
+ font-size: 11pt !important; line-height: 1.25 !important; font-weight: 700 !important; color: #0f172a !important;
310
+ }
311
+ .__section-description, .__section-description--rich {
312
+ margin: 2px 0 0 !important; padding-inline-start: 0 !important; font-size: 8.5pt !important; line-height: 1.35 !important; color: #64748b !important;
313
+ }
314
+ .__question-item { break-inside: avoid; page-break-inside: avoid; }
315
+ .__question-item.__print-plain {
316
+ background: transparent !important; box-shadow: none !important; border: none !important; border-bottom: 1px solid #e8eef3 !important;
317
+ border-radius: 0 !important; padding: 8px 0 !important; margin: 0 !important;
318
+ }
319
+ .__print-questions .__question-item.__print-plain:last-child { border-bottom: none !important; }
320
+ .__question-header { display: flex !important; align-items: flex-start !important; gap: 8px !important; width: 100% !important; }
321
+ .__question-number {
322
+ display: inline-block !important; min-width: 0 !important; padding: 0 !important; border-radius: 0 !important;
323
+ background: transparent !important; color: #0f172a !important; font-size: 10pt !important; font-weight: 700 !important; text-align: start !important;
324
+ }
325
+ .__question-title, h3.__question-title, .__question-title--rich {
326
+ margin: 0 !important; font-size: 10pt !important; line-height: 1.4 !important; font-weight: 600 !important; color: #0f172a !important;
327
+ }
328
+ .__question-widget { margin-top: 4px !important; }
329
+ .__question-comment, .__question-comment--readonly {
330
+ margin-top: 4px !important; padding-inline-start: 2px !important;
331
+ }
332
+ .__question-comment-text {
333
+ margin: 0 !important; font-size: 9pt !important; line-height: 1.4 !important; font-weight: 400 !important;
334
+ font-style: italic !important; color: #64748b !important;
335
+ }
336
+ .axp-widget-print-field {
337
+ display: inline-block !important; width: auto !important; min-width: 22mm !important; max-width: 100% !important;
338
+ min-height: 0 !important; margin: 0 !important; padding: 3px 10px !important; border: none !important; border-bottom: none !important;
339
+ border-radius: 4px !important; background: #f1f5f9 !important; color: #0f172a !important; font-size: 11pt !important;
340
+ font-weight: 600 !important; line-height: 1.4 !important; word-break: break-word;
341
+ }
342
+ .axp-widget-print-field--empty {
343
+ background: transparent !important; color: #94a3b8 !important; font-weight: 400 !important; border: 1px dashed #cbd5e1 !important;
344
+ }
345
+ .axp-widget-print-field--multiline, .axp-widget-print-field--rich {
346
+ display: block !important; width: 100% !important; font-weight: 400 !important; background: #f8fafc !important;
347
+ border: 1px solid #e2e8f0 !important; min-height: 3.2em;
348
+ }
349
+ .axp-widget-print-choice { display: inline-flex; align-items: center; gap: 8px; font-size: 10.5pt; color: #0f172a; }
350
+ .axp-widget-print-mark { font-size: 13pt; line-height: 1; }
351
+ .__print-document[data-print-paging="page-per-group"] .__print-section + .__print-section,
352
+ .__print-document[data-print-paging="side-menu"] .__print-section + .__print-section { break-before: page; page-break-before: always; }
353
+ .__print-document[data-print-paging="page-per-question"] axm-questionnaire-viewer-question + axm-questionnaire-viewer-question { break-before: page; page-break-before: always; }
354
+ .__print-extra,
355
+ .__print-outcomes,
356
+ .__print-summary {
357
+ display: block !important;
358
+ break-inside: auto !important;
359
+ page-break-inside: auto !important;
360
+ }
361
+ .__print-outcomes,
362
+ .__print-summary {
363
+ break-before: page !important;
364
+ page-break-before: always !important;
365
+ }
366
+ .__print-outcomes .axp-outcome-results-viewer,
367
+ .__print-outcomes .__root,
368
+ .__print-summary .axm-post-questionnaire-content,
369
+ .__print-summary .__summary {
370
+ display: block !important;
371
+ min-height: 0 !important;
372
+ }
373
+ `;
374
+ }
375
+ function minifyPrintHtml(html) {
376
+ return html
377
+ .replace(/<!--[\s\S]*?-->/g, '')
378
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
379
+ .replace(/\sng-reflect-[a-z0-9-]+="[^"]*"/gi, '')
380
+ .replace(/\s_ngcontent-[a-z0-9-]+="[^"]*"/gi, '')
381
+ .replace(/\s_nghost-[a-z0-9-]+="[^"]*"/gi, '')
382
+ .replace(/\sdata-testid="[^"]*"/gi, '')
383
+ .replace(/\sclass=""/g, '')
384
+ .replace(/\s{2,}/g, ' ')
385
+ .replace(/>\s+</g, '><')
386
+ .trim();
387
+ }
388
+ function resolvePrintPlainText(value) {
389
+ if (typeof value === 'string') {
390
+ return value.trim();
391
+ }
392
+ if (!value || typeof value !== 'object') {
393
+ return '';
394
+ }
395
+ const record = value;
396
+ for (const key of ['en-US', 'en', ...Object.keys(record)]) {
397
+ const text = record[key];
398
+ if (typeof text === 'string' && text.trim()) {
399
+ return text.trim();
400
+ }
401
+ }
402
+ return '';
403
+ }
404
+ function formatPrintAnswerText(value) {
405
+ if (value == null || value === '') {
406
+ return '—';
407
+ }
408
+ if (typeof value === 'string') {
409
+ const trimmed = value.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
410
+ return trimmed || '—';
411
+ }
412
+ if (typeof value === 'number' || typeof value === 'boolean') {
413
+ return String(value);
414
+ }
415
+ if (value instanceof Date) {
416
+ return value.toISOString();
417
+ }
418
+ if (Array.isArray(value)) {
419
+ const parts = value.map((item) => formatPrintAnswerText(item)).filter((item) => item !== '—');
420
+ return parts.length ? parts.join(', ') : '—';
421
+ }
422
+ if (typeof value === 'object') {
423
+ const record = value;
424
+ const labeled = record['title'] ?? record['text'] ?? record['label'] ?? record['name'] ?? record['displayName'];
425
+ const resolved = resolvePrintPlainText(labeled);
426
+ if (resolved) {
427
+ return resolved.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
428
+ }
429
+ if (record['value'] != null) {
430
+ return formatPrintAnswerText(record['value']);
431
+ }
432
+ try {
433
+ return JSON.stringify(value);
434
+ }
435
+ catch {
436
+ return '—';
437
+ }
438
+ }
439
+ return String(value);
440
+ }
441
+ function buildQuestionnaireQuestionCardHtml(input) {
442
+ if (input.cardHtml?.trim()) {
443
+ return input.cardHtml.trim();
444
+ }
445
+ const titleHtml = input.titleHtml.includes('__question-title')
446
+ ? input.titleHtml
447
+ : `<h3 class="__question-title">${input.titleHtml}</h3>`;
448
+ const number = input.number
449
+ ? `<div class="__question-header-left"><span class="__question-number">${escapePrintDocumentText(input.number)}</span></div>`
450
+ : '';
451
+ const itemClass = '__question-item __print-plain';
452
+ return (`<div class="${itemClass}">` +
453
+ `<div class="__question-header">${number}` +
454
+ `<div class="__question-header-right"><div class="__question-title-block">${titleHtml}</div></div>` +
455
+ `</div>` +
456
+ `<div class="__question-widget">${input.answerHtml}</div>` +
457
+ `</div>`);
458
+ }
459
+ function buildQuestionnairePrintBodyHtml(input) {
460
+ const title = escapePrintDocumentText(input.title || 'Questionnaire');
461
+ const meta = [
462
+ input.caseTitle ? `<div><span>Case</span>${escapePrintDocumentText(input.caseTitle)}</div>` : '',
463
+ input.status ? `<div><span>Status</span>${escapePrintDocumentText(input.status)}</div>` : '',
464
+ ]
465
+ .filter(Boolean)
466
+ .join('');
467
+ const sections = input.sections
468
+ .map((section) => {
469
+ const questions = section.questions.map((question) => buildQuestionnaireQuestionCardHtml(question)).join('');
470
+ return `<section class="section"><h2>${escapePrintDocumentText(section.title)}</h2>${questions}</section>`;
471
+ })
472
+ .join('');
473
+ return `<h1>${title}</h1>${meta ? `<div class="meta">${meta}</div>` : ''}${sections}`;
474
+ }
475
+ /** Q&A sections only — title/date live in the questionnaire print child template. */
476
+ function buildQuestionnaireSessionBodyHtml(input) {
477
+ return input.sections
478
+ .map((section) => {
479
+ const questions = section.questions.map((question) => buildQuestionnaireQuestionCardHtml(question)).join('');
480
+ return `<section class="section"><h2>${escapePrintDocumentText(section.title)}</h2>${questions}</section>`;
481
+ })
482
+ .join('');
483
+ }
484
+ /** Compact repeating questionnaire band: title left, date/status right. */
485
+ function buildQuestionnairePrintHeaderRegion(input) {
486
+ const title = escapePrintDocumentText(input.title.trim() || 'Questionnaire');
487
+ const caseTitle = escapePrintDocumentText((input.caseTitle ?? '').trim());
488
+ const sessionDate = escapePrintDocumentText((input.sessionDate ?? '').trim());
489
+ const statusLabel = escapePrintDocumentText((input.statusLabel ?? '').trim());
490
+ const sub = caseTitle
491
+ ? `<div style="font-size:7.5pt;line-height:1.25;color:#64748b;margin-top:1px;">${caseTitle}</div>`
492
+ : '';
493
+ const right = [sessionDate, statusLabel].filter(Boolean).join(' · ');
494
+ const rightCell = right
495
+ ? `<td style="vertical-align:middle;text-align:right;padding:1px 0 2px;white-space:nowrap;font-size:8pt;line-height:1.25;color:#64748b;width:36%;">${right}</td>`
496
+ : '';
497
+ return (`<header data-print-region="header" class="__print-page-header">` +
498
+ `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" ` +
499
+ `style="width:100%;border-collapse:collapse;margin-top:2px;">` +
500
+ `<tr><td style="vertical-align:middle;padding:1px 0 2px;">` +
501
+ `<div style="font-size:10pt;line-height:1.2;font-weight:700;color:#0f172a;">${title}</div>${sub}` +
502
+ `</td>${rightCell}</tr></table>` +
503
+ `<div style="border-bottom:1px solid #cbd5e1;font-size:0;line-height:0;">&nbsp;</div>` +
504
+ `</header>`);
505
+ }
506
+ /**
507
+ * Ensures the compact questionnaire header is the last `data-print-region="header"`
508
+ * (under the company letterhead) and removes the old first-page-only title block.
509
+ */
510
+ function applyQuestionnairePrintHeader(html, input) {
511
+ const region = buildQuestionnairePrintHeaderRegion(input);
512
+ let next = html.replace(/<div class="__print-doc-head">\s*<h1\b[^>]*>[\s\S]*?<\/h1>\s*<div class="__print-doc-meta">[\s\S]*?<\/div>\s*<\/div>/i, '');
513
+ const headerRe = /<header\b[^>]*data-print-region=["']header["'][^>]*>[\s\S]*?<\/header>/gi;
514
+ const headers = next.match(headerRe) ?? [];
515
+ if (headers.length >= 2) {
516
+ let index = 0;
517
+ return next.replace(headerRe, (block) => {
518
+ index += 1;
519
+ return index === headers.length ? region : block;
520
+ });
521
+ }
522
+ if (headers.length === 1) {
523
+ return next.replace(headerRe, (block) => `${block}${region}`);
524
+ }
525
+ return `${region}${next}`;
526
+ }
527
+ /** Inner body of a wrapped print document, without watermark. */
528
+ function extractPrintDocumentBodyHtml(html) {
529
+ const trimmed = html.trim();
530
+ const bodyMatch = trimmed.match(/<body[^>]*>([\s\S]*)<\/body>/i);
531
+ const inner = (bodyMatch?.[1] ?? trimmed).trim();
532
+ return inner.replace(/<div class="__print-watermark"[^>]*>[\s\S]*?<\/div>/gi, '').trim();
533
+ }
534
+ function wrapPrintDocument(input) {
535
+ const title = escapePrintDocumentText(input.title || 'Questionnaire');
536
+ const print = resolvePrintSettings(input.print);
537
+ const watermark = print.watermark
538
+ ? `<div class="__print-watermark" aria-hidden="true">${escapePrintDocumentText(print.watermark)}</div>`
539
+ : '';
540
+ const html = `<!doctype html><html><head><meta charset="utf-8"/>` +
541
+ `<title>${title}</title>` +
542
+ `<style>${buildPrintDocumentCss(print)}</style></head><body>` +
543
+ `${watermark}${input.bodyHtml}` +
544
+ `</body></html>`;
545
+ return {
546
+ html,
547
+ headerHtml: input.headerHtml !== undefined
548
+ ? input.headerHtml
549
+ : `<span style="font-weight:700;">${title}</span>`,
550
+ footerHtml: input.footerHtml !== undefined
551
+ ? input.footerHtml
552
+ : '<span style="font-size:8pt;color:#94a3b8;font-family:Arial,Helvetica,sans-serif;"><span class="pageNumber"></span> / <span class="totalPages"></span></span>',
553
+ };
554
+ }
555
+ function escapePrintDocumentText(value) {
556
+ return value
557
+ .replace(/&/g, '&amp;')
558
+ .replace(/</g, '&lt;')
559
+ .replace(/>/g, '&gt;')
560
+ .replace(/"/g, '&quot;')
561
+ .replace(/'/g, '&#39;');
562
+ }
563
+ //#endregion
564
+
565
+ //#endregion
566
+
567
+ //#endregion
568
+
569
+ //#endregion
570
+
571
+ //#region ---- Question comment value object ----
572
+ /**
573
+ * Default when no configuration is stored.
574
+ */
575
+ const DEFAULT_QUESTION_COMMENT_CONFIG = {
576
+ enabled: false,
577
+ required: false,
578
+ };
579
+ /**
580
+ * Normalizes `enabled` / `isVisible`-style flags: booleans, legacy string booleans, or expression strings.
581
+ */
582
+ function normalizeCommentFlag(value, emptyAs) {
583
+ if (value === undefined || value === null) {
584
+ return emptyAs;
585
+ }
586
+ if (value === true || value === false) {
587
+ return value;
588
+ }
589
+ if (typeof value === 'string') {
590
+ const trimmed = value.trim();
591
+ if (!trimmed) {
592
+ return emptyAs;
593
+ }
594
+ const lower = trimmed.toLowerCase();
595
+ if (lower === 'true' || lower === '1') {
596
+ return true;
597
+ }
598
+ if (lower === 'false' || lower === '0') {
599
+ return false;
600
+ }
601
+ return trimmed;
602
+ }
603
+ return value;
604
+ }
605
+ /**
606
+ * Normalizes API / partial values to a complete config.
607
+ * Preserves expression strings on `enabled` (Allow Comments toggle binding).
608
+ */
609
+ function normalizeQuestionCommentConfig(value) {
610
+ const enabled = normalizeCommentFlag(value?.enabled, false);
611
+ const isVisible = normalizeCommentFlag(value?.isVisible, undefined);
612
+ return {
613
+ enabled: enabled === undefined ? false : enabled,
614
+ required: value?.required === true,
615
+ ...(isVisible !== undefined ? { isVisible } : {}),
616
+ };
617
+ }
618
+ /**
619
+ * Merges question bank defaults with optional questionnaire item overrides.
620
+ */
621
+ function mergeQuestionCommentConfig(bank, override) {
622
+ const b = normalizeQuestionCommentConfig(bank);
623
+ if (!override) {
624
+ return b;
625
+ }
626
+ const overrideEnabled = override.enabled !== undefined ? normalizeCommentFlag(override.enabled, false) : undefined;
627
+ const overrideVis = normalizeCommentFlag(override.isVisible, undefined);
628
+ return {
629
+ enabled: overrideEnabled !== undefined ? overrideEnabled : b.enabled,
630
+ required: override.required !== undefined ? override.required === true : b.required,
631
+ ...(overrideVis !== undefined
632
+ ? { isVisible: overrideVis }
633
+ : b.isVisible !== undefined
634
+ ? { isVisible: b.isVisible }
635
+ : {}),
636
+ };
637
+ }
638
+ /**
639
+ * Returns only fields that differ from the bank default (for persisting questionnaire item overrides).
640
+ */
641
+ function diffQuestionCommentOverride(bank, edited) {
642
+ const diff = {};
643
+ if (edited.enabled !== bank.enabled) {
644
+ diff.enabled = edited.enabled;
645
+ }
646
+ if (edited.required !== bank.required) {
647
+ diff.required = edited.required;
648
+ }
649
+ if (edited.isVisible !== bank.isVisible) {
650
+ diff.isVisible = edited.isVisible;
651
+ }
652
+ return Object.keys(diff).length > 0 ? diff : undefined;
653
+ }
654
+ /**
655
+ * Form context for the question edit dialog: surface legacy `isVisible` expressions on `enabled`
656
+ * (Allow Comments) so they display and save like question `isVisible`.
657
+ */
658
+ function commentConfigForEditForm(merged) {
659
+ const legacyVis = merged.isVisible;
660
+ if (merged.enabled === true && typeof legacyVis === 'string' && legacyVis.trim()) {
661
+ return {
662
+ enabled: legacyVis.trim(),
663
+ required: merged.required,
664
+ };
665
+ }
666
+ return {
667
+ enabled: merged.enabled,
668
+ required: merged.required,
669
+ };
670
+ }
671
+ /**
672
+ * Effective visibility flag for the comment box (for async evaluation).
673
+ * Prefers expression/`false` on `enabled`; falls back to legacy `isVisible` when `enabled === true`.
674
+ */
675
+ function getQuestionCommentVisibilityFlag(comment) {
676
+ if (!comment) {
677
+ return false;
678
+ }
679
+ const { enabled, isVisible } = comment;
680
+ if (enabled === false) {
681
+ return false;
682
+ }
683
+ if (typeof enabled === 'string') {
684
+ return enabled;
685
+ }
686
+ // enabled === true — optional legacy gate
687
+ return isVisible;
688
+ }
689
+ /**
690
+ * Context path for the respondent comment text for a questionnaire item (`answers.comments.<itemName>`).
691
+ */
692
+ function getQuestionAnswerCommentStoragePath(questionItemName) {
693
+ return `answers.comments.${questionItemName}`;
694
+ }
695
+ //#endregion
696
+
697
+ //#region ---- Question hint value object ----
698
+ const DEFAULT_QUESTION_HINT_CONFIG = { mode: 'off' };
699
+ const VISUAL_MODES = [
700
+ 'info',
701
+ 'warning',
702
+ 'danger',
703
+ 'success',
704
+ 'neutral',
705
+ ];
706
+ function isEmptyHintContent(c) {
707
+ if (c == null) {
708
+ return true;
709
+ }
710
+ if (typeof c === 'string') {
711
+ return c.trim().length === 0;
712
+ }
713
+ if (typeof c === 'object') {
714
+ return Object.values(c).every((v) => typeof v !== 'string' || v.trim().length === 0);
715
+ }
716
+ return true;
717
+ }
718
+ /** Normalizes select / API values to a hint mode (exported for questionnaire builder forms). */
719
+ function normalizeQuestionHintMode(raw) {
720
+ if (raw == null) {
721
+ return undefined;
722
+ }
723
+ if (typeof raw === 'string') {
724
+ if (raw === 'off') {
725
+ return 'off';
726
+ }
727
+ if (VISUAL_MODES.includes(raw)) {
728
+ return raw;
729
+ }
730
+ return undefined;
731
+ }
732
+ if (typeof raw === 'object' && raw !== null && 'id' in raw) {
733
+ const id = raw.id;
734
+ return normalizeQuestionHintMode(id);
735
+ }
736
+ return undefined;
737
+ }
738
+ /**
739
+ * Reads optional `hint` or legacy flat `hintContent` / `hintMode` from a record (API / persisted rows).
740
+ */
741
+ function readHintFieldsFromRecord(rec) {
742
+ if (!rec) {
743
+ return {};
744
+ }
745
+ const hint = rec['hint'] ?? rec['Hint'];
746
+ const legacyContent = rec['hintContent'] ?? rec['hint_content'];
747
+ const legacyMode = rec['hintMode'] ?? rec['hint_mode'];
748
+ return { hint, legacyContent, legacyMode };
749
+ }
750
+ /**
751
+ * Normalizes API / partial values to a complete config. Coerces legacy flat fields when `hint` is absent.
752
+ */
753
+ function normalizeQuestionHintConfig(value, legacy) {
754
+ let mode = normalizeQuestionHintMode(value && typeof value === 'object' && 'mode' in value ? value.mode : undefined);
755
+ let content = value && typeof value === 'object' && 'content' in value
756
+ ? value.content
757
+ : undefined;
758
+ const hintObj = value && typeof value === 'object' && ('mode' in value || 'content' in value) ? value : null;
759
+ const noStructuredHint = !hintObj || (mode === undefined && content === undefined);
760
+ if (noStructuredHint && legacy) {
761
+ const lm = normalizeQuestionHintMode(legacy.hintMode);
762
+ const lc = legacy.hintContent;
763
+ if (lm !== undefined) {
764
+ mode = lm;
765
+ }
766
+ if (lc !== undefined) {
767
+ content = lc;
768
+ }
769
+ }
770
+ if (mode === 'off' || mode === undefined) {
771
+ if (mode === undefined && content != null && !isEmptyHintContent(content)) {
772
+ const fallbackMode = 'info';
773
+ return { mode: fallbackMode, content: content };
774
+ }
775
+ return { mode: 'off' };
776
+ }
777
+ if (isEmptyHintContent(content)) {
778
+ return { mode: 'off' };
779
+ }
780
+ return { mode, content: content };
781
+ }
782
+ /**
783
+ * Merges question bank defaults with optional questionnaire item overrides (per-field, like former hintContent/hintMode).
784
+ */
785
+ function mergeQuestionHintForViewer(bank, item) {
786
+ const b = normalizeQuestionHintConfig(bank);
787
+ if (!item || (item.mode === undefined && item.content === undefined)) {
788
+ return b;
789
+ }
790
+ if (item.mode !== undefined) {
791
+ const mode = normalizeQuestionHintMode(item.mode) ?? 'off';
792
+ if (mode === 'off') {
793
+ return { mode: 'off' };
794
+ }
795
+ const content = item.content !== undefined
796
+ ? item.content
797
+ : b.mode !== 'off'
798
+ ? b.content
799
+ : undefined;
800
+ return normalizeQuestionHintConfig({ mode, content });
801
+ }
802
+ const mode = b.mode !== 'off' ? b.mode : 'info';
803
+ return normalizeQuestionHintConfig({ mode, content: item.content });
804
+ }
805
+ /**
806
+ * Returns only fields that differ from the bank default (for persisting questionnaire item overrides).
807
+ */
808
+ function diffQuestionHintOverride(bank, edited) {
809
+ if (edited.mode === 'off' && bank.mode === 'off') {
810
+ return undefined;
811
+ }
812
+ if (edited.mode === 'off' && bank.mode !== 'off') {
813
+ return { mode: 'off' };
814
+ }
815
+ if (bank.mode === 'off' && edited.mode !== 'off') {
816
+ return { mode: edited.mode, content: edited.content };
817
+ }
818
+ const diff = {};
819
+ if (edited.mode !== bank.mode) {
820
+ diff.mode = edited.mode;
821
+ }
822
+ if (edited.mode !== 'off') {
823
+ const bc = bank.mode !== 'off' ? bank.content : undefined;
824
+ const ec = edited.content;
825
+ if (JSON.stringify(ec ?? null) !== JSON.stringify(bc ?? null)) {
826
+ diff.content = ec;
827
+ }
828
+ }
829
+ return Object.keys(diff).length > 0 ? diff : undefined;
830
+ }
831
+ /**
832
+ * Normalizes a question bank row (or similar) to a full hint config, including legacy flat fields.
833
+ */
834
+ function normalizeQuestionHintFromEntityRow(row) {
835
+ const fr = readHintFieldsFromRecord(row);
836
+ return normalizeQuestionHintConfig(fr.hint, {
837
+ hintContent: fr.legacyContent,
838
+ hintMode: fr.legacyMode,
839
+ });
840
+ }
841
+ /**
842
+ * Reads optional questionnaire item hint override from a persisted item record (including legacy keys).
843
+ */
844
+ function parseQuestionHintPartialFromRecord(row) {
845
+ if (!row) {
846
+ return undefined;
847
+ }
848
+ const fr = readHintFieldsFromRecord(row);
849
+ if (fr.hint != null && typeof fr.hint === 'object' && !Array.isArray(fr.hint)) {
850
+ const h = fr.hint;
851
+ if (h.mode !== undefined || h.content !== undefined) {
852
+ const out = {};
853
+ if (h.mode !== undefined) {
854
+ const m = normalizeQuestionHintMode(h.mode);
855
+ if (m !== undefined) {
856
+ out.mode = m;
857
+ }
858
+ }
859
+ if (h.content !== undefined) {
860
+ out.content = h.content;
861
+ }
862
+ return Object.keys(out).length > 0 ? out : undefined;
863
+ }
864
+ }
865
+ if (fr.legacyContent == null && fr.legacyMode == null) {
866
+ return undefined;
867
+ }
868
+ const out = {};
869
+ if (fr.legacyMode !== undefined) {
870
+ const m = normalizeQuestionHintMode(fr.legacyMode);
871
+ if (m !== undefined) {
872
+ out.mode = m;
873
+ }
874
+ }
875
+ if (fr.legacyContent !== undefined) {
876
+ out.content = fr.legacyContent;
877
+ }
878
+ return Object.keys(out).length > 0 ? out : undefined;
879
+ }
880
+ //#endregion
881
+
882
+ const DEFAULT_PRE_QUESTIONNAIRE_CONFIG = { enabled: false };
883
+ const DEFAULT_POST_QUESTIONNAIRE_CONFIG = {
884
+ reviewEnabled: false,
885
+ summaryEnabled: false,
886
+ showOutcomeResults: false,
887
+ };
888
+ const DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY = {
889
+ viewMode: 'single-page',
890
+ showProgressBar: true,
891
+ showTimer: false,
892
+ showQuestionNumbers: true,
893
+ validationStrategy: 'step',
894
+ completionMode: 'submit-only',
895
+ };
896
+ const DEFAULT_QUESTIONNAIRE_ENTITY_PRINT = DEFAULT_QUESTIONNAIRE_PRINT_SETTINGS;
897
+ const OUTCOME_DISPLAY_OPERATORS = [
898
+ 'eq',
899
+ 'neq',
900
+ 'gt',
901
+ 'gte',
902
+ 'lt',
903
+ 'lte',
904
+ ];
905
+ const OUTCOME_DISPLAY_EMPHASIS = ['normal', 'medium', 'bold'];
906
+ function normalizeOutcomeDisplayColor(value) {
907
+ if (typeof value !== 'string') {
908
+ return undefined;
909
+ }
910
+ const trimmed = value.trim();
911
+ const parts = trimmed.split(/\s+/);
912
+ if (parts.length !== 3) {
913
+ return undefined;
914
+ }
915
+ return trimmed;
916
+ }
917
+ function normalizeOutcomeDisplayOperator(value) {
918
+ const raw = typeof value === 'string'
919
+ ? value
920
+ : typeof value === 'object' && value !== null && 'id' in value
921
+ ? String(value.id ?? '')
922
+ : '';
923
+ const normalized = raw.trim().toLowerCase();
924
+ return OUTCOME_DISPLAY_OPERATORS.includes(normalized)
925
+ ? normalized
926
+ : undefined;
927
+ }
928
+ function normalizeOutcomeDisplayCompareValue(value) {
929
+ if (typeof value === 'number' && Number.isFinite(value)) {
930
+ return value;
931
+ }
932
+ if (typeof value === 'boolean') {
933
+ return value;
934
+ }
935
+ if (typeof value === 'string') {
936
+ const trimmed = value.trim();
937
+ if (!trimmed) {
938
+ return undefined;
939
+ }
940
+ if (trimmed === 'true')
941
+ return true;
942
+ if (trimmed === 'false')
943
+ return false;
944
+ const asNumber = Number(trimmed);
945
+ if (Number.isFinite(asNumber)) {
946
+ return asNumber;
947
+ }
948
+ return trimmed;
949
+ }
950
+ return undefined;
951
+ }
952
+ function normalizeOutcomeDisplayEmphasis(value) {
953
+ const raw = typeof value === 'string'
954
+ ? value
955
+ : typeof value === 'object' && value !== null && 'id' in value
956
+ ? String(value.id ?? '')
957
+ : '';
958
+ const normalized = raw.trim().toLowerCase();
959
+ return OUTCOME_DISPLAY_EMPHASIS.includes(normalized)
960
+ ? normalized
961
+ : 'normal';
962
+ }
963
+ function normalizeOutcomeDisplayIcon(icon) {
964
+ if (icon == null || icon === '') {
965
+ return undefined;
966
+ }
967
+ if (typeof icon === 'string') {
968
+ const trimmed = icon.trim();
969
+ return trimmed.length > 0 ? trimmed : undefined;
970
+ }
971
+ const styleClass = String(icon.styleClass ?? '').trim();
972
+ const iconClass = String(icon.iconClass ?? '').trim();
973
+ const combined = [styleClass, iconClass].filter(Boolean).join(' ');
974
+ return combined.length > 0 ? combined : undefined;
975
+ }
976
+ /** Parses a theme color chooser value into foreground, background, and border parts. */
977
+ function parseQuestionnaireOutcomeDisplayColor(value) {
978
+ if (!value?.trim()) {
979
+ return null;
980
+ }
981
+ const parts = value.trim().split(/\s+/);
982
+ if (parts.length !== 3) {
983
+ return null;
984
+ }
985
+ return {
986
+ foreground: parts[0],
987
+ background: parts[1],
988
+ border: parts[2],
989
+ };
990
+ }
991
+ function isQuestionnaireOutcomeDisplayColorHex(value) {
992
+ return value.trim().startsWith('#');
993
+ }
994
+ /**
995
+ * Returns true when the evaluated outcome value matches the display rule condition.
996
+ */
997
+ function matchesQuestionnaireOutcomeDisplayRule(value, rule) {
998
+ const right = rule.compareValue;
999
+ switch (rule.operator) {
1000
+ case 'eq':
1001
+ return value == right;
1002
+ case 'neq':
1003
+ return value != right;
1004
+ case 'gt':
1005
+ return Number(value) > Number(right);
1006
+ case 'gte':
1007
+ return Number(value) >= Number(right);
1008
+ case 'lt':
1009
+ return Number(value) < Number(right);
1010
+ case 'lte':
1011
+ return Number(value) <= Number(right);
1012
+ default:
1013
+ return false;
1014
+ }
1015
+ }
1016
+ function normalizeQuestionnaireOutcomeDisplayRules(rules) {
1017
+ if (!Array.isArray(rules)) {
1018
+ return [];
1019
+ }
1020
+ const normalized = [];
1021
+ for (const [index, rule] of rules.entries()) {
1022
+ const operator = normalizeOutcomeDisplayOperator(rule?.operator);
1023
+ const compareValue = normalizeOutcomeDisplayCompareValue(rule?.compareValue);
1024
+ const color = normalizeOutcomeDisplayColor(rule?.color);
1025
+ if (!operator || compareValue === undefined || !color) {
1026
+ continue;
1027
+ }
1028
+ normalized.push({
1029
+ id: String(rule?.id ?? '').trim() || `rule-${index + 1}`,
1030
+ order: rule?.order ?? index,
1031
+ operator,
1032
+ compareValue,
1033
+ color,
1034
+ emphasis: normalizeOutcomeDisplayEmphasis(rule?.emphasis),
1035
+ icon: normalizeOutcomeDisplayIcon(rule?.icon),
1036
+ });
1037
+ }
1038
+ return normalized.sort((a, b) => a.order - b.order);
1039
+ }
1040
+ const DEFAULT_QUESTIONNAIRE_OUTCOMES = { sections: [] };
1041
+ function normalizeQuestionnaireOutcomesValue(value) {
1042
+ const sections = value?.sections;
1043
+ if (!Array.isArray(sections)) {
1044
+ return { sections: [] };
1045
+ }
1046
+ return {
1047
+ sections: sections.map((section, index) => ({
1048
+ ...section,
1049
+ name: (section.name ?? '').trim() || `section-${index + 1}`,
1050
+ order: section.order ?? index,
1051
+ items: Array.isArray(section.items)
1052
+ ? section.items.map((item) => ({
1053
+ ...item,
1054
+ displayRules: normalizeQuestionnaireOutcomeDisplayRules(item.displayRules),
1055
+ }))
1056
+ : [],
1057
+ })),
1058
+ };
1059
+ }
1060
+ /** All outcome items across sections (evaluation, automations, duplicate checks). */
1061
+ function flattenQuestionnaireOutcomeItems(value) {
1062
+ return normalizeQuestionnaireOutcomesValue(value).sections.flatMap((section) => section.items);
1063
+ }
1064
+ function findDuplicateOutcomeNames(value) {
1065
+ const seen = new Map();
1066
+ for (const item of flattenQuestionnaireOutcomeItems(value)) {
1067
+ const key = (item.name ?? '').trim();
1068
+ if (!key)
1069
+ continue;
1070
+ seen.set(key, (seen.get(key) ?? 0) + 1);
1071
+ }
1072
+ return Array.from(seen.entries())
1073
+ .filter(([, count]) => count > 1)
1074
+ .map(([name]) => name);
1075
+ }
1076
+ /**
1077
+ * Maps entity pre/post to viewer config (defaults when omitted).
1078
+ */
1079
+ function toPrePostConfig(q) {
1080
+ if (!q) {
1081
+ return null;
1082
+ }
1083
+ const pre = {
1084
+ ...DEFAULT_PRE_QUESTIONNAIRE_CONFIG,
1085
+ ...q.pre,
1086
+ enabled: q.pre?.enabled === true,
1087
+ };
1088
+ const post = {
1089
+ ...DEFAULT_POST_QUESTIONNAIRE_CONFIG,
1090
+ ...q.post,
1091
+ reviewEnabled: q.post?.reviewEnabled === true,
1092
+ summaryEnabled: q.post?.summaryEnabled === true,
1093
+ showOutcomeResults: q.post?.showOutcomeResults === true,
1094
+ };
1095
+ return { pre, post };
1096
+ }
1097
+ const VIEW_MODE_VALUES = ['single-page', 'page-per-group', 'page-per-question', 'side-menu'];
1098
+ const VALIDATION_STRATEGY_VALUES = ['step', 'end'];
1099
+ const COMPLETION_MODE_VALUES = ['submit-only', 'save-and-continue'];
1100
+ /**
1101
+ * Coerces stored view mode (string or SelectBox `{ id }`) to a known value.
1102
+ */
1103
+ function normalizeViewMode(value) {
1104
+ if (typeof value === 'string' && VIEW_MODE_VALUES.includes(value)) {
1105
+ return value;
1106
+ }
1107
+ if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
1108
+ return normalizeViewMode(value.id);
1109
+ }
1110
+ return 'single-page';
1111
+ }
1112
+ /**
1113
+ * Coerces stored validation strategy (string or SelectBox `{ id }`) to a known value.
1114
+ */
1115
+ function normalizeValidationStrategy(value) {
1116
+ if (typeof value === 'string' && VALIDATION_STRATEGY_VALUES.includes(value)) {
1117
+ return value;
1118
+ }
1119
+ if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
1120
+ return normalizeValidationStrategy(value.id);
1121
+ }
1122
+ return 'step';
1123
+ }
1124
+ /**
1125
+ * Coerces stored completion mode (string or SelectBox `{ id }`) to a known value.
1126
+ */
1127
+ function normalizeCompletionMode(value) {
1128
+ if (typeof value === 'string' && COMPLETION_MODE_VALUES.includes(value)) {
1129
+ return value;
1130
+ }
1131
+ if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
1132
+ return normalizeCompletionMode(value.id);
1133
+ }
1134
+ return 'submit-only';
1135
+ }
1136
+ /**
1137
+ * Maps entity `display` to {@link QuestionnaireDisplaySettings} (defaults when omitted).
1138
+ */
1139
+ function toDisplaySettings(q) {
1140
+ if (!q) {
1141
+ return null;
1142
+ }
1143
+ const d = { ...DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY, ...q.display };
1144
+ return {
1145
+ viewMode: normalizeViewMode(d.viewMode),
1146
+ showProgressBar: d.showProgressBar ?? true,
1147
+ showTimer: d.showTimer ?? false,
1148
+ showQuestionNumbers: d.showQuestionNumbers ?? true,
1149
+ validationStrategy: normalizeValidationStrategy(d.validationStrategy),
1150
+ completionMode: normalizeCompletionMode(d.completionMode),
1151
+ };
1152
+ }
1153
+ /**
1154
+ * Maps entity `print` to {@link QuestionnairePrintSettings} (defaults when omitted).
1155
+ */
1156
+ function toPrintSettings(q) {
1157
+ if (!q) {
1158
+ return null;
1159
+ }
1160
+ return resolvePrintSettings(q.print);
1161
+ }
1162
+ //#endregion
1163
+
1164
+ //#region ---- Contracts barrel ----
1165
+
1166
+ /**
1167
+ * Generated bundle index. Do not edit.
1168
+ */
1169
+
1170
+ export { ASSESSMENT_PRINT_CONTENT_WIDTH_MM, ASSESSMENT_PRINT_HTML_GZIP_ENCODING, ASSESSMENT_PRINT_PAGE_FORMAT, AXMAssessmentManagementFeatureKeys, AXMAssessmentManagementPermissionKeys, AXM_ASSESSMENT_PRINT_MERGE_KEYS, AXM_ASSESSMENT_SESSION_PREVIEW_PDF_CLIENT_COMMAND_KEY, AXM_ASSESSMENT_SESSION_PREVIEW_PDF_COMMAND_KEY, AXPAssessmentManagementMenuKeys, AXPSafetyRiskPermissionKeys, DEFAULT_POST_QUESTIONNAIRE_CONFIG, DEFAULT_PRE_QUESTIONNAIRE_CONFIG, DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY, DEFAULT_QUESTIONNAIRE_ENTITY_PRINT, DEFAULT_QUESTIONNAIRE_OUTCOMES, DEFAULT_QUESTIONNAIRE_PRINT_SETTINGS, DEFAULT_QUESTION_COMMENT_CONFIG, DEFAULT_QUESTION_HINT_CONFIG, PRINT_PAGE_LETTERHEAD_BOTTOM_MARGIN_MM, PRINT_PAGE_LETTERHEAD_TOP_MARGIN_MM, PRINT_PAGE_SIDE_MARGIN_MM, QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY, QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY, RootConfig, applyQuestionnairePrintHeader, buildQuestionnairePrintBodyHtml, buildQuestionnairePrintHeaderRegion, buildQuestionnaireQuestionCardHtml, buildQuestionnaireSessionBodyHtml, commentConfigForEditForm, diffQuestionCommentOverride, diffQuestionHintOverride, escapePrintDocumentText, extractPrintDocumentBodyHtml, findDuplicateOutcomeNames, flattenQuestionnaireOutcomeItems, formatPrintAnswerText, getQuestionAnswerCommentStoragePath, getQuestionCommentVisibilityFlag, isQuestionnaireOutcomeDisplayColorHex, matchesQuestionnaireOutcomeDisplayRule, mergeQuestionCommentConfig, mergeQuestionHintForViewer, minifyPrintHtml, normalizeCompletionMode, normalizePrintOrientation, normalizePrintPaperSize, normalizePrintWatermark, normalizeQuestionCommentConfig, normalizeQuestionHintConfig, normalizeQuestionHintFromEntityRow, normalizeQuestionHintMode, normalizeQuestionnaireOutcomeDisplayRules, normalizeQuestionnaireOutcomesValue, normalizeValidationStrategy, normalizeViewMode, parseQuestionHintPartialFromRecord, parseQuestionnaireOutcomeDisplayColor, readHintFieldsFromRecord, resolvePrintContentWidthMm, resolvePrintLetterheadPdfMargin, resolvePrintPaging, resolvePrintPdfPageOptions, resolvePrintPlainText, resolvePrintSettings, toDisplaySettings, toPrePostConfig, toPrintSettings, wrapPrintDocument };
1171
+ //# sourceMappingURL=acorex-platform-module-assessment-management-client-contracts.mjs.map