@acorex/modules 21.0.0-next.59 → 21.0.0-next.63

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 (49) hide show
  1. package/assessment-management/README.md +4 -4
  2. package/fesm2022/{acorex-modules-assessment-management-acorex-modules-assessment-management-o8aFazwA.mjs → acorex-modules-assessment-management-acorex-modules-assessment-management-DlhSQ0cB.mjs} +3608 -367
  3. package/fesm2022/acorex-modules-assessment-management-acorex-modules-assessment-management-DlhSQ0cB.mjs.map +1 -0
  4. package/fesm2022/{acorex-modules-assessment-management-assessment-case.entity-yrc_Yybq.mjs → acorex-modules-assessment-management-assessment-case.entity-B6C2PRZw.mjs} +43 -28
  5. package/fesm2022/acorex-modules-assessment-management-assessment-case.entity-B6C2PRZw.mjs.map +1 -0
  6. package/fesm2022/{acorex-modules-assessment-management-assessment-session-answers-view.util-BUQa_TSY.mjs → acorex-modules-assessment-management-assessment-session-answers-view.util-BRdwcIvw.mjs} +2 -2
  7. package/fesm2022/{acorex-modules-assessment-management-assessment-session-answers-view.util-BUQa_TSY.mjs.map → acorex-modules-assessment-management-assessment-session-answers-view.util-BRdwcIvw.mjs.map} +1 -1
  8. package/fesm2022/{acorex-modules-assessment-management-assessment-session.entity-Cwf30iuu.mjs → acorex-modules-assessment-management-assessment-session.entity-DquzZNaQ.mjs} +19 -3
  9. package/fesm2022/acorex-modules-assessment-management-assessment-session.entity-DquzZNaQ.mjs.map +1 -0
  10. package/fesm2022/{acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs → acorex-modules-assessment-management-fill-assessment-session.command-CRE8SxzC.mjs} +2 -2
  11. package/fesm2022/acorex-modules-assessment-management-fill-assessment-session.command-CRE8SxzC.mjs.map +1 -0
  12. package/fesm2022/{acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs → acorex-modules-assessment-management-preview-question.command-Ce7yOnJY.mjs} +2 -2
  13. package/fesm2022/acorex-modules-assessment-management-preview-question.command-Ce7yOnJY.mjs.map +1 -0
  14. package/fesm2022/{acorex-modules-assessment-management-preview-questionnaire.command-wqPNgv7q.mjs → acorex-modules-assessment-management-preview-questionnaire.command-S-8MDeB7.mjs} +2 -2
  15. package/fesm2022/{acorex-modules-assessment-management-preview-questionnaire.command-wqPNgv7q.mjs.map → acorex-modules-assessment-management-preview-questionnaire.command-S-8MDeB7.mjs.map} +1 -1
  16. package/fesm2022/{acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-DYyUhSWd.mjs → acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-wdHMGBxi.mjs} +2 -2
  17. package/fesm2022/acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-wdHMGBxi.mjs.map +1 -0
  18. package/fesm2022/{acorex-modules-assessment-management-question-bank-item.entity-DUf2ceKY.mjs → acorex-modules-assessment-management-question-bank-item.entity-1Pm9O0kv.mjs} +2 -2
  19. package/fesm2022/{acorex-modules-assessment-management-question-bank-item.entity-DUf2ceKY.mjs.map → acorex-modules-assessment-management-question-bank-item.entity-1Pm9O0kv.mjs.map} +1 -1
  20. package/fesm2022/{acorex-modules-assessment-management-questionnaire-viewer-popup.component-Cy5ElBmU.mjs → acorex-modules-assessment-management-questionnaire-viewer-popup.component-Dwx0y7WY.mjs} +9 -121
  21. package/fesm2022/acorex-modules-assessment-management-questionnaire-viewer-popup.component-Dwx0y7WY.mjs.map +1 -0
  22. package/fesm2022/{acorex-modules-assessment-management-questionnaire.entity-RWSkzNbA.mjs → acorex-modules-assessment-management-questionnaire.entity-BI_jc4C6.mjs} +12 -36
  23. package/fesm2022/acorex-modules-assessment-management-questionnaire.entity-BI_jc4C6.mjs.map +1 -0
  24. package/fesm2022/{acorex-modules-assessment-management-view-case-last-session-answers.command-NTuDdoYk.mjs → acorex-modules-assessment-management-view-case-last-session-answers.command-Ds-QGMPy.mjs} +3 -3
  25. package/fesm2022/{acorex-modules-assessment-management-view-case-last-session-answers.command-NTuDdoYk.mjs.map → acorex-modules-assessment-management-view-case-last-session-answers.command-Ds-QGMPy.mjs.map} +1 -1
  26. package/fesm2022/{acorex-modules-assessment-management-view-session-answers.command-0btGV66_.mjs → acorex-modules-assessment-management-view-session-answers.command-COTVoRRG.mjs} +3 -3
  27. package/fesm2022/{acorex-modules-assessment-management-view-session-answers.command-0btGV66_.mjs.map → acorex-modules-assessment-management-view-session-answers.command-COTVoRRG.mjs.map} +1 -1
  28. package/fesm2022/acorex-modules-assessment-management-view-session-outcomes.command-2buH7ydV.mjs +239 -0
  29. package/fesm2022/acorex-modules-assessment-management-view-session-outcomes.command-2buH7ydV.mjs.map +1 -0
  30. package/fesm2022/acorex-modules-assessment-management.mjs +1 -1
  31. package/fesm2022/acorex-modules-common.mjs +1 -0
  32. package/fesm2022/acorex-modules-common.mjs.map +1 -1
  33. package/package.json +2 -2
  34. package/types/acorex-modules-assessment-management.d.ts +383 -271
  35. package/types/acorex-modules-common.d.ts +1 -0
  36. package/fesm2022/acorex-modules-assessment-management-acorex-modules-assessment-management-o8aFazwA.mjs.map +0 -1
  37. package/fesm2022/acorex-modules-assessment-management-assessment-case.entity-yrc_Yybq.mjs.map +0 -1
  38. package/fesm2022/acorex-modules-assessment-management-assessment-session.entity-Cwf30iuu.mjs.map +0 -1
  39. package/fesm2022/acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs.map +0 -1
  40. package/fesm2022/acorex-modules-assessment-management-index-D8KjfHAH.mjs +0 -1509
  41. package/fesm2022/acorex-modules-assessment-management-index-D8KjfHAH.mjs.map +0 -1
  42. package/fesm2022/acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs.map +0 -1
  43. package/fesm2022/acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-DYyUhSWd.mjs.map +0 -1
  44. package/fesm2022/acorex-modules-assessment-management-questionnaire-calculation.entity-CYF0k42_.mjs +0 -236
  45. package/fesm2022/acorex-modules-assessment-management-questionnaire-calculation.entity-CYF0k42_.mjs.map +0 -1
  46. package/fesm2022/acorex-modules-assessment-management-questionnaire-viewer-popup.component-Cy5ElBmU.mjs.map +0 -1
  47. package/fesm2022/acorex-modules-assessment-management-questionnaire.entity-RWSkzNbA.mjs.map +0 -1
  48. package/fesm2022/acorex-modules-assessment-management-save-questionnaire-questions.command-koGmUbNE.mjs +0 -61
  49. package/fesm2022/acorex-modules-assessment-management-save-questionnaire-questions.command-koGmUbNE.mjs.map +0 -1
@@ -1,48 +1,53 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injector, Injectable, NgModule, computed, input, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, signal, afterNextRender, viewChild, effect, output } from '@angular/core';
2
+ import { inject, Injector, Injectable, NgModule, input, output, signal, viewChild, computed, effect, untracked, ChangeDetectionStrategy, Component, ViewEncapsulation, ElementRef, afterNextRender } from '@angular/core';
3
3
  import { AXPCommonMenuKeys } from '@acorex/modules/common';
4
- import { AXPEntityService, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
5
- import { AXPSearchCommandProvider, createEntityCommandOptions, AXPFileStorageService, AXPStickyDirective, AXP_STATUS_PROVIDERS, AXP_MENU_PROVIDER, AXP_SEARCH_PROVIDER } from '@acorex/platform/common';
4
+ import { AXPEntityService, AXPEntityDefinitionRegistryService, AXPEntityEventsKeys, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
5
+ import { AXPSearchCommandProvider, createEntityCommandOptions, AXPFileStorageService, AXPStickyDirective, AXP_STATUS_PROVIDERS, AXP_MENU_PROVIDER, AXP_SEARCH_PROVIDER, AXPSystemStatusType } from '@acorex/platform/common';
6
6
  import { AXPAuthGuard, AXP_PERMISSION_DEFINITION_PROVIDER, AXPSessionService } from '@acorex/platform/auth';
7
- import { AXPStateMessageComponent, AXP_PAGE_COMPONENT_PROVIDER, AXPThemeLayoutBlockComponent, AXPStopwatchComponent } from '@acorex/platform/layout/components';
8
- import { provideCommandSetups, provideCommand } from '@acorex/platform/runtime';
9
- import { AXPExpressionEvaluatorService, AXPDeviceService, containsHtmlMarkup, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, provideLazyProvider, AXP_FEATURE_DEFINITION_PROVIDER, AXPContextStore } from '@acorex/platform/core';
7
+ import { AXPDataSelectorService, AXPPropertyViewerService, AXPStandardSectionItemsBuilderComponent, AXPStateMessageComponent, AXP_PAGE_COMPONENT_PROVIDER, AXPOutcomeResultsViewerComponent, AXPThemeLayoutBlockComponent, AXPStopwatchComponent } from '@acorex/platform/layout/components';
8
+ import { AXPCommandService, provideCommandSetups, provideCommand } from '@acorex/platform/runtime';
9
+ import { AXPExpressionEvaluatorService, AXPDeviceService, AXPBroadcastEventService, AXPDataGenerator, containsHtmlMarkup, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, provideLazyProvider, AXP_FEATURE_DEFINITION_PROVIDER, AXPContextStore } from '@acorex/platform/core';
10
10
  import { ROUTES, ActivatedRoute, Router } from '@angular/router';
11
11
  import { AXPRootLayoutComponent } from '@acorex/platform/themes/default';
12
12
  import { AXMMetaDataDefinitionEntityModule } from '@acorex/modules/data-management';
13
- import * as i1 from '@acorex/platform/layout/widget-core';
14
- import { AXPWidgetGroupEnum, AXP_WIDGETS_EDITOR_CATEGORY, AXPWidgetCoreModule, AXP_WIDGET_DEFINITION_PROVIDER, AXPWidgetContainerComponent, AXPWidgetCoreService } from '@acorex/platform/layout/widget-core';
13
+ import * as i1$1 from '@acorex/platform/layout/widget-core';
14
+ import { AXPWidgetGroupEnum, AXP_WIDGETS_EDITOR_CATEGORY, AXPWidgetCoreModule, AXP_WIDGET_DEFINITION_PROVIDER, AXPWidgetsCatalog, createBooleanProperty, AXPWidgetRegistryService, AXPWidgetContainerComponent, AXPWidgetCoreService } from '@acorex/platform/layout/widget-core';
15
15
  import { AXP_NAME_PROPERTY, AXP_DATA_PATH_PROPERTY } from '@acorex/platform/layout/widgets';
16
16
  import * as i3 from '@angular/common';
17
17
  import { CommonModule, AsyncPipe } from '@angular/common';
18
18
  import * as i1$3 from '@acorex/components/form';
19
19
  import { AXFormModule } from '@acorex/components/form';
20
- import * as i4$1 from '@acorex/core/translation';
21
- import { resolveMultiLanguageString, AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
22
- import * as i2$1 from '@acorex/components/decorators';
20
+ import * as i4 from '@acorex/core/translation';
21
+ import { resolveMultiLanguageString, AXTranslationService, AXTranslationModule, createMultiLanguageString, isEffectivelyEmptyLocalizedValue } from '@acorex/core/translation';
22
+ import * as i2 from '@acorex/components/decorators';
23
23
  import { AXDecoratorModule } from '@acorex/components/decorators';
24
24
  import { AXToastService } from '@acorex/components/toast';
25
25
  import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
26
- import { cloneDeep, get } from 'lodash-es';
26
+ import { cloneDeep, get, isEqual, isNil, first, defaultTo, isEmpty, isObject } from 'lodash-es';
27
+ import { AXDataSource } from '@acorex/cdk/common';
28
+ import { AXFullScreenDirective } from '@acorex/cdk/full-screen';
29
+ import * as i1 from '@acorex/components/button';
30
+ import { AXButtonModule } from '@acorex/components/button';
31
+ import { AXDropdownModule } from '@acorex/components/dropdown';
32
+ import { AXLabelModule } from '@acorex/components/label';
33
+ import { AXSwitchModule } from '@acorex/components/switch';
34
+ import * as i5 from '@angular/forms';
35
+ import { FormsModule } from '@angular/forms';
27
36
  import { AXPopupService } from '@acorex/components/popup';
28
- import * as i2 from '@acorex/components/alert';
37
+ import { AXPPageLayoutBaseComponent, AXPPageLayoutBase, AXPPageLayoutComponent } from '@acorex/platform/layout/views';
38
+ import * as i2$1 from '@acorex/components/alert';
29
39
  import { AXAlertModule } from '@acorex/components/alert';
30
- import * as i4 from '@acorex/components/text-area';
40
+ import * as i4$1 from '@acorex/components/text-area';
31
41
  import { AXTextAreaModule } from '@acorex/components/text-area';
32
42
  import { AXSafePipe } from '@acorex/core/pipes';
33
- import * as i5 from '@angular/forms';
34
- import { FormsModule } from '@angular/forms';
35
43
  import { AXDrawerDirectiveModule } from '@acorex/cdk/drawer';
36
44
  import { AXResizableDirective } from '@acorex/cdk/resizable';
37
45
  import * as i2$2 from '@acorex/components/badge';
38
46
  import { AXBadgeModule } from '@acorex/components/badge';
39
- import * as i1$2 from '@acorex/components/button';
40
- import { AXButtonModule } from '@acorex/components/button';
41
47
  import * as i3$1 from '@acorex/components/drawer';
42
48
  import { AXDrawerModule } from '@acorex/components/drawer';
43
- import * as i1$1 from '@acorex/components/tabs';
49
+ import * as i1$2 from '@acorex/components/tabs';
44
50
  import { AXTabsComponent, AXTabsModule } from '@acorex/components/tabs';
45
- import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageLayoutBase } from '@acorex/platform/layout/views';
46
51
  import { DomSanitizer } from '@angular/platform-browser';
47
52
 
48
53
  //#region ---- Root Configuration ----
@@ -81,12 +86,6 @@ const RootConfig = {
81
86
  titlePlural: '@assessment-management:sessions.entities.assessment-session.plural',
82
87
  icon: 'fa-light fa-clipboard-check',
83
88
  },
84
- questionnaireCalculation: {
85
- name: 'QuestionnaireCalculation',
86
- title: '@assessment-management:questionnaire-calculations.entities.questionnaire-calculation.title',
87
- titlePlural: '@assessment-management:questionnaire-calculations.entities.questionnaire-calculation.plural',
88
- icon: 'fa-light fa-calculator',
89
- },
90
89
  },
91
90
  };
92
91
  //#endregion
@@ -102,15 +101,13 @@ class AXMAssessmentManagementEntityProvider {
102
101
  if (moduleName === RootConfig.module.name) {
103
102
  switch (entityName) {
104
103
  case RootConfig.entities.questionBankItem.name:
105
- return (await import('./acorex-modules-assessment-management-question-bank-item.entity-DUf2ceKY.mjs')).factory(this.injector);
104
+ return (await import('./acorex-modules-assessment-management-question-bank-item.entity-1Pm9O0kv.mjs')).factory(this.injector);
106
105
  case RootConfig.entities.questionnaire.name:
107
- return (await import('./acorex-modules-assessment-management-questionnaire.entity-RWSkzNbA.mjs')).factory();
106
+ return (await import('./acorex-modules-assessment-management-questionnaire.entity-BI_jc4C6.mjs')).factory();
108
107
  case RootConfig.entities.assessmentCase.name:
109
- return (await import('./acorex-modules-assessment-management-assessment-case.entity-yrc_Yybq.mjs')).factory(this.injector);
108
+ return (await import('./acorex-modules-assessment-management-assessment-case.entity-B6C2PRZw.mjs')).factory(this.injector);
110
109
  case RootConfig.entities.assessmentSession.name:
111
- return (await import('./acorex-modules-assessment-management-assessment-session.entity-Cwf30iuu.mjs')).factory(this.injector);
112
- case RootConfig.entities.questionnaireCalculation.name:
113
- return (await import('./acorex-modules-assessment-management-questionnaire-calculation.entity-CYF0k42_.mjs')).factory(this.injector);
110
+ return (await import('./acorex-modules-assessment-management-assessment-session.entity-DquzZNaQ.mjs')).factory(this.injector);
114
111
  }
115
112
  }
116
113
  return null;
@@ -177,9 +174,6 @@ const AXMAssessmentManagementPermissionKeys = {
177
174
  Session: {
178
175
  Management: 'AssessmentManagement:Session:Management',
179
176
  },
180
- QuestionnaireCalculation: {
181
- Management: 'AssessmentManagement:QuestionnaireCalculation:Management',
182
- },
183
177
  Automation: {
184
178
  Management: 'AssessmentManagement:Automation:Management',
185
179
  },
@@ -270,7 +264,6 @@ class AXMAssessmentManagementPermissionDefinitionProvider {
270
264
  const QUESTIONNAIRE_PERMISSIONS = '@assessment-management:questionnaires.permissions.questionnaire';
271
265
  const CASE_PERMISSIONS = '@assessment-management:cases.permissions.assessment-case';
272
266
  const SESSION_PERMISSIONS = '@assessment-management:sessions.permissions.assessment-session';
273
- const QUESTIONNAIRE_CALCULATION_PERMISSIONS = '@assessment-management:questionnaire-calculations.permissions.questionnaire-calculation';
274
267
  const AUTOMATION_PERMISSIONS = '@assessment-management:automations.permissions.automation';
275
268
  context
276
269
  .addGroup(RootConfig.module.name, `${MODULE_PERMISSIONS}.title`, `${MODULE_PERMISSIONS}.description`)
@@ -289,9 +282,6 @@ class AXMAssessmentManagementPermissionDefinitionProvider {
289
282
  // AssessmentSession Permissions (view-only; sessions are created by workflows)
290
283
  .addPermission(keys.Session.Management, `${SESSION_PERMISSIONS}.management.title`, `${SESSION_PERMISSIONS}.management.description`)
291
284
  .endPermission()
292
- // QuestionnaireCalculation Permissions
293
- .addPermission(keys.QuestionnaireCalculation.Management, `${QUESTIONNAIRE_CALCULATION_PERMISSIONS}.management.title`, `${QUESTIONNAIRE_CALCULATION_PERMISSIONS}.management.description`)
294
- .endPermission()
295
285
  // Automation Permissions
296
286
  .addPermission(keys.Automation.Management, `${AUTOMATION_PERMISSIONS}.management.title`, `${AUTOMATION_PERMISSIONS}.management.description`)
297
287
  .endPermission()
@@ -440,10 +430,10 @@ const AXMQuestionBankInterfaceEditorWidget = {
440
430
  ],
441
431
  components: {
442
432
  edit: {
443
- component: () => import('./acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-DYyUhSWd.mjs').then((c) => c.AXMQuestionBankInterfaceEditorWidgetEditComponent),
433
+ component: () => import('./acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-wdHMGBxi.mjs').then((c) => c.AXMQuestionBankInterfaceEditorWidgetEditComponent),
444
434
  },
445
435
  designer: {
446
- component: () => import('./acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-DYyUhSWd.mjs').then((c) => c.AXMQuestionBankInterfaceEditorWidgetEditComponent),
436
+ component: () => import('./acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-wdHMGBxi.mjs').then((c) => c.AXMQuestionBankInterfaceEditorWidgetEditComponent),
447
437
  },
448
438
  column: {
449
439
  component: () => import('@acorex/platform/layout/widgets').then((m) => m.AXPWidgetFieldConfiguratorWidgetColumnComponent),
@@ -998,6 +988,279 @@ function allocateNextQuestionItemName(used) {
998
988
  }
999
989
  //#endregion
1000
990
 
991
+ //#region ---- Imports ----
992
+ const DEFAULT_PRE_QUESTIONNAIRE_CONFIG = { enabled: false };
993
+ const DEFAULT_POST_QUESTIONNAIRE_CONFIG = {
994
+ reviewEnabled: false,
995
+ summaryEnabled: false,
996
+ showOutcomeResults: false,
997
+ };
998
+ const DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY = {
999
+ viewMode: 'single-page',
1000
+ showProgressBar: true,
1001
+ showTimer: false,
1002
+ showQuestionNumbers: true,
1003
+ validationStrategy: 'step',
1004
+ completionMode: 'submit-only',
1005
+ };
1006
+ const OUTCOME_DISPLAY_OPERATORS = [
1007
+ 'eq',
1008
+ 'neq',
1009
+ 'gt',
1010
+ 'gte',
1011
+ 'lt',
1012
+ 'lte',
1013
+ ];
1014
+ const OUTCOME_DISPLAY_EMPHASIS = ['normal', 'medium', 'bold'];
1015
+ function normalizeOutcomeDisplayColor(value) {
1016
+ if (typeof value !== 'string') {
1017
+ return undefined;
1018
+ }
1019
+ const trimmed = value.trim();
1020
+ const parts = trimmed.split(/\s+/);
1021
+ if (parts.length !== 3) {
1022
+ return undefined;
1023
+ }
1024
+ return trimmed;
1025
+ }
1026
+ function normalizeOutcomeDisplayOperator(value) {
1027
+ const raw = typeof value === 'string'
1028
+ ? value
1029
+ : typeof value === 'object' && value !== null && 'id' in value
1030
+ ? String(value.id ?? '')
1031
+ : '';
1032
+ const normalized = raw.trim().toLowerCase();
1033
+ return OUTCOME_DISPLAY_OPERATORS.includes(normalized)
1034
+ ? normalized
1035
+ : undefined;
1036
+ }
1037
+ function normalizeOutcomeDisplayCompareValue(value) {
1038
+ if (typeof value === 'number' && Number.isFinite(value)) {
1039
+ return value;
1040
+ }
1041
+ if (typeof value === 'boolean') {
1042
+ return value;
1043
+ }
1044
+ if (typeof value === 'string') {
1045
+ const trimmed = value.trim();
1046
+ if (!trimmed) {
1047
+ return undefined;
1048
+ }
1049
+ if (trimmed === 'true')
1050
+ return true;
1051
+ if (trimmed === 'false')
1052
+ return false;
1053
+ const asNumber = Number(trimmed);
1054
+ if (Number.isFinite(asNumber)) {
1055
+ return asNumber;
1056
+ }
1057
+ return trimmed;
1058
+ }
1059
+ return undefined;
1060
+ }
1061
+ function normalizeOutcomeDisplayEmphasis(value) {
1062
+ const raw = typeof value === 'string'
1063
+ ? value
1064
+ : typeof value === 'object' && value !== null && 'id' in value
1065
+ ? String(value.id ?? '')
1066
+ : '';
1067
+ const normalized = raw.trim().toLowerCase();
1068
+ return OUTCOME_DISPLAY_EMPHASIS.includes(normalized)
1069
+ ? normalized
1070
+ : 'normal';
1071
+ }
1072
+ function normalizeOutcomeDisplayIcon(icon) {
1073
+ if (icon == null || icon === '') {
1074
+ return undefined;
1075
+ }
1076
+ if (typeof icon === 'string') {
1077
+ const trimmed = icon.trim();
1078
+ return trimmed.length > 0 ? trimmed : undefined;
1079
+ }
1080
+ const styleClass = String(icon.styleClass ?? '').trim();
1081
+ const iconClass = String(icon.iconClass ?? '').trim();
1082
+ const combined = [styleClass, iconClass].filter(Boolean).join(' ');
1083
+ return combined.length > 0 ? combined : undefined;
1084
+ }
1085
+ /** Parses a theme color chooser value into foreground, background, and border parts. */
1086
+ function parseQuestionnaireOutcomeDisplayColor(value) {
1087
+ if (!value?.trim()) {
1088
+ return null;
1089
+ }
1090
+ const parts = value.trim().split(/\s+/);
1091
+ if (parts.length !== 3) {
1092
+ return null;
1093
+ }
1094
+ return {
1095
+ foreground: parts[0],
1096
+ background: parts[1],
1097
+ border: parts[2],
1098
+ };
1099
+ }
1100
+ function isQuestionnaireOutcomeDisplayColorHex(value) {
1101
+ return value.trim().startsWith('#');
1102
+ }
1103
+ /**
1104
+ * Returns true when the evaluated outcome value matches the display rule condition.
1105
+ */
1106
+ function matchesQuestionnaireOutcomeDisplayRule(value, rule) {
1107
+ const right = rule.compareValue;
1108
+ switch (rule.operator) {
1109
+ case 'eq':
1110
+ return value == right;
1111
+ case 'neq':
1112
+ return value != right;
1113
+ case 'gt':
1114
+ return Number(value) > Number(right);
1115
+ case 'gte':
1116
+ return Number(value) >= Number(right);
1117
+ case 'lt':
1118
+ return Number(value) < Number(right);
1119
+ case 'lte':
1120
+ return Number(value) <= Number(right);
1121
+ default:
1122
+ return false;
1123
+ }
1124
+ }
1125
+ function normalizeQuestionnaireOutcomeDisplayRules(rules) {
1126
+ if (!Array.isArray(rules)) {
1127
+ return [];
1128
+ }
1129
+ const normalized = [];
1130
+ for (const [index, rule] of rules.entries()) {
1131
+ const operator = normalizeOutcomeDisplayOperator(rule?.operator);
1132
+ const compareValue = normalizeOutcomeDisplayCompareValue(rule?.compareValue);
1133
+ const color = normalizeOutcomeDisplayColor(rule?.color);
1134
+ if (!operator || compareValue === undefined || !color) {
1135
+ continue;
1136
+ }
1137
+ normalized.push({
1138
+ id: String(rule?.id ?? '').trim() || `rule-${index + 1}`,
1139
+ order: rule?.order ?? index,
1140
+ operator,
1141
+ compareValue,
1142
+ color,
1143
+ emphasis: normalizeOutcomeDisplayEmphasis(rule?.emphasis),
1144
+ icon: normalizeOutcomeDisplayIcon(rule?.icon),
1145
+ });
1146
+ }
1147
+ return normalized.sort((a, b) => a.order - b.order);
1148
+ }
1149
+ const DEFAULT_QUESTIONNAIRE_OUTCOMES = { sections: [] };
1150
+ function normalizeQuestionnaireOutcomesValue(value) {
1151
+ const sections = value?.sections;
1152
+ if (!Array.isArray(sections)) {
1153
+ return { sections: [] };
1154
+ }
1155
+ return {
1156
+ sections: sections.map((section, index) => ({
1157
+ ...section,
1158
+ name: (section.name ?? '').trim() || `section-${index + 1}`,
1159
+ order: section.order ?? index,
1160
+ items: Array.isArray(section.items)
1161
+ ? section.items.map((item) => ({
1162
+ ...item,
1163
+ displayRules: normalizeQuestionnaireOutcomeDisplayRules(item.displayRules),
1164
+ }))
1165
+ : [],
1166
+ })),
1167
+ };
1168
+ }
1169
+ /** All outcome items across sections (evaluation, automations, duplicate checks). */
1170
+ function flattenQuestionnaireOutcomeItems(value) {
1171
+ return normalizeQuestionnaireOutcomesValue(value).sections.flatMap((section) => section.items);
1172
+ }
1173
+ function findDuplicateOutcomeNames(value) {
1174
+ const seen = new Map();
1175
+ for (const item of flattenQuestionnaireOutcomeItems(value)) {
1176
+ const key = (item.name ?? '').trim();
1177
+ if (!key)
1178
+ continue;
1179
+ seen.set(key, (seen.get(key) ?? 0) + 1);
1180
+ }
1181
+ return Array.from(seen.entries())
1182
+ .filter(([, count]) => count > 1)
1183
+ .map(([name]) => name);
1184
+ }
1185
+ /**
1186
+ * Maps entity pre/post to viewer config (defaults when omitted).
1187
+ */
1188
+ function toPrePostConfig(q) {
1189
+ if (!q) {
1190
+ return null;
1191
+ }
1192
+ const pre = {
1193
+ ...DEFAULT_PRE_QUESTIONNAIRE_CONFIG,
1194
+ ...q.pre,
1195
+ enabled: q.pre?.enabled === true,
1196
+ };
1197
+ const post = {
1198
+ ...DEFAULT_POST_QUESTIONNAIRE_CONFIG,
1199
+ ...q.post,
1200
+ reviewEnabled: q.post?.reviewEnabled === true,
1201
+ summaryEnabled: q.post?.summaryEnabled === true,
1202
+ showOutcomeResults: q.post?.showOutcomeResults === true,
1203
+ };
1204
+ return { pre, post };
1205
+ }
1206
+ const VIEW_MODE_VALUES = ['single-page', 'page-per-group', 'page-per-question', 'side-menu'];
1207
+ const VALIDATION_STRATEGY_VALUES = ['step', 'end'];
1208
+ const COMPLETION_MODE_VALUES = ['submit-only', 'save-and-continue'];
1209
+ /**
1210
+ * Coerces stored view mode (string or SelectBox `{ id }`) to a known value.
1211
+ */
1212
+ function normalizeViewMode(value) {
1213
+ if (typeof value === 'string' && VIEW_MODE_VALUES.includes(value)) {
1214
+ return value;
1215
+ }
1216
+ if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
1217
+ return normalizeViewMode(value.id);
1218
+ }
1219
+ return 'single-page';
1220
+ }
1221
+ /**
1222
+ * Coerces stored validation strategy (string or SelectBox `{ id }`) to a known value.
1223
+ */
1224
+ function normalizeValidationStrategy$1(value) {
1225
+ if (typeof value === 'string' && VALIDATION_STRATEGY_VALUES.includes(value)) {
1226
+ return value;
1227
+ }
1228
+ if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
1229
+ return normalizeValidationStrategy$1(value.id);
1230
+ }
1231
+ return 'step';
1232
+ }
1233
+ /**
1234
+ * Coerces stored completion mode (string or SelectBox `{ id }`) to a known value.
1235
+ */
1236
+ function normalizeCompletionMode(value) {
1237
+ if (typeof value === 'string' && COMPLETION_MODE_VALUES.includes(value)) {
1238
+ return value;
1239
+ }
1240
+ if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
1241
+ return normalizeCompletionMode(value.id);
1242
+ }
1243
+ return 'submit-only';
1244
+ }
1245
+ /**
1246
+ * Maps entity `display` to {@link QuestionnaireDisplaySettings} (defaults when omitted).
1247
+ */
1248
+ function toDisplaySettings(q) {
1249
+ if (!q) {
1250
+ return null;
1251
+ }
1252
+ const d = { ...DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY, ...q.display };
1253
+ return {
1254
+ viewMode: normalizeViewMode(d.viewMode),
1255
+ showProgressBar: d.showProgressBar ?? true,
1256
+ showTimer: d.showTimer ?? false,
1257
+ showQuestionNumbers: d.showQuestionNumbers ?? true,
1258
+ validationStrategy: normalizeValidationStrategy$1(d.validationStrategy),
1259
+ completionMode: normalizeCompletionMode(d.completionMode),
1260
+ };
1261
+ }
1262
+ //#endregion
1263
+
1001
1264
  //#region ---- Pre/Post Content Utilities ----
1002
1265
  /**
1003
1266
  * Formats elapsed seconds as `m:ss` or `h:mm:ss` when an hour or more.
@@ -1015,7 +1278,7 @@ function formatElapsedDuration(totalSeconds) {
1015
1278
  /**
1016
1279
  * Resolve placeholders in summary content.
1017
1280
  * Fixed: {{totalQuestions}}, {{answeredCount}}, {{totalTime}}, {{elapsedSeconds}}.
1018
- * Plus any {{calculationName}} from values.outcomes.
1281
+ * Plus any {{outcomeName}} from values.outcomes.
1019
1282
  */
1020
1283
  function resolveSummaryPlaceholders(content, values) {
1021
1284
  if (!content)
@@ -1105,7 +1368,7 @@ function isQuestionnaireAnswerValueEmpty(value) {
1105
1368
  //#endregion
1106
1369
  //#region ---- Service ----
1107
1370
  /**
1108
- * Opens the questionnaire viewer dialog and evaluates questionnaire calculations when a questionnaire id is provided.
1371
+ * Opens the questionnaire viewer dialog and evaluates questionnaire outcomes when a questionnaire id is provided.
1109
1372
  */
1110
1373
  class AXMQuestionnaireViewerService {
1111
1374
  constructor() {
@@ -1121,10 +1384,13 @@ class AXMQuestionnaireViewerService {
1121
1384
  * Open questionnaire viewer in a dialog
1122
1385
  */
1123
1386
  async open(config) {
1124
- const { AXMQuestionnaireViewerPopupComponent } = await import('./acorex-modules-assessment-management-questionnaire-viewer-popup.component-Cy5ElBmU.mjs');
1387
+ const { AXMQuestionnaireViewerPopupComponent } = await import('./acorex-modules-assessment-management-questionnaire-viewer-popup.component-Dwx0y7WY.mjs');
1125
1388
  const structure = config.structure;
1126
1389
  const submitHandler = config.submitHandler ??
1127
- ((answers) => this.buildSummaryPlaceholderValues(structure, answers, { questionnaireId: config.questionnaireId }));
1390
+ ((answers) => this.buildSummaryPlaceholderValues(structure, answers, {
1391
+ questionnaireId: config.questionnaireId,
1392
+ outcomeRules: config.outcomeRules,
1393
+ }));
1128
1394
  const result = await this.popupService.open(AXMQuestionnaireViewerPopupComponent, {
1129
1395
  title: config.title,
1130
1396
  size: this.deviceService.isSmall() ? 'full' : 'lg',
@@ -1137,17 +1403,18 @@ class AXMQuestionnaireViewerService {
1137
1403
  saveHandler: config.saveHandler,
1138
1404
  assessmentFillContext: config.assessmentFillContext,
1139
1405
  questionnaireId: config.questionnaireId,
1406
+ outcomeRules: config.outcomeRules,
1140
1407
  },
1141
1408
  });
1142
1409
  return result.data || null;
1143
1410
  }
1144
1411
  /**
1145
- * Evaluate all questionnaire calculations for the given questionnaire and answers.
1412
+ * Evaluate all questionnaire outcomes for the given questionnaire and answers.
1146
1413
  * Pass `structure` so expressions can use `answer.byId`, `answer.byTag`, and `answer.allByTag`.
1147
1414
  */
1148
1415
  /**
1149
1416
  * Resolves `isVisible` for a section or question: `false` hides; `true`/undefined shows;
1150
- * non-empty string is evaluated with the same scope as questionnaire calculations (`answer.*`, `fn.*`, etc.).
1417
+ * non-empty string is evaluated with the same scope as questionnaire outcomes (`answer.*`, `fn.*`, etc.).
1151
1418
  */
1152
1419
  async evaluateQuestionnaireVisibilityFlag(isVisible, answers, structure) {
1153
1420
  if (isVisible === false) {
@@ -1172,11 +1439,11 @@ class AXMQuestionnaireViewerService {
1172
1439
  }
1173
1440
  }
1174
1441
  /**
1175
- * Evaluates questionnaire calculations and returns values plus display titles from calculation entities.
1442
+ * Evaluates questionnaire outcome rules and returns values plus display titles.
1176
1443
  */
1177
- async evaluateOutcomes(questionnaireId, answers, structure) {
1178
- const rules = await this.loadCalculationsForQuestionnaire(questionnaireId);
1179
- if (!rules?.length)
1444
+ async evaluateOutcomes(answers, options) {
1445
+ const rules = await this.resolveOutcomeRules(options?.questionnaireId, options?.outcomeRules);
1446
+ if (!rules.length)
1180
1447
  return { values: {}, outcomeTitles: {} };
1181
1448
  const sorted = this.topologicalSort(rules);
1182
1449
  const outcomes = {};
@@ -1185,25 +1452,32 @@ class AXMQuestionnaireViewerService {
1185
1452
  if (rule.name) {
1186
1453
  outcomeTitles[rule.name] = this.resolveOutcomeDisplayTitle(rule);
1187
1454
  }
1188
- const value = await this.computeResult(rule, answers, outcomes, structure);
1455
+ const value = await this.computeResult(rule, answers, outcomes, options?.structure);
1189
1456
  if (rule.name && value !== undefined)
1190
1457
  outcomes[rule.name] = value;
1191
1458
  }
1192
1459
  return { values: outcomes, outcomeTitles };
1193
1460
  }
1194
1461
  /**
1195
- * Progress metrics and optional calculation results for summary / preview.
1462
+ * Progress metrics and optional outcome results for summary / preview.
1196
1463
  */
1197
1464
  async buildSummaryPlaceholderValues(structure, answers, options) {
1198
1465
  const { answeredQuestionsCount } = this.calculateProgressFromStructure(structure, answers);
1199
1466
  const totalQuestions = structure?.sections?.reduce((s, sec) => s + (sec.items?.length ?? 0), 0) ?? 0;
1200
1467
  let outcomes;
1201
1468
  let outcomeTitles;
1202
- if (options?.questionnaireId) {
1203
- const { values: evaluated, outcomeTitles: titles } = await this.evaluateOutcomes(options.questionnaireId, answers, structure);
1469
+ let outcomeSections;
1470
+ if (options?.outcomeRules?.length || options?.questionnaireId) {
1471
+ const { values: evaluated, outcomeTitles: titles } = await this.evaluateOutcomes(answers, {
1472
+ questionnaireId: options?.questionnaireId,
1473
+ outcomeRules: options?.outcomeRules,
1474
+ structure,
1475
+ });
1204
1476
  if (Object.keys(evaluated).length > 0) {
1205
1477
  outcomes = evaluated;
1206
1478
  outcomeTitles = titles;
1479
+ const sections = await this.resolveOutcomeSections(options?.questionnaireId, options?.outcomeRules);
1480
+ outcomeSections = this.buildOutcomeResultSections(sections, evaluated, titles);
1207
1481
  }
1208
1482
  }
1209
1483
  const elapsed = options?.elapsedSeconds;
@@ -1215,6 +1489,30 @@ class AXMQuestionnaireViewerService {
1215
1489
  totalTimeFormatted,
1216
1490
  outcomes,
1217
1491
  outcomeTitles,
1492
+ outcomeSections,
1493
+ };
1494
+ }
1495
+ /**
1496
+ * Builds outcome section rows from stored session values (no expression re-evaluation).
1497
+ */
1498
+ async buildOutcomeResultsFromStoredValues(storedOutcomes, options) {
1499
+ const keys = Object.keys(storedOutcomes);
1500
+ if (!keys.length) {
1501
+ return { outcomes: {}, outcomeTitles: {}, outcomeSections: [] };
1502
+ }
1503
+ const rules = await this.resolveOutcomeRules(options.questionnaireId, undefined);
1504
+ const outcomeTitles = {};
1505
+ for (const rule of rules) {
1506
+ if (rule.name) {
1507
+ outcomeTitles[rule.name] = this.resolveOutcomeDisplayTitle(rule);
1508
+ }
1509
+ }
1510
+ const sections = await this.resolveOutcomeSections(options.questionnaireId, undefined);
1511
+ const outcomeSections = this.buildOutcomeResultSections(sections, storedOutcomes, outcomeTitles);
1512
+ return {
1513
+ outcomes: storedOutcomes,
1514
+ outcomeTitles,
1515
+ outcomeSections,
1218
1516
  };
1219
1517
  }
1220
1518
  //#endregion
@@ -1246,38 +1544,100 @@ class AXMQuestionnaireViewerService {
1246
1544
  return { answeredQuestionsCount: answered, progressPercentage };
1247
1545
  }
1248
1546
  //#endregion
1249
- //#region ---- Questionnaire calculations (evaluation) ----
1250
- async loadCalculationsForQuestionnaire(questionnaireId) {
1251
- const result = await this.entityService
1252
- .withEntity(RootConfig.module.name, RootConfig.entities.questionnaireCalculation.name)
1547
+ //#region ---- Questionnaire outcomes (evaluation) ----
1548
+ async resolveOutcomeRules(questionnaireId, inlineRules) {
1549
+ if (inlineRules?.length) {
1550
+ return inlineRules;
1551
+ }
1552
+ if (!questionnaireId) {
1553
+ return [];
1554
+ }
1555
+ const questionnaire = await this.loadQuestionnaire(questionnaireId);
1556
+ return flattenQuestionnaireOutcomeItems(questionnaire?.outcomes);
1557
+ }
1558
+ async resolveOutcomeSections(questionnaireId, inlineRules) {
1559
+ if (inlineRules?.length) {
1560
+ return [{ name: 'outcomes', order: 0, items: inlineRules }];
1561
+ }
1562
+ if (!questionnaireId) {
1563
+ return [];
1564
+ }
1565
+ const questionnaire = await this.loadQuestionnaire(questionnaireId);
1566
+ return normalizeQuestionnaireOutcomesValue(questionnaire?.outcomes).sections;
1567
+ }
1568
+ async loadQuestionnaire(questionnaireId) {
1569
+ return this.entityService
1570
+ .withEntity(RootConfig.module.name, RootConfig.entities.questionnaire.name)
1253
1571
  .data()
1254
- .query({
1255
- filter: {
1256
- field: 'questionnaireId',
1257
- operator: { type: 'equal' },
1258
- value: questionnaireId,
1259
- },
1260
- take: 500,
1261
- skip: 0,
1262
- });
1263
- return (result?.items ?? []);
1572
+ .byKey(questionnaireId);
1573
+ }
1574
+ buildOutcomeResultSections(sections, values, titles) {
1575
+ const sortedSections = sections.slice().sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
1576
+ const groups = [];
1577
+ for (const [index, section] of sortedSections.entries()) {
1578
+ const sectionName = (section.name ?? '').trim() || `section-${index + 1}`;
1579
+ const sectionOrder = section.order ?? index;
1580
+ const items = [];
1581
+ for (const item of section.items ?? []) {
1582
+ if (!item.name || values[item.name] === undefined) {
1583
+ continue;
1584
+ }
1585
+ const value = values[item.name];
1586
+ const display = this.resolveOutcomeDisplay(item, value);
1587
+ items.push({
1588
+ key: item.name,
1589
+ value,
1590
+ label: (titles[item.name] ?? '').trim() || this.humanizeOutcomeKey(item.name),
1591
+ display,
1592
+ });
1593
+ }
1594
+ if (items.length === 0) {
1595
+ continue;
1596
+ }
1597
+ groups.push({
1598
+ trackKey: `${sectionOrder}::${sectionName}`,
1599
+ sectionName,
1600
+ sectionOrder,
1601
+ sectionTitle: section.title ?? { 'en-US': sectionName },
1602
+ sectionDescription: section.description,
1603
+ items,
1604
+ });
1605
+ }
1606
+ return groups;
1607
+ }
1608
+ /**
1609
+ * First matching display rule for an evaluated outcome value.
1610
+ */
1611
+ resolveOutcomeDisplay(rule, value) {
1612
+ const displayRules = normalizeQuestionnaireOutcomeDisplayRules(rule.displayRules);
1613
+ if (!displayRules.length) {
1614
+ return undefined;
1615
+ }
1616
+ for (const displayRule of displayRules) {
1617
+ if (matchesQuestionnaireOutcomeDisplayRule(value, displayRule)) {
1618
+ return {
1619
+ color: displayRule.color,
1620
+ emphasis: displayRule.emphasis ?? 'normal',
1621
+ iconClass: typeof displayRule.icon === 'string' ? displayRule.icon : undefined,
1622
+ };
1623
+ }
1624
+ }
1625
+ return undefined;
1264
1626
  }
1265
1627
  /**
1266
- * Display label for a calculation: entity `title` when set, otherwise a humanized `name`.
1628
+ * Display label for an outcome rule: `title` when set, otherwise a humanized `name`.
1267
1629
  */
1268
1630
  resolveOutcomeDisplayTitle(rule) {
1269
- const title = rule.title;
1270
- if (typeof title === 'string') {
1271
- const t = title.trim();
1272
- if (t.length > 0)
1273
- return t;
1631
+ const resolved = resolveMultiLanguageString(rule.title, 'en-US').trim();
1632
+ if (resolved.length > 0) {
1633
+ return resolved;
1274
1634
  }
1275
1635
  const name = rule.name?.trim();
1276
1636
  if (name)
1277
- return this.humanizeCalculationKey(name);
1637
+ return this.humanizeOutcomeKey(name);
1278
1638
  return '';
1279
1639
  }
1280
- humanizeCalculationKey(name) {
1640
+ humanizeOutcomeKey(name) {
1281
1641
  return name
1282
1642
  .replace(/_/g, ' ')
1283
1643
  .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
@@ -1287,14 +1647,14 @@ class AXMQuestionnaireViewerService {
1287
1647
  .join(' ');
1288
1648
  }
1289
1649
  async computeResult(rule, answers, outcomes, structure) {
1290
- const exprText = this.calculationExpressionText(rule);
1650
+ const exprText = this.outcomeExpressionText(rule);
1291
1651
  if (!exprText)
1292
1652
  return undefined;
1293
1653
  const raw = await this.evaluateOutcomeExpression(exprText, answers, outcomes, structure);
1294
1654
  return this.coerceOutcome(raw, this.inferResultKind(rule));
1295
1655
  }
1296
1656
  /** Non-empty trimmed `expression`, or empty string if missing. */
1297
- calculationExpressionText(rule) {
1657
+ outcomeExpressionText(rule) {
1298
1658
  const exp = rule.expression;
1299
1659
  return typeof exp === 'string' ? exp.trim() : '';
1300
1660
  }
@@ -1332,17 +1692,17 @@ class AXMQuestionnaireViewerService {
1332
1692
  return raw;
1333
1693
  }
1334
1694
  }
1335
- async evaluateOutcomeExpression(expression, answers, outcomes, structure) {
1695
+ async evaluateOutcomeExpression(expression, answers, outcomes, structure, value) {
1336
1696
  const trimmed = expression?.trim();
1337
1697
  if (!trimmed)
1338
1698
  return undefined;
1339
1699
  try {
1340
- const scope = this.buildExpressionScope(answers, outcomes, structure);
1700
+ const scope = this.buildExpressionScope(answers, outcomes, structure, value);
1341
1701
  const wrapped = trimmed.includes('{{') ? trimmed : `{{ ${trimmed} }}`;
1342
1702
  return await this.expressionEvaluator.evaluate(wrapped, scope);
1343
1703
  }
1344
1704
  catch (err) {
1345
- console.warn('[QuestionnaireViewer] Calculation expression evaluation failed:', expression, err);
1705
+ console.warn('[QuestionnaireViewer] Outcome expression evaluation failed:', expression, err);
1346
1706
  return undefined;
1347
1707
  }
1348
1708
  }
@@ -1376,7 +1736,7 @@ class AXMQuestionnaireViewerService {
1376
1736
  /**
1377
1737
  * Scope: answer.eval (path), answer.byId / byTag / allByTag, formula.eval / outcome.eval, fn.* helpers.
1378
1738
  */
1379
- buildExpressionScope(answers, outcomes, structure) {
1739
+ buildExpressionScope(answers, outcomes, structure, value) {
1380
1740
  const resolveOutcome = (key) => outcomes[key];
1381
1741
  const itemsOrdered = this.flattenQuestionnaireItems(structure ?? null);
1382
1742
  const itemHasTag = (item, normalizedTag) => {
@@ -1472,6 +1832,7 @@ class AXMQuestionnaireViewerService {
1472
1832
  formula: { eval: resolveOutcome },
1473
1833
  outcome: { eval: resolveOutcome },
1474
1834
  fn,
1835
+ ...(value !== undefined ? { value } : {}),
1475
1836
  };
1476
1837
  }
1477
1838
  expressionOutcomeRefs(expression, ruleNames) {
@@ -1513,7 +1874,7 @@ class AXMQuestionnaireViewerService {
1513
1874
  visiting.add(name);
1514
1875
  const rule = nameToRule.get(name);
1515
1876
  if (rule) {
1516
- const exprStr = this.calculationExpressionText(rule);
1877
+ const exprStr = this.outcomeExpressionText(rule);
1517
1878
  const formDeps = this.expressionOutcomeRefs(exprStr, ruleNames);
1518
1879
  for (const d of formDeps)
1519
1880
  visit(d);
@@ -1531,13 +1892,2774 @@ class AXMQuestionnaireViewerService {
1531
1892
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1532
1893
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerService, providedIn: 'root' }); }
1533
1894
  }
1534
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerService, decorators: [{
1535
- type: Injectable,
1895
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerService, decorators: [{
1896
+ type: Injectable,
1897
+ args: [{
1898
+ providedIn: 'root',
1899
+ }]
1900
+ }] });
1901
+
1902
+ //#region ---- Imports ----
1903
+ //#endregion
1904
+ //#region ---- Groups ----
1905
+ const GROUP_IDENTITY = {
1906
+ name: 'identity',
1907
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.identity.title',
1908
+ order: 0,
1909
+ };
1910
+ const GROUP_VALIDATION = {
1911
+ name: 'validation',
1912
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.validation.title',
1913
+ order: 1,
1914
+ };
1915
+ const GROUP_VISIBILITY = {
1916
+ name: 'visibility',
1917
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.visibility.title',
1918
+ order: 2,
1919
+ };
1920
+ const GROUP_HINT = {
1921
+ name: 'hint',
1922
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.hint.title',
1923
+ order: 3,
1924
+ };
1925
+ const GROUP_COMMENT = {
1926
+ name: 'comment',
1927
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.comment.title',
1928
+ order: 4,
1929
+ };
1930
+ //#endregion
1931
+ //#region ---- Shared options ----
1932
+ const HINT_MODE_OPTIONS = [
1933
+ {
1934
+ id: 'off',
1935
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.off',
1936
+ },
1937
+ {
1938
+ id: 'info',
1939
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.info',
1940
+ },
1941
+ {
1942
+ id: 'warning',
1943
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.warning',
1944
+ },
1945
+ {
1946
+ id: 'danger',
1947
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.danger',
1948
+ },
1949
+ {
1950
+ id: 'success',
1951
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.success',
1952
+ },
1953
+ {
1954
+ id: 'neutral',
1955
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.neutral',
1956
+ },
1957
+ ];
1958
+ //#endregion
1959
+ //#region ---- Properties by group ----
1960
+ const IDENTITY_PROPS = [
1961
+ {
1962
+ name: 'name',
1963
+ title: '@general:terms.common.name',
1964
+ group: GROUP_IDENTITY,
1965
+ order: 10,
1966
+ schema: {
1967
+ dataType: 'string',
1968
+ defaultValue: '',
1969
+ interface: {
1970
+ name: 'name',
1971
+ path: 'name',
1972
+ type: AXPWidgetsCatalog.text,
1973
+ options: {
1974
+ placeholder: '@general:terms.common.name',
1975
+ },
1976
+ },
1977
+ },
1978
+ visible: true,
1979
+ },
1980
+ {
1981
+ name: 'tags',
1982
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.tags.title',
1983
+ group: GROUP_IDENTITY,
1984
+ order: 20,
1985
+ schema: {
1986
+ dataType: 'array',
1987
+ defaultValue: [],
1988
+ interface: {
1989
+ name: 'tags',
1990
+ path: 'tags',
1991
+ type: 'tag-editor',
1992
+ options: {
1993
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-builder.fields.tags.placeholder',
1994
+ allowDuplicate: false,
1995
+ },
1996
+ },
1997
+ },
1998
+ visible: true,
1999
+ },
2000
+ ];
2001
+ const VALIDATION_PROPS = [
2002
+ {
2003
+ ...createBooleanProperty({
2004
+ name: 'isRequired',
2005
+ path: 'isRequired',
2006
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.is-required.title',
2007
+ group: GROUP_VALIDATION,
2008
+ defaultValue: false,
2009
+ }),
2010
+ order: 10,
2011
+ },
2012
+ ];
2013
+ const VISIBILITY_PROPS = [
2014
+ {
2015
+ ...createBooleanProperty({
2016
+ name: 'isVisible',
2017
+ path: 'isVisible',
2018
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.visible.title',
2019
+ group: GROUP_VISIBILITY,
2020
+ defaultValue: true,
2021
+ }),
2022
+ order: 10,
2023
+ },
2024
+ ];
2025
+ const HINT_PROPS = [
2026
+ {
2027
+ name: 'hintMode',
2028
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.title',
2029
+ group: GROUP_HINT,
2030
+ order: 10,
2031
+ schema: {
2032
+ dataType: 'string',
2033
+ defaultValue: { id: 'off', title: HINT_MODE_OPTIONS[0].title },
2034
+ interface: {
2035
+ name: 'hintMode',
2036
+ path: 'hint.mode',
2037
+ type: AXPWidgetsCatalog.select,
2038
+ options: {
2039
+ valueField: 'id',
2040
+ textField: 'title',
2041
+ dataSource: [...HINT_MODE_OPTIONS],
2042
+ },
2043
+ },
2044
+ },
2045
+ visible: true,
2046
+ },
2047
+ {
2048
+ name: 'hintContent',
2049
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint.title',
2050
+ group: GROUP_HINT,
2051
+ order: 20,
2052
+ schema: {
2053
+ dataType: 'string',
2054
+ defaultValue: '',
2055
+ interface: {
2056
+ name: 'hintContent',
2057
+ path: 'hint.content',
2058
+ type: AXPWidgetsCatalog.richText,
2059
+ options: {},
2060
+ },
2061
+ },
2062
+ visible: "{{ context.eval('hint.mode') && (context.eval('hint.mode').id || context.eval('hint.mode')) != 'off' }}",
2063
+ },
2064
+ ];
2065
+ const COMMENT_PROPS = [
2066
+ {
2067
+ ...createBooleanProperty({
2068
+ name: 'commentEnabled',
2069
+ path: 'comment.enabled',
2070
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.comment-enabled.title',
2071
+ group: GROUP_COMMENT,
2072
+ defaultValue: false,
2073
+ }),
2074
+ order: 10,
2075
+ },
2076
+ {
2077
+ ...createBooleanProperty({
2078
+ name: 'commentRequired',
2079
+ path: 'comment.required',
2080
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.comment-required.title',
2081
+ group: GROUP_COMMENT,
2082
+ defaultValue: false,
2083
+ visible: "{{ context.eval('comment.enabled') === true }}",
2084
+ }),
2085
+ order: 20,
2086
+ },
2087
+ ];
2088
+ //#endregion
2089
+ //#region ---- Hint form context helper ----
2090
+ /**
2091
+ * Builds initial `hint` object for the question property viewer from the effective (merged) hint config.
2092
+ */
2093
+ function createQuestionHintFormContext(hint) {
2094
+ let content = '';
2095
+ if (hint.mode !== 'off') {
2096
+ const hc = hint.content;
2097
+ if (typeof hc === 'string') {
2098
+ content = hc;
2099
+ }
2100
+ else if (hc && typeof hc === 'object') {
2101
+ content = Object.values(hc)[0] ?? '';
2102
+ }
2103
+ }
2104
+ const modeId = hint.mode;
2105
+ const opt = HINT_MODE_OPTIONS.find((o) => o.id === modeId) ?? HINT_MODE_OPTIONS[0];
2106
+ return {
2107
+ content,
2108
+ mode: { id: opt.id, title: opt.title },
2109
+ };
2110
+ }
2111
+ //#endregion
2112
+ //#region ---- Tab definition ----
2113
+ /**
2114
+ * Property viewer tabs for editing a single questionnaire question row (metadata).
2115
+ */
2116
+ const QUESTIONNAIRE_QUESTION_EDIT_TABS = [
2117
+ {
2118
+ name: 'general',
2119
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.tabs.general.title',
2120
+ groups: [
2121
+ {
2122
+ name: GROUP_IDENTITY.name,
2123
+ title: GROUP_IDENTITY.title,
2124
+ isCollapsed: false,
2125
+ props: IDENTITY_PROPS,
2126
+ },
2127
+ {
2128
+ name: GROUP_VALIDATION.name,
2129
+ title: GROUP_VALIDATION.title,
2130
+ isCollapsed: false,
2131
+ props: VALIDATION_PROPS,
2132
+ },
2133
+ {
2134
+ name: GROUP_VISIBILITY.name,
2135
+ title: GROUP_VISIBILITY.title,
2136
+ isCollapsed: false,
2137
+ props: VISIBILITY_PROPS,
2138
+ },
2139
+ {
2140
+ name: GROUP_HINT.name,
2141
+ title: GROUP_HINT.title,
2142
+ isCollapsed: false,
2143
+ props: HINT_PROPS,
2144
+ },
2145
+ {
2146
+ name: GROUP_COMMENT.name,
2147
+ title: GROUP_COMMENT.title,
2148
+ isCollapsed: false,
2149
+ props: COMMENT_PROPS,
2150
+ },
2151
+ ],
2152
+ },
2153
+ ];
2154
+ //#endregion
2155
+
2156
+ //#region ---- Imports ----
2157
+ //#endregion
2158
+ //#region ---- Component ----
2159
+ /**
2160
+ * Reusable Questionnaire Builder Component
2161
+ * Can be used both in dialog and inline contexts
2162
+ */
2163
+ class AXMQuestionnaireQuestionsBuilderComponent {
2164
+ //#endregion
2165
+ //#region ---- Question bank entity data ----
2166
+ questionBankData() {
2167
+ return this.entityService
2168
+ .withEntity(RootConfig.module.name, RootConfig.entities.questionBankItem.name)
2169
+ .data();
2170
+ }
2171
+ questionBankCategoryData() {
2172
+ return this.entityService
2173
+ .withEntity(RootConfig.module.name, `${RootConfig.entities.questionBankItem.name}Category`)
2174
+ .data();
2175
+ }
2176
+ //#endregion
2177
+ //#region ---- Lifecycle ----
2178
+ constructor() {
2179
+ //#region ---- Services & Dependencies ----
2180
+ this.dataSelectorService = inject(AXPDataSelectorService);
2181
+ this.entityService = inject(AXPEntityService);
2182
+ this.entityResolver = inject(AXPEntityDefinitionRegistryService);
2183
+ this.translationService = inject(AXTranslationService);
2184
+ this.propertyViewerService = inject(AXPPropertyViewerService);
2185
+ this.questionnaireViewerService = inject(AXMQuestionnaireViewerService);
2186
+ this.toastService = inject(AXToastService);
2187
+ this.widgetRegistry = inject(AXPWidgetRegistryService);
2188
+ this.commandService = inject(AXPCommandService);
2189
+ //#endregion
2190
+ //#region ---- Inputs & Outputs ----
2191
+ /**
2192
+ * Initial value (QuestionnaireBuilderValue)
2193
+ */
2194
+ this.value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
2195
+ /**
2196
+ * Readonly mode
2197
+ */
2198
+ this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
2199
+ /**
2200
+ * Pre/post config for preview (welcome, review, summary).
2201
+ * When provided, passed to the preview dialog.
2202
+ */
2203
+ this.prePostConfig = input(null, ...(ngDevMode ? [{ debugName: "prePostConfig" }] : /* istanbul ignore next */ []));
2204
+ /**
2205
+ * When false, the header (Add Section, Settings, Preview, Full Screen) is hidden.
2206
+ * Use when actions are provided by the host (e.g. page primary header actions).
2207
+ */
2208
+ this.showHeader = input(true, ...(ngDevMode ? [{ debugName: "showHeader" }] : /* istanbul ignore next */ []));
2209
+ /**
2210
+ * Display settings (view mode, progress bar, timer, etc.) when provided by the host (e.g. entity form).
2211
+ * Used for preview. When not provided, falls back to state/value or defaults.
2212
+ */
2213
+ this.displaySettingsInput = input(null, ...(ngDevMode ? [{ debugName: "displaySettingsInput" }] : /* istanbul ignore next */ []));
2214
+ /**
2215
+ * Persisted questionnaire id (e.g. from entity form) — enables questionnaire outcomes in preview.
2216
+ */
2217
+ this.questionnaireEntityId = input(undefined, ...(ngDevMode ? [{ debugName: "questionnaireEntityId" }] : /* istanbul ignore next */ []));
2218
+ /**
2219
+ * Value change event
2220
+ */
2221
+ this.valueChange = output();
2222
+ //#endregion
2223
+ //#region ---- Class Properties ----
2224
+ // Designer state management - always start with at least one default section
2225
+ this.builderState = signal({
2226
+ sections: [createDefaultSection()],
2227
+ }, ...(ngDevMode ? [{ debugName: "builderState" }] : /* istanbul ignore next */ []));
2228
+ // Selected item tracking
2229
+ this.selectedItemName = signal(null, ...(ngDevMode ? [{ debugName: "selectedItemName" }] : /* istanbul ignore next */ []));
2230
+ this.selectedSectionName = signal(null, ...(ngDevMode ? [{ debugName: "selectedSectionName" }] : /* istanbul ignore next */ []));
2231
+ // Fullscreen directive reference
2232
+ this.fullscreenDirective = viewChild('fullscreen', ...(ngDevMode ? [{ debugName: "fullscreenDirective" }] : /* istanbul ignore next */ []));
2233
+ this.standardRef = viewChild(AXPStandardSectionItemsBuilderComponent, ...(ngDevMode ? [{ debugName: "standardRef" }] : /* istanbul ignore next */ []));
2234
+ //#endregion
2235
+ //#region ---- Computed Properties ----
2236
+ /** Value for {@link AXPStandardSectionItemsBuilderComponent} (sections only; drag/reorder handled there). */
2237
+ this.builderValue = computed(() => ({
2238
+ sections: this.builderState().sections.map((s) => ({
2239
+ id: s.name,
2240
+ name: s.name,
2241
+ order: s.order,
2242
+ title: s.title,
2243
+ description: s.description,
2244
+ tags: s.tags,
2245
+ isVisible: s.isVisible,
2246
+ items: s.items.map((it) => ({
2247
+ id: it.name,
2248
+ name: it.name,
2249
+ order: it.order,
2250
+ questionBankItemId: it.questionBankItemId,
2251
+ isRequired: it.isRequired,
2252
+ isVisible: it.isVisible,
2253
+ interfaceOverride: it.interfaceOverride,
2254
+ hint: it.hint,
2255
+ comment: it.comment,
2256
+ tags: it.tags,
2257
+ question: it.question,
2258
+ isNewlyAdded: it.isNewlyAdded,
2259
+ })),
2260
+ })),
2261
+ }), ...(ngDevMode ? [{ debugName: "builderValue" }] : /* istanbul ignore next */ []));
2262
+ this.isPreviewDisabled = computed(() => this.builderState().sections.length === 0 || this.builderState().sections.every((s) => s.items.length === 0), ...(ngDevMode ? [{ debugName: "isPreviewDisabled" }] : /* istanbul ignore next */ []));
2263
+ this.standardConfig = computed(() => ({
2264
+ showSectionTechnicalName: true,
2265
+ minSectionCount: 1,
2266
+ texts: {
2267
+ addSection: '@assessment-management:questionnaires.components.questionnaire-builder.actions.add-section.title',
2268
+ addItem: '@assessment-management:questionnaires.components.questionnaire-builder.actions.add-question.title',
2269
+ emptySectionsTitle: '@assessment-management:questionnaires.components.questionnaire-builder.empty-states.no-sections.title',
2270
+ emptySectionsDescription: '@assessment-management:questionnaires.components.questionnaire-builder.empty-states.no-sections.description',
2271
+ emptyItemsTitle: '@assessment-management:questionnaires.components.questionnaire-builder.empty-states.empty-section.title',
2272
+ emptyItemsDescription: '@assessment-management:questionnaires.components.questionnaire-builder.empty-states.empty-section.description',
2273
+ defaultSectionBadge: '@assessment-management:questionnaires.components.questionnaire-builder.default-section-badge',
2274
+ },
2275
+ mapItemToView: (item, section) => this.mapQuestionnaireItemToView(item, section),
2276
+ promptAddItems: (sid, v) => this.questionnairePromptAddItems(sid, v),
2277
+ promptEditItem: (item, sid, v) => this.questionnairePromptEditItem(item, sid, v),
2278
+ }), ...(ngDevMode ? [{ debugName: "standardConfig" }] : /* istanbul ignore next */ []));
2279
+ /**
2280
+ * Get current display settings from state or default
2281
+ */
2282
+ this.displaySettings = computed(() => {
2283
+ const fromInput = this.displaySettingsInput();
2284
+ if (fromInput != null) {
2285
+ return fromInput;
2286
+ }
2287
+ const state = this.builderState();
2288
+ if (state.displaySettings) {
2289
+ return state.displaySettings;
2290
+ }
2291
+ const currentValue = convertStateToValue(state, this.value() || undefined);
2292
+ return (currentValue.displaySettings || {
2293
+ viewMode: 'single-page',
2294
+ showProgressBar: true,
2295
+ showTimer: false,
2296
+ showQuestionNumbers: true,
2297
+ });
2298
+ }, ...(ngDevMode ? [{ debugName: "displaySettings" }] : /* istanbul ignore next */ []));
2299
+ // Track last emitted value to prevent circular updates
2300
+ let lastEmittedValue = null;
2301
+ // Sync value input to state (only when value changes externally)
2302
+ effect(() => {
2303
+ const val = this.value();
2304
+ if (val !== null && val !== undefined) {
2305
+ const currentState = untracked(() => this.builderState());
2306
+ const currentValue = convertStateToValue(currentState);
2307
+ const valStr = JSON.stringify(val);
2308
+ const currentStr = JSON.stringify(currentValue);
2309
+ const lastEmittedStr = lastEmittedValue ? JSON.stringify(lastEmittedValue) : null;
2310
+ if (valStr !== currentStr && valStr !== lastEmittedStr) {
2311
+ const newState = convertValueToState(val);
2312
+ untracked(() => {
2313
+ this.builderState.set(newState);
2314
+ this.loadQuestionDetailsForState(newState);
2315
+ });
2316
+ }
2317
+ }
2318
+ else if (val === null || val === undefined) {
2319
+ // If value is null/undefined, ensure we have at least one default section
2320
+ const currentState = untracked(() => this.builderState());
2321
+ if (currentState.sections.length === 0) {
2322
+ const defaultSection = createDefaultSection();
2323
+ defaultSection.order = 0;
2324
+ untracked(() => {
2325
+ this.builderState.set({ sections: [defaultSection] });
2326
+ });
2327
+ }
2328
+ }
2329
+ });
2330
+ // Sync state changes to output
2331
+ effect(() => {
2332
+ const state = this.builderState();
2333
+ const currentValue = this.value();
2334
+ const value = convertStateToValue(state, currentValue || undefined);
2335
+ untracked(() => {
2336
+ lastEmittedValue = value;
2337
+ this.valueChange.emit(value);
2338
+ });
2339
+ });
2340
+ }
2341
+ /**
2342
+ * Load question details for all items in the state
2343
+ */
2344
+ async loadQuestionDetailsForState(state) {
2345
+ const questionIds = new Set();
2346
+ // Collect all question bank item IDs
2347
+ state.sections.forEach((section) => {
2348
+ section.items.forEach((item) => {
2349
+ if (item.questionBankItemId && !item.question) {
2350
+ questionIds.add(item.questionBankItemId);
2351
+ }
2352
+ });
2353
+ });
2354
+ if (questionIds.size === 0)
2355
+ return;
2356
+ // Load all questions in parallel
2357
+ const questionPromises = Array.from(questionIds).map((id) => this.questionBankData()
2358
+ .byKey(id)
2359
+ .catch(() => null));
2360
+ const questions = await Promise.all(questionPromises);
2361
+ // Create a map of question ID to question data
2362
+ const questionMap = new Map();
2363
+ questions.forEach((q) => {
2364
+ if (q) {
2365
+ questionMap.set(q.id, q);
2366
+ }
2367
+ });
2368
+ // Update state with question details
2369
+ this.builderState.update((currentState) => ({
2370
+ ...currentState,
2371
+ sections: currentState.sections.map((section) => ({
2372
+ ...section,
2373
+ items: section.items.map((item) => {
2374
+ if (item.question)
2375
+ return item; // Already has question data
2376
+ const questionData = questionMap.get(item.questionBankItemId);
2377
+ if (questionData) {
2378
+ return {
2379
+ ...item,
2380
+ question: {
2381
+ id: questionData.id,
2382
+ title: questionData.title,
2383
+ question: questionData.question,
2384
+ note: questionData.note,
2385
+ difficulty: questionData.difficulty,
2386
+ interface: questionData.interface,
2387
+ },
2388
+ };
2389
+ }
2390
+ return item;
2391
+ }),
2392
+ })),
2393
+ }));
2394
+ }
2395
+ mapQuestionnaireItemToView(item, _section) {
2396
+ const q = item;
2397
+ const title = this.itemQuestionHeading(q);
2398
+ const note = this.itemQuestionNotePreview(q);
2399
+ const badges = [];
2400
+ const req = q.isRequired;
2401
+ const rawReq = typeof req === 'boolean' || typeof req === 'string' ? req : undefined;
2402
+ const requiredMode = getQuestionnaireItemRequiredBadgeMode(rawReq);
2403
+ if (requiredMode === 'required') {
2404
+ badges.push({
2405
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.required',
2406
+ variant: 'danger',
2407
+ });
2408
+ }
2409
+ else if (requiredMode === 'conditional') {
2410
+ badges.push({
2411
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.conditional-required',
2412
+ variant: 'warning',
2413
+ });
2414
+ }
2415
+ else {
2416
+ badges.push({
2417
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.optional',
2418
+ variant: 'success',
2419
+ });
2420
+ }
2421
+ const rawVis = q.isVisible;
2422
+ const visibilityMode = getQuestionnaireItemVisibilityBadgeMode(typeof rawVis === 'boolean' || typeof rawVis === 'string' ? rawVis : undefined);
2423
+ if (visibilityMode === 'hidden') {
2424
+ badges.push({
2425
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.visibility-hidden',
2426
+ variant: 'danger',
2427
+ });
2428
+ }
2429
+ else if (visibilityMode === 'conditional') {
2430
+ badges.push({
2431
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.visibility-conditional',
2432
+ variant: 'warning',
2433
+ });
2434
+ }
2435
+ else {
2436
+ badges.push({
2437
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.visibility-visible',
2438
+ variant: 'success',
2439
+ });
2440
+ }
2441
+ return {
2442
+ icon: this.getQuestionWidgetIcon(q),
2443
+ title,
2444
+ name: q.name,
2445
+ description: note || undefined,
2446
+ badges,
2447
+ };
2448
+ }
2449
+ /**
2450
+ * Standard section-items builder emits here. Uses direct `builderState.update` so the `valueChange`
2451
+ * effect runs in the same turn and hosts (e.g. questions page) can mark dirty.
2452
+ */
2453
+ onBuilderValueChange(next) {
2454
+ this.builderState.update((state) => {
2455
+ const updatedState = {
2456
+ ...state,
2457
+ sections: this.mergeSectionsFromStandardBuilder(next.sections, state.sections),
2458
+ };
2459
+ if (updatedState.sections.length === 0) {
2460
+ const defaultSection = createDefaultSection();
2461
+ defaultSection.order = 0;
2462
+ return { ...updatedState, sections: [defaultSection] };
2463
+ }
2464
+ return updatedState;
2465
+ });
2466
+ }
2467
+ mergeSectionsFromStandardBuilder(incoming, previous) {
2468
+ const prevByName = new Map(previous.map((s) => [s.name, s]));
2469
+ return incoming.map((sec) => {
2470
+ const axSec = sec;
2471
+ const incomingName = String(axSec['name'] ?? axSec['id'] ?? '').trim();
2472
+ const prevSec = prevByName.get(incomingName);
2473
+ if (!prevSec) {
2474
+ return this.normalizeIncomingSection(sec);
2475
+ }
2476
+ const prevItemByName = new Map(prevSec.items.map((i) => [i.name, i]));
2477
+ const merged = this.normalizeIncomingSection(sec);
2478
+ return {
2479
+ ...merged,
2480
+ isNewlyAdded: prevSec.isNewlyAdded ?? merged.isNewlyAdded,
2481
+ items: merged.items.map((it) => {
2482
+ const prev = prevItemByName.get(it.name);
2483
+ if (!prev) {
2484
+ return it;
2485
+ }
2486
+ return {
2487
+ ...prev,
2488
+ ...it,
2489
+ question: it.question ?? prev.question,
2490
+ };
2491
+ }),
2492
+ };
2493
+ });
2494
+ }
2495
+ /** Map platform section rows (id + name) into domain sections (name only). */
2496
+ normalizeIncomingSection(sec) {
2497
+ const ax = sec;
2498
+ const name = String(ax['name'] ?? ax['id'] ?? '').trim() || `section-${Date.now()}`;
2499
+ return {
2500
+ name,
2501
+ title: sec.title,
2502
+ description: sec.description,
2503
+ order: sec.order,
2504
+ tags: sec.tags,
2505
+ isVisible: sec.isVisible,
2506
+ isNewlyAdded: sec.isNewlyAdded,
2507
+ items: sec.items.map((it) => this.normalizeIncomingItem(it)),
2508
+ };
2509
+ }
2510
+ normalizeIncomingItem(it) {
2511
+ const name = String(it['name'] ?? it['id'] ?? '').trim() || `item-${Date.now()}`;
2512
+ const qbi = it.questionBankItemId;
2513
+ return {
2514
+ name,
2515
+ questionBankItemId: qbi,
2516
+ order: it.order,
2517
+ isRequired: it.isRequired,
2518
+ isVisible: it.isVisible,
2519
+ interfaceOverride: it.interfaceOverride,
2520
+ hint: it.hint,
2521
+ comment: it.comment,
2522
+ tags: it.tags,
2523
+ question: it.question,
2524
+ isNewlyAdded: it.isNewlyAdded,
2525
+ };
2526
+ }
2527
+ /** Edit dialog context: default visible; normalize legacy string booleans; expressions pass through (property viewer handles binding). */
2528
+ questionEditFormIsVisible(raw) {
2529
+ if (raw === undefined || raw === null) {
2530
+ return true;
2531
+ }
2532
+ if (typeof raw === 'boolean') {
2533
+ return raw;
2534
+ }
2535
+ if (raw.trim() === '') {
2536
+ return true;
2537
+ }
2538
+ const t = raw.trim().toLowerCase();
2539
+ if (t === 'true' || t === '1') {
2540
+ return true;
2541
+ }
2542
+ if (t === 'false' || t === '0') {
2543
+ return false;
2544
+ }
2545
+ return raw.trim();
2546
+ }
2547
+ questionEditFormIsRequired(item) {
2548
+ const r = item.isRequired;
2549
+ if (typeof r === 'string') {
2550
+ const t = r.trim();
2551
+ if (!t) {
2552
+ return false;
2553
+ }
2554
+ const tl = t.toLowerCase();
2555
+ if (tl === 'true' || tl === '1') {
2556
+ return true;
2557
+ }
2558
+ if (tl === 'false' || tl === '0') {
2559
+ return false;
2560
+ }
2561
+ return t;
2562
+ }
2563
+ return r ?? false;
2564
+ }
2565
+ /** Persist from property viewer: omit when default visible; `false` when hidden; trimmed non-empty string = expression. */
2566
+ persistQuestionIsVisibleFromForm(value) {
2567
+ if (value === undefined || value === null) {
2568
+ return undefined;
2569
+ }
2570
+ if (typeof value === 'boolean') {
2571
+ return value ? undefined : false;
2572
+ }
2573
+ if (typeof value === 'string') {
2574
+ const t = value.trim();
2575
+ return t || undefined;
2576
+ }
2577
+ return undefined;
2578
+ }
2579
+ persistQuestionIsRequiredFromForm(value) {
2580
+ if (value === undefined || value === null) {
2581
+ return false;
2582
+ }
2583
+ if (typeof value === 'boolean') {
2584
+ return value;
2585
+ }
2586
+ if (typeof value === 'string') {
2587
+ const t = value.trim();
2588
+ if (!t) {
2589
+ return false;
2590
+ }
2591
+ return t;
2592
+ }
2593
+ return false;
2594
+ }
2595
+ async questionnairePromptEditItem(item, sectionId, _value) {
2596
+ const qItem = item;
2597
+ const validationFailed = async (titleKey, descKey, fallTitle, fallDesc) => {
2598
+ const title = (await this.translationService.translateAsync(titleKey)) || fallTitle;
2599
+ const content = (await this.translationService.translateAsync(descKey)) || fallDesc;
2600
+ this.toastService.show({ color: 'warning', title, content });
2601
+ throw new Error('QuestionnaireEditQuestionValidation');
2602
+ };
2603
+ try {
2604
+ const bankRow = qItem.questionBankItemId
2605
+ ? await this.entityService
2606
+ .withEntity(RootConfig.module.name, RootConfig.entities.questionBankItem.name)
2607
+ .data()
2608
+ .byKey(qItem.questionBankItemId)
2609
+ .catch(() => null)
2610
+ : null;
2611
+ const bankComment = normalizeQuestionCommentConfig(bankRow?.comment);
2612
+ const mergedCommentForForm = mergeQuestionCommentConfig(bankComment, qItem.comment);
2613
+ const bankHint = normalizeQuestionHintFromEntityRow(bankRow ?? {});
2614
+ const mergedHintForForm = mergeQuestionHintForViewer(bankHint, parseQuestionHintPartialFromRecord(qItem));
2615
+ const result = await this.propertyViewerService
2616
+ .create()
2617
+ .dialog((d) => {
2618
+ d.setTitle('@assessment-management:questionnaires.components.questionnaire-builder.actions.edit-question.title')
2619
+ .setSize('md')
2620
+ .setCloseButton(true)
2621
+ .setMode('advanced')
2622
+ .setTabs(QUESTIONNAIRE_QUESTION_EDIT_TABS)
2623
+ .setContext({
2624
+ name: qItem.name,
2625
+ tags: qItem.tags ?? [],
2626
+ isRequired: this.questionEditFormIsRequired(qItem),
2627
+ isVisible: this.questionEditFormIsVisible(qItem.isVisible),
2628
+ hint: createQuestionHintFormContext(mergedHintForForm),
2629
+ comment: mergedCommentForForm,
2630
+ })
2631
+ .onAction(async (ref) => {
2632
+ const context = ref.context();
2633
+ const rawId = String(context.name ?? '').trim();
2634
+ if (!rawId) {
2635
+ await validationFailed('@assessment-management:questionnaires.components.questionnaire-builder.validation.question-id-required.title', '@assessment-management:questionnaires.components.questionnaire-builder.validation.question-id-required.description', 'Question ID required', 'Enter a unique question ID.');
2636
+ }
2637
+ if (!this.isValidQuestionnaireItemName(rawId)) {
2638
+ await validationFailed('@assessment-management:questionnaires.components.questionnaire-builder.validation.question-id-invalid.title', '@assessment-management:questionnaires.components.questionnaire-builder.validation.question-id-invalid.description', 'Invalid question ID', 'Use letters, numbers, underscores, and hyphens (1–128 characters).');
2639
+ }
2640
+ const isDuplicate = this.builderState().sections.some((s) => s.items.some((i) => (s.name !== sectionId || i.name !== qItem.name) && i.name === rawId));
2641
+ if (isDuplicate) {
2642
+ await validationFailed('@assessment-management:questionnaires.components.questionnaire-builder.validation.question-id-duplicate.title', '@assessment-management:questionnaires.components.questionnaire-builder.validation.question-id-duplicate.description', 'Duplicate question ID', 'Another question already uses this ID.');
2643
+ }
2644
+ });
2645
+ })
2646
+ .show();
2647
+ if (!result) {
2648
+ return null;
2649
+ }
2650
+ const formData = result.values;
2651
+ const rawIdOut = String(formData.name ?? '').trim();
2652
+ const tags = this.normalizeTagsFromForm(formData.tags);
2653
+ const hintPersist = this.persistQuestionHintFromForm(bankHint, formData.hint);
2654
+ const commentPersist = this.persistQuestionCommentFromForm(bankComment, formData.comment);
2655
+ const nextItem = {
2656
+ ...qItem,
2657
+ name: rawIdOut,
2658
+ id: rawIdOut,
2659
+ tags,
2660
+ isRequired: this.persistQuestionIsRequiredFromForm(formData.isRequired),
2661
+ isVisible: this.persistQuestionIsVisibleFromForm(formData.isVisible),
2662
+ };
2663
+ if (hintPersist != null) {
2664
+ nextItem.hint = hintPersist;
2665
+ }
2666
+ else {
2667
+ delete nextItem.hint;
2668
+ }
2669
+ if (commentPersist != null) {
2670
+ nextItem.comment = commentPersist;
2671
+ }
2672
+ else {
2673
+ delete nextItem.comment;
2674
+ }
2675
+ return nextItem;
2676
+ }
2677
+ catch (error) {
2678
+ if (error instanceof Error && error.message === 'QuestionnaireEditQuestionValidation') {
2679
+ return null;
2680
+ }
2681
+ console.error('Error editing question:', error);
2682
+ return null;
2683
+ }
2684
+ }
2685
+ //#endregion
2686
+ //#region ---- Section Management ----
2687
+ /** Toolbar / page command: delegates to {@link AXPStandardSectionItemsBuilderComponent#addSection}. */
2688
+ async addSection() {
2689
+ await this.standardRef()?.addSection();
2690
+ }
2691
+ //#endregion
2692
+ //#region ---- Item Management ----
2693
+ async questionnairePromptAddItems(sectionId, value) {
2694
+ const all = value.sections;
2695
+ const section = all.find((x) => x.name === sectionId);
2696
+ if (!section)
2697
+ return null;
2698
+ return (await this.selectQuestionsFromQuestionBankForSection(section, all));
2699
+ }
2700
+ /**
2701
+ * Open question bank selector and return new items (standard builder appends them).
2702
+ */
2703
+ async selectQuestionsFromQuestionBankForSection(section, allSections) {
2704
+ try {
2705
+ const dataSource = new AXDataSource({
2706
+ key: 'id',
2707
+ pageSize: 20,
2708
+ load: async (e) => {
2709
+ const questions = await this.questionBankData().query(e);
2710
+ return {
2711
+ items: questions.items,
2712
+ total: questions.total,
2713
+ };
2714
+ },
2715
+ });
2716
+ // Create category tree data source for filtering
2717
+ const categoryTreeDataSource = {
2718
+ loadRootNodes: async () => {
2719
+ const result = await this.questionBankCategoryData().query({
2720
+ skip: 0,
2721
+ take: 1000,
2722
+ filter: {
2723
+ field: 'parentId',
2724
+ operator: { type: 'isEmpty' },
2725
+ value: true,
2726
+ },
2727
+ });
2728
+ return result.items.map((item) => ({
2729
+ id: item.id,
2730
+ title: item.title || item.name,
2731
+ description: item.description || '',
2732
+ parentId: item.parentId,
2733
+ childrenCount: item.childrenCount || 0,
2734
+ }));
2735
+ },
2736
+ loadChildNodes: async (parentId) => {
2737
+ const result = await this.questionBankCategoryData().query({
2738
+ skip: 0,
2739
+ take: 1000,
2740
+ filter: {
2741
+ field: 'parentId',
2742
+ operator: { type: 'equal' },
2743
+ value: parentId,
2744
+ },
2745
+ });
2746
+ return result.items.map((item) => ({
2747
+ id: item.id,
2748
+ title: item.title || item.name,
2749
+ description: item.description || '',
2750
+ parentId: item.parentId,
2751
+ childrenCount: item.childrenCount || 0,
2752
+ }));
2753
+ },
2754
+ searchNodes: async (searchValue) => {
2755
+ const result = await this.questionBankCategoryData().query({
2756
+ skip: 0,
2757
+ take: 1000,
2758
+ filter: {
2759
+ filters: [
2760
+ {
2761
+ field: 'title',
2762
+ operator: { type: 'contains' },
2763
+ value: searchValue,
2764
+ },
2765
+ {
2766
+ field: 'name',
2767
+ operator: { type: 'contains' },
2768
+ value: searchValue,
2769
+ },
2770
+ ],
2771
+ logic: 'or',
2772
+ },
2773
+ });
2774
+ return result.items.map((item) => ({
2775
+ id: item.id,
2776
+ title: item.title || item.name,
2777
+ description: item.description || '',
2778
+ parentId: item.parentId,
2779
+ childrenCount: item.childrenCount || 0,
2780
+ }));
2781
+ },
2782
+ };
2783
+ const entity = await this.entityResolver.resolve(RootConfig.module.name, RootConfig.entities.questionBankItem.name);
2784
+ if (!entity) {
2785
+ throw new Error('Entity not found: ' + RootConfig.entities.questionBankItem.name);
2786
+ }
2787
+ const columns = entity.columns
2788
+ ?.filter((column) => entity.properties.some((property) => property.name === column.name))
2789
+ .map((column) => {
2790
+ const property = entity.properties.find((prop) => prop.name === column.name);
2791
+ return {
2792
+ name: column.name,
2793
+ title: property?.title ?? '',
2794
+ visible: column.options?.visible !== false,
2795
+ widget: {
2796
+ type: column.showAs?.type ?? property?.schema?.interface?.type ?? 'text-editor',
2797
+ options: column.showAs?.options ?? property?.schema?.interface?.options ?? {},
2798
+ },
2799
+ };
2800
+ }) || [];
2801
+ // Translate titles
2802
+ const selectQuestionsTitle = (await this.translationService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.select-questions')) || 'Select Questions from Question Bank';
2803
+ const categoriesTitle = (await this.translationService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.categories')) || 'Categories';
2804
+ const titleLabel = (await this.translationService.translateAsync('@general:terms.common.title')) || 'Title';
2805
+ const noteLabel = (await this.translationService.translateAsync('@assessment-management:question-bank.entities.question-bank-item.fields.note.title')) || 'Note';
2806
+ // Open data selector with category filter
2807
+ const result = await this.dataSelectorService.openWithCategoryFilter({
2808
+ title: selectQuestionsTitle,
2809
+ dataSource: dataSource,
2810
+ columns: columns,
2811
+ selectionMode: 'multiple',
2812
+ searchFields: [
2813
+ { name: 'title', title: titleLabel },
2814
+ { name: 'note', title: noteLabel },
2815
+ ],
2816
+ allowCreate: 'full',
2817
+ onCreate: (mode) => this.createQuestionBankItem(entity, mode),
2818
+ }, {
2819
+ title: categoriesTitle,
2820
+ dataSource: categoryTreeDataSource,
2821
+ filterField: 'categoryIds',
2822
+ filterOperator: 'contains',
2823
+ width: '350px',
2824
+ });
2825
+ if (result?.items && result.items.length > 0) {
2826
+ const questionPromises = result.items.map((item) => this.questionBankData()
2827
+ .byKey(item.id)
2828
+ .catch(() => null));
2829
+ const questions = await Promise.all(questionPromises);
2830
+ const used = collectQuestionnaireItemNames(allSections);
2831
+ const baseOrder = section.items.length;
2832
+ const newItems = result.items.map((item, index) => {
2833
+ const fullQuestion = questions[index];
2834
+ const qName = allocateNextQuestionItemName(used);
2835
+ used.add(qName);
2836
+ return {
2837
+ name: qName,
2838
+ questionBankItemId: item.id,
2839
+ order: baseOrder + index,
2840
+ isRequired: false,
2841
+ isVisible: undefined,
2842
+ isNewlyAdded: true,
2843
+ question: fullQuestion
2844
+ ? {
2845
+ id: fullQuestion.id,
2846
+ title: fullQuestion.title,
2847
+ question: fullQuestion.question,
2848
+ note: fullQuestion.note,
2849
+ difficulty: fullQuestion.difficulty,
2850
+ }
2851
+ : {
2852
+ id: item.id,
2853
+ title: item.title || 'Question',
2854
+ question: item.question,
2855
+ note: item.note,
2856
+ difficulty: item.difficulty,
2857
+ },
2858
+ };
2859
+ });
2860
+ return newItems;
2861
+ }
2862
+ return null;
2863
+ }
2864
+ catch (error) {
2865
+ console.error('Error loading questions:', error);
2866
+ return null;
2867
+ }
2868
+ }
2869
+ /**
2870
+ * Create a new question bank item via Entity:Create and return it for selection in the data selector.
2871
+ */
2872
+ async createQuestionBankItem(entity, _mode) {
2873
+ const result = await this.commandService.execute('Entity:Create', {
2874
+ __context__: {
2875
+ entity: `${entity.module}.${entity.name}`,
2876
+ entityInfo: {
2877
+ name: entity.name,
2878
+ module: entity.module,
2879
+ title: entity.title,
2880
+ parentKey: entity.parentKey,
2881
+ },
2882
+ data: {},
2883
+ options: {},
2884
+ },
2885
+ });
2886
+ if (result?.success && result?.data) {
2887
+ const item = Array.isArray(result.data) ? result.data[0] : result.data;
2888
+ return item;
2889
+ }
2890
+ return null;
2891
+ }
2892
+ /**
2893
+ * Maps nested `hint` from the question property viewer to persisted questionnaire item `hint` partial (diff from bank).
2894
+ */
2895
+ persistQuestionHintFromForm(bank, hint) {
2896
+ if (!hint) {
2897
+ return undefined;
2898
+ }
2899
+ const modeRaw = hint.mode == null ? undefined : typeof hint.mode === 'string' ? hint.mode : hint.mode.id;
2900
+ const mode = normalizeQuestionHintMode(modeRaw) ?? 'off';
2901
+ const c = hint.content;
2902
+ let content;
2903
+ if (c == null) {
2904
+ content = undefined;
2905
+ }
2906
+ else if (typeof c === 'string') {
2907
+ const t = c.trim();
2908
+ content = t || undefined;
2909
+ }
2910
+ else if (typeof c === 'object') {
2911
+ content = c;
2912
+ }
2913
+ const edited = normalizeQuestionHintConfig({ mode, content });
2914
+ return diffQuestionHintOverride(bank, edited);
2915
+ }
2916
+ /**
2917
+ * Persists questionnaire item `comment` override: only fields that differ from the question bank default.
2918
+ */
2919
+ persistQuestionCommentFromForm(bank, form) {
2920
+ if (!form) {
2921
+ return undefined;
2922
+ }
2923
+ const edited = normalizeQuestionCommentConfig(form);
2924
+ return diffQuestionCommentOverride(bank, edited);
2925
+ }
2926
+ /**
2927
+ * Normalize tag-editor output to trimmed unique strings (supports string[] or { title }[] from tag service).
2928
+ */
2929
+ normalizeTagsFromForm(raw) {
2930
+ if (raw == null || !Array.isArray(raw))
2931
+ return undefined;
2932
+ const seen = new Set();
2933
+ const out = [];
2934
+ for (const entry of raw) {
2935
+ let t = '';
2936
+ if (typeof entry === 'string')
2937
+ t = entry.trim();
2938
+ else if (entry && typeof entry === 'object' && 'title' in entry) {
2939
+ t = String(entry.title).trim();
2940
+ }
2941
+ if (!t)
2942
+ continue;
2943
+ const key = t.toLowerCase();
2944
+ if (seen.has(key))
2945
+ continue;
2946
+ seen.add(key);
2947
+ out.push(t);
2948
+ }
2949
+ return out.length ? out : undefined;
2950
+ }
2951
+ /** Allowed characters for questionnaire item technical name (UUID-safe). */
2952
+ isValidQuestionnaireItemName(name) {
2953
+ return name.length >= 1 && name.length <= 128 && /^[a-zA-Z0-9_-]+$/.test(name);
2954
+ }
2955
+ /**
2956
+ * Plain heading for builder cards: prefer rich `question`, else admin list `title`.
2957
+ */
2958
+ itemQuestionHeading(item) {
2959
+ const q = item.question;
2960
+ if (!q) {
2961
+ return 'Question';
2962
+ }
2963
+ const locale = this.translationService.getActiveLang?.() ?? 'en-US';
2964
+ const raw = q.question;
2965
+ if (raw != null && raw !== '') {
2966
+ const html = typeof raw === 'string' ? raw : resolveMultiLanguageString(raw, locale);
2967
+ const plain = stripHtmlToPlain(html);
2968
+ if (plain.trim()) {
2969
+ return plain;
2970
+ }
2971
+ }
2972
+ const titleResolved = resolveMultiLanguageString(q.title, locale);
2973
+ return titleResolved.trim() || 'Question';
2974
+ }
2975
+ /** Plain preview line for optional note under the heading. */
2976
+ itemQuestionNotePreview(item) {
2977
+ const raw = item.question?.note;
2978
+ if (raw == null || raw === '') {
2979
+ return '';
2980
+ }
2981
+ const locale = this.translationService.getActiveLang?.() ?? 'en-US';
2982
+ const html = typeof raw === 'string' ? raw : resolveMultiLanguageString(raw, locale);
2983
+ return stripHtmlToPlain(html);
2984
+ }
2985
+ /**
2986
+ * Resolve icon based on widget type, with safe fallback.
2987
+ */
2988
+ getQuestionWidgetIcon(item) {
2989
+ const fallbackIcon = 'fa-light fa-question-circle';
2990
+ const widgetType = item.interfaceOverride?.type ?? item.question?.interface?.type;
2991
+ if (!widgetType) {
2992
+ return fallbackIcon;
2993
+ }
2994
+ try {
2995
+ return this.widgetRegistry.resolve(widgetType).icon ?? fallbackIcon;
2996
+ }
2997
+ catch {
2998
+ return fallbackIcon;
2999
+ }
3000
+ }
3001
+ //#endregion
3002
+ //#region ---- Preview ----
3003
+ /**
3004
+ * Open preview dialog with questionnaire viewer
3005
+ */
3006
+ async handlePreview() {
3007
+ if (this.isPreviewDisabled()) {
3008
+ return;
3009
+ }
3010
+ try {
3011
+ const currentValue = convertStateToValue(this.builderState(), this.value() || undefined);
3012
+ // Always use the current displaySettings from the computed property to ensure preview uses latest settings
3013
+ currentValue.displaySettings = this.displaySettings();
3014
+ const previewTitle = (await this.translationService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.actions.preview.title')) || 'Preview';
3015
+ const result = await this.questionnaireViewerService.open({
3016
+ title: previewTitle,
3017
+ structure: currentValue,
3018
+ prePostConfig: this.prePostConfig() ?? undefined,
3019
+ questionnaireId: this.questionnaireEntityId(),
3020
+ });
3021
+ // Handle result if needed (answers, submitted status)
3022
+ if (result) {
3023
+ console.log('Questionnaire preview result:', result);
3024
+ }
3025
+ }
3026
+ catch (error) {
3027
+ console.error('Error opening preview:', error);
3028
+ }
3029
+ }
3030
+ /**
3031
+ * Toggle fullscreen mode using the directive
3032
+ */
3033
+ async handleFullscreen() {
3034
+ const fullscreen = this.fullscreenDirective();
3035
+ if (fullscreen) {
3036
+ await fullscreen.toggle();
3037
+ }
3038
+ }
3039
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3040
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXMQuestionnaireQuestionsBuilderComponent, isStandalone: true, selector: "axm-questionnaire-questions-builder", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, prePostConfig: { classPropertyName: "prePostConfig", publicName: "prePostConfig", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, displaySettingsInput: { classPropertyName: "displaySettingsInput", publicName: "displaySettingsInput", isSignal: true, isRequired: false, transformFunction: null }, questionnaireEntityId: { classPropertyName: "questionnaireEntityId", publicName: "questionnaireEntityId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, viewQueries: [{ propertyName: "fullscreenDirective", first: true, predicate: ["fullscreen"], descendants: true, isSignal: true }, { propertyName: "standardRef", first: true, predicate: AXPStandardSectionItemsBuilderComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"axm-questionnaire-questions-builder\" axFullscreen #fullscreen=\"axFullscreen\">\n @if (showHeader()) {\n <div class=\"__header\">\n <div class=\"__header-left\"></div>\n <div class=\"__header-right\">\n @if (!readonly()) {\n <ax-button class=\"ax-sm\" [text]=\"\n ('@assessment-management:questionnaires.components.questionnaire-builder.actions.add-section.title'\n | translate\n | async) || 'Add Section'\n \" [color]=\"'primary'\" (onClick)=\"addSection()\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-layer-group\"></ax-icon>\n </ax-prefix>\n </ax-button>\n <ax-button class=\"ax-sm\" [title]=\"\n ('@assessment-management:questionnaires.components.questionnaire-builder.actions.preview.title'\n | translate\n | async) || 'Preview'\n \" [disabled]=\"isPreviewDisabled()\" (onClick)=\"handlePreview()\">\n <ax-icon icon=\"fa-light fa-eye\"></ax-icon>\n </ax-button>\n }\n <ax-button class=\"ax-sm\" [title]=\"\n ('@assessment-management:questionnaires.components.questionnaire-builder.actions.fullscreen.title'\n | translate\n | async) || 'Full Screen'\n \" (onClick)=\"handleFullscreen()\">\n <ax-icon icon=\"fa-light fa-expand\"></ax-icon>\n </ax-button>\n </div>\n </div>\n }\n\n <axp-standard-section-items-builder #standardRef class=\"ax-block ax-min-h-[200px]\" [value]=\"builderValue()\"\n [config]=\"standardConfig()\" [readonly]=\"readonly()\" (valueChange)=\"onBuilderValueChange($event)\" />\n</div>", styles: [".axm-questionnaire-questions-builder{display:flex;height:100%;width:100%;flex-direction:column}.axm-questionnaire-questions-builder .__header{display:flex;align-items:center;justify-content:space-between;gap:.75rem;border-bottom-width:1px;padding:.75rem}.axm-questionnaire-questions-builder .__header .__header-left,.axm-questionnaire-questions-builder .__header .__header-right{display:flex;gap:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: AXSwitchModule }, { kind: "ngmodule", type: AXLabelModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStandardSectionItemsBuilderComponent, selector: "axp-standard-section-items-builder", inputs: ["value", "config", "readonly"], outputs: ["valueChange"] }, { kind: "directive", type: AXFullScreenDirective, selector: "[axFullscreen]", outputs: ["fullscreenChange"], exportAs: ["axFullscreen"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3041
+ }
3042
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsBuilderComponent, decorators: [{
3043
+ type: Component,
3044
+ args: [{ selector: 'axm-questionnaire-questions-builder', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
3045
+ CommonModule,
3046
+ FormsModule,
3047
+ AXButtonModule,
3048
+ AXDecoratorModule,
3049
+ AXDropdownModule,
3050
+ AXFormModule,
3051
+ AXSwitchModule,
3052
+ AXLabelModule,
3053
+ AXTranslationModule,
3054
+ AXPStandardSectionItemsBuilderComponent,
3055
+ AXFullScreenDirective,
3056
+ ], template: "<div class=\"axm-questionnaire-questions-builder\" axFullscreen #fullscreen=\"axFullscreen\">\n @if (showHeader()) {\n <div class=\"__header\">\n <div class=\"__header-left\"></div>\n <div class=\"__header-right\">\n @if (!readonly()) {\n <ax-button class=\"ax-sm\" [text]=\"\n ('@assessment-management:questionnaires.components.questionnaire-builder.actions.add-section.title'\n | translate\n | async) || 'Add Section'\n \" [color]=\"'primary'\" (onClick)=\"addSection()\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-layer-group\"></ax-icon>\n </ax-prefix>\n </ax-button>\n <ax-button class=\"ax-sm\" [title]=\"\n ('@assessment-management:questionnaires.components.questionnaire-builder.actions.preview.title'\n | translate\n | async) || 'Preview'\n \" [disabled]=\"isPreviewDisabled()\" (onClick)=\"handlePreview()\">\n <ax-icon icon=\"fa-light fa-eye\"></ax-icon>\n </ax-button>\n }\n <ax-button class=\"ax-sm\" [title]=\"\n ('@assessment-management:questionnaires.components.questionnaire-builder.actions.fullscreen.title'\n | translate\n | async) || 'Full Screen'\n \" (onClick)=\"handleFullscreen()\">\n <ax-icon icon=\"fa-light fa-expand\"></ax-icon>\n </ax-button>\n </div>\n </div>\n }\n\n <axp-standard-section-items-builder #standardRef class=\"ax-block ax-min-h-[200px]\" [value]=\"builderValue()\"\n [config]=\"standardConfig()\" [readonly]=\"readonly()\" (valueChange)=\"onBuilderValueChange($event)\" />\n</div>", styles: [".axm-questionnaire-questions-builder{display:flex;height:100%;width:100%;flex-direction:column}.axm-questionnaire-questions-builder .__header{display:flex;align-items:center;justify-content:space-between;gap:.75rem;border-bottom-width:1px;padding:.75rem}.axm-questionnaire-questions-builder .__header .__header-left,.axm-questionnaire-questions-builder .__header .__header-right{display:flex;gap:.5rem}\n"] }]
3057
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], prePostConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "prePostConfig", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], displaySettingsInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "displaySettingsInput", required: false }] }], questionnaireEntityId: [{ type: i0.Input, args: [{ isSignal: true, alias: "questionnaireEntityId", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], fullscreenDirective: [{ type: i0.ViewChild, args: ['fullscreen', { isSignal: true }] }], standardRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPStandardSectionItemsBuilderComponent), { isSignal: true }] }] } });
3058
+
3059
+ /** Component key for the questionnaire Questions page tab. Register via AXP_PAGE_COMPONENT_PROVIDER. */
3060
+ const QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY = 'questionnaire-questions-page';
3061
+
3062
+ const QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND = 'Questionnaire:Questions:Save';
3063
+ /**
3064
+ * Command to save the questionnaire questions field.
3065
+ * By default uses entity update (patch) with { questions }.
3066
+ * Can be overridden for custom API logic.
3067
+ */
3068
+ class AXMSaveQuestionnaireQuestionsCommand {
3069
+ constructor() {
3070
+ this.entityService = inject(AXPEntityService);
3071
+ this.translationService = inject(AXTranslationService);
3072
+ }
3073
+ async execute(input) {
3074
+ const { id, questions } = input;
3075
+ if (!id) {
3076
+ return {
3077
+ success: false,
3078
+ message: {
3079
+ text: await this.translationService.translateAsync('@general:messages.entity.invalid-data'),
3080
+ },
3081
+ };
3082
+ }
3083
+ try {
3084
+ const entityRef = `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`;
3085
+ const dataAccessor = this.entityService.withEntity(entityRef).data();
3086
+ await dataAccessor.update(id, { questions });
3087
+ return {
3088
+ success: true,
3089
+ data: { id, questions },
3090
+ message: {
3091
+ text: await this.translationService.translateAsync('@general:messages.generic.success.description'),
3092
+ },
3093
+ };
3094
+ }
3095
+ catch (error) {
3096
+ const message = error instanceof Error
3097
+ ? error.message
3098
+ : await this.translationService.translateAsync('@general:messages.generic.error.description');
3099
+ return {
3100
+ success: false,
3101
+ message: { text: message },
3102
+ };
3103
+ }
3104
+ }
3105
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireQuestionsCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3106
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireQuestionsCommand, providedIn: 'root' }); }
3107
+ }
3108
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireQuestionsCommand, decorators: [{
3109
+ type: Injectable,
3110
+ args: [{
3111
+ providedIn: 'root',
3112
+ }]
3113
+ }] });
3114
+
3115
+ var saveQuestionnaireQuestions_command = /*#__PURE__*/Object.freeze({
3116
+ __proto__: null,
3117
+ AXMSaveQuestionnaireQuestionsCommand: AXMSaveQuestionnaireQuestionsCommand,
3118
+ QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND: QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND
3119
+ });
3120
+
3121
+ /** Local command handled in execute(); reverts questions to rootContext (no AXPCommand). */
3122
+ const QUESTIONNAIRE_QUESTIONS_DISCARD_COMMAND = 'Questionnaire:Questions:Discard';
3123
+ const QUESTIONNAIRE_QUESTIONS_ADD_SECTION_COMMAND = 'Questionnaire:Questions:AddSection';
3124
+ const QUESTIONNAIRE_QUESTIONS_PREVIEW_COMMAND = 'Questionnaire:Questions:Preview';
3125
+ //#region ---- Questionnaire Questions Page Component ----
3126
+ /**
3127
+ * Page component that renders the questions property using axm-questionnaire-questions-builder.
3128
+ * Extends AXPPageLayoutBaseComponent to provide a Save button that persists questions via Questionnaire:Questions:Save.
3129
+ * Receives rootContext (entity data).
3130
+ */
3131
+ class AXMQuestionnaireQuestionsPageComponent extends AXPPageLayoutBaseComponent {
3132
+ //#endregion
3133
+ //#region ---- Lifecycle ----
3134
+ constructor() {
3135
+ super();
3136
+ //#region ---- Inputs ----
3137
+ this.rootContext = input(...(ngDevMode ? [undefined, { debugName: "rootContext" }] : /* istanbul ignore next */ []));
3138
+ /** Optional page config (e.g. field, layout) set by the page-component-renderer. */
3139
+ this.pageConfig = input(...(ngDevMode ? [undefined, { debugName: "pageConfig" }] : /* istanbul ignore next */ []));
3140
+ //#endregion
3141
+ //#region ---- Services & Dependencies ----
3142
+ this.commandService = inject(AXPCommandService);
3143
+ this.eventService = inject(AXPBroadcastEventService);
3144
+ this.hostPageLayout = inject(AXPPageLayoutBase, { optional: true, skipSelf: true });
3145
+ //#endregion
3146
+ //#region ---- View References ----
3147
+ this.builderRef = viewChild('builderRef', ...(ngDevMode ? [{ debugName: "builderRef" }] : /* istanbul ignore next */ []));
3148
+ //#endregion
3149
+ //#region ---- State Signals ----
3150
+ /** Latest context (includes edited questions). Used for Save when rootContext is not updated by the parent. */
3151
+ this.localContext = signal(null, ...(ngDevMode ? [{ debugName: "localContext" }] : /* istanbul ignore next */ []));
3152
+ /** When true, the next valueChange from the builder is ignored (avoids undoing discard due to stale emit). */
3153
+ this.ignoreNextValueChange = signal(false, ...(ngDevMode ? [{ debugName: "ignoreNextValueChange" }] : /* istanbul ignore next */ []));
3154
+ /** Stable pre/post config so we don't pass a new object every CD (avoids infinite loop). */
3155
+ this.prePostConfigStable = signal(null, ...(ngDevMode ? [{ debugName: "prePostConfigStable" }] : /* istanbul ignore next */ []));
3156
+ /** Baseline snapshot of questions for dirty checks. */
3157
+ this.baselineQuestions = signal(null, ...(ngDevMode ? [{ debugName: "baselineQuestions" }] : /* istanbul ignore next */ []));
3158
+ /**
3159
+ * When true, the next builder emission should realign the baseline to the builder's canonical shape.
3160
+ * Root/API `questions` often differs from the normalized object emitted by the builder (defaults, ordering, derived fields), which would otherwise keep `isDirty` true on load and after discard/save.
3161
+ */
3162
+ this.questionsBaselinePendingReconcile = signal(false, ...(ngDevMode ? [{ debugName: "questionsBaselinePendingReconcile" }] : /* istanbul ignore next */ []));
3163
+ this.isDirty = signal(false, ...(ngDevMode ? [{ debugName: "isDirty" }] : /* istanbul ignore next */ []));
3164
+ //#endregion
3165
+ //#region ---- Computed State ----
3166
+ /** Current context: prefer local updates so edits are preserved. */
3167
+ this.context = computed(() => {
3168
+ const local = this.localContext();
3169
+ if (local != null)
3170
+ return local;
3171
+ return this.rootContext() ?? {};
3172
+ }, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
3173
+ /** Questions value passed to the builder (from context). */
3174
+ this.questionsValue = computed(() => {
3175
+ const ctx = this.context();
3176
+ const q = ctx['questions'];
3177
+ return (q != null && typeof q === 'object' ? q : null);
3178
+ }, ...(ngDevMode ? [{ debugName: "questionsValue" }] : /* istanbul ignore next */ []));
3179
+ /** Pre/post config for preview; stable reference to avoid infinite change detection. */
3180
+ this.prePostConfig = computed(() => this.prePostConfigStable(), ...(ngDevMode ? [{ debugName: "prePostConfig" }] : /* istanbul ignore next */ []));
3181
+ /** Display settings from entity (for builder preview). */
3182
+ this.entityDisplaySettings = computed(() => toDisplaySettings((this.rootContext() ?? undefined)) ?? null, ...(ngDevMode ? [{ debugName: "entityDisplaySettings" }] : /* istanbul ignore next */ []));
3183
+ /** Questionnaire entity id for preview outcome evaluation. */
3184
+ this.questionnaireEntityId = computed(() => {
3185
+ const id = this.rootContext()?.['id'];
3186
+ return typeof id === 'string' && id.length > 0 ? id : undefined;
3187
+ }, ...(ngDevMode ? [{ debugName: "questionnaireEntityId" }] : /* istanbul ignore next */ []));
3188
+ let previousRootId = undefined;
3189
+ effect(() => {
3190
+ const ctx = this.rootContext();
3191
+ const currentId = ctx != null && typeof ctx === 'object' ? ctx['id'] : undefined;
3192
+ if (currentId !== previousRootId) {
3193
+ previousRootId = currentId;
3194
+ this.localContext.set(null);
3195
+ const rootQuestions = ctx?.['questions'];
3196
+ this.baselineQuestions.set(this.cloneQuestions(rootQuestions));
3197
+ this.isDirty.set(false);
3198
+ this.questionsBaselinePendingReconcile.set(true);
3199
+ }
3200
+ });
3201
+ let lastPrePostKey = '';
3202
+ effect(() => {
3203
+ const ctx = this.rootContext();
3204
+ const next = toPrePostConfig(ctx ?? undefined);
3205
+ const key = next ? JSON.stringify(next) : '';
3206
+ if (key !== lastPrePostKey) {
3207
+ lastPrePostKey = key;
3208
+ this.prePostConfigStable.set(next);
3209
+ }
3210
+ });
3211
+ }
3212
+ //#endregion
3213
+ //#region ---- Page Actions ----
3214
+ async getPrimaryMenuItems() {
3215
+ const addSectionTitle = (await this.translateService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.actions.add-section.title')) || 'Add Section';
3216
+ const previewTitle = (await this.translateService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.actions.preview.title')) || 'Preview';
3217
+ const discardTitle = (await this.translateService.translateAsync('@general:actions.discard.title')) || 'Discard';
3218
+ const saveTitle = (await this.translateService.translateAsync('@general:actions.save.title')) || 'Save';
3219
+ const addSectionAction = {
3220
+ title: addSectionTitle,
3221
+ icon: 'fa-light fa-layer-group',
3222
+ zone: 'header',
3223
+ priority: 'primary',
3224
+ color: 'primary',
3225
+ command: {
3226
+ name: QUESTIONNAIRE_QUESTIONS_ADD_SECTION_COMMAND,
3227
+ options: {},
3228
+ },
3229
+ };
3230
+ const previewAction = {
3231
+ title: previewTitle,
3232
+ icon: 'fa-light fa-eye',
3233
+ zone: 'header',
3234
+ priority: 'primary',
3235
+ color: 'default',
3236
+ command: {
3237
+ name: QUESTIONNAIRE_QUESTIONS_PREVIEW_COMMAND,
3238
+ options: {},
3239
+ },
3240
+ };
3241
+ const discardAction = {
3242
+ title: discardTitle,
3243
+ icon: 'fa-light fa-rotate-left',
3244
+ zone: 'footer',
3245
+ priority: 'secondary',
3246
+ color: 'default',
3247
+ visible: this.isDirty(),
3248
+ command: {
3249
+ name: QUESTIONNAIRE_QUESTIONS_DISCARD_COMMAND,
3250
+ options: {},
3251
+ },
3252
+ };
3253
+ const saveAction = {
3254
+ title: saveTitle,
3255
+ icon: 'fa-light fa-floppy-disk',
3256
+ zone: 'footer',
3257
+ priority: 'primary',
3258
+ color: 'primary',
3259
+ visible: this.isDirty(),
3260
+ command: {
3261
+ name: QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND,
3262
+ options: {},
3263
+ },
3264
+ };
3265
+ return [addSectionAction, previewAction, discardAction, saveAction];
3266
+ }
3267
+ //#endregion
3268
+ //#region ---- Command Handling ----
3269
+ execute(command) {
3270
+ if (command.name === QUESTIONNAIRE_QUESTIONS_DISCARD_COMMAND) {
3271
+ this.ignoreNextValueChange.set(true);
3272
+ this.localContext.set(null);
3273
+ this.isDirty.set(false);
3274
+ this.questionsBaselinePendingReconcile.set(true);
3275
+ this.recompute();
3276
+ return;
3277
+ }
3278
+ if (command.name === QUESTIONNAIRE_QUESTIONS_ADD_SECTION_COMMAND) {
3279
+ void this.builderRef()?.addSection();
3280
+ return;
3281
+ }
3282
+ if (command.name === QUESTIONNAIRE_QUESTIONS_PREVIEW_COMMAND) {
3283
+ this.builderRef()?.handlePreview();
3284
+ return;
3285
+ }
3286
+ if (command.name !== QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND) {
3287
+ return;
3288
+ }
3289
+ // Return result for delegateExecute; cast needed to satisfy base signature
3290
+ return this.handleSaveExecute();
3291
+ }
3292
+ async handleSaveExecute() {
3293
+ const ctx = this.localContext() ?? this.rootContext() ?? {};
3294
+ const id = typeof ctx['id'] === 'string' ? ctx['id'] : undefined;
3295
+ const questions = ctx['questions'];
3296
+ if (!id) {
3297
+ return {
3298
+ success: false,
3299
+ message: {
3300
+ text: await this.translateService.translateAsync('@general:messages.entity.invalid-data'),
3301
+ },
3302
+ };
3303
+ }
3304
+ const input = {
3305
+ id,
3306
+ questions: questions ?? { sections: [] },
3307
+ };
3308
+ const result = await this.commandService.execute(QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND, input);
3309
+ const execResult = (result ?? { success: false });
3310
+ if (execResult.success) {
3311
+ const savedQuestions = questions ?? { sections: [] };
3312
+ this.baselineQuestions.set(this.cloneQuestions(savedQuestions));
3313
+ this.localContext.set(null);
3314
+ this.isDirty.set(false);
3315
+ this.questionsBaselinePendingReconcile.set(true);
3316
+ this.recompute();
3317
+ this.eventService.publish(AXPEntityEventsKeys.REFRESH_LAYOUT, {
3318
+ name: `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`,
3319
+ });
3320
+ // Success toast is shown by details-view when execute() returns success
3321
+ }
3322
+ return execResult;
3323
+ }
3324
+ //#endregion
3325
+ //#region ---- UI Handlers ----
3326
+ /** Updates local context when the builder emits a new questions value. Skip if unchanged to avoid CD loop. */
3327
+ onQuestionsChange(value) {
3328
+ if (this.ignoreNextValueChange()) {
3329
+ this.ignoreNextValueChange.set(false);
3330
+ return;
3331
+ }
3332
+ const current = this.questionsValue();
3333
+ if (this.areQuestionsEqual(current ?? undefined, value ?? undefined)) {
3334
+ if (this.questionsBaselinePendingReconcile()) {
3335
+ const aligned = current ?? value;
3336
+ this.baselineQuestions.set(this.cloneQuestions(aligned ?? undefined));
3337
+ this.questionsBaselinePendingReconcile.set(false);
3338
+ this.isDirty.set(false);
3339
+ }
3340
+ return;
3341
+ }
3342
+ const base = this.localContext() ?? this.rootContext() ?? {};
3343
+ if (this.questionsBaselinePendingReconcile() && this.localContext() == null) {
3344
+ this.baselineQuestions.set(this.cloneQuestions(value));
3345
+ this.isDirty.set(false);
3346
+ this.questionsBaselinePendingReconcile.set(false);
3347
+ this.localContext.set({ ...base, questions: value });
3348
+ this.recompute();
3349
+ return;
3350
+ }
3351
+ this.localContext.set({ ...base, questions: value });
3352
+ const baseline = this.baselineQuestions();
3353
+ const dirty = !this.areQuestionsEqual(baseline, value);
3354
+ this.isDirty.set(dirty);
3355
+ this.recompute();
3356
+ }
3357
+ //#endregion
3358
+ //#region ---- Utility Methods ----
3359
+ areQuestionsEqual(a, b) {
3360
+ return isEqual(a, b);
3361
+ }
3362
+ cloneQuestions(value) {
3363
+ if (isNil(value))
3364
+ return value;
3365
+ return cloneDeep(value);
3366
+ }
3367
+ recompute() {
3368
+ super.recompute();
3369
+ const maybeLayout = this.hostPageLayout;
3370
+ maybeLayout.recompute?.();
3371
+ }
3372
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3373
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: AXMQuestionnaireQuestionsPageComponent, isStandalone: true, selector: "axm-questionnaire-questions-page", inputs: { rootContext: { classPropertyName: "rootContext", publicName: "rootContext", isSignal: true, isRequired: false, transformFunction: null }, pageConfig: { classPropertyName: "pageConfig", publicName: "pageConfig", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
3374
+ {
3375
+ provide: AXPPageLayoutBase,
3376
+ useExisting: AXMQuestionnaireQuestionsPageComponent,
3377
+ },
3378
+ ], viewQueries: [{ propertyName: "builderRef", first: true, predicate: ["builderRef"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
3379
+ <axm-questionnaire-questions-builder
3380
+ #builderRef
3381
+ [value]="questionsValue()"
3382
+ [prePostConfig]="prePostConfig()"
3383
+ [displaySettingsInput]="entityDisplaySettings()"
3384
+ [questionnaireEntityId]="questionnaireEntityId()"
3385
+ [showHeader]="false"
3386
+ (valueChange)="onQuestionsChange($event)">
3387
+ </axm-questionnaire-questions-builder>
3388
+ `, isInline: true, dependencies: [{ kind: "component", type: AXMQuestionnaireQuestionsBuilderComponent, selector: "axm-questionnaire-questions-builder", inputs: ["value", "readonly", "prePostConfig", "showHeader", "displaySettingsInput", "questionnaireEntityId"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3389
+ }
3390
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponent, decorators: [{
3391
+ type: Component,
3392
+ args: [{
3393
+ selector: 'axm-questionnaire-questions-page',
3394
+ standalone: true,
3395
+ imports: [AXMQuestionnaireQuestionsBuilderComponent],
3396
+ template: `
3397
+ <axm-questionnaire-questions-builder
3398
+ #builderRef
3399
+ [value]="questionsValue()"
3400
+ [prePostConfig]="prePostConfig()"
3401
+ [displaySettingsInput]="entityDisplaySettings()"
3402
+ [questionnaireEntityId]="questionnaireEntityId()"
3403
+ [showHeader]="false"
3404
+ (valueChange)="onQuestionsChange($event)">
3405
+ </axm-questionnaire-questions-builder>
3406
+ `,
3407
+ changeDetection: ChangeDetectionStrategy.OnPush,
3408
+ providers: [
3409
+ {
3410
+ provide: AXPPageLayoutBase,
3411
+ useExisting: AXMQuestionnaireQuestionsPageComponent,
3412
+ },
3413
+ ],
3414
+ }]
3415
+ }], ctorParameters: () => [], propDecorators: { rootContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootContext", required: false }] }], pageConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageConfig", required: false }] }], builderRef: [{ type: i0.ViewChild, args: ['builderRef', { isSignal: true }] }] } });
3416
+
3417
+ var questionnaireQuestionsPage_component = /*#__PURE__*/Object.freeze({
3418
+ __proto__: null,
3419
+ AXMQuestionnaireQuestionsPageComponent: AXMQuestionnaireQuestionsPageComponent
3420
+ });
3421
+
3422
+ //#region ---- Questionnaire Questions Page Component Provider ----
3423
+ class AXMQuestionnaireQuestionsPageComponentProvider {
3424
+ async components() {
3425
+ return [
3426
+ {
3427
+ key: QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY,
3428
+ loader: () => Promise.resolve().then(function () { return questionnaireQuestionsPage_component; }).then((m) => m.AXMQuestionnaireQuestionsPageComponent),
3429
+ },
3430
+ ];
3431
+ }
3432
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponentProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3433
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponentProvider }); }
3434
+ }
3435
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponentProvider, decorators: [{
3436
+ type: Injectable
3437
+ }] });
3438
+
3439
+ var index$1 = /*#__PURE__*/Object.freeze({
3440
+ __proto__: null,
3441
+ AXMQuestionnaireQuestionsPageComponent: AXMQuestionnaireQuestionsPageComponent,
3442
+ AXMQuestionnaireQuestionsPageComponentProvider: AXMQuestionnaireQuestionsPageComponentProvider,
3443
+ AXMSaveQuestionnaireQuestionsCommand: AXMSaveQuestionnaireQuestionsCommand,
3444
+ QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY: QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY,
3445
+ QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND: QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND
3446
+ });
3447
+
3448
+ //#region ---- Questions builder exports ----
3449
+ //#endregion
3450
+
3451
+ //#region ---- Imports ----
3452
+ //#endregion
3453
+ //#region ---- Select options ----
3454
+ /** i18n keys for comparison operator select options. */
3455
+ const OUTCOME_DISPLAY_OPERATOR_LABEL_KEYS = {
3456
+ eq: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.eq',
3457
+ neq: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.neq',
3458
+ gt: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.gt',
3459
+ gte: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.gte',
3460
+ lt: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.lt',
3461
+ lte: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.lte',
3462
+ };
3463
+ /** i18n keys for emphasis select options. */
3464
+ const OUTCOME_DISPLAY_EMPHASIS_LABEL_KEYS = {
3465
+ normal: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-emphasis.options.normal',
3466
+ medium: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-emphasis.options.medium',
3467
+ bold: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-emphasis.options.bold',
3468
+ };
3469
+ const OUTCOME_DISPLAY_OPERATOR_SELECT_OPTIONS = Object.keys(OUTCOME_DISPLAY_OPERATOR_LABEL_KEYS).map((id) => ({
3470
+ id,
3471
+ title: OUTCOME_DISPLAY_OPERATOR_LABEL_KEYS[id],
3472
+ }));
3473
+ const OUTCOME_DISPLAY_OPERATOR_STRING_SELECT_OPTIONS = OUTCOME_DISPLAY_OPERATOR_SELECT_OPTIONS.filter((option) => option.id === 'eq' || option.id === 'neq');
3474
+ const OUTCOME_DISPLAY_OPERATOR_BOOLEAN_SELECT_OPTIONS = OUTCOME_DISPLAY_OPERATOR_SELECT_OPTIONS.filter((option) => option.id === 'eq');
3475
+ const OUTCOME_DISPLAY_EMPHASIS_SELECT_OPTIONS = Object.keys(OUTCOME_DISPLAY_EMPHASIS_LABEL_KEYS).map((id) => ({
3476
+ id,
3477
+ title: OUTCOME_DISPLAY_EMPHASIS_LABEL_KEYS[id],
3478
+ }));
3479
+ function toOutcomeDisplayOperatorSelectValue(operator) {
3480
+ return { id: operator, title: OUTCOME_DISPLAY_OPERATOR_LABEL_KEYS[operator] };
3481
+ }
3482
+ function toOutcomeDisplayEmphasisSelectValue(emphasis) {
3483
+ return { id: emphasis, title: OUTCOME_DISPLAY_EMPHASIS_LABEL_KEYS[emphasis] };
3484
+ }
3485
+ //#endregion
3486
+
3487
+ //#region ---- Imports ----
3488
+ //#endregion
3489
+ //#region ---- Result kind labels ----
3490
+ /** i18n keys for select options (property viewer). */
3491
+ const OUTCOME_RESULT_KIND_LABEL_KEYS = {
3492
+ number: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.result-kind.options.number',
3493
+ string: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.result-kind.options.string',
3494
+ boolean: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.result-kind.options.boolean',
3495
+ };
3496
+ /**
3497
+ * Sync labels for list badges — mirrors i18n `result-kind.options` (en-US Title Case).
3498
+ * Select/editor fields still use {@link OUTCOME_RESULT_KIND_LABEL_KEYS} with the translate pipe.
3499
+ */
3500
+ const OUTCOME_RESULT_KIND_LABELS = {
3501
+ number: createMultiLanguageString('Number', 'عدد'),
3502
+ string: createMultiLanguageString('Text', 'متن'),
3503
+ boolean: createMultiLanguageString('Yes / No', 'بله / خیر'),
3504
+ };
3505
+ const OUTCOME_RESULT_KIND_SELECT_OPTIONS = Object.keys(OUTCOME_RESULT_KIND_LABEL_KEYS).map((id) => ({
3506
+ id,
3507
+ title: OUTCOME_RESULT_KIND_LABEL_KEYS[id],
3508
+ }));
3509
+ function resolveOutcomeResultKindLabel(kind, locale) {
3510
+ return resolveMultiLanguageString(OUTCOME_RESULT_KIND_LABELS[kind], locale);
3511
+ }
3512
+ function toOutcomeResultKindSelectValue(kind) {
3513
+ return { id: kind, title: OUTCOME_RESULT_KIND_LABEL_KEYS[kind] };
3514
+ }
3515
+ //#endregion
3516
+
3517
+ //#region ---- Imports ----
3518
+ //#endregion
3519
+ //#region ---- Groups ----
3520
+ const GROUP_BASIC = {
3521
+ name: 'basic-info',
3522
+ title: '@general:terms.interface.basic-info',
3523
+ order: 0,
3524
+ };
3525
+ const GROUP_RESULT = {
3526
+ name: 'result',
3527
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.groups.result',
3528
+ order: 1,
3529
+ };
3530
+ const GROUP_DISPLAY_RULES = {
3531
+ name: 'display-rules',
3532
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.groups.display-rules',
3533
+ order: 2,
3534
+ };
3535
+ const RESULT_KIND_OPTIONS = OUTCOME_RESULT_KIND_SELECT_OPTIONS;
3536
+ const DISPLAY_RULE_GRID_CELL = {
3537
+ alignSelf: 'center',
3538
+ alignItems: 'center',
3539
+ justifyContent: 'center',
3540
+ };
3541
+ const COMPARE_VALUE_PLACEHOLDER = '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-compare-value.placeholder';
3542
+ //#endregion
3543
+ //#region ---- Display rule row layout (result-kind aware) ----
3544
+ function buildDisplayRuleOperatorWidget(resultKind) {
3545
+ const dataSource = resultKind === 'boolean'
3546
+ ? OUTCOME_DISPLAY_OPERATOR_BOOLEAN_SELECT_OPTIONS
3547
+ : resultKind === 'string'
3548
+ ? OUTCOME_DISPLAY_OPERATOR_STRING_SELECT_OPTIONS
3549
+ : OUTCOME_DISPLAY_OPERATOR_SELECT_OPTIONS;
3550
+ return {
3551
+ type: AXPWidgetsCatalog.select,
3552
+ mode: 'edit',
3553
+ path: 'operator',
3554
+ name: 'operator',
3555
+ options: {
3556
+ valueField: 'id',
3557
+ textField: 'title',
3558
+ dataSource: [...dataSource],
3559
+ },
3560
+ };
3561
+ }
3562
+ function buildDisplayRuleCompareValueWidget(resultKind) {
3563
+ if (resultKind === 'boolean') {
3564
+ return {
3565
+ type: AXPWidgetsCatalog.toggle,
3566
+ mode: 'edit',
3567
+ path: 'compareValue',
3568
+ name: 'compareValue',
3569
+ options: {},
3570
+ };
3571
+ }
3572
+ if (resultKind === 'string') {
3573
+ return {
3574
+ type: AXPWidgetsCatalog.text,
3575
+ mode: 'edit',
3576
+ path: 'compareValue',
3577
+ name: 'compareValue',
3578
+ options: {
3579
+ placeholder: COMPARE_VALUE_PLACEHOLDER,
3580
+ },
3581
+ };
3582
+ }
3583
+ return {
3584
+ type: AXPWidgetsCatalog.number,
3585
+ mode: 'edit',
3586
+ path: 'compareValue',
3587
+ name: 'compareValue',
3588
+ options: {
3589
+ placeholder: COMPARE_VALUE_PLACEHOLDER,
3590
+ },
3591
+ };
3592
+ }
3593
+ function buildDisplayRuleRepeaterChildren(resultKind) {
3594
+ return [
3595
+ {
3596
+ type: AXPWidgetsCatalog.grid,
3597
+ mode: 'edit',
3598
+ options: {
3599
+ width: '100%',
3600
+ grid: {
3601
+ default: {
3602
+ columns: 12,
3603
+ gap: '8px',
3604
+ alignItems: 'center',
3605
+ },
3606
+ },
3607
+ },
3608
+ children: [
3609
+ {
3610
+ type: AXPWidgetsCatalog.gridItem,
3611
+ mode: 'edit',
3612
+ options: { colSpan: 3, ...DISPLAY_RULE_GRID_CELL },
3613
+ children: [buildDisplayRuleOperatorWidget(resultKind)],
3614
+ },
3615
+ {
3616
+ type: AXPWidgetsCatalog.gridItem,
3617
+ mode: 'edit',
3618
+ options: { colSpan: 2, ...DISPLAY_RULE_GRID_CELL },
3619
+ children: [buildDisplayRuleCompareValueWidget(resultKind)],
3620
+ },
3621
+ {
3622
+ type: AXPWidgetsCatalog.gridItem,
3623
+ mode: 'edit',
3624
+ options: { colSpan: 3, ...DISPLAY_RULE_GRID_CELL },
3625
+ children: [
3626
+ {
3627
+ type: AXPWidgetsCatalog.themeColorChooser,
3628
+ mode: 'edit',
3629
+ path: 'color',
3630
+ name: 'color',
3631
+ options: {
3632
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-color.placeholder',
3633
+ },
3634
+ },
3635
+ ],
3636
+ },
3637
+ {
3638
+ type: AXPWidgetsCatalog.gridItem,
3639
+ mode: 'edit',
3640
+ options: { colSpan: 2, ...DISPLAY_RULE_GRID_CELL },
3641
+ children: [
3642
+ {
3643
+ type: AXPWidgetsCatalog.select,
3644
+ mode: 'edit',
3645
+ path: 'emphasis',
3646
+ name: 'emphasis',
3647
+ options: {
3648
+ valueField: 'id',
3649
+ textField: 'title',
3650
+ dataSource: [...OUTCOME_DISPLAY_EMPHASIS_SELECT_OPTIONS],
3651
+ },
3652
+ },
3653
+ ],
3654
+ },
3655
+ {
3656
+ type: AXPWidgetsCatalog.gridItem,
3657
+ mode: 'edit',
3658
+ options: { colSpan: 2, ...DISPLAY_RULE_GRID_CELL },
3659
+ children: [
3660
+ {
3661
+ type: AXPWidgetsCatalog.iconChooser,
3662
+ mode: 'edit',
3663
+ path: 'icon',
3664
+ name: 'icon',
3665
+ options: {},
3666
+ },
3667
+ ],
3668
+ },
3669
+ ],
3670
+ },
3671
+ ];
3672
+ }
3673
+ function buildDisplayRulesProps(resultKind) {
3674
+ return [
3675
+ {
3676
+ name: 'displayRules',
3677
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-rules.title',
3678
+ description: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-rules.description',
3679
+ group: GROUP_DISPLAY_RULES,
3680
+ order: 10,
3681
+ showLabel: false,
3682
+ schema: {
3683
+ dataType: 'array',
3684
+ defaultValue: [],
3685
+ nullable: true,
3686
+ interface: {
3687
+ name: 'displayRules',
3688
+ path: 'displayRules',
3689
+ type: AXPWidgetsCatalog.repeaterLayout,
3690
+ options: {
3691
+ hasControls: true,
3692
+ allowReorder: true,
3693
+ width: '100%',
3694
+ },
3695
+ children: buildDisplayRuleRepeaterChildren(resultKind),
3696
+ },
3697
+ },
3698
+ visible: true,
3699
+ },
3700
+ ];
3701
+ }
3702
+ //#endregion
3703
+ //#region ---- Properties ----
3704
+ const BASIC_PROPS = [
3705
+ {
3706
+ name: 'name',
3707
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.name.title',
3708
+ group: GROUP_BASIC,
3709
+ order: 10,
3710
+ schema: {
3711
+ dataType: 'string',
3712
+ defaultValue: '',
3713
+ interface: {
3714
+ name: 'name',
3715
+ path: 'name',
3716
+ type: AXPWidgetsCatalog.text,
3717
+ options: {
3718
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.name.placeholder',
3719
+ },
3720
+ },
3721
+ },
3722
+ validations: [{ rule: 'required' }, { rule: 'variable-name' }],
3723
+ visible: true,
3724
+ },
3725
+ {
3726
+ name: 'title',
3727
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.title.title',
3728
+ group: GROUP_BASIC,
3729
+ order: 20,
3730
+ schema: {
3731
+ dataType: 'string',
3732
+ defaultValue: '',
3733
+ interface: {
3734
+ name: 'title',
3735
+ path: 'title',
3736
+ type: AXPWidgetsCatalog.text,
3737
+ options: {
3738
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.title.placeholder',
3739
+ },
3740
+ },
3741
+ },
3742
+ validations: [{ rule: 'required' }],
3743
+ visible: true,
3744
+ },
3745
+ {
3746
+ name: 'description',
3747
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.description.title',
3748
+ group: GROUP_BASIC,
3749
+ order: 30,
3750
+ schema: {
3751
+ dataType: 'string',
3752
+ defaultValue: '',
3753
+ interface: {
3754
+ name: 'description',
3755
+ path: 'description',
3756
+ type: AXPWidgetsCatalog.largeText,
3757
+ options: {
3758
+ rows: 3,
3759
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.description.placeholder',
3760
+ },
3761
+ },
3762
+ },
3763
+ visible: true,
3764
+ },
3765
+ ];
3766
+ const RESULT_PROPS = [
3767
+ {
3768
+ name: 'resultKind',
3769
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.result-kind.title',
3770
+ group: GROUP_RESULT,
3771
+ order: 10,
3772
+ schema: {
3773
+ dataType: 'string',
3774
+ defaultValue: { id: 'number', title: RESULT_KIND_OPTIONS[0].title },
3775
+ interface: {
3776
+ name: 'resultKind',
3777
+ path: 'resultKind',
3778
+ type: AXPWidgetsCatalog.select,
3779
+ options: {
3780
+ valueField: 'id',
3781
+ textField: 'title',
3782
+ dataSource: [...RESULT_KIND_OPTIONS],
3783
+ },
3784
+ },
3785
+ },
3786
+ validations: [{ rule: 'required' }],
3787
+ visible: true,
3788
+ },
3789
+ {
3790
+ name: 'expression',
3791
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.expression.title',
3792
+ group: GROUP_RESULT,
3793
+ order: 20,
3794
+ schema: {
3795
+ dataType: 'string',
3796
+ defaultValue: '',
3797
+ interface: {
3798
+ name: 'expression',
3799
+ path: 'expression',
3800
+ type: 'code-editor',
3801
+ options: {
3802
+ language: 'javascript',
3803
+ lineNumbers: true,
3804
+ height: 'min-h-72',
3805
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.expression.placeholder',
3806
+ },
3807
+ },
3808
+ },
3809
+ validations: [{ rule: 'required' }],
3810
+ visible: true,
3811
+ },
3812
+ ];
3813
+ //#endregion
3814
+ //#region ---- Tabs ----
3815
+ /** Builds outcome edit tabs; display-rule value widgets match the selected result kind. */
3816
+ function buildQuestionnaireOutcomeEditTabs(resultKind = 'number') {
3817
+ return [
3818
+ {
3819
+ name: 'general',
3820
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.tabs.general.title',
3821
+ groups: [
3822
+ {
3823
+ name: GROUP_BASIC.name,
3824
+ title: GROUP_BASIC.title,
3825
+ isCollapsed: false,
3826
+ props: BASIC_PROPS,
3827
+ },
3828
+ {
3829
+ name: GROUP_RESULT.name,
3830
+ title: GROUP_RESULT.title,
3831
+ isCollapsed: false,
3832
+ props: RESULT_PROPS,
3833
+ },
3834
+ {
3835
+ name: GROUP_DISPLAY_RULES.name,
3836
+ title: GROUP_DISPLAY_RULES.title,
3837
+ isCollapsed: false,
3838
+ props: buildDisplayRulesProps(resultKind),
3839
+ },
3840
+ ],
3841
+ },
3842
+ ];
3843
+ }
3844
+ //#endregion
3845
+
3846
+ //#region ---- Imports ----
3847
+ //#endregion
3848
+ //#region ---- Mapping ----
3849
+ function outcomeItemToBuilderItem(item, index) {
3850
+ return {
3851
+ id: item.id,
3852
+ name: item.name,
3853
+ order: item.order ?? index,
3854
+ title: item.title,
3855
+ description: item.description,
3856
+ resultKind: item.resultKind,
3857
+ expression: item.expression,
3858
+ displayRules: item.displayRules,
3859
+ isNewlyAdded: item.isNewlyAdded,
3860
+ };
3861
+ }
3862
+ function builderItemToOutcomeItem(item, index) {
3863
+ return {
3864
+ id: String(item.id ?? item['name'] ?? AXPDataGenerator.uuid()),
3865
+ name: String(item['name'] ?? ''),
3866
+ title: item['title'],
3867
+ description: item['description'],
3868
+ resultKind: (item['resultKind'] ?? 'number'),
3869
+ expression: String(item['expression'] ?? ''),
3870
+ displayRules: item['displayRules'],
3871
+ order: item.order ?? index,
3872
+ isNewlyAdded: item['isNewlyAdded'] === true,
3873
+ };
3874
+ }
3875
+ function sectionToBuilderSection(section, index) {
3876
+ const name = section.name.trim() || `section-${index + 1}`;
3877
+ return {
3878
+ id: name,
3879
+ name,
3880
+ order: section.order ?? index,
3881
+ title: section.title,
3882
+ description: section.description,
3883
+ isNewlyAdded: section.isNewlyAdded,
3884
+ items: (section.items ?? []).map((item, itemIndex) => outcomeItemToBuilderItem(item, itemIndex)),
3885
+ };
3886
+ }
3887
+ function builderSectionToOutcomeSection(section, index) {
3888
+ const name = String(section['name'] ?? section.id ?? '').trim() || `section-${index + 1}`;
3889
+ return {
3890
+ name,
3891
+ title: section['title'],
3892
+ description: section['description'],
3893
+ order: section.order ?? index,
3894
+ isNewlyAdded: section['isNewlyAdded'] === true,
3895
+ items: section.items.map((item, itemIndex) => builderItemToOutcomeItem(item, itemIndex)),
3896
+ };
3897
+ }
3898
+ function outcomesValueToBuilderValue(value) {
3899
+ const normalized = normalizeQuestionnaireOutcomesValue(value);
3900
+ return {
3901
+ sections: normalized.sections.map((section, index) => sectionToBuilderSection(section, index)),
3902
+ };
3903
+ }
3904
+ function builderValueToOutcomesValue(value) {
3905
+ return {
3906
+ sections: value.sections.map((section, index) => builderSectionToOutcomeSection(section, index)),
3907
+ };
3908
+ }
3909
+ //#endregion
3910
+
3911
+ //#region ---- Imports ----
3912
+ //#endregion
3913
+ //#region ---- Helpers ----
3914
+ function normalizeOutcomeName(name) {
3915
+ return (name ?? '').trim();
3916
+ }
3917
+ function createDefaultOutcomeSection(order = 0) {
3918
+ return {
3919
+ name: `section-${Date.now()}`,
3920
+ title: 'New Section',
3921
+ order,
3922
+ items: [],
3923
+ isNewlyAdded: true,
3924
+ };
3925
+ }
3926
+ //#endregion
3927
+
3928
+ //#region ---- Imports ----
3929
+ //#endregion
3930
+ //#region ---- Component ----
3931
+ class AXMQuestionnaireOutcomesBuilderComponent {
3932
+ //#endregion
3933
+ //#region ---- Lifecycle ----
3934
+ constructor() {
3935
+ //#region ---- Inputs / Outputs ----
3936
+ this.value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
3937
+ this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
3938
+ this.showHeader = input(true, ...(ngDevMode ? [{ debugName: "showHeader" }] : /* istanbul ignore next */ []));
3939
+ this.valueChange = output();
3940
+ //#endregion
3941
+ //#region ---- Services ----
3942
+ this.propertyViewerService = inject(AXPPropertyViewerService);
3943
+ this.translationService = inject(AXTranslationService);
3944
+ this.toastService = inject(AXToastService);
3945
+ //#endregion
3946
+ //#region ---- View references ----
3947
+ this.standardRef = viewChild(AXPStandardSectionItemsBuilderComponent, ...(ngDevMode ? [{ debugName: "standardRef" }] : /* istanbul ignore next */ []));
3948
+ //#endregion
3949
+ //#region ---- State ----
3950
+ this.builderValue = signal({ sections: [] }, ...(ngDevMode ? [{ debugName: "builderValue" }] : /* istanbul ignore next */ []));
3951
+ //#endregion
3952
+ //#region ---- Computed ----
3953
+ this.standardConfig = computed(() => ({
3954
+ showSectionTechnicalName: true,
3955
+ minSectionCount: 1,
3956
+ texts: {
3957
+ addSection: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-section.title',
3958
+ addItem: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-outcome.title',
3959
+ emptySectionsTitle: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.empty.no-sections.title',
3960
+ emptySectionsDescription: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.empty.no-sections.description',
3961
+ emptyItemsTitle: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.empty.empty-section.title',
3962
+ emptyItemsDescription: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.empty.empty-section.description',
3963
+ },
3964
+ mapItemToView: (item, _section) => this.mapOutcomeItemToView(item),
3965
+ promptAddItems: (sectionId, value) => this.promptAddOutcome(sectionId, value),
3966
+ promptEditItem: (item, sectionId, value) => this.promptEditOutcome(item, sectionId, value),
3967
+ }), ...(ngDevMode ? [{ debugName: "standardConfig" }] : /* istanbul ignore next */ []));
3968
+ effect(() => {
3969
+ const next = normalizeQuestionnaireOutcomesValue(this.value());
3970
+ const mapped = outcomesValueToBuilderValue(next);
3971
+ untracked(() => this.builderValue.set(this.ensureMinOneSection(mapped)));
3972
+ });
3973
+ }
3974
+ //#endregion
3975
+ //#region ---- Public API ----
3976
+ /** Page header command: delegates to standard builder add-section dialog. */
3977
+ async addSection() {
3978
+ await this.standardRef()?.addSection();
3979
+ }
3980
+ //#endregion
3981
+ //#region ---- Builder handlers ----
3982
+ onBuilderValueChange(next) {
3983
+ const normalized = this.ensureMinOneSection(next);
3984
+ this.builderValue.set(normalized);
3985
+ this.valueChange.emit(builderValueToOutcomesValue(normalized));
3986
+ }
3987
+ //#endregion
3988
+ //#region ---- Item mapping ----
3989
+ mapOutcomeItemToView(item) {
3990
+ const outcome = item;
3991
+ const locale = this.translationService.getActiveLang() ?? 'en-US';
3992
+ const title = resolveMultiLanguageString(outcome.title, locale).trim();
3993
+ const description = resolveMultiLanguageString(outcome.description, locale).trim();
3994
+ const badges = [];
3995
+ const kind = this.coerceResultKind(outcome.resultKind);
3996
+ badges.push({
3997
+ text: resolveOutcomeResultKindLabel(kind, locale),
3998
+ variant: 'neutral',
3999
+ });
4000
+ return {
4001
+ icon: 'fa-light fa-calculator',
4002
+ title: title || outcome.name || '—',
4003
+ name: outcome.name,
4004
+ description: description || undefined,
4005
+ badges,
4006
+ };
4007
+ }
4008
+ //#endregion
4009
+ //#region ---- Prompts ----
4010
+ async promptAddOutcome(sectionId, value) {
4011
+ const section = value.sections.find((s) => s.id === sectionId);
4012
+ if (!section) {
4013
+ return null;
4014
+ }
4015
+ const takenNames = this.collectOutcomeNames(value);
4016
+ const defaultName = this.allocateOutcomeName(takenNames);
4017
+ const item = await this.openOutcomeDialog({
4018
+ mode: 'add',
4019
+ context: {
4020
+ name: defaultName,
4021
+ title: '',
4022
+ description: '',
4023
+ resultKind: toOutcomeResultKindSelectValue('number'),
4024
+ expression: '',
4025
+ displayRules: [],
4026
+ },
4027
+ sectionId,
4028
+ value,
4029
+ excludeItemId: undefined,
4030
+ });
4031
+ return item ? [item] : null;
4032
+ }
4033
+ async promptEditOutcome(item, sectionId, value) {
4034
+ const outcome = item;
4035
+ return this.openOutcomeDialog({
4036
+ mode: 'edit',
4037
+ context: {
4038
+ name: outcome.name,
4039
+ title: outcome.title ?? '',
4040
+ description: outcome.description ?? '',
4041
+ resultKind: toOutcomeResultKindSelectValue(this.coerceResultKind(outcome.resultKind)),
4042
+ expression: outcome.expression ?? '',
4043
+ displayRules: this.toDisplayRulesFormContext(outcome.displayRules),
4044
+ },
4045
+ sectionId,
4046
+ value,
4047
+ excludeItemId: item.id,
4048
+ existingItem: item,
4049
+ });
4050
+ }
4051
+ async openOutcomeDialog(options) {
4052
+ const validationFailed = async (titleKey, descKey, fallTitle, fallDesc) => {
4053
+ const title = (await this.translationService.translateAsync(titleKey)) || fallTitle;
4054
+ const content = (await this.translationService.translateAsync(descKey)) || fallDesc;
4055
+ this.toastService.show({ color: 'warning', title, content });
4056
+ throw new Error('QuestionnaireOutcomeEditValidation');
4057
+ };
4058
+ try {
4059
+ const titleKey = options.mode === 'add'
4060
+ ? '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-outcome.title'
4061
+ : '@general:actions.edit.title';
4062
+ const initialResultKind = this.coerceResultKind(options.context.resultKind);
4063
+ const result = await this.propertyViewerService
4064
+ .create()
4065
+ .dialog((d) => {
4066
+ d.setTitle(titleKey)
4067
+ .setSize('lg')
4068
+ .setCloseButton(true)
4069
+ .setMode('advanced')
4070
+ .setTabs(buildQuestionnaireOutcomeEditTabs(initialResultKind))
4071
+ .setContext(options.context)
4072
+ .onAction(async (ref) => {
4073
+ const context = ref.context();
4074
+ const rawName = normalizeOutcomeName(context.name);
4075
+ if (!rawName) {
4076
+ await validationFailed('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.name-required.title', '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.name-required.description', 'Name required', 'Enter a unique outcome name.');
4077
+ }
4078
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(rawName)) {
4079
+ await validationFailed('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.name-invalid.title', '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.name-invalid.description', 'Invalid name', 'Use letters, numbers, underscores, and hyphens.');
4080
+ }
4081
+ const taken = this.collectOutcomeNames(options.value, options.excludeItemId);
4082
+ if (taken.has(rawName)) {
4083
+ await validationFailed('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.duplicate-name', '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.duplicate-name', 'Duplicate name', 'Another outcome already uses this name.');
4084
+ }
4085
+ const title = context.title;
4086
+ if (isEffectivelyEmptyLocalizedValue(title)) {
4087
+ await validationFailed('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.title-required.title', '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.title-required.description', 'Title required', 'Enter a display title for this outcome.');
4088
+ }
4089
+ const expression = String(context.expression ?? '').trim();
4090
+ if (!expression) {
4091
+ await validationFailed('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.expression-required.title', '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.expression-required.description', 'Expression required', 'Enter an expression for this outcome.');
4092
+ }
4093
+ });
4094
+ })
4095
+ .show();
4096
+ if (!result) {
4097
+ return null;
4098
+ }
4099
+ const form = result.values;
4100
+ const name = normalizeOutcomeName(form.name);
4101
+ const title = this.normalizeLocalizedField(form.title);
4102
+ const description = this.normalizeLocalizedField(form.description);
4103
+ const resultKind = this.coerceResultKind(form.resultKind);
4104
+ const expression = String(form.expression ?? '').trim();
4105
+ const displayRules = this.normalizeDisplayRulesFromForm(form.displayRules, resultKind);
4106
+ const id = options.mode === 'edit' && options.existingItem
4107
+ ? String(options.existingItem.id)
4108
+ : AXPDataGenerator.uuid();
4109
+ return {
4110
+ ...(options.existingItem ?? {}),
4111
+ id,
4112
+ name,
4113
+ title,
4114
+ description,
4115
+ resultKind,
4116
+ expression,
4117
+ displayRules,
4118
+ order: options.existingItem?.order ?? 0,
4119
+ isNewlyAdded: options.mode === 'add',
4120
+ };
4121
+ }
4122
+ catch (error) {
4123
+ if (error instanceof Error && error.message === 'QuestionnaireOutcomeEditValidation') {
4124
+ return null;
4125
+ }
4126
+ console.error('Error editing outcome:', error);
4127
+ return null;
4128
+ }
4129
+ }
4130
+ //#endregion
4131
+ //#region ---- Utility ----
4132
+ ensureMinOneSection(value) {
4133
+ if (value.sections.length > 0) {
4134
+ return value;
4135
+ }
4136
+ const section = createDefaultOutcomeSection(0);
4137
+ return outcomesValueToBuilderValue({ sections: [section] });
4138
+ }
4139
+ collectOutcomeNames(value, excludeItemId) {
4140
+ const names = new Set();
4141
+ for (const section of value.sections) {
4142
+ for (const item of section.items) {
4143
+ if (excludeItemId != null && item.id === excludeItemId) {
4144
+ continue;
4145
+ }
4146
+ const name = normalizeOutcomeName(String(item['name'] ?? ''));
4147
+ if (name) {
4148
+ names.add(name);
4149
+ }
4150
+ }
4151
+ }
4152
+ return names;
4153
+ }
4154
+ allocateOutcomeName(taken) {
4155
+ let index = taken.size + 1;
4156
+ let name = `outcome_${index}`;
4157
+ while (taken.has(name)) {
4158
+ index += 1;
4159
+ name = `outcome_${index}`;
4160
+ }
4161
+ return name;
4162
+ }
4163
+ coerceResultKind(value) {
4164
+ if (value === 'number' || value === 'string' || value === 'boolean') {
4165
+ return value;
4166
+ }
4167
+ if (value != null && typeof value === 'object' && 'id' in value) {
4168
+ const id = value.id;
4169
+ if (id === 'number' || id === 'string' || id === 'boolean') {
4170
+ return id;
4171
+ }
4172
+ }
4173
+ return 'number';
4174
+ }
4175
+ coerceDisplayOperator(value, resultKind) {
4176
+ let operator;
4177
+ if (value === 'eq' || value === 'neq' || value === 'gt' || value === 'gte' || value === 'lt' || value === 'lte') {
4178
+ operator = value;
4179
+ }
4180
+ else if (value != null && typeof value === 'object' && 'id' in value) {
4181
+ const id = value.id;
4182
+ if (id === 'eq' || id === 'neq' || id === 'gt' || id === 'gte' || id === 'lt' || id === 'lte') {
4183
+ operator = id;
4184
+ }
4185
+ }
4186
+ if (resultKind === 'boolean') {
4187
+ return operator === 'eq' || operator === 'neq' ? operator : 'eq';
4188
+ }
4189
+ if (resultKind === 'string') {
4190
+ return operator === 'eq' || operator === 'neq' ? operator : 'eq';
4191
+ }
4192
+ return operator ?? 'gte';
4193
+ }
4194
+ coerceDisplayCompareValue(value, resultKind) {
4195
+ if (resultKind === 'boolean') {
4196
+ if (typeof value === 'boolean') {
4197
+ return value;
4198
+ }
4199
+ if (value === 'true') {
4200
+ return true;
4201
+ }
4202
+ if (value === 'false') {
4203
+ return false;
4204
+ }
4205
+ return undefined;
4206
+ }
4207
+ if (resultKind === 'number') {
4208
+ if (typeof value === 'number' && Number.isFinite(value)) {
4209
+ return value;
4210
+ }
4211
+ if (typeof value === 'string') {
4212
+ const trimmed = value.trim();
4213
+ if (!trimmed) {
4214
+ return undefined;
4215
+ }
4216
+ const asNumber = Number(trimmed);
4217
+ return Number.isFinite(asNumber) ? asNumber : undefined;
4218
+ }
4219
+ return undefined;
4220
+ }
4221
+ if (typeof value === 'string') {
4222
+ const trimmed = value.trim();
4223
+ return trimmed.length > 0 ? trimmed : undefined;
4224
+ }
4225
+ if (typeof value === 'number' && Number.isFinite(value)) {
4226
+ return String(value);
4227
+ }
4228
+ if (typeof value === 'boolean') {
4229
+ return value ? 'true' : 'false';
4230
+ }
4231
+ return undefined;
4232
+ }
4233
+ normalizeDisplayColorValue(value) {
4234
+ if (typeof value !== 'string') {
4235
+ return undefined;
4236
+ }
4237
+ const trimmed = value.trim();
4238
+ const parts = trimmed.split(/\s+/);
4239
+ if (parts.length !== 3) {
4240
+ return undefined;
4241
+ }
4242
+ return trimmed;
4243
+ }
4244
+ coerceDisplayEmphasis(value) {
4245
+ if (value === 'normal' || value === 'medium' || value === 'bold') {
4246
+ return value;
4247
+ }
4248
+ if (value != null && typeof value === 'object' && 'id' in value) {
4249
+ const id = value.id;
4250
+ if (id === 'normal' || id === 'medium' || id === 'bold') {
4251
+ return id;
4252
+ }
4253
+ }
4254
+ return 'normal';
4255
+ }
4256
+ normalizeDisplayIconValue(value) {
4257
+ if (value == null || value === '') {
4258
+ return undefined;
4259
+ }
4260
+ if (typeof value === 'string') {
4261
+ const trimmed = value.trim();
4262
+ return trimmed.length > 0 ? trimmed : undefined;
4263
+ }
4264
+ if (typeof value === 'object') {
4265
+ const styleClass = String(get(value, 'styleClass') ?? '').trim();
4266
+ const iconClass = String(get(value, 'iconClass') ?? '').trim();
4267
+ const combined = [styleClass, iconClass].filter(Boolean).join(' ');
4268
+ return combined.length > 0 ? combined : undefined;
4269
+ }
4270
+ return undefined;
4271
+ }
4272
+ toDisplayRulesFormContext(rules) {
4273
+ return normalizeQuestionnaireOutcomeDisplayRules(rules).map((rule) => ({
4274
+ id: rule.id,
4275
+ operator: toOutcomeDisplayOperatorSelectValue(rule.operator),
4276
+ compareValue: rule.compareValue,
4277
+ color: rule.color,
4278
+ emphasis: toOutcomeDisplayEmphasisSelectValue(rule.emphasis ?? 'normal'),
4279
+ icon: rule.icon,
4280
+ }));
4281
+ }
4282
+ normalizeDisplayRulesFromForm(raw, resultKind) {
4283
+ if (!Array.isArray(raw)) {
4284
+ return [];
4285
+ }
4286
+ const rows = [];
4287
+ for (const [index, row] of raw.entries()) {
4288
+ const compareValue = this.coerceDisplayCompareValue(get(row, 'compareValue'), resultKind);
4289
+ const color = this.normalizeDisplayColorValue(get(row, 'color'));
4290
+ if (compareValue === undefined || !color) {
4291
+ continue;
4292
+ }
4293
+ rows.push({
4294
+ id: String(get(row, 'id') ?? AXPDataGenerator.uuid()),
4295
+ order: index,
4296
+ operator: this.coerceDisplayOperator(get(row, 'operator'), resultKind),
4297
+ compareValue,
4298
+ color,
4299
+ emphasis: this.coerceDisplayEmphasis(get(row, 'emphasis')),
4300
+ icon: this.normalizeDisplayIconValue(get(row, 'icon')),
4301
+ });
4302
+ }
4303
+ return normalizeQuestionnaireOutcomeDisplayRules(rows);
4304
+ }
4305
+ normalizeLocalizedField(value) {
4306
+ if (value == null || value === '') {
4307
+ return undefined;
4308
+ }
4309
+ if (typeof value === 'string') {
4310
+ const trimmed = value.trim();
4311
+ return trimmed ? createMultiLanguageString(trimmed, trimmed) : undefined;
4312
+ }
4313
+ if (typeof value === 'object') {
4314
+ return value;
4315
+ }
4316
+ return undefined;
4317
+ }
4318
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4319
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXMQuestionnaireOutcomesBuilderComponent, isStandalone: true, selector: "axm-questionnaire-outcomes-builder", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, viewQueries: [{ propertyName: "standardRef", first: true, predicate: AXPStandardSectionItemsBuilderComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"axm-questionnaire-outcomes-builder\">\n @if (showHeader()) {\n <div class=\"__header\">\n <div class=\"__header-left\"></div>\n <div class=\"__header-right\">\n @if (!readonly()) {\n <ax-button\n class=\"ax-sm\"\n [text]=\"\n ('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-section.title'\n | translate\n | async) || 'Add Section'\n \"\n [color]=\"'primary'\"\n (onClick)=\"addSection()\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-layer-group\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </div>\n </div>\n }\n\n <axp-standard-section-items-builder\n #standardRef\n class=\"ax-block ax-min-h-[200px]\"\n [value]=\"builderValue()\"\n [config]=\"standardConfig()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"onBuilderValueChange($event)\" />\n</div>\n", styles: [".axm-questionnaire-outcomes-builder{display:flex;height:100%;width:100%;flex-direction:column}.axm-questionnaire-outcomes-builder .__header{display:flex;align-items:center;justify-content:space-between;gap:.75rem;border-bottom-width:1px;padding:.75rem}.axm-questionnaire-outcomes-builder .__header .__header-left,.axm-questionnaire-outcomes-builder .__header .__header-right{display:flex;gap:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStandardSectionItemsBuilderComponent, selector: "axp-standard-section-items-builder", inputs: ["value", "config", "readonly"], outputs: ["valueChange"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4320
+ }
4321
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesBuilderComponent, decorators: [{
4322
+ type: Component,
4323
+ args: [{ selector: 'axm-questionnaire-outcomes-builder', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
4324
+ CommonModule,
4325
+ AXButtonModule,
4326
+ AXDecoratorModule,
4327
+ AXTranslationModule,
4328
+ AXPStandardSectionItemsBuilderComponent,
4329
+ ], template: "<div class=\"axm-questionnaire-outcomes-builder\">\n @if (showHeader()) {\n <div class=\"__header\">\n <div class=\"__header-left\"></div>\n <div class=\"__header-right\">\n @if (!readonly()) {\n <ax-button\n class=\"ax-sm\"\n [text]=\"\n ('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-section.title'\n | translate\n | async) || 'Add Section'\n \"\n [color]=\"'primary'\"\n (onClick)=\"addSection()\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-layer-group\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </div>\n </div>\n }\n\n <axp-standard-section-items-builder\n #standardRef\n class=\"ax-block ax-min-h-[200px]\"\n [value]=\"builderValue()\"\n [config]=\"standardConfig()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"onBuilderValueChange($event)\" />\n</div>\n", styles: [".axm-questionnaire-outcomes-builder{display:flex;height:100%;width:100%;flex-direction:column}.axm-questionnaire-outcomes-builder .__header{display:flex;align-items:center;justify-content:space-between;gap:.75rem;border-bottom-width:1px;padding:.75rem}.axm-questionnaire-outcomes-builder .__header .__header-left,.axm-questionnaire-outcomes-builder .__header .__header-right{display:flex;gap:.5rem}\n"] }]
4330
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], standardRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPStandardSectionItemsBuilderComponent), { isSignal: true }] }] } });
4331
+
4332
+ /** Component key for the questionnaire Outcomes page tab. Register via AXP_PAGE_COMPONENT_PROVIDER. */
4333
+ const QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY = 'questionnaire-outcomes-page';
4334
+
4335
+ const QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND = 'Questionnaire:Outcomes:Save';
4336
+ /**
4337
+ * Command to save the questionnaire outcomes field.
4338
+ * Patches entity with { outcomes }.
4339
+ */
4340
+ class AXMSaveQuestionnaireOutcomesCommand {
4341
+ constructor() {
4342
+ this.entityService = inject(AXPEntityService);
4343
+ this.translationService = inject(AXTranslationService);
4344
+ }
4345
+ async execute(input) {
4346
+ const { id, outcomes } = input;
4347
+ if (!id) {
4348
+ return {
4349
+ success: false,
4350
+ message: {
4351
+ text: await this.translationService.translateAsync('@general:messages.entity.invalid-data'),
4352
+ },
4353
+ };
4354
+ }
4355
+ try {
4356
+ const entityRef = `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`;
4357
+ const dataAccessor = this.entityService.withEntity(entityRef).data();
4358
+ await dataAccessor.update(id, { outcomes });
4359
+ return {
4360
+ success: true,
4361
+ data: { id, outcomes },
4362
+ message: {
4363
+ text: await this.translationService.translateAsync('@general:messages.generic.success.description'),
4364
+ },
4365
+ };
4366
+ }
4367
+ catch (error) {
4368
+ const message = error instanceof Error
4369
+ ? error.message
4370
+ : await this.translationService.translateAsync('@general:messages.generic.error.description');
4371
+ return {
4372
+ success: false,
4373
+ message: { text: message },
4374
+ };
4375
+ }
4376
+ }
4377
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireOutcomesCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4378
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireOutcomesCommand, providedIn: 'root' }); }
4379
+ }
4380
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireOutcomesCommand, decorators: [{
4381
+ type: Injectable,
4382
+ args: [{
4383
+ providedIn: 'root',
4384
+ }]
4385
+ }] });
4386
+
4387
+ var saveQuestionnaireOutcomes_command = /*#__PURE__*/Object.freeze({
4388
+ __proto__: null,
4389
+ AXMSaveQuestionnaireOutcomesCommand: AXMSaveQuestionnaireOutcomesCommand,
4390
+ QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND: QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND
4391
+ });
4392
+
4393
+ const QUESTIONNAIRE_OUTCOMES_DISCARD_COMMAND = 'Questionnaire:Outcomes:Discard';
4394
+ const QUESTIONNAIRE_OUTCOMES_ADD_SECTION_COMMAND = 'Questionnaire:Outcomes:AddSection';
4395
+ //#region ---- Questionnaire Outcomes Page Component ----
4396
+ class AXMQuestionnaireOutcomesPageComponent extends AXPPageLayoutBaseComponent {
4397
+ //#endregion
4398
+ //#region ---- Lifecycle ----
4399
+ constructor() {
4400
+ super();
4401
+ //#region ---- Inputs ----
4402
+ this.rootContext = input(...(ngDevMode ? [undefined, { debugName: "rootContext" }] : /* istanbul ignore next */ []));
4403
+ this.pageConfig = input(...(ngDevMode ? [undefined, { debugName: "pageConfig" }] : /* istanbul ignore next */ []));
4404
+ //#endregion
4405
+ //#region ---- Services ----
4406
+ this.commandService = inject(AXPCommandService);
4407
+ this.eventService = inject(AXPBroadcastEventService);
4408
+ this.hostPageLayout = inject(AXPPageLayoutBase, { optional: true, skipSelf: true });
4409
+ //#endregion
4410
+ //#region ---- View References ----
4411
+ this.builderRef = viewChild('builderRef', ...(ngDevMode ? [{ debugName: "builderRef" }] : /* istanbul ignore next */ []));
4412
+ //#endregion
4413
+ //#region ---- State ----
4414
+ this.localContext = signal(null, ...(ngDevMode ? [{ debugName: "localContext" }] : /* istanbul ignore next */ []));
4415
+ this.ignoreNextValueChange = signal(false, ...(ngDevMode ? [{ debugName: "ignoreNextValueChange" }] : /* istanbul ignore next */ []));
4416
+ this.baselineOutcomes = signal(null, ...(ngDevMode ? [{ debugName: "baselineOutcomes" }] : /* istanbul ignore next */ []));
4417
+ this.outcomesBaselinePendingReconcile = signal(false, ...(ngDevMode ? [{ debugName: "outcomesBaselinePendingReconcile" }] : /* istanbul ignore next */ []));
4418
+ this.isDirty = signal(false, ...(ngDevMode ? [{ debugName: "isDirty" }] : /* istanbul ignore next */ []));
4419
+ //#endregion
4420
+ //#region ---- Computed ----
4421
+ this.context = computed(() => {
4422
+ const local = this.localContext();
4423
+ if (local != null)
4424
+ return local;
4425
+ return this.rootContext() ?? {};
4426
+ }, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
4427
+ this.outcomesValue = computed(() => {
4428
+ const ctx = this.context();
4429
+ return normalizeQuestionnaireOutcomesValue(ctx['outcomes']);
4430
+ }, ...(ngDevMode ? [{ debugName: "outcomesValue" }] : /* istanbul ignore next */ []));
4431
+ let previousRootId = undefined;
4432
+ effect(() => {
4433
+ const ctx = this.rootContext();
4434
+ const currentId = ctx != null && typeof ctx === 'object' ? ctx['id'] : undefined;
4435
+ if (currentId !== previousRootId) {
4436
+ previousRootId = currentId;
4437
+ this.localContext.set(null);
4438
+ const rootOutcomes = normalizeQuestionnaireOutcomesValue(ctx?.['outcomes']);
4439
+ this.baselineOutcomes.set(this.cloneOutcomes(rootOutcomes));
4440
+ this.isDirty.set(false);
4441
+ this.outcomesBaselinePendingReconcile.set(true);
4442
+ }
4443
+ });
4444
+ }
4445
+ //#endregion
4446
+ //#region ---- Page Actions ----
4447
+ async getPrimaryMenuItems() {
4448
+ const addSectionTitle = (await this.translateService.translateAsync('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-section.title')) || 'Add Section';
4449
+ const discardTitle = (await this.translateService.translateAsync('@general:actions.discard.title')) || 'Discard';
4450
+ const saveTitle = (await this.translateService.translateAsync('@general:actions.save.title')) || 'Save';
4451
+ const addSectionAction = {
4452
+ title: addSectionTitle,
4453
+ icon: 'fa-light fa-layer-group',
4454
+ zone: 'header',
4455
+ priority: 'primary',
4456
+ color: 'primary',
4457
+ command: {
4458
+ name: QUESTIONNAIRE_OUTCOMES_ADD_SECTION_COMMAND,
4459
+ options: {},
4460
+ },
4461
+ };
4462
+ const discardAction = {
4463
+ title: discardTitle,
4464
+ icon: 'fa-light fa-rotate-left',
4465
+ zone: 'footer',
4466
+ priority: 'secondary',
4467
+ color: 'default',
4468
+ visible: this.isDirty(),
4469
+ command: {
4470
+ name: QUESTIONNAIRE_OUTCOMES_DISCARD_COMMAND,
4471
+ options: {},
4472
+ },
4473
+ };
4474
+ const saveAction = {
4475
+ title: saveTitle,
4476
+ icon: 'fa-light fa-floppy-disk',
4477
+ zone: 'footer',
4478
+ priority: 'primary',
4479
+ color: 'primary',
4480
+ visible: this.isDirty(),
4481
+ command: {
4482
+ name: QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND,
4483
+ options: {},
4484
+ },
4485
+ };
4486
+ return [addSectionAction, discardAction, saveAction];
4487
+ }
4488
+ //#endregion
4489
+ //#region ---- Command Handling ----
4490
+ execute(command) {
4491
+ if (command.name === QUESTIONNAIRE_OUTCOMES_DISCARD_COMMAND) {
4492
+ this.ignoreNextValueChange.set(true);
4493
+ this.localContext.set(null);
4494
+ this.isDirty.set(false);
4495
+ this.outcomesBaselinePendingReconcile.set(true);
4496
+ this.recompute();
4497
+ return;
4498
+ }
4499
+ if (command.name === QUESTIONNAIRE_OUTCOMES_ADD_SECTION_COMMAND) {
4500
+ void this.builderRef()?.addSection();
4501
+ return;
4502
+ }
4503
+ if (command.name !== QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND) {
4504
+ return;
4505
+ }
4506
+ return this.handleSaveExecute();
4507
+ }
4508
+ async handleSaveExecute() {
4509
+ const ctx = this.localContext() ?? this.rootContext() ?? {};
4510
+ const id = typeof ctx['id'] === 'string' ? ctx['id'] : undefined;
4511
+ const outcomes = normalizeQuestionnaireOutcomesValue(ctx['outcomes']);
4512
+ if (!id) {
4513
+ return {
4514
+ success: false,
4515
+ message: {
4516
+ text: await this.translateService.translateAsync('@general:messages.entity.invalid-data'),
4517
+ },
4518
+ };
4519
+ }
4520
+ if (findDuplicateOutcomeNames(outcomes).length > 0) {
4521
+ return {
4522
+ success: false,
4523
+ message: {
4524
+ text: await this.translateService.translateAsync('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.duplicate-name'),
4525
+ },
4526
+ };
4527
+ }
4528
+ const input = { id, outcomes };
4529
+ const result = await this.commandService.execute(QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND, input);
4530
+ const execResult = (result ?? { success: false });
4531
+ if (execResult.success) {
4532
+ this.baselineOutcomes.set(this.cloneOutcomes(outcomes));
4533
+ this.localContext.set(null);
4534
+ this.isDirty.set(false);
4535
+ this.outcomesBaselinePendingReconcile.set(true);
4536
+ this.recompute();
4537
+ this.eventService.publish(AXPEntityEventsKeys.REFRESH_LAYOUT, {
4538
+ name: `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`,
4539
+ });
4540
+ }
4541
+ return execResult;
4542
+ }
4543
+ //#endregion
4544
+ //#region ---- UI Handlers ----
4545
+ onOutcomesChange(value) {
4546
+ if (this.ignoreNextValueChange()) {
4547
+ this.ignoreNextValueChange.set(false);
4548
+ return;
4549
+ }
4550
+ const current = this.outcomesValue();
4551
+ if (this.areOutcomesEqual(current, value)) {
4552
+ if (this.outcomesBaselinePendingReconcile()) {
4553
+ this.baselineOutcomes.set(this.cloneOutcomes(current));
4554
+ this.outcomesBaselinePendingReconcile.set(false);
4555
+ this.isDirty.set(false);
4556
+ }
4557
+ return;
4558
+ }
4559
+ const base = this.localContext() ?? this.rootContext() ?? {};
4560
+ if (this.outcomesBaselinePendingReconcile() && this.localContext() == null) {
4561
+ this.baselineOutcomes.set(this.cloneOutcomes(value));
4562
+ this.isDirty.set(false);
4563
+ this.outcomesBaselinePendingReconcile.set(false);
4564
+ this.localContext.set({ ...base, outcomes: value });
4565
+ this.recompute();
4566
+ return;
4567
+ }
4568
+ this.localContext.set({ ...base, outcomes: value });
4569
+ const baseline = this.baselineOutcomes();
4570
+ this.isDirty.set(!this.areOutcomesEqual(baseline, value));
4571
+ this.recompute();
4572
+ }
4573
+ //#endregion
4574
+ //#region ---- Utility ----
4575
+ areOutcomesEqual(a, b) {
4576
+ return isEqual(normalizeQuestionnaireOutcomesValue(a), normalizeQuestionnaireOutcomesValue(b));
4577
+ }
4578
+ cloneOutcomes(value) {
4579
+ if (isNil(value)) {
4580
+ return { sections: [] };
4581
+ }
4582
+ return cloneDeep(normalizeQuestionnaireOutcomesValue(value));
4583
+ }
4584
+ recompute() {
4585
+ super.recompute();
4586
+ const maybeLayout = this.hostPageLayout;
4587
+ maybeLayout.recompute?.();
4588
+ }
4589
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4590
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: AXMQuestionnaireOutcomesPageComponent, isStandalone: true, selector: "axm-questionnaire-outcomes-page", inputs: { rootContext: { classPropertyName: "rootContext", publicName: "rootContext", isSignal: true, isRequired: false, transformFunction: null }, pageConfig: { classPropertyName: "pageConfig", publicName: "pageConfig", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
4591
+ {
4592
+ provide: AXPPageLayoutBase,
4593
+ useExisting: AXMQuestionnaireOutcomesPageComponent,
4594
+ },
4595
+ ], viewQueries: [{ propertyName: "builderRef", first: true, predicate: ["builderRef"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
4596
+ <axm-questionnaire-outcomes-builder
4597
+ #builderRef
4598
+ [value]="outcomesValue()"
4599
+ [showHeader]="false"
4600
+ (valueChange)="onOutcomesChange($event)">
4601
+ </axm-questionnaire-outcomes-builder>
4602
+ `, isInline: true, dependencies: [{ kind: "component", type: AXMQuestionnaireOutcomesBuilderComponent, selector: "axm-questionnaire-outcomes-builder", inputs: ["value", "readonly", "showHeader"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4603
+ }
4604
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponent, decorators: [{
4605
+ type: Component,
1536
4606
  args: [{
1537
- providedIn: 'root',
4607
+ selector: 'axm-questionnaire-outcomes-page',
4608
+ standalone: true,
4609
+ imports: [AXMQuestionnaireOutcomesBuilderComponent],
4610
+ template: `
4611
+ <axm-questionnaire-outcomes-builder
4612
+ #builderRef
4613
+ [value]="outcomesValue()"
4614
+ [showHeader]="false"
4615
+ (valueChange)="onOutcomesChange($event)">
4616
+ </axm-questionnaire-outcomes-builder>
4617
+ `,
4618
+ changeDetection: ChangeDetectionStrategy.OnPush,
4619
+ providers: [
4620
+ {
4621
+ provide: AXPPageLayoutBase,
4622
+ useExisting: AXMQuestionnaireOutcomesPageComponent,
4623
+ },
4624
+ ],
1538
4625
  }]
4626
+ }], ctorParameters: () => [], propDecorators: { rootContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootContext", required: false }] }], pageConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageConfig", required: false }] }], builderRef: [{ type: i0.ViewChild, args: ['builderRef', { isSignal: true }] }] } });
4627
+
4628
+ var questionnaireOutcomesPage_component = /*#__PURE__*/Object.freeze({
4629
+ __proto__: null,
4630
+ AXMQuestionnaireOutcomesPageComponent: AXMQuestionnaireOutcomesPageComponent
4631
+ });
4632
+
4633
+ //#region ---- Questionnaire Outcomes Page Component Provider ----
4634
+ class AXMQuestionnaireOutcomesPageComponentProvider {
4635
+ async components() {
4636
+ return [
4637
+ {
4638
+ key: QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY,
4639
+ loader: () => Promise.resolve().then(function () { return questionnaireOutcomesPage_component; }).then((m) => m.AXMQuestionnaireOutcomesPageComponent),
4640
+ },
4641
+ ];
4642
+ }
4643
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponentProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4644
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponentProvider }); }
4645
+ }
4646
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponentProvider, decorators: [{
4647
+ type: Injectable
1539
4648
  }] });
1540
4649
 
4650
+ var index = /*#__PURE__*/Object.freeze({
4651
+ __proto__: null,
4652
+ AXMQuestionnaireOutcomesPageComponent: AXMQuestionnaireOutcomesPageComponent,
4653
+ AXMQuestionnaireOutcomesPageComponentProvider: AXMQuestionnaireOutcomesPageComponentProvider,
4654
+ QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY: QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY
4655
+ });
4656
+
4657
+ //#region ---- Outcomes builder exports ----
4658
+ //#endregion
4659
+
4660
+ //#region ---- Questionnaire builder feature exports ----
4661
+ //#endregion
4662
+
1541
4663
  //#region ---- Imports ----
1542
4664
  //#endregion
1543
4665
  /** Guards concurrent `refreshVisibility` runs; incremented on each refresh. */
@@ -1558,7 +4680,7 @@ function persistedItemName(item, index) {
1558
4680
  const id = typeof legacy.id === 'string' ? legacy.id.trim() : '';
1559
4681
  return id || `item-${index}`;
1560
4682
  }
1561
- function normalizeValidationStrategy$1(value) {
4683
+ function normalizeValidationStrategy(value) {
1562
4684
  if (value === 'step' || value === 'end') {
1563
4685
  return value;
1564
4686
  }
@@ -1566,7 +4688,7 @@ function normalizeValidationStrategy$1(value) {
1566
4688
  typeof value === 'object' &&
1567
4689
  'id' in value &&
1568
4690
  typeof value.id === 'string') {
1569
- return normalizeValidationStrategy$1(value.id);
4691
+ return normalizeValidationStrategy(value.id);
1570
4692
  }
1571
4693
  return 'step';
1572
4694
  }
@@ -1963,8 +5085,8 @@ const AXMQuestionnaireViewerViewModel = signalStore(withState(() => ({
1963
5085
  const displaySettings = structure?.displaySettings;
1964
5086
  const displaySettingsValue = displaySettings?.validationStrategy;
1965
5087
  return configValue !== undefined
1966
- ? normalizeValidationStrategy$1(configValue)
1967
- : normalizeValidationStrategy$1(displaySettingsValue);
5088
+ ? normalizeValidationStrategy(configValue)
5089
+ : normalizeValidationStrategy(displaySettingsValue);
1968
5090
  }),
1969
5091
  //#endregion
1970
5092
  //#region ---- Navigation ----
@@ -3483,7 +6605,7 @@ class AXMQuestionnaireViewerQuestionComponent {
3483
6605
  </div>
3484
6606
  }
3485
6607
  </div>
3486
- `, isInline: true, styles: ["axm-questionnaire-viewer-question{display:block}axm-questionnaire-viewer-question .__question-item{display:flex;flex-direction:column;gap:.25rem;padding:1rem;text-align:start;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}axm-questionnaire-viewer-question .__question-item.__answered{border-radius:.5rem;background:rgba(var(--ax-sys-color-primary-surface),.14)}axm-questionnaire-viewer-question .__question-item.__answered:not(.__view-mode){border-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1))}axm-questionnaire-viewer-question .__question-item .__question-header{display:flex;width:100%;min-width:0px;align-items:flex-start;justify-content:space-between;gap:1rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-left{display:flex;flex-shrink:0;align-items:center;gap:.75rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-left .__question-number{font-size:.875rem;line-height:1.25rem;font-weight:600;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-primary-600),var(--tw-text-opacity, 1))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-left .__question-counter{font-size:.75rem;line-height:1rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right{display:flex;min-width:0px;flex:1 1 0%;flex-wrap:wrap;align-items:flex-start;gap:.5rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block{min-width:0px;flex:1 1 0%;text-align:start}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title{margin:0;min-width:0px;flex:1 1 0%;text-align:start;font-size:1rem;line-height:1.5rem;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block h3.__question-title{font-weight:500}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich{width:100%;max-width:100%;text-align:start;font-size:1rem;line-height:1.5rem;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich p{margin:0}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich p:first-child{margin-top:0}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich ul{list-style-type:disc;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich ol{list-style-type:decimal;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich li{margin-block:.125rem;padding-inline-start:.25rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich ul ul{list-style-type:circle;margin-block:.25rem 0}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title.__question-title--rich{font-weight:400}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__answered-badge{border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500;background-color:rgb(var(--ax-sys-color-success-lighter-surface));color:rgb(var(--ax-sys-color-on-success-lighter-surface))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__required-badge{border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500;background-color:rgb(var(--ax-sys-color-warning-lighter-surface));color:rgb(var(--ax-sys-color-on-warning-lighter-surface))}axm-questionnaire-viewer-question .__question-item .__question-note{font-size:.875rem;line-height:1.25rem;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__question-note p{margin:0}axm-questionnaire-viewer-question .__question-item .__question-note p:first-child{margin-top:0}axm-questionnaire-viewer-question .__question-item .__question-note ul{list-style-type:disc;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-question .__question-item .__question-note ol{list-style-type:decimal;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-question .__question-item .__question-note li{margin-block:.125rem}axm-questionnaire-viewer-question .__question-item .__question-description{margin-top:.25rem;font-size:.875rem;line-height:1.25rem;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__question-widget{text-align:start}axm-questionnaire-viewer-question .__question-item .__question-widget>*{margin-bottom:0}axm-questionnaire-viewer-question .__question-item .__widget-placeholder{border-radius:.25rem;border-width:1px;border-style:dashed;padding:1rem;font-size:.875rem;line-height:1.25rem;font-style:italic;color:rgb(var(--ax-sys-color-on-surface-variant));--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}axm-questionnaire-viewer-question .__question-item .__question-comment{display:flex;width:100%;flex-direction:column;gap:.25rem}axm-questionnaire-viewer-question .__question-item .__question-comment ax-text-area{margin-top:.5rem}axm-questionnaire-viewer-question .__question-item .__question-comment--readonly{margin-top:.5rem}axm-questionnaire-viewer-question .__question-item .__question-comment-text{margin:0;width:100%;font-size:.875rem;line-height:1.25rem;white-space:pre-wrap;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-question .__question-item .__question-comment-label{font-size:.875rem;line-height:1.25rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__question-comment-required{color:rgb(var(--ax-sys-color-danger-600))}axm-questionnaire-viewer-question .__question-item .__attachments{border-top-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));padding-top:.75rem}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-label{margin-inline-end:.5rem;font-size:.875rem;line-height:1.25rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list{margin:0;margin-top:.25rem;list-style-type:none;padding-left:0}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list li{margin-top:.25rem}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list li .__attachment-link{display:inline-flex;align-items:center;gap:.5rem;font-size:.875rem;line-height:1.25rem;text-decoration-line:underline;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;color:rgb(var(--ax-sys-color-primary-dark-surface))}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list li .__attachment-link:hover{opacity:.9}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list li .__attachment-link i{flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "directive", type: i1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXAlertModule }, { kind: "component", type: i2.AXAlertComponent, selector: "ax-alert", inputs: ["color", "timeOut"], outputs: ["colorChange", "onClosed", "timeOutChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTextAreaModule }, { kind: "component", type: i4.AXTextAreaComponent, selector: "ax-text-area", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "placeholder", "maxLength", "look", "rows", "allowResize", "showCounter", "class"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AXSafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
6608
+ `, isInline: true, styles: ["axm-questionnaire-viewer-question{display:block}axm-questionnaire-viewer-question .__question-item{display:flex;flex-direction:column;gap:.25rem;padding:1rem;text-align:start;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}axm-questionnaire-viewer-question .__question-item.__answered{border-radius:.5rem;background:rgba(var(--ax-sys-color-primary-surface),.14)}axm-questionnaire-viewer-question .__question-item.__answered:not(.__view-mode){border-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1))}axm-questionnaire-viewer-question .__question-item .__question-header{display:flex;width:100%;min-width:0px;align-items:flex-start;justify-content:space-between;gap:1rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-left{display:flex;flex-shrink:0;align-items:center;gap:.75rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-left .__question-number{font-size:.875rem;line-height:1.25rem;font-weight:600;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-primary-600),var(--tw-text-opacity, 1))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-left .__question-counter{font-size:.75rem;line-height:1rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right{display:flex;min-width:0px;flex:1 1 0%;flex-wrap:wrap;align-items:flex-start;gap:.5rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block{min-width:0px;flex:1 1 0%;text-align:start}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title{margin:0;min-width:0px;flex:1 1 0%;text-align:start;font-size:1rem;line-height:1.5rem;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block h3.__question-title{font-weight:500}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich{width:100%;max-width:100%;text-align:start;font-size:1rem;line-height:1.5rem;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich p{margin:0}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich p:first-child{margin-top:0}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich ul{list-style-type:disc;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich ol{list-style-type:decimal;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich li{margin-block:.125rem;padding-inline-start:.25rem}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title--rich ul ul{list-style-type:circle;margin-block:.25rem 0}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__question-title-block .__question-title.__question-title--rich{font-weight:400}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__answered-badge{border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500;background-color:rgb(var(--ax-sys-color-success-lighter-surface));color:rgb(var(--ax-sys-color-on-success-lighter-surface))}axm-questionnaire-viewer-question .__question-item .__question-header .__question-header-right .__required-badge{border-radius:.25rem;padding:.25rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500;background-color:rgb(var(--ax-sys-color-warning-lighter-surface));color:rgb(var(--ax-sys-color-on-warning-lighter-surface))}axm-questionnaire-viewer-question .__question-item .__question-note{font-size:.875rem;line-height:1.25rem;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__question-note p{margin:0}axm-questionnaire-viewer-question .__question-item .__question-note p:first-child{margin-top:0}axm-questionnaire-viewer-question .__question-item .__question-note ul{list-style-type:disc;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-question .__question-item .__question-note ol{list-style-type:decimal;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-question .__question-item .__question-note li{margin-block:.125rem}axm-questionnaire-viewer-question .__question-item .__question-description{margin-top:.25rem;font-size:.875rem;line-height:1.25rem;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__question-widget{text-align:start}axm-questionnaire-viewer-question .__question-item .__question-widget>*{margin-bottom:0}axm-questionnaire-viewer-question .__question-item .__widget-placeholder{border-radius:.25rem;border-width:1px;border-style:dashed;padding:1rem;font-size:.875rem;line-height:1.25rem;font-style:italic;color:rgb(var(--ax-sys-color-on-surface-variant));--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}axm-questionnaire-viewer-question .__question-item .__question-comment{display:flex;width:100%;flex-direction:column;gap:.25rem}axm-questionnaire-viewer-question .__question-item .__question-comment ax-text-area{margin-top:.5rem}axm-questionnaire-viewer-question .__question-item .__question-comment--readonly{margin-top:.5rem}axm-questionnaire-viewer-question .__question-item .__question-comment-text{margin:0;width:100%;font-size:.875rem;line-height:1.25rem;white-space:pre-wrap;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-question .__question-item .__question-comment-label{font-size:.875rem;line-height:1.25rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__question-comment-required{color:rgb(var(--ax-sys-color-danger-600))}axm-questionnaire-viewer-question .__question-item .__attachments{border-top-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));padding-top:.75rem}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-label{margin-inline-end:.5rem;font-size:.875rem;line-height:1.25rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list{margin:0;margin-top:.25rem;list-style-type:none;padding-left:0}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list li{margin-top:.25rem}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list li .__attachment-link{display:inline-flex;align-items:center;gap:.5rem;font-size:.875rem;line-height:1.25rem;text-decoration-line:underline;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;color:rgb(var(--ax-sys-color-primary-dark-surface))}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list li .__attachment-link:hover{opacity:.9}axm-questionnaire-viewer-question .__question-item .__attachments .__attachments-list li .__attachment-link i{flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "directive", type: i1$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXAlertModule }, { kind: "component", type: i2$1.AXAlertComponent, selector: "ax-alert", inputs: ["color", "timeOut"], outputs: ["colorChange", "onClosed", "timeOutChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTextAreaModule }, { kind: "component", type: i4$1.AXTextAreaComponent, selector: "ax-text-area", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "placeholder", "maxLength", "look", "rows", "allowResize", "showCounter", "class"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AXSafePipe, name: "safe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3487
6609
  }
3488
6610
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerQuestionComponent, decorators: [{
3489
6611
  type: Component,
@@ -3685,7 +6807,7 @@ class AXMQuestionnaireViewerSectionHeaderComponent {
3685
6807
  }
3686
6808
  }
3687
6809
  </div>
3688
- `, isInline: true, styles: ["axm-questionnaire-viewer-section-header{display:block;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-section-header .__section-header{display:flex;flex-shrink:0;flex-direction:column;gap:.125rem;text-align:start}axm-questionnaire-viewer-section-header .__section-title-lg{margin:0;font-size:1.5rem;line-height:2rem;font-weight:600;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-section-header .__section-title-md{margin:0;font-size:1.25rem;line-height:1.75rem;font-weight:600;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-section-header .__section-description{margin:0;font-size:.875rem;line-height:1.25rem;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-section-header .__section-description--rich{font-size:.875rem;line-height:1.25rem;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-section-header .__section-description--rich p{margin:0}axm-questionnaire-viewer-section-header .__section-description--rich p:first-child{margin-top:0}axm-questionnaire-viewer-section-header .__section-description--rich ul{list-style-type:disc;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-section-header .__section-description--rich ol{list-style-type:decimal;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-section-header .__section-description--rich li{margin-block:.125rem;padding-inline-start:.25rem}axm-questionnaire-viewer-section-header .__mb-sm{margin-bottom:.5rem}axm-questionnaire-viewer-section-header .__mb-md{margin-bottom:1rem}\n"], dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AXSafePipe, name: "safe" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
6810
+ `, isInline: true, styles: ["axm-questionnaire-viewer-section-header{display:block;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-section-header .__section-header{display:flex;flex-shrink:0;flex-direction:column;gap:.125rem;text-align:start}axm-questionnaire-viewer-section-header .__section-title-lg{margin:0;font-size:1.5rem;line-height:2rem;font-weight:600;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-section-header .__section-title-md{margin:0;font-size:1.25rem;line-height:1.75rem;font-weight:600;color:rgb(var(--ax-sys-color-on-surface))}axm-questionnaire-viewer-section-header .__section-description{margin:0;font-size:.875rem;line-height:1.25rem;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-section-header .__section-description--rich{font-size:.875rem;line-height:1.25rem;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-section-header .__section-description--rich p{margin:0}axm-questionnaire-viewer-section-header .__section-description--rich p:first-child{margin-top:0}axm-questionnaire-viewer-section-header .__section-description--rich ul{list-style-type:disc;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-section-header .__section-description--rich ol{list-style-type:decimal;list-style-position:outside;margin-block:.375rem 0;padding-inline-start:1.25rem}axm-questionnaire-viewer-section-header .__section-description--rich li{margin-block:.125rem;padding-inline-start:.25rem}axm-questionnaire-viewer-section-header .__mb-sm{margin-bottom:.5rem}axm-questionnaire-viewer-section-header .__mb-md{margin-bottom:1rem}\n"], dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AXSafePipe, name: "safe" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3689
6811
  }
3690
6812
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerSectionHeaderComponent, decorators: [{
3691
6813
  type: Component,
@@ -3918,7 +7040,7 @@ class AXMQuestionnaireViewerSideMenuViewComponent {
3918
7040
  return null;
3919
7041
  }
3920
7042
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerSideMenuViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3921
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXMQuestionnaireViewerSideMenuViewComponent, isStandalone: true, selector: "axm-questionnaire-viewer-side-menu-view", viewQueries: [{ propertyName: "tabRef", first: true, predicate: AXTabsComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"__side-menu-container\">\n <!-- Desktop Sidebar (Large screens) -->\n @if (deviceService.isLarge()) {\n <div class=\"__desktop-sidebar\" [axResizable]=\"true\">\n <ax-tabs class=\"__sidebar-tabs\" [look]=\"'with-line-color'\" [location]=\"'end'\" [fitParent]=\"true\">\n @for (sectionIndex of vm.sectionIndicesWithVisibleContent(); track sectionIndex) {\n <ax-tab-item\n [text]=\"(vm.sections()[sectionIndex].title | translate | async) || ''\"\n (onClick)=\"handleSectionClick(sectionIndex)\"\n [disabled]=\"isSectionDisabled(sectionIndex)\"\n >\n @if (getSectionBadgeCount(vm.sections()[sectionIndex].name) > 0) {\n <ax-badge\n [text]=\"getSectionBadgeCount(vm.sections()[sectionIndex].name).toString()\"\n [color]=\"'danger'\"\n >\n </ax-badge>\n }\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n }\n\n <!-- Mobile/Tablet: Drawer Container -->\n @if (deviceService.isSmall() || deviceService.isMedium()) {\n <ax-drawer-container class=\"__drawer-container\">\n <!-- Sidebar Drawer (Mobile) -->\n <ax-drawer\n #sidebarDrawer\n [location]=\"'start'\"\n [mode]=\"'overlay'\"\n [collapsed]=\"sidebarDrawerCollapsed()\"\n [closeOnBackdropClick]=\"true\"\n (onBackdropClick)=\"sidebarDrawerCollapsed.set(true)\"\n class=\"__sidebar-drawer ax-w-[250px]\"\n >\n <ax-content class=\"__sidebar-content\">\n <ax-tabs class=\"__sidebar-tabs\" [look]=\"'with-line-color'\" [location]=\"'end'\" [fitParent]=\"true\">\n @for (sectionIndex of vm.sectionIndicesWithVisibleContent(); track sectionIndex) {\n <ax-tab-item\n [text]=\"(vm.sections()[sectionIndex].title | translate | async) || ''\"\n (onClick)=\"handleSectionClick(sectionIndex)\"\n [active]=\"vm.currentSectionIndex() === sectionIndex\"\n [disabled]=\"isSectionDisabled(sectionIndex)\"\n >\n @if (getSectionBadgeCount(vm.sections()[sectionIndex].name) > 0) {\n <ax-badge\n [text]=\"getSectionBadgeCount(vm.sections()[sectionIndex].name).toString()\"\n [color]=\"'danger'\"\n >\n </ax-badge>\n }\n </ax-tab-item>\n }\n </ax-tabs>\n </ax-content>\n </ax-drawer>\n\n <!-- Main Content (Mobile) -->\n <ax-content class=\"__main-content\">\n <!-- Questions Content -->\n @if (vm.currentSection(); as section) {\n @if (vm.isSectionVisible(section)) {\n @if (stickyParent(); as parent) {\n <div class=\"__mobile-header-row\" [axpSticky]=\"'--stuck'\" [stickyParent]=\"parent\" [stickyOffset]=\"0\">\n <div class=\"__mobile-menu-button\">\n <ax-button look=\"blank\" (onClick)=\"sidebarDrawerCollapsed.set(!sidebarDrawerCollapsed())\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-bars\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n\n <axm-questionnaire-viewer-section-header\n [title]=\"section.title\"\n [description]=\"section.description\"\n [titleSize]=\"'lg'\"\n [marginBottom]=\"'none'\"\n >\n </axm-questionnaire-viewer-section-header>\n </div>\n }\n\n <div class=\"__page-section\">\n <div class=\"__questions-list\">\n @for (question of vm.getVisibleQuestionsInSection(section); track question.name; let i = $index) {\n <axm-questionnaire-viewer-question\n [question]=\"question\"\n [sectionOrder]=\"section.order\"\n [questionIndex]=\"i + 1\"\n [showQuestionNumbers]=\"vm.showQuestionNumbers()\"\n [showQuestionCounter]=\"false\"\n >\n </axm-questionnaire-viewer-question>\n }\n </div>\n </div>\n }\n }\n </ax-content>\n </ax-drawer-container>\n } @else {\n <!-- Desktop Main Content -->\n <div class=\"__main-content\">\n <!-- Questions Content -->\n @if (vm.currentSection(); as section) {\n @if (vm.isSectionVisible(section)) {\n <div class=\"__page-section\">\n @if (stickyParent(); as parent) {\n <div\n class=\"__section-title-sticky\"\n [axpSticky]=\"'--stuck'\"\n [stickyParent]=\"parent\"\n [stickyOffset]=\"0\"\n >\n <axm-questionnaire-viewer-section-header\n [title]=\"section.title\"\n [description]=\"section.description\"\n [titleSize]=\"'lg'\"\n [marginBottom]=\"'none'\"\n >\n </axm-questionnaire-viewer-section-header>\n </div>\n }\n\n <div class=\"__questions-list\">\n @for (question of vm.getVisibleQuestionsInSection(section); track question.name; let i = $index) {\n <axm-questionnaire-viewer-question\n [question]=\"question\"\n [sectionOrder]=\"section.order\"\n [questionIndex]=\"i + 1\"\n [showQuestionNumbers]=\"vm.showQuestionNumbers()\"\n [showQuestionCounter]=\"false\"\n >\n </axm-questionnaire-viewer-question>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n</div>\n", styles: ["axm-questionnaire-viewer-side-menu-view{display:block;min-height:0px;width:100%;overflow:hidden}@media(min-width:640px){axm-questionnaire-viewer-side-menu-view{height:100%}}@media(min-width:1024px){axm-questionnaire-viewer-side-menu-view{height:65vh}}.__side-menu-container{display:flex;height:100%;min-height:0px;width:100%;overflow:hidden}.__drawer-container{display:flex;height:100%;width:100%;overflow:hidden}.__sidebar-drawer{height:100%;border-inline-end-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.__sidebar-content{height:100%;overflow-y:auto}.__desktop-sidebar{display:flex;height:100%;min-height:0px;flex-shrink:0;flex-direction:column;overflow:hidden;border-inline-end-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.__sidebar-tabs{--ax-comp-tabs-default-border-radius: 0;height:100%;min-height:0px;flex:1 1 0%;overflow-y:auto}.__sidebar-tabs ax-tab-item{margin-top:0;margin-bottom:0;padding-top:.75rem;padding-bottom:.75rem;font-weight:600}.__main-content{display:flex;max-height:83vh;min-height:0px;flex:1 1 0%;flex-direction:column;overflow-y:auto}@media(min-width:768px){.__main-content{max-height:75vh}}.__page-section{display:flex;flex-shrink:0;flex-direction:column;gap:1rem;padding:1rem}.__page-section .__section-title-sticky{position:sticky;top:0;z-index:10;isolation:isolate;min-width:0px;align-self:stretch;border-bottom-width:1px;padding-top:.5rem;padding-bottom:.5rem;transition:padding-block .28s cubic-bezier(.33,1,.68,1),border-color .22s ease-out}.__page-section .__section-title-sticky:before{content:\"\";position:absolute;z-index:-1;inset-block:0;inset-inline:0;pointer-events:none;box-shadow:none;transition:inset-inline .28s cubic-bezier(.22,1,.36,1),box-shadow .28s cubic-bezier(.33,1,.68,1)}@media(prefers-reduced-motion:reduce){.__page-section .__section-title-sticky{transition-duration:1ms;transition-timing-function:linear}.__page-section .__section-title-sticky:before{transition-duration:1ms;transition-timing-function:linear}}.__page-section .__section-title-sticky.--stuck{border-bottom-width:1px;border-style:none;padding-top:.75rem;padding-bottom:.75rem;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.__page-section .__section-title-sticky.--stuck:before{inset-inline:-1rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.__mobile-header-row{position:sticky;top:0;z-index:10;isolation:isolate;display:flex;flex-shrink:0;align-items:flex-start;gap:.5rem;border-bottom-width:1px;padding:.5rem 1rem;transition:padding-block .28s cubic-bezier(.33,1,.68,1),border-color .22s ease-out}.__mobile-header-row:before{content:\"\";position:absolute;z-index:-1;inset-block:0;inset-inline:0;pointer-events:none;box-shadow:none;transition:inset-inline .28s cubic-bezier(.22,1,.36,1),box-shadow .28s cubic-bezier(.33,1,.68,1)}.__mobile-header-row.--stuck{border-bottom-width:1px;border-style:none;padding-top:.75rem;padding-bottom:.75rem;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.__mobile-header-row.--stuck:before{inset-inline:-1rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.__mobile-menu-button{margin-inline-start:-.5rem;flex-shrink:0}.__questions-list{display:flex;flex-direction:column;gap:1rem}\n"], dependencies: [{ kind: "component", type: AXMQuestionnaireViewerQuestionComponent, selector: "axm-questionnaire-viewer-question", inputs: ["question", "contextData", "sectionOrder", "questionIndex", "showQuestionNumbers", "showQuestionCounter", "currentPosition", "mode"] }, { kind: "component", type: AXMQuestionnaireViewerSectionHeaderComponent, selector: "axm-questionnaire-viewer-section-header", inputs: ["title", "description", "titleSize", "marginBottom"] }, { kind: "ngmodule", type: AXTabsModule }, { kind: "component", type: i1$1.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i1$1.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXDrawerModule }, { kind: "component", type: i3$1.AXDrawerComponent, selector: "ax-drawer", inputs: ["location", "showBackdrop", "mode", "transition", "closeOnBackdropClick", "backdropClass", "collapsed", "singleOpenMode"], outputs: ["onBackdropClick", "collapsedStateChanged"] }, { kind: "component", type: i3$1.AXDrawerContainerComponent, selector: "ax-drawer-container" }, { kind: "ngmodule", type: AXDrawerDirectiveModule }, { kind: "directive", type: AXResizableDirective, selector: "[axResizable]", inputs: ["axResizable", "minWidth", "maxWidth", "dblClickAction", "width", "defaultWidth"], outputs: ["axResizableChange", "minWidthChange", "maxWidthChange", "dblClickActionChange", "widthChange", "defaultWidthChange", "onResizingStarted", "onResizingEnded", "onResizingDblClick"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "directive", type: AXPStickyDirective, selector: "[axpSticky]", inputs: ["axpSticky", "stickyOffset", "stickyParent", "stickyTarget"], outputs: ["isStickyChange"], exportAs: ["axpSticky"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
7043
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXMQuestionnaireViewerSideMenuViewComponent, isStandalone: true, selector: "axm-questionnaire-viewer-side-menu-view", viewQueries: [{ propertyName: "tabRef", first: true, predicate: AXTabsComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"__side-menu-container\">\n <!-- Desktop Sidebar (Large screens) -->\n @if (deviceService.isLarge()) {\n <div class=\"__desktop-sidebar\" [axResizable]=\"true\">\n <ax-tabs class=\"__sidebar-tabs\" [look]=\"'with-line-color'\" [location]=\"'end'\" [fitParent]=\"true\">\n @for (sectionIndex of vm.sectionIndicesWithVisibleContent(); track sectionIndex) {\n <ax-tab-item\n [text]=\"(vm.sections()[sectionIndex].title | translate | async) || ''\"\n (onClick)=\"handleSectionClick(sectionIndex)\"\n [disabled]=\"isSectionDisabled(sectionIndex)\"\n >\n @if (getSectionBadgeCount(vm.sections()[sectionIndex].name) > 0) {\n <ax-badge\n [text]=\"getSectionBadgeCount(vm.sections()[sectionIndex].name).toString()\"\n [color]=\"'danger'\"\n >\n </ax-badge>\n }\n </ax-tab-item>\n }\n </ax-tabs>\n </div>\n }\n\n <!-- Mobile/Tablet: Drawer Container -->\n @if (deviceService.isSmall() || deviceService.isMedium()) {\n <ax-drawer-container class=\"__drawer-container\">\n <!-- Sidebar Drawer (Mobile) -->\n <ax-drawer\n #sidebarDrawer\n [location]=\"'start'\"\n [mode]=\"'overlay'\"\n [collapsed]=\"sidebarDrawerCollapsed()\"\n [closeOnBackdropClick]=\"true\"\n (onBackdropClick)=\"sidebarDrawerCollapsed.set(true)\"\n class=\"__sidebar-drawer ax-w-[250px]\"\n >\n <ax-content class=\"__sidebar-content\">\n <ax-tabs class=\"__sidebar-tabs\" [look]=\"'with-line-color'\" [location]=\"'end'\" [fitParent]=\"true\">\n @for (sectionIndex of vm.sectionIndicesWithVisibleContent(); track sectionIndex) {\n <ax-tab-item\n [text]=\"(vm.sections()[sectionIndex].title | translate | async) || ''\"\n (onClick)=\"handleSectionClick(sectionIndex)\"\n [active]=\"vm.currentSectionIndex() === sectionIndex\"\n [disabled]=\"isSectionDisabled(sectionIndex)\"\n >\n @if (getSectionBadgeCount(vm.sections()[sectionIndex].name) > 0) {\n <ax-badge\n [text]=\"getSectionBadgeCount(vm.sections()[sectionIndex].name).toString()\"\n [color]=\"'danger'\"\n >\n </ax-badge>\n }\n </ax-tab-item>\n }\n </ax-tabs>\n </ax-content>\n </ax-drawer>\n\n <!-- Main Content (Mobile) -->\n <ax-content class=\"__main-content\">\n <!-- Questions Content -->\n @if (vm.currentSection(); as section) {\n @if (vm.isSectionVisible(section)) {\n @if (stickyParent(); as parent) {\n <div class=\"__mobile-header-row\" [axpSticky]=\"'--stuck'\" [stickyParent]=\"parent\" [stickyOffset]=\"0\">\n <div class=\"__mobile-menu-button\">\n <ax-button look=\"blank\" (onClick)=\"sidebarDrawerCollapsed.set(!sidebarDrawerCollapsed())\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-bars\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n\n <axm-questionnaire-viewer-section-header\n [title]=\"section.title\"\n [description]=\"section.description\"\n [titleSize]=\"'lg'\"\n [marginBottom]=\"'none'\"\n >\n </axm-questionnaire-viewer-section-header>\n </div>\n }\n\n <div class=\"__page-section\">\n <div class=\"__questions-list\">\n @for (question of vm.getVisibleQuestionsInSection(section); track question.name; let i = $index) {\n <axm-questionnaire-viewer-question\n [question]=\"question\"\n [sectionOrder]=\"section.order\"\n [questionIndex]=\"i + 1\"\n [showQuestionNumbers]=\"vm.showQuestionNumbers()\"\n [showQuestionCounter]=\"false\"\n >\n </axm-questionnaire-viewer-question>\n }\n </div>\n </div>\n }\n }\n </ax-content>\n </ax-drawer-container>\n } @else {\n <!-- Desktop Main Content -->\n <div class=\"__main-content\">\n <!-- Questions Content -->\n @if (vm.currentSection(); as section) {\n @if (vm.isSectionVisible(section)) {\n <div class=\"__page-section\">\n @if (stickyParent(); as parent) {\n <div\n class=\"__section-title-sticky\"\n [axpSticky]=\"'--stuck'\"\n [stickyParent]=\"parent\"\n [stickyOffset]=\"0\"\n >\n <axm-questionnaire-viewer-section-header\n [title]=\"section.title\"\n [description]=\"section.description\"\n [titleSize]=\"'lg'\"\n [marginBottom]=\"'none'\"\n >\n </axm-questionnaire-viewer-section-header>\n </div>\n }\n\n <div class=\"__questions-list\">\n @for (question of vm.getVisibleQuestionsInSection(section); track question.name; let i = $index) {\n <axm-questionnaire-viewer-question\n [question]=\"question\"\n [sectionOrder]=\"section.order\"\n [questionIndex]=\"i + 1\"\n [showQuestionNumbers]=\"vm.showQuestionNumbers()\"\n [showQuestionCounter]=\"false\"\n >\n </axm-questionnaire-viewer-question>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n</div>\n", styles: ["axm-questionnaire-viewer-side-menu-view{display:block;min-height:0px;width:100%;overflow:hidden}@media(min-width:640px){axm-questionnaire-viewer-side-menu-view{height:100%}}@media(min-width:1024px){axm-questionnaire-viewer-side-menu-view{height:65vh}}.__side-menu-container{display:flex;height:100%;min-height:0px;width:100%;overflow:hidden}.__drawer-container{display:flex;height:100%;width:100%;overflow:hidden}.__sidebar-drawer{height:100%;border-inline-end-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.__sidebar-content{height:100%;overflow-y:auto}.__desktop-sidebar{display:flex;height:100%;min-height:0px;flex-shrink:0;flex-direction:column;overflow:hidden;border-inline-end-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.__sidebar-tabs{--ax-comp-tabs-default-border-radius: 0;height:100%;min-height:0px;flex:1 1 0%;overflow-y:auto}.__sidebar-tabs ax-tab-item{margin-top:0;margin-bottom:0;padding-top:.75rem;padding-bottom:.75rem;font-weight:600}.__main-content{display:flex;max-height:83vh;min-height:0px;flex:1 1 0%;flex-direction:column;overflow-y:auto}@media(min-width:768px){.__main-content{max-height:75vh}}.__page-section{display:flex;flex-shrink:0;flex-direction:column;gap:1rem;padding:1rem}.__page-section .__section-title-sticky{position:sticky;top:0;z-index:10;isolation:isolate;min-width:0px;align-self:stretch;border-bottom-width:1px;padding-top:.5rem;padding-bottom:.5rem;transition:padding-block .28s cubic-bezier(.33,1,.68,1),border-color .22s ease-out}.__page-section .__section-title-sticky:before{content:\"\";position:absolute;z-index:-1;inset-block:0;inset-inline:0;pointer-events:none;box-shadow:none;transition:inset-inline .28s cubic-bezier(.22,1,.36,1),box-shadow .28s cubic-bezier(.33,1,.68,1)}@media(prefers-reduced-motion:reduce){.__page-section .__section-title-sticky{transition-duration:1ms;transition-timing-function:linear}.__page-section .__section-title-sticky:before{transition-duration:1ms;transition-timing-function:linear}}.__page-section .__section-title-sticky.--stuck{border-bottom-width:1px;border-style:none;padding-top:.75rem;padding-bottom:.75rem;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.__page-section .__section-title-sticky.--stuck:before{inset-inline:-1rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.__mobile-header-row{position:sticky;top:0;z-index:10;isolation:isolate;display:flex;flex-shrink:0;align-items:flex-start;gap:.5rem;border-bottom-width:1px;padding:.5rem 1rem;transition:padding-block .28s cubic-bezier(.33,1,.68,1),border-color .22s ease-out}.__mobile-header-row:before{content:\"\";position:absolute;z-index:-1;inset-block:0;inset-inline:0;pointer-events:none;box-shadow:none;transition:inset-inline .28s cubic-bezier(.22,1,.36,1),box-shadow .28s cubic-bezier(.33,1,.68,1)}.__mobile-header-row.--stuck{border-bottom-width:1px;border-style:none;padding-top:.75rem;padding-bottom:.75rem;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface))}.__mobile-header-row.--stuck:before{inset-inline:-1rem;--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.__mobile-menu-button{margin-inline-start:-.5rem;flex-shrink:0}.__questions-list{display:flex;flex-direction:column;gap:1rem}\n"], dependencies: [{ kind: "component", type: AXMQuestionnaireViewerQuestionComponent, selector: "axm-questionnaire-viewer-question", inputs: ["question", "contextData", "sectionOrder", "questionIndex", "showQuestionNumbers", "showQuestionCounter", "currentPosition", "mode"] }, { kind: "component", type: AXMQuestionnaireViewerSectionHeaderComponent, selector: "axm-questionnaire-viewer-section-header", inputs: ["title", "description", "titleSize", "marginBottom"] }, { kind: "ngmodule", type: AXTabsModule }, { kind: "component", type: i1$2.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i1$2.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXDrawerModule }, { kind: "component", type: i3$1.AXDrawerComponent, selector: "ax-drawer", inputs: ["location", "showBackdrop", "mode", "transition", "closeOnBackdropClick", "backdropClass", "collapsed", "singleOpenMode"], outputs: ["onBackdropClick", "collapsedStateChanged"] }, { kind: "component", type: i3$1.AXDrawerContainerComponent, selector: "ax-drawer-container" }, { kind: "ngmodule", type: AXDrawerDirectiveModule }, { kind: "directive", type: AXResizableDirective, selector: "[axResizable]", inputs: ["axResizable", "minWidth", "maxWidth", "dblClickAction", "width", "defaultWidth"], outputs: ["axResizableChange", "minWidthChange", "maxWidthChange", "dblClickActionChange", "widthChange", "defaultWidthChange", "onResizingStarted", "onResizingEnded", "onResizingDblClick"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "directive", type: AXPStickyDirective, selector: "[axpSticky]", inputs: ["axpSticky", "stickyOffset", "stickyParent", "stickyTarget"], outputs: ["isStickyChange"], exportAs: ["axpSticky"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3922
7044
  }
3923
7045
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerSideMenuViewComponent, decorators: [{
3924
7046
  type: Component,
@@ -4183,9 +7305,9 @@ class AXMQuestionnaireViewerComponent {
4183
7305
  this.answersChanged.emit(answers);
4184
7306
  }
4185
7307
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4186
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXMQuestionnaireViewerComponent, isStandalone: true, selector: "axm-questionnaire-viewer", inputs: { questionnaireId: { classPropertyName: "questionnaireId", publicName: "questionnaireId", isSignal: true, isRequired: false, transformFunction: null }, structure: { classPropertyName: "structure", publicName: "structure", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, initialAnswers: { classPropertyName: "initialAnswers", publicName: "initialAnswers", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { answersChanged: "answersChanged", questionAnswered: "questionAnswered" }, host: { classAttribute: "axm-questionnaire-viewer" }, providers: [AXMQuestionnaireViewerViewModel], viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, isSignal: true }, { propertyName: "widgetContainer", first: true, predicate: AXPWidgetContainerComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<axp-widgets-container #widgetContainer [attr.dir]=\"layoutDirection()\" [context]=\"{}\"\n (onContextChanged)=\"handleContextChanged()\">\n @if (vm.isLoading()) {\n <div class=\"__loading\">\n <axp-state-message icon=\"fa-light fa-spinner fa-spin\"\n [title]=\"('@assessment-management:questionnaires.components.questionnaire-viewer.loading.title' | translate | async) || ''\"\n [description]=\"('@assessment-management:questionnaires.components.questionnaire-viewer.loading.description' | translate | async) || ''\">\n </axp-state-message>\n </div>\n } @else if (vm.error()) {\n <div class=\"__error\">\n <axp-state-message icon=\"fa-light fa-exclamation-triangle\"\n [title]=\"('@assessment-management:questionnaires.components.questionnaire-viewer.error.title' | translate | async) || ''\"\n [description]=\"vm.error() || ''\">\n </axp-state-message>\n </div>\n } @else if (vm.questionnaireData()) {\n <div class=\"__questionnaire-container\">\n <ax-form #form>\n @if (vm.viewMode() === 'single-page') {\n <axm-questionnaire-viewer-single-page-view></axm-questionnaire-viewer-single-page-view>\n } @else if (vm.viewMode() === 'page-per-group') {\n <axm-questionnaire-viewer-page-per-group-view></axm-questionnaire-viewer-page-per-group-view>\n } @else if (vm.viewMode() === 'page-per-question') {\n <axm-questionnaire-viewer-page-per-question-view></axm-questionnaire-viewer-page-per-question-view>\n } @else if (vm.viewMode() === 'side-menu') {\n <axm-questionnaire-viewer-side-menu-view></axm-questionnaire-viewer-side-menu-view>\n }\n </ax-form>\n </div>\n }\n</axp-widgets-container>\n", styles: [".axm-questionnaire-viewer{display:flex;height:100%;max-height:100%;min-height:0px;width:100%;flex:1 1 0%;flex-direction:column}.axm-questionnaire-viewer ax-form{display:flex;height:100%;min-height:0px;flex:1 1 0%;flex-direction:column}.axm-questionnaire-viewer axp-widgets-container{display:flex;height:100%;min-height:0px;flex:1 1 0%;flex-direction:column}.axm-questionnaire-viewer .__loading,.axm-questionnaire-viewer .__error{display:flex;height:100%;align-items:center;justify-content:center}.axm-questionnaire-viewer .__questionnaire-container{display:flex;min-height:0px;flex:1 1 0%;flex-direction:column;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i1$3.AXFormComponent, selector: "ax-form", inputs: ["disabled", "readonly", "labelMode", "look", "messageStyle", "updateOn", "inUserInteractionActive"], outputs: ["onValidate", "updateOnChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "component", type:
7308
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXMQuestionnaireViewerComponent, isStandalone: true, selector: "axm-questionnaire-viewer", inputs: { questionnaireId: { classPropertyName: "questionnaireId", publicName: "questionnaireId", isSignal: true, isRequired: false, transformFunction: null }, structure: { classPropertyName: "structure", publicName: "structure", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, initialAnswers: { classPropertyName: "initialAnswers", publicName: "initialAnswers", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { answersChanged: "answersChanged", questionAnswered: "questionAnswered" }, host: { classAttribute: "axm-questionnaire-viewer" }, providers: [AXMQuestionnaireViewerViewModel], viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, isSignal: true }, { propertyName: "widgetContainer", first: true, predicate: AXPWidgetContainerComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<axp-widgets-container #widgetContainer [attr.dir]=\"layoutDirection()\" [context]=\"{}\"\n (onContextChanged)=\"handleContextChanged()\">\n @if (vm.isLoading()) {\n <div class=\"__loading\">\n <axp-state-message icon=\"fa-light fa-spinner fa-spin\"\n [title]=\"('@assessment-management:questionnaires.components.questionnaire-viewer.loading.title' | translate | async) || ''\"\n [description]=\"('@assessment-management:questionnaires.components.questionnaire-viewer.loading.description' | translate | async) || ''\">\n </axp-state-message>\n </div>\n } @else if (vm.error()) {\n <div class=\"__error\">\n <axp-state-message icon=\"fa-light fa-exclamation-triangle\"\n [title]=\"('@assessment-management:questionnaires.components.questionnaire-viewer.error.title' | translate | async) || ''\"\n [description]=\"vm.error() || ''\">\n </axp-state-message>\n </div>\n } @else if (vm.questionnaireData()) {\n <div class=\"__questionnaire-container\">\n <ax-form #form>\n @if (vm.viewMode() === 'single-page') {\n <axm-questionnaire-viewer-single-page-view></axm-questionnaire-viewer-single-page-view>\n } @else if (vm.viewMode() === 'page-per-group') {\n <axm-questionnaire-viewer-page-per-group-view></axm-questionnaire-viewer-page-per-group-view>\n } @else if (vm.viewMode() === 'page-per-question') {\n <axm-questionnaire-viewer-page-per-question-view></axm-questionnaire-viewer-page-per-question-view>\n } @else if (vm.viewMode() === 'side-menu') {\n <axm-questionnaire-viewer-side-menu-view></axm-questionnaire-viewer-side-menu-view>\n }\n </ax-form>\n </div>\n }\n</axp-widgets-container>\n", styles: [".axm-questionnaire-viewer{display:flex;height:100%;max-height:100%;min-height:0px;width:100%;flex:1 1 0%;flex-direction:column}.axm-questionnaire-viewer ax-form{display:flex;height:100%;min-height:0px;flex:1 1 0%;flex-direction:column}.axm-questionnaire-viewer axp-widgets-container{display:flex;height:100%;min-height:0px;flex:1 1 0%;flex-direction:column}.axm-questionnaire-viewer .__loading,.axm-questionnaire-viewer .__error{display:flex;height:100%;align-items:center;justify-content:center}.axm-questionnaire-viewer .__questionnaire-container{display:flex;min-height:0px;flex:1 1 0%;flex-direction:column;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i1$3.AXFormComponent, selector: "ax-form", inputs: ["disabled", "readonly", "labelMode", "look", "messageStyle", "updateOn", "inUserInteractionActive"], outputs: ["onValidate", "updateOnChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "component", type:
4187
7309
  // View components
4188
- AXMQuestionnaireViewerSinglePageViewComponent, selector: "axm-questionnaire-viewer-single-page-view" }, { kind: "component", type: AXMQuestionnaireViewerPagePerGroupViewComponent, selector: "axm-questionnaire-viewer-page-per-group-view" }, { kind: "component", type: AXMQuestionnaireViewerPagePerQuestionViewComponent, selector: "axm-questionnaire-viewer-page-per-question-view" }, { kind: "component", type: AXMQuestionnaireViewerSideMenuViewComponent, selector: "axm-questionnaire-viewer-side-menu-view" }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
7310
+ AXMQuestionnaireViewerSinglePageViewComponent, selector: "axm-questionnaire-viewer-single-page-view" }, { kind: "component", type: AXMQuestionnaireViewerPagePerGroupViewComponent, selector: "axm-questionnaire-viewer-page-per-group-view" }, { kind: "component", type: AXMQuestionnaireViewerPagePerQuestionViewComponent, selector: "axm-questionnaire-viewer-page-per-question-view" }, { kind: "component", type: AXMQuestionnaireViewerSideMenuViewComponent, selector: "axm-questionnaire-viewer-side-menu-view" }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
4189
7311
  }
4190
7312
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerComponent, decorators: [{
4191
7313
  type: Component,
@@ -4255,7 +7377,6 @@ class AXMQuestionnaireEvaluatorScopeProvider {
4255
7377
  async provide(context) {
4256
7378
  const questionnaireEntity = `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`;
4257
7379
  const questionBankEntity = `${RootConfig.module.name}.${RootConfig.entities.questionBankItem.name}`;
4258
- const questionnaireCalculationEntity = `${RootConfig.module.name}.${RootConfig.entities.questionnaireCalculation.name}`;
4259
7380
  const filterDefinitionsImpl = async (questionnaireId) => {
4260
7381
  if (!questionnaireId) {
4261
7382
  return [];
@@ -4346,27 +7467,20 @@ class AXMQuestionnaireEvaluatorScopeProvider {
4346
7467
  return [];
4347
7468
  }
4348
7469
  };
4349
- /** Filter definitions from questionnaire calculations (for automation condition: rules.ruleName). */
7470
+ /** Filter definitions from embedded questionnaire outcomes (for automation condition: rules.ruleName). */
4350
7471
  const forRules = async (questionnaireId) => {
4351
7472
  if (!questionnaireId) {
4352
7473
  return [];
4353
7474
  }
4354
7475
  try {
4355
- const dataAccessor = this.entityService.withEntity(questionnaireCalculationEntity).data();
4356
- const queryResult = await dataAccessor.query({
4357
- skip: 0,
4358
- take: 500,
4359
- filter: {
4360
- field: 'questionnaireId',
4361
- operator: { type: 'equal' },
4362
- value: questionnaireId,
4363
- },
4364
- });
4365
- const rules = (queryResult?.items ?? []);
7476
+ const dataAccessor = this.entityService.withEntity(questionnaireEntity).data();
7477
+ const questionnaire = await dataAccessor.byKey(questionnaireId);
7478
+ const rules = flattenQuestionnaireOutcomeItems(questionnaire?.outcomes);
7479
+ const locale = this.translationService.getActiveLang() ?? 'en-US';
4366
7480
  return rules.map((rule, order) => ({
4367
7481
  id: rule.id,
4368
7482
  field: `rules.${rule.name}`,
4369
- title: rule.title ?? rule.name,
7483
+ title: resolveMultiLanguageString(rule.title, locale) || rule.name,
4370
7484
  operator: { type: 'equal' },
4371
7485
  widget: {
4372
7486
  type: 'string-filter',
@@ -4389,7 +7503,7 @@ class AXMQuestionnaireEvaluatorScopeProvider {
4389
7503
  context.addScope('questionnaire', {
4390
7504
  /** Filter definitions from questionnaire answers (e.g. visibility / automation conditions). */
4391
7505
  answers: filterDefinitionsImpl,
4392
- /** Filter definitions from questionnaire calculations for automation condition builder (rules.ruleName). */
7506
+ /** Filter definitions from questionnaire outcomes for automation condition builder (rules.ruleName). */
4393
7507
  rules: forRules,
4394
7508
  });
4395
7509
  // Automation condition builder: provide rules for refType = AssessmentManagement.Questionnaire
@@ -4422,31 +7536,35 @@ class AXMQuestionnaireViewerFeatureModule {
4422
7536
  provideCommandSetups([
4423
7537
  {
4424
7538
  key: `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}:Preview`,
4425
- command: () => import('./acorex-modules-assessment-management-preview-questionnaire.command-wqPNgv7q.mjs').then((c) => c.PreviewQuestionnaireCommand),
7539
+ command: () => import('./acorex-modules-assessment-management-preview-questionnaire.command-S-8MDeB7.mjs').then((c) => c.PreviewQuestionnaireCommand),
4426
7540
  },
4427
7541
  {
4428
7542
  key: `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}:PreviewQuestion`,
4429
- command: () => import('./acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs').then((c) => c.PreviewQuestionCommand),
7543
+ command: () => import('./acorex-modules-assessment-management-preview-question.command-Ce7yOnJY.mjs').then((c) => c.PreviewQuestionCommand),
4430
7544
  },
4431
7545
  {
4432
7546
  key: `${RootConfig.module.name}.${RootConfig.entities.questionBankItem.name}:Preview`,
4433
- command: () => import('./acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs').then((c) => c.PreviewQuestionCommand),
7547
+ command: () => import('./acorex-modules-assessment-management-preview-question.command-Ce7yOnJY.mjs').then((c) => c.PreviewQuestionCommand),
4434
7548
  },
4435
7549
  {
4436
7550
  key: `${RootConfig.module.name}.${RootConfig.entities.assessmentSession.name}:ViewAnswers`,
4437
- command: () => import('./acorex-modules-assessment-management-view-session-answers.command-0btGV66_.mjs').then((c) => c.ViewSessionAnswersCommand),
7551
+ command: () => import('./acorex-modules-assessment-management-view-session-answers.command-COTVoRRG.mjs').then((c) => c.ViewSessionAnswersCommand),
7552
+ },
7553
+ {
7554
+ key: `${RootConfig.module.name}.${RootConfig.entities.assessmentSession.name}:ViewOutcomes`,
7555
+ command: () => import('./acorex-modules-assessment-management-view-session-outcomes.command-2buH7ydV.mjs').then((c) => c.ViewSessionOutcomesCommand),
4438
7556
  },
4439
7557
  {
4440
7558
  key: `${RootConfig.module.name}.${RootConfig.entities.assessmentSession.name}:Fill`,
4441
- command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs').then((c) => c.FillAssessmentSessionCommand),
7559
+ command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-CRE8SxzC.mjs').then((c) => c.FillAssessmentSessionCommand),
4442
7560
  },
4443
7561
  {
4444
7562
  key: `${RootConfig.module.name}.${RootConfig.entities.assessmentCase.name}:Fill`,
4445
- command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs').then((c) => c.FillAssessmentSessionCommand),
7563
+ command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-CRE8SxzC.mjs').then((c) => c.FillAssessmentSessionCommand),
4446
7564
  },
4447
7565
  {
4448
7566
  key: `${RootConfig.module.name}.${RootConfig.entities.assessmentCase.name}:ViewLastSessionAnswers`,
4449
- command: () => import('./acorex-modules-assessment-management-view-case-last-session-answers.command-NTuDdoYk.mjs').then((c) => c.ViewCaseLastSessionAnswersCommand),
7567
+ command: () => import('./acorex-modules-assessment-management-view-case-last-session-answers.command-Ds-QGMPy.mjs').then((c) => c.ViewCaseLastSessionAnswersCommand),
4450
7568
  },
4451
7569
  ]),
4452
7570
  ], imports: [AXPWidgetCoreModule,
@@ -4473,31 +7591,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4473
7591
  provideCommandSetups([
4474
7592
  {
4475
7593
  key: `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}:Preview`,
4476
- command: () => import('./acorex-modules-assessment-management-preview-questionnaire.command-wqPNgv7q.mjs').then((c) => c.PreviewQuestionnaireCommand),
7594
+ command: () => import('./acorex-modules-assessment-management-preview-questionnaire.command-S-8MDeB7.mjs').then((c) => c.PreviewQuestionnaireCommand),
4477
7595
  },
4478
7596
  {
4479
7597
  key: `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}:PreviewQuestion`,
4480
- command: () => import('./acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs').then((c) => c.PreviewQuestionCommand),
7598
+ command: () => import('./acorex-modules-assessment-management-preview-question.command-Ce7yOnJY.mjs').then((c) => c.PreviewQuestionCommand),
4481
7599
  },
4482
7600
  {
4483
7601
  key: `${RootConfig.module.name}.${RootConfig.entities.questionBankItem.name}:Preview`,
4484
- command: () => import('./acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs').then((c) => c.PreviewQuestionCommand),
7602
+ command: () => import('./acorex-modules-assessment-management-preview-question.command-Ce7yOnJY.mjs').then((c) => c.PreviewQuestionCommand),
4485
7603
  },
4486
7604
  {
4487
7605
  key: `${RootConfig.module.name}.${RootConfig.entities.assessmentSession.name}:ViewAnswers`,
4488
- command: () => import('./acorex-modules-assessment-management-view-session-answers.command-0btGV66_.mjs').then((c) => c.ViewSessionAnswersCommand),
7606
+ command: () => import('./acorex-modules-assessment-management-view-session-answers.command-COTVoRRG.mjs').then((c) => c.ViewSessionAnswersCommand),
7607
+ },
7608
+ {
7609
+ key: `${RootConfig.module.name}.${RootConfig.entities.assessmentSession.name}:ViewOutcomes`,
7610
+ command: () => import('./acorex-modules-assessment-management-view-session-outcomes.command-2buH7ydV.mjs').then((c) => c.ViewSessionOutcomesCommand),
4489
7611
  },
4490
7612
  {
4491
7613
  key: `${RootConfig.module.name}.${RootConfig.entities.assessmentSession.name}:Fill`,
4492
- command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs').then((c) => c.FillAssessmentSessionCommand),
7614
+ command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-CRE8SxzC.mjs').then((c) => c.FillAssessmentSessionCommand),
4493
7615
  },
4494
7616
  {
4495
7617
  key: `${RootConfig.module.name}.${RootConfig.entities.assessmentCase.name}:Fill`,
4496
- command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs').then((c) => c.FillAssessmentSessionCommand),
7618
+ command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-CRE8SxzC.mjs').then((c) => c.FillAssessmentSessionCommand),
4497
7619
  },
4498
7620
  {
4499
7621
  key: `${RootConfig.module.name}.${RootConfig.entities.assessmentCase.name}:ViewLastSessionAnswers`,
4500
- command: () => import('./acorex-modules-assessment-management-view-case-last-session-answers.command-NTuDdoYk.mjs').then((c) => c.ViewCaseLastSessionAnswersCommand),
7622
+ command: () => import('./acorex-modules-assessment-management-view-case-last-session-answers.command-Ds-QGMPy.mjs').then((c) => c.ViewCaseLastSessionAnswersCommand),
4501
7623
  },
4502
7624
  ]),
4503
7625
  ],
@@ -4550,8 +7672,10 @@ class AXMAssessmentManagementModule {
4550
7672
  provideLazyProvider(AXP_PERMISSION_DEFINITION_PROVIDER, () => Promise.resolve().then(function () { return permissionDefinition_provider; }).then((m) => m.AXMAssessmentManagementPermissionDefinitionProvider)),
4551
7673
  provideLazyProvider(AXP_MENU_PROVIDER, () => Promise.resolve().then(function () { return menu_provider; }).then((m) => m.AXMAssessmentManagementMenuProvider)),
4552
7674
  provideLazyProvider(AXP_ENTITY_DEFINITION_LOADER, () => Promise.resolve().then(function () { return entity_provider; }).then((m) => m.AXMAssessmentManagementEntityProvider)),
4553
- provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-assessment-management-index-D8KjfHAH.mjs').then((m) => m.AXMQuestionnaireQuestionsPageComponentProvider)),
4554
- provideCommand('Questionnaire:Questions:Save', () => import('./acorex-modules-assessment-management-save-questionnaire-questions.command-koGmUbNE.mjs').then((m) => m.AXMSaveQuestionnaireQuestionsCommand)),
7675
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index$1; }).then((m) => m.AXMQuestionnaireQuestionsPageComponentProvider)),
7676
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index; }).then((m) => m.AXMQuestionnaireOutcomesPageComponentProvider)),
7677
+ provideCommand('Questionnaire:Questions:Save', () => Promise.resolve().then(function () { return saveQuestionnaireQuestions_command; }).then((m) => m.AXMSaveQuestionnaireQuestionsCommand)),
7678
+ provideCommand('Questionnaire:Outcomes:Save', () => Promise.resolve().then(function () { return saveQuestionnaireOutcomes_command; }).then((m) => m.AXMSaveQuestionnaireOutcomesCommand)),
4555
7679
  provideLazyProvider(AXP_SEARCH_PROVIDER, () => Promise.resolve().then(function () { return searchCommand_provider; }).then((m) => m.AXMAssessmentManagementSearchCommandProvider)),
4556
7680
  ], imports: [AXMQuestionnaireBuilderFeatureModule,
4557
7681
  AXMQuestionnaireViewerFeatureModule] }); }
@@ -4583,8 +7707,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4583
7707
  provideLazyProvider(AXP_PERMISSION_DEFINITION_PROVIDER, () => Promise.resolve().then(function () { return permissionDefinition_provider; }).then((m) => m.AXMAssessmentManagementPermissionDefinitionProvider)),
4584
7708
  provideLazyProvider(AXP_MENU_PROVIDER, () => Promise.resolve().then(function () { return menu_provider; }).then((m) => m.AXMAssessmentManagementMenuProvider)),
4585
7709
  provideLazyProvider(AXP_ENTITY_DEFINITION_LOADER, () => Promise.resolve().then(function () { return entity_provider; }).then((m) => m.AXMAssessmentManagementEntityProvider)),
4586
- provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-assessment-management-index-D8KjfHAH.mjs').then((m) => m.AXMQuestionnaireQuestionsPageComponentProvider)),
4587
- provideCommand('Questionnaire:Questions:Save', () => import('./acorex-modules-assessment-management-save-questionnaire-questions.command-koGmUbNE.mjs').then((m) => m.AXMSaveQuestionnaireQuestionsCommand)),
7710
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index$1; }).then((m) => m.AXMQuestionnaireQuestionsPageComponentProvider)),
7711
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index; }).then((m) => m.AXMQuestionnaireOutcomesPageComponentProvider)),
7712
+ provideCommand('Questionnaire:Questions:Save', () => Promise.resolve().then(function () { return saveQuestionnaireQuestions_command; }).then((m) => m.AXMSaveQuestionnaireQuestionsCommand)),
7713
+ provideCommand('Questionnaire:Outcomes:Save', () => Promise.resolve().then(function () { return saveQuestionnaireOutcomes_command; }).then((m) => m.AXMSaveQuestionnaireOutcomesCommand)),
4588
7714
  provideLazyProvider(AXP_SEARCH_PROVIDER, () => Promise.resolve().then(function () { return searchCommand_provider; }).then((m) => m.AXMAssessmentManagementSearchCommandProvider)),
4589
7715
  ],
4590
7716
  }]
@@ -4593,109 +7719,136 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4593
7719
  //#region ---- QuestionBankItem Model ----
4594
7720
  //#endregion
4595
7721
 
7722
+ //#region ---- Types ----
7723
+ //#endregion
7724
+
7725
+ //#region ---- Types ----
7726
+ //#endregion
7727
+
4596
7728
  //#region ---- Imports ----
4597
- const DEFAULT_PRE_QUESTIONNAIRE_CONFIG = { enabled: false };
4598
- const DEFAULT_POST_QUESTIONNAIRE_CONFIG = {
4599
- reviewEnabled: false,
4600
- summaryEnabled: false,
4601
- showCalculationResults: false,
4602
- };
4603
- const DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY = {
4604
- viewMode: 'single-page',
4605
- showProgressBar: true,
4606
- showTimer: false,
4607
- showQuestionNumbers: true,
4608
- validationStrategy: 'step',
4609
- completionMode: 'submit-only',
4610
- };
7729
+ //#endregion
7730
+ //#region ---- Persistence ----
4611
7731
  /**
4612
- * Maps entity pre/post to viewer config (defaults when omitted).
7732
+ * Loads a session by id using list query first, then byKey.
4613
7733
  */
4614
- function toPrePostConfig(q) {
4615
- if (!q) {
4616
- return null;
7734
+ async function loadSessionById(entityService, sessionId) {
7735
+ const sessionAccessor = entityService
7736
+ .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
7737
+ .data();
7738
+ const listMatch = await sessionAccessor.query({
7739
+ skip: 0,
7740
+ take: 1,
7741
+ filter: {
7742
+ field: 'id',
7743
+ operator: { type: 'equal' },
7744
+ value: sessionId,
7745
+ },
7746
+ });
7747
+ const fromQuery = first(listMatch.items);
7748
+ if (fromQuery) {
7749
+ return fromQuery;
4617
7750
  }
4618
- const pre = {
4619
- ...DEFAULT_PRE_QUESTIONNAIRE_CONFIG,
4620
- ...q.pre,
4621
- enabled: q.pre?.enabled === true,
4622
- };
4623
- const post = {
4624
- ...DEFAULT_POST_QUESTIONNAIRE_CONFIG,
4625
- ...q.post,
4626
- reviewEnabled: q.post?.reviewEnabled === true,
4627
- summaryEnabled: q.post?.summaryEnabled === true,
4628
- showCalculationResults: q.post?.showCalculationResults === true,
4629
- };
4630
- return { pre, post };
7751
+ return defaultTo(await sessionAccessor.byKey(sessionId), null);
4631
7752
  }
4632
- const VIEW_MODE_VALUES = ['single-page', 'page-per-group', 'page-per-question', 'side-menu'];
4633
- const VALIDATION_STRATEGY_VALUES = ['step', 'end'];
4634
- const COMPLETION_MODE_VALUES = ['submit-only', 'save-and-continue'];
4635
7753
  /**
4636
- * Coerces stored view mode (string or SelectBox `{ id }`) to a known value.
7754
+ * Loads assessment case by id using query first, then byKey.
4637
7755
  */
4638
- function normalizeViewMode(value) {
4639
- if (typeof value === 'string' && VIEW_MODE_VALUES.includes(value)) {
4640
- return value;
4641
- }
4642
- if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
4643
- return normalizeViewMode(value.id);
7756
+ async function loadCaseById(entityService, caseId) {
7757
+ const caseAccessor = entityService
7758
+ .withEntity(RootConfig.module.name, RootConfig.entities.assessmentCase.name)
7759
+ .data();
7760
+ const listMatch = await caseAccessor.query({
7761
+ skip: 0,
7762
+ take: 1,
7763
+ filter: {
7764
+ field: 'id',
7765
+ operator: { type: 'equal' },
7766
+ value: caseId,
7767
+ },
7768
+ });
7769
+ const fromQuery = first(listMatch.items);
7770
+ if (fromQuery) {
7771
+ return fromQuery;
4644
7772
  }
4645
- return 'single-page';
7773
+ return defaultTo(await caseAccessor.byKey(caseId), null);
4646
7774
  }
4647
7775
  /**
4648
- * Coerces stored validation strategy (string or SelectBox `{ id }`) to a known value.
7776
+ * Bumps case audit metadata by performing a domain update (history middleware sets auditInfo.updated.at).
4649
7777
  */
4650
- function normalizeValidationStrategy(value) {
4651
- if (typeof value === 'string' && VALIDATION_STRATEGY_VALUES.includes(value)) {
4652
- return value;
4653
- }
4654
- if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
4655
- return normalizeValidationStrategy(value.id);
4656
- }
4657
- return 'step';
7778
+ async function touchAssessmentCaseActivity(entityService, caseId) {
7779
+ const assessmentCase = await loadCaseById(entityService, caseId);
7780
+ if (!assessmentCase) {
7781
+ return;
7782
+ }
7783
+ const caseAccessor = entityService
7784
+ .withEntity(RootConfig.module.name, RootConfig.entities.assessmentCase.name)
7785
+ .data();
7786
+ const caseStatus = assessmentCase.statusId;
7787
+ const nextCaseStatus = caseStatus === AXPSystemStatusType.Closed || caseStatus === AXPSystemStatusType.Cancelled
7788
+ ? caseStatus
7789
+ : AXPSystemStatusType.InProgress;
7790
+ await caseAccessor.update(caseId, { statusId: nextCaseStatus });
4658
7791
  }
4659
7792
  /**
4660
- * Coerces stored completion mode (string or SelectBox `{ id }`) to a known value.
7793
+ * Upserts assessment session answers and status; moves case to in-progress when saving or submitting.
4661
7794
  */
4662
- function normalizeCompletionMode(value) {
4663
- if (typeof value === 'string' && COMPLETION_MODE_VALUES.includes(value)) {
4664
- return value;
7795
+ async function persistAssessmentSession(input) {
7796
+ const { entityService, questionnaireViewerService, caseId, sessionId, existingSession, responderId, role, structure, answers, finalize, progressMetrics, timeSpentSeconds, } = input;
7797
+ const calculatedProgress = questionnaireViewerService.calculateProgressFromStructure(structure, answers);
7798
+ const answeredQuestionsCount = defaultTo(get(progressMetrics, 'answeredQuestionsCount'), calculatedProgress.answeredQuestionsCount);
7799
+ const progressPercentage = defaultTo(get(progressMetrics, 'progressPercentage'), calculatedProgress.progressPercentage);
7800
+ const now = new Date();
7801
+ const sessionAccessor = entityService
7802
+ .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
7803
+ .data();
7804
+ let resolvedSessionId = sessionId;
7805
+ let currentSession = existingSession;
7806
+ if (resolvedSessionId && !currentSession) {
7807
+ currentSession = await loadSessionById(entityService, resolvedSessionId);
7808
+ }
7809
+ const sessionStatusId = finalize
7810
+ ? 'submitted'
7811
+ : get(currentSession, 'statusId') === 'submitted'
7812
+ ? 'submitted'
7813
+ : AXPSystemStatusType.InProgress;
7814
+ const sessionData = {
7815
+ caseId,
7816
+ responderId,
7817
+ role,
7818
+ answers,
7819
+ progressPercentage,
7820
+ answeredQuestionsCount,
7821
+ lastSavedAt: now,
7822
+ statusId: sessionStatusId,
7823
+ };
7824
+ if (isEmpty(get(currentSession, 'startedAt'))) {
7825
+ sessionData['startedAt'] = now;
4665
7826
  }
4666
- if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
4667
- return normalizeCompletionMode(value.id);
7827
+ if (timeSpentSeconds != null && timeSpentSeconds >= 0) {
7828
+ sessionData['timeSpent'] = timeSpentSeconds;
4668
7829
  }
4669
- return 'submit-only';
4670
- }
4671
- /**
4672
- * Maps entity `display` to {@link QuestionnaireDisplaySettings} (defaults when omitted).
4673
- */
4674
- function toDisplaySettings(q) {
4675
- if (!q) {
4676
- return null;
7830
+ const questionnaireId = input.questionnaireId;
7831
+ if (questionnaireId) {
7832
+ const { values } = await questionnaireViewerService.evaluateOutcomes(answers, {
7833
+ questionnaireId,
7834
+ structure,
7835
+ });
7836
+ if (isObject(values) && !isEmpty(values)) {
7837
+ sessionData['outcomes'] = values;
7838
+ }
4677
7839
  }
4678
- const d = { ...DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY, ...q.display };
4679
- return {
4680
- viewMode: normalizeViewMode(d.viewMode),
4681
- showProgressBar: d.showProgressBar ?? true,
4682
- showTimer: d.showTimer ?? false,
4683
- showQuestionNumbers: d.showQuestionNumbers ?? true,
4684
- validationStrategy: normalizeValidationStrategy(d.validationStrategy),
4685
- completionMode: normalizeCompletionMode(d.completionMode),
4686
- };
7840
+ if (resolvedSessionId) {
7841
+ await sessionAccessor.update(resolvedSessionId, sessionData);
7842
+ }
7843
+ else {
7844
+ sessionData['startedAt'] = now;
7845
+ resolvedSessionId = await sessionAccessor.create(sessionData);
7846
+ }
7847
+ await touchAssessmentCaseActivity(entityService, caseId);
7848
+ return { sessionId: resolvedSessionId };
4687
7849
  }
4688
7850
  //#endregion
4689
7851
 
4690
- //#region ---- Types ----
4691
- //#endregion
4692
-
4693
- //#region ---- Types ----
4694
- //#endregion
4695
-
4696
- //#region ---- Types ----
4697
- //#endregion
4698
-
4699
7852
  //#region ---- Imports ----
4700
7853
  //#endregion
4701
7854
  //#region ---- Component ----
@@ -4720,7 +7873,7 @@ class AXMPreQuestionnaireContentComponent {
4720
7873
  [innerHTML]="content() | translate | async | safe: 'html'"
4721
7874
  ></div>
4722
7875
  </div>
4723
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AXSafePipe, name: "safe" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
7876
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AXSafePipe, name: "safe" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
4724
7877
  }
4725
7878
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPreQuestionnaireContentComponent, decorators: [{
4726
7879
  type: Component,
@@ -4743,6 +7896,166 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4743
7896
  }]
4744
7897
  }], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }] } });
4745
7898
 
7899
+ //#region ---- Imports ----
7900
+ //#endregion
7901
+ //#region ---- Presentation ----
7902
+ /**
7903
+ * Maps questionnaire outcome display metadata to platform value chip classes/styles.
7904
+ */
7905
+ function mapOutcomeDisplayToValuePresentation(display) {
7906
+ const classes = ['__outcome-value'];
7907
+ if (display?.emphasis === 'medium') {
7908
+ classes.push('__emphasis-medium');
7909
+ }
7910
+ else if (display?.emphasis === 'bold') {
7911
+ classes.push('__emphasis-bold');
7912
+ }
7913
+ if (!display?.color) {
7914
+ classes.push('__tone-default');
7915
+ return { classes: classes.join(' ') };
7916
+ }
7917
+ const parsed = parseQuestionnaireOutcomeDisplayColor(display.color);
7918
+ if (!parsed) {
7919
+ classes.push('__tone-default');
7920
+ return { classes: classes.join(' ') };
7921
+ }
7922
+ if (isQuestionnaireOutcomeDisplayColorHex(parsed.background)) {
7923
+ return {
7924
+ classes: classes.join(' '),
7925
+ color: parsed.foreground,
7926
+ backgroundColor: parsed.background,
7927
+ borderColor: parsed.border,
7928
+ };
7929
+ }
7930
+ return {
7931
+ classes: [...classes, parsed.foreground, parsed.background, parsed.border].join(' '),
7932
+ };
7933
+ }
7934
+ function mapOutcomeRow(entry) {
7935
+ return {
7936
+ key: entry.key,
7937
+ label: entry.label,
7938
+ value: entry.value,
7939
+ iconClass: entry.display?.iconClass,
7940
+ valuePresentation: mapOutcomeDisplayToValuePresentation(entry.display),
7941
+ };
7942
+ }
7943
+ function resolveSectionText(value, locale, fallback) {
7944
+ const resolved = resolveMultiLanguageString(value, locale).trim();
7945
+ return resolved.length > 0 ? resolved : fallback;
7946
+ }
7947
+ //#endregion
7948
+ //#region ---- View model ----
7949
+ /**
7950
+ * Maps post-submit placeholder values or snapshot sections to {@link AXPOutcomeResultsViewModel}.
7951
+ */
7952
+ function mapToOutcomeResultsViewModel(source, options) {
7953
+ const locale = options?.locale ?? 'en-US';
7954
+ const sections = source?.outcomeSections;
7955
+ if (Array.isArray(sections) && sections.length > 0) {
7956
+ return {
7957
+ sections: sections.map((group) => mapSectionGroup(group, locale)),
7958
+ rows: sections.length === 1 ? sections[0].items.map(mapOutcomeRow) : undefined,
7959
+ };
7960
+ }
7961
+ const outcomes = source?.outcomes;
7962
+ const titles = source?.outcomeTitles;
7963
+ if (!outcomes || typeof outcomes !== 'object') {
7964
+ return { rows: [] };
7965
+ }
7966
+ const rows = Object.entries(outcomes).map(([key, value]) => mapOutcomeRow({
7967
+ key,
7968
+ value,
7969
+ label: (titles?.[key] ?? '').trim() || humanizeOutcomeKey(key),
7970
+ }));
7971
+ return { rows };
7972
+ }
7973
+ function mapSectionGroup(group, locale) {
7974
+ const fallbackTitle = group.sectionName || group.trackKey;
7975
+ return {
7976
+ trackKey: group.trackKey,
7977
+ title: resolveSectionText(group.sectionTitle, locale, fallbackTitle),
7978
+ description: group.sectionDescription
7979
+ ? resolveSectionText(group.sectionDescription, locale, '')
7980
+ : undefined,
7981
+ items: group.items.map(mapOutcomeRow),
7982
+ };
7983
+ }
7984
+ function humanizeOutcomeKey(key) {
7985
+ return key
7986
+ .replace(/_/g, ' ')
7987
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
7988
+ .trim()
7989
+ .split(/\s+/)
7990
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
7991
+ .join(' ');
7992
+ }
7993
+ //#endregion
7994
+
7995
+ //#region ---- Imports ----
7996
+ //#endregion
7997
+ //#region ---- Component ----
7998
+ /**
7999
+ * Post-submit outcome results step; delegates presentation to {@link AXPOutcomeResultsViewerComponent}.
8000
+ */
8001
+ class AXMQuestionnaireOutcomeResultsComponent {
8002
+ constructor() {
8003
+ //#region ---- Services & Dependencies ----
8004
+ this.translationService = inject(AXTranslationService);
8005
+ //#endregion
8006
+ //#region ---- Inputs ----
8007
+ /** Evaluated outcomes, titles, and optional grouped sections from the viewer service. */
8008
+ this.placeholderValues = input(null, ...(ngDevMode ? [{ debugName: "placeholderValues" }] : /* istanbul ignore next */ []));
8009
+ //#endregion
8010
+ //#region ---- Computed ----
8011
+ this.viewModel = computed(() => {
8012
+ const locale = this.translationService.getActiveLang?.() ?? 'en-US';
8013
+ return mapToOutcomeResultsViewModel(this.placeholderValues(), { locale });
8014
+ }, ...(ngDevMode ? [{ debugName: "viewModel" }] : /* istanbul ignore next */ []));
8015
+ }
8016
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomeResultsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8017
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: AXMQuestionnaireOutcomeResultsComponent, isStandalone: true, selector: "axm-questionnaire-outcome-results", inputs: { placeholderValues: { classPropertyName: "placeholderValues", publicName: "placeholderValues", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "axm-questionnaire-outcome-results ax-block" }, ngImport: i0, template: `
8018
+ <axp-outcome-results-viewer
8019
+ [model]="viewModel()"
8020
+ [title]="
8021
+ ('@assessment-management:questionnaires.components.questionnaire-viewer.flow.outcomes-section-title'
8022
+ | translate
8023
+ | async) ?? ''
8024
+ "
8025
+ [emptyMessage]="
8026
+ ('@assessment-management:questionnaires.components.questionnaire-viewer.flow.no-outcomes'
8027
+ | translate
8028
+ | async) ?? ''
8029
+ "
8030
+ />
8031
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPOutcomeResultsViewerComponent, selector: "axp-outcome-results-viewer", inputs: ["model", "title", "emptyMessage"] }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
8032
+ }
8033
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomeResultsComponent, decorators: [{
8034
+ type: Component,
8035
+ args: [{
8036
+ selector: 'axm-questionnaire-outcome-results',
8037
+ template: `
8038
+ <axp-outcome-results-viewer
8039
+ [model]="viewModel()"
8040
+ [title]="
8041
+ ('@assessment-management:questionnaires.components.questionnaire-viewer.flow.outcomes-section-title'
8042
+ | translate
8043
+ | async) ?? ''
8044
+ "
8045
+ [emptyMessage]="
8046
+ ('@assessment-management:questionnaires.components.questionnaire-viewer.flow.no-outcomes'
8047
+ | translate
8048
+ | async) ?? ''
8049
+ "
8050
+ />
8051
+ `,
8052
+ changeDetection: ChangeDetectionStrategy.OnPush,
8053
+ imports: [AXTranslationModule, AsyncPipe, AXPOutcomeResultsViewerComponent],
8054
+ encapsulation: ViewEncapsulation.None,
8055
+ host: { class: 'axm-questionnaire-outcome-results ax-block' },
8056
+ }]
8057
+ }], propDecorators: { placeholderValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderValues", required: false }] }] } });
8058
+
4746
8059
  //#region ---- Imports ----
4747
8060
  //#endregion
4748
8061
  //#region ---- Component ----
@@ -4758,7 +8071,7 @@ class AXMPostQuestionnaireContentComponent {
4758
8071
  this.translationService = inject(AXTranslationService);
4759
8072
  //#endregion
4760
8073
  //#region ---- Inputs ----
4761
- /** Display mode: review, summary (rich text), or results (calculation list). */
8074
+ /** Display mode: review, summary (rich text), or results (outcome list). */
4762
8075
  this.mode = input('review', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
4763
8076
  /** Questions with answers for review mode. */
4764
8077
  this.questionsWithAnswers = input([], ...(ngDevMode ? [{ debugName: "questionsWithAnswers" }] : /* istanbul ignore next */ []));
@@ -4766,7 +8079,7 @@ class AXMPostQuestionnaireContentComponent {
4766
8079
  this.reviewContext = input(null, ...(ngDevMode ? [{ debugName: "reviewContext" }] : /* istanbul ignore next */ []));
4767
8080
  /** Summary content (rich text). Supports multi-language and placeholders. */
4768
8081
  this.summaryContent = input(null, ...(ngDevMode ? [{ debugName: "summaryContent" }] : /* istanbul ignore next */ []));
4769
- /** Placeholder values: totals, time, and calculation outcomes. */
8082
+ /** Placeholder values: totals, time, and outcome values. */
4770
8083
  this.placeholderValues = input(null, ...(ngDevMode ? [{ debugName: "placeholderValues" }] : /* istanbul ignore next */ []));
4771
8084
  /** Locale for multi-language content. Defaults to active lang. */
4772
8085
  this.locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
@@ -4807,19 +8120,6 @@ class AXMPostQuestionnaireContentComponent {
4807
8120
  }
4808
8121
  return groups;
4809
8122
  }, ...(ngDevMode ? [{ debugName: "reviewSectionGroups" }] : /* istanbul ignore next */ []));
4810
- /** Calculation name/value pairs for results step; `label` is entity title or humanized name. */
4811
- this.outcomeEntries = computed(() => {
4812
- const pv = this.placeholderValues();
4813
- const o = pv?.outcomes;
4814
- const titles = pv?.outcomeTitles;
4815
- if (!o || typeof o !== 'object')
4816
- return [];
4817
- return Object.entries(o).map(([key, value]) => ({
4818
- key,
4819
- value,
4820
- label: (titles?.[key] ?? '').trim() || this.humanizeOutcomeKey(key),
4821
- }));
4822
- }, ...(ngDevMode ? [{ debugName: "outcomeEntries" }] : /* istanbul ignore next */ []));
4823
8123
  this.summaryContentHtml = computed(() => {
4824
8124
  const content = this.summaryContent();
4825
8125
  const values = this.placeholderValues();
@@ -4836,16 +8136,6 @@ class AXMPostQuestionnaireContentComponent {
4836
8136
  }
4837
8137
  //#endregion
4838
8138
  //#region ---- Helpers ----
4839
- /** Fallback when no calculation entity title is available. */
4840
- humanizeOutcomeKey(key) {
4841
- return key
4842
- .replace(/_/g, ' ')
4843
- .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
4844
- .trim()
4845
- .split(/\s+/)
4846
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
4847
- .join(' ');
4848
- }
4849
8139
  formatAnswer(answer) {
4850
8140
  if (answer == null || answer === '')
4851
8141
  return '—';
@@ -4934,43 +8224,7 @@ class AXMPostQuestionnaireContentComponent {
4934
8224
  </div>
4935
8225
  </div>
4936
8226
  } @else if (mode() === 'results') {
4937
- <div
4938
- class="axm-post-questionnaire-content __results ax-flex ax-flex-col ax-items-center ax-justify-center ax-p-8 ax-min-h-[200px] ax-text-on-surface"
4939
- >
4940
- @if (outcomeEntries().length > 0) {
4941
- <div class="ax-max-w-2xl ax-w-full">
4942
- <h3 class="ax-text-base ax-font-semibold ax-mb-3 ax-text-on-surface">
4943
- {{
4944
- '@assessment-management:questionnaires.components.questionnaire-viewer.flow.outcomes-section-title'
4945
- | translate
4946
- | async
4947
- }}
4948
- </h3>
4949
- <ul
4950
- class="ax-list-none ax-p-0 ax-m-0 ax-flex ax-flex-col ax-gap-1 ax-rounded-lg ax-border ax-border-surface ax-lighter-surface ax-overflow-hidden"
4951
- >
4952
- @for (row of outcomeEntries(); track row.key) {
4953
- <li
4954
- class="ax-flex ax-justify-between ax-gap-4 ax-py-3 ax-px-4 ax-border-b ax-border-surface last:ax-border-b-0"
4955
- >
4956
- <span class="ax-text-sm ax-font-medium __post-text-muted">{{ row.label }}</span>
4957
- <span class="ax-text-sm ax-text-on-surface ax-text-right ax-break-all">{{
4958
- formatAnswer(row.value)
4959
- }}</span>
4960
- </li>
4961
- }
4962
- </ul>
4963
- </div>
4964
- } @else {
4965
- <p class="ax-text-sm __post-text-muted">
4966
- {{
4967
- '@assessment-management:questionnaires.components.questionnaire-viewer.flow.no-outcomes'
4968
- | translate
4969
- | async
4970
- }}
4971
- </p>
4972
- }
4973
- </div>
8227
+ <axm-questionnaire-outcome-results [placeholderValues]="placeholderValues()" />
4974
8228
  } @else {
4975
8229
  <div
4976
8230
  class="axm-post-questionnaire-content __summary ax-flex ax-flex-col ax-items-center ax-justify-center ax-p-8 ax-min-h-[200px] ax-text-on-surface"
@@ -4978,7 +8232,7 @@ class AXMPostQuestionnaireContentComponent {
4978
8232
  <div class="ax-max-w-2xl ax-w-full ax-prose ax-text-on-surface" [innerHTML]="summaryContentHtml()"></div>
4979
8233
  </div>
4980
8234
  }
4981
- `, isInline: true, styles: [".axm-post-questionnaire-content .__post-text-muted{color:rgb(var(--ax-sys-color-on-surface-variant))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "component", type: AXMQuestionnaireViewerQuestionComponent, selector: "axm-questionnaire-viewer-question", inputs: ["question", "contextData", "sectionOrder", "questionIndex", "showQuestionNumbers", "showQuestionCounter", "currentPosition", "mode"] }, { kind: "component", type: AXMQuestionnaireViewerSectionHeaderComponent, selector: "axm-questionnaire-viewer-section-header", inputs: ["title", "description", "titleSize", "marginBottom"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
8235
+ `, isInline: true, styles: [".axm-post-questionnaire-content .__post-text-muted{color:rgb(var(--ax-sys-color-on-surface-variant))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "component", type: AXMQuestionnaireOutcomeResultsComponent, selector: "axm-questionnaire-outcome-results", inputs: ["placeholderValues"] }, { kind: "component", type: AXMQuestionnaireViewerQuestionComponent, selector: "axm-questionnaire-viewer-question", inputs: ["question", "contextData", "sectionOrder", "questionIndex", "showQuestionNumbers", "showQuestionCounter", "currentPosition", "mode"] }, { kind: "component", type: AXMQuestionnaireViewerSectionHeaderComponent, selector: "axm-questionnaire-viewer-section-header", inputs: ["title", "description", "titleSize", "marginBottom"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
4982
8236
  }
4983
8237
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPostQuestionnaireContentComponent, decorators: [{
4984
8238
  type: Component,
@@ -5056,43 +8310,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5056
8310
  </div>
5057
8311
  </div>
5058
8312
  } @else if (mode() === 'results') {
5059
- <div
5060
- class="axm-post-questionnaire-content __results ax-flex ax-flex-col ax-items-center ax-justify-center ax-p-8 ax-min-h-[200px] ax-text-on-surface"
5061
- >
5062
- @if (outcomeEntries().length > 0) {
5063
- <div class="ax-max-w-2xl ax-w-full">
5064
- <h3 class="ax-text-base ax-font-semibold ax-mb-3 ax-text-on-surface">
5065
- {{
5066
- '@assessment-management:questionnaires.components.questionnaire-viewer.flow.outcomes-section-title'
5067
- | translate
5068
- | async
5069
- }}
5070
- </h3>
5071
- <ul
5072
- class="ax-list-none ax-p-0 ax-m-0 ax-flex ax-flex-col ax-gap-1 ax-rounded-lg ax-border ax-border-surface ax-lighter-surface ax-overflow-hidden"
5073
- >
5074
- @for (row of outcomeEntries(); track row.key) {
5075
- <li
5076
- class="ax-flex ax-justify-between ax-gap-4 ax-py-3 ax-px-4 ax-border-b ax-border-surface last:ax-border-b-0"
5077
- >
5078
- <span class="ax-text-sm ax-font-medium __post-text-muted">{{ row.label }}</span>
5079
- <span class="ax-text-sm ax-text-on-surface ax-text-right ax-break-all">{{
5080
- formatAnswer(row.value)
5081
- }}</span>
5082
- </li>
5083
- }
5084
- </ul>
5085
- </div>
5086
- } @else {
5087
- <p class="ax-text-sm __post-text-muted">
5088
- {{
5089
- '@assessment-management:questionnaires.components.questionnaire-viewer.flow.no-outcomes'
5090
- | translate
5091
- | async
5092
- }}
5093
- </p>
5094
- }
5095
- </div>
8313
+ <axm-questionnaire-outcome-results [placeholderValues]="placeholderValues()" />
5096
8314
  } @else {
5097
8315
  <div
5098
8316
  class="axm-post-questionnaire-content __summary ax-flex ax-flex-col ax-items-center ax-justify-center ax-p-8 ax-min-h-[200px] ax-text-on-surface"
@@ -5104,6 +8322,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5104
8322
  CommonModule,
5105
8323
  AXTranslationModule,
5106
8324
  AXPWidgetCoreModule,
8325
+ AXMQuestionnaireOutcomeResultsComponent,
5107
8326
  AXMQuestionnaireViewerQuestionComponent,
5108
8327
  AXMQuestionnaireViewerSectionHeaderComponent,
5109
8328
  ], encapsulation: ViewEncapsulation.None, host: { class: 'axm-post-questionnaire-content' }, styles: [".axm-post-questionnaire-content .__post-text-muted{color:rgb(var(--ax-sys-color-on-surface-variant))}\n"] }]
@@ -5133,6 +8352,7 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5133
8352
  this.isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
5134
8353
  this.loadError = signal(null, ...(ngDevMode ? [{ debugName: "loadError" }] : /* istanbul ignore next */ []));
5135
8354
  this.currentSessionId = signal(null, ...(ngDevMode ? [{ debugName: "currentSessionId" }] : /* istanbul ignore next */ []));
8355
+ this.existingSession = signal(null, ...(ngDevMode ? [{ debugName: "existingSession" }] : /* istanbul ignore next */ []));
5136
8356
  this.flowStep = signal('questions', ...(ngDevMode ? [{ debugName: "flowStep" }] : /* istanbul ignore next */ []));
5137
8357
  this.reviewSnapshot = signal([], ...(ngDevMode ? [{ debugName: "reviewSnapshot" }] : /* istanbul ignore next */ []));
5138
8358
  this.reviewContext = signal(null, ...(ngDevMode ? [{ debugName: "reviewContext" }] : /* istanbul ignore next */ []));
@@ -5219,9 +8439,12 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5219
8439
  .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
5220
8440
  .data()
5221
8441
  .byKey(existingSessionId);
5222
- if (session?.answers) {
5223
- answers = session.answers;
8442
+ if (session) {
8443
+ if (session.answers) {
8444
+ answers = session.answers;
8445
+ }
5224
8446
  this.currentSessionId.set(existingSessionId);
8447
+ this.existingSession.set(session);
5225
8448
  }
5226
8449
  }
5227
8450
  this.questionnaire.set(questionnaire);
@@ -5253,7 +8476,7 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5253
8476
  this.handleDone();
5254
8477
  return;
5255
8478
  }
5256
- if (cfg?.post.showCalculationResults === true) {
8479
+ if (cfg?.post.showOutcomeResults === true) {
5257
8480
  this.flowStep.set('results');
5258
8481
  return;
5259
8482
  }
@@ -5269,7 +8492,6 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5269
8492
  const q = this.questionnaire();
5270
8493
  if (!caseIdVal || !structureVal || !q)
5271
8494
  return null;
5272
- const { answeredQuestionsCount, progressPercentage } = this.questionnaireViewerService.calculateProgressFromStructure(structureVal, answers);
5273
8495
  const responderId = this.authSession.user?.id;
5274
8496
  if (!responderId) {
5275
8497
  this.toastService.show({
@@ -5279,39 +8501,41 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5279
8501
  });
5280
8502
  return null;
5281
8503
  }
5282
- const now = new Date();
5283
- const sessionIdVal = this.currentSessionId();
5284
- const role = 'responder';
5285
- const sessionData = {
8504
+ const vm = this.viewerViewModel();
8505
+ const result = await persistAssessmentSession({
8506
+ entityService: this.entityService,
8507
+ questionnaireViewerService: this.questionnaireViewerService,
5286
8508
  caseId: caseIdVal,
8509
+ sessionId: this.currentSessionId(),
8510
+ existingSession: this.existingSession(),
5287
8511
  responderId,
5288
- role,
8512
+ role: 'responder',
8513
+ structure: structureVal,
8514
+ questionnaireId: q.id,
5289
8515
  answers,
5290
- progressPercentage,
5291
- answeredQuestionsCount,
5292
- lastSavedAt: now,
5293
- };
5294
- const sessionDataAccessor = this.entityService
8516
+ finalize: true,
8517
+ progressMetrics: vm
8518
+ ? {
8519
+ answeredQuestionsCount: vm.answeredQuestionsCount(),
8520
+ progressPercentage: vm.progressPercentage(),
8521
+ }
8522
+ : undefined,
8523
+ timeSpentSeconds: vm?.timerElapsed(),
8524
+ });
8525
+ this.currentSessionId.set(result.sessionId);
8526
+ const session = await this.entityService
5295
8527
  .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
5296
- .data();
5297
- if (sessionIdVal) {
5298
- await sessionDataAccessor.update(sessionIdVal, sessionData);
5299
- this.toastService.show({
5300
- color: 'success',
5301
- title: await this.translateService.translateAsync('@general:messages.form.saved.success.title'),
5302
- content: await this.translateService.translateAsync('@general:messages.form.saved.success.description'),
5303
- });
5304
- }
5305
- else {
5306
- const newSessionData = { ...sessionData, startedAt: now };
5307
- await sessionDataAccessor.create(newSessionData);
5308
- this.toastService.show({
5309
- color: 'success',
5310
- title: await this.translateService.translateAsync('@assessment-management:sessions.states.submitted'),
5311
- content: '',
5312
- });
8528
+ .data()
8529
+ .byKey(result.sessionId);
8530
+ if (session) {
8531
+ this.existingSession.set(session);
5313
8532
  }
5314
- const elapsed = this.viewerViewModel()?.timerElapsed() ?? 0;
8533
+ this.toastService.show({
8534
+ color: 'success',
8535
+ title: await this.translateService.translateAsync('@assessment-management:sessions.states.submitted'),
8536
+ content: '',
8537
+ });
8538
+ const elapsed = vm?.timerElapsed() ?? 0;
5315
8539
  return this.questionnaireViewerService.buildSummaryPlaceholderValues(structureVal, answers, {
5316
8540
  questionnaireId: q.id,
5317
8541
  elapsedSeconds: elapsed,
@@ -5405,9 +8629,9 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5405
8629
  this.router.navigateByUrl(this.getCasesListPath());
5406
8630
  }
5407
8631
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5408
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXMQuestionnaireViewerPageComponent, isStandalone: true, selector: "axm-questionnaire-viewer-page", host: { classAttribute: "axm-questionnaire-viewer-page" }, providers: [{ provide: AXPPageLayoutBase, useExisting: AXMQuestionnaireViewerPageComponent }], viewQueries: [{ propertyName: "viewer", first: true, predicate: ["viewer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<!-- Page shell layout -->\n<axp-page-layout>\n <!-- Main content area -->\n <axp-page-content class=\"ax-flex ax-flex-col ax-flex-1 ax-min-h-0\"\n [class.ax-overflow-hidden]=\"flowStep() === 'questions'\" [class.ax-overflow-y-auto]=\"flowStep() !== 'questions'\">\n <!-- Loading state -->\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-justify-center ax-min-h-[200px]\">\n <span>{{ '@assessment-management:questionnaires.components.questionnaire-viewer.loading.title' | translate | async\n }}</span>\n </div>\n }\n <!-- Error state -->\n @else if (loadError()) {\n <div class=\"ax-p-4 ax-text-error-600\">\n {{ loadError() }}\n </div>\n }\n <!-- Content state -->\n @else if (structure()) {\n <!-- Pre-questionnaire introduction content -->\n @if (flowStep() === 'pre') {\n <axm-pre-questionnaire-content [content]=\"prePostConfig()?.pre?.content\"></axm-pre-questionnaire-content>\n }\n <!-- Post-submit results content -->\n @else if (flowStep() === 'results') {\n <axm-post-questionnaire-content mode=\"results\"\n [placeholderValues]=\"submitResult()\"></axm-post-questionnaire-content>\n }\n <!-- Post-submit summary content -->\n @else if (flowStep() === 'summary') {\n <axm-post-questionnaire-content mode=\"summary\" [summaryContent]=\"prePostConfig()?.post?.summaryContent\"\n [placeholderValues]=\"submitResult()\"></axm-post-questionnaire-content>\n }\n <!-- Questionnaire and review flow -->\n @else {\n <!-- Review mode content -->\n @if (flowStep() === 'review') {\n <axm-post-questionnaire-content mode=\"review\" [questionsWithAnswers]=\"reviewSnapshot()\"\n [reviewContext]=\"reviewContext()\"></axm-post-questionnaire-content>\n }\n\n <!-- Questionnaire workspace (hidden while in review mode) -->\n <div class=\"__questions ax-flex ax-flex-col ax-flex-1 ax-min-h-0 ax-overflow-hidden\"\n [class.ax-invisible]=\"flowStep() === 'review'\" [style.position]=\"flowStep() === 'review' ? 'fixed' : null\"\n [style.left]=\"flowStep() === 'review' ? '-9999px' : null\" [class.ax-size-0]=\"flowStep() === 'review'\"\n [class.ax-overflow-hidden]=\"flowStep() === 'review'\">\n\n <!-- Questionnaire rendering container -->\n <div class=\"__content ax-flex ax-flex-col ax-flex-1 ax-min-h-0 ax-overflow-y-auto ax-overflow-x-hidden\">\n <axm-questionnaire-viewer #viewer [structure]=\"structure()!\" [config]=\"viewerConfig()\"\n [initialAnswers]=\"effectiveInitialAnswers()\"\n (answersChanged)=\"handleAnswersChanged($event)\"></axm-questionnaire-viewer>\n </div>\n </div>\n }\n }\n </axp-page-content>\n\n <!-- Footer actions (only when content is ready) -->\n @if (structure() && !isLoading() && !loadError()) {\n <axp-page-footer class=\"--animated\">\n <!-- Footer prefix: progress indicator -->\n <axp-layout-prefix>\n @if (flowStep() === 'questions') {\n <div class=\"__footer-progress ax-text-sm ax-font-mono ax-tabular-nums\" dir=\"ltr\">\n @if (showProgressBar()) {\n {{ answeredQuestionsCount() }} / {{ totalQuestionsCount() }}\n }\n </div>\n }\n <!-- @if (showTimer()) {\n <axp-stopwatch [mode]=\"timerTimeLimit() ? 'count-down' : 'count-up'\" [timeLimit]=\"timerTimeLimit()\"\n [format]=\"timerTimeLimit() && timerTimeLimit()! >= 3600 ? 'hh:mm:ss' : 'mm:ss'\" [autoStart]=\"true\"\n [showControls]=\"false\" [value]=\"timerElapsed()\"\n (valueChange)=\"viewerViewModel()!.updateTimerElapsed($event)\"></axp-stopwatch>\n } -->\n </axp-layout-prefix>\n\n <!-- Footer suffix: flow action buttons -->\n <axp-layout-suffix>\n @if (flowStep() === 'pre') {\n <ax-button look=\"solid\" color=\"primary\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.start' | translate | async\"\n (onClick)=\"flowStep.set('questions')\"></ax-button>\n }\n <!-- Question step actions -->\n @else if (flowStep() === 'questions') {\n <!-- Question navigation buttons -->\n @if (showNavigationButtons()) {\n <ax-button look=\"solid\" [disabled]=\"!canNavigatePrevious()\"\n [text]=\"'@general:actions.previous.title' | translate | async\" (onClick)=\"handlePreviousClick()\"></ax-button>\n @if (!isLastStep()) {\n <ax-button look=\"solid\" color=\"primary\" [disabled]=\"!canNavigateNext()\"\n [text]=\"'@general:actions.next.title' | translate | async\" (onClick)=\"handleNextClick()\"></ax-button>\n }\n }\n\n <!-- Final-step actions -->\n @if (isLastStep() && showReviewButton()) {\n <ax-button look=\"solid\" color=\"primary\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.review' | translate | async\"\n [disabled]=\"!areAllRequiredQuestionsAnswered()\" (onClick)=\"handleReviewClick()\"></ax-button>\n }\n @if (isLastStep() && !showReviewButton()) {\n <ax-button look=\"solid\" color=\"primary\" [text]=\"'@general:actions.submit.title' | translate | async\"\n [disabled]=\"isReadonly() || isSubmitting() || !areAllRequiredQuestionsAnswered()\"\n (onClick)=\"handleSubmitClick()\"></ax-button>\n }\n }\n <!-- Review step actions -->\n @else if (flowStep() === 'review') {\n <ax-button look=\"outline\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.edit' | translate | async\"\n (onClick)=\"flowStep.set('questions')\"></ax-button>\n <ax-button look=\"solid\" color=\"primary\" [text]=\"'@general:actions.submit.title' | translate | async\"\n [disabled]=\"isSubmitting()\" (onClick)=\"handleReviewSubmit()\"></ax-button>\n }\n <!-- Results step actions -->\n @else if (flowStep() === 'results') {\n @if (prePostConfig()?.post?.summaryEnabled) {\n <ax-button look=\"solid\" color=\"primary\" [text]=\"'@general:actions.next.title' | translate | async\"\n (onClick)=\"flowStep.set('summary')\"></ax-button>\n } @else {\n <ax-button look=\"solid\" color=\"primary\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.done' | translate | async\"\n (onClick)=\"handleDone()\"></ax-button>\n }\n }\n <!-- Summary step action -->\n @else if (flowStep() === 'summary') {\n <ax-button look=\"solid\" color=\"primary\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.done' | translate | async\"\n (onClick)=\"handleDone()\"></ax-button>\n }\n </axp-layout-suffix>\n </axp-page-footer>\n }\n</axp-page-layout>", styles: ["axm-questionnaire-viewer-page .__questions{min-height:0px;overflow:hidden}axm-questionnaire-viewer-page .__header{z-index:20;display:flex;flex-shrink:0;flex-direction:column;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));position:sticky;top:0;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-page .__content axm-questionnaire-viewer{display:flex;height:auto;max-height:none;min-height:0px;width:100%;max-width:100%;flex:none;flex-direction:column}axm-questionnaire-viewer-page .__content axm-questionnaire-viewer .__questionnaire-container{min-height:0px;flex:none;overflow:visible}axm-questionnaire-viewer-page .__toolbar-meta{color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-page .__footer-progress{color:rgb(var(--ax-sys-color-on-surface-variant))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type:
8632
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXMQuestionnaireViewerPageComponent, isStandalone: true, selector: "axm-questionnaire-viewer-page", host: { classAttribute: "axm-questionnaire-viewer-page" }, providers: [{ provide: AXPPageLayoutBase, useExisting: AXMQuestionnaireViewerPageComponent }], viewQueries: [{ propertyName: "viewer", first: true, predicate: ["viewer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<!-- Page shell layout -->\n<axp-page-layout>\n <!-- Main content area -->\n <axp-page-content class=\"ax-flex ax-flex-col ax-flex-1 ax-min-h-0\"\n [class.ax-overflow-hidden]=\"flowStep() === 'questions'\" [class.ax-overflow-y-auto]=\"flowStep() !== 'questions'\">\n <!-- Loading state -->\n @if (isLoading()) {\n <div class=\"ax-flex ax-items-center ax-justify-center ax-min-h-[200px]\">\n <span>{{ '@assessment-management:questionnaires.components.questionnaire-viewer.loading.title' | translate | async\n }}</span>\n </div>\n }\n <!-- Error state -->\n @else if (loadError()) {\n <div class=\"ax-p-4 ax-text-error-600\">\n {{ loadError() }}\n </div>\n }\n <!-- Content state -->\n @else if (structure()) {\n <!-- Pre-questionnaire introduction content -->\n @if (flowStep() === 'pre') {\n <axm-pre-questionnaire-content [content]=\"prePostConfig()?.pre?.content\"></axm-pre-questionnaire-content>\n }\n <!-- Post-submit results content -->\n @else if (flowStep() === 'results') {\n <axm-post-questionnaire-content mode=\"results\"\n [placeholderValues]=\"submitResult()\"></axm-post-questionnaire-content>\n }\n <!-- Post-submit summary content -->\n @else if (flowStep() === 'summary') {\n <axm-post-questionnaire-content mode=\"summary\" [summaryContent]=\"prePostConfig()?.post?.summaryContent\"\n [placeholderValues]=\"submitResult()\"></axm-post-questionnaire-content>\n }\n <!-- Questionnaire and review flow -->\n @else {\n <!-- Review mode content -->\n @if (flowStep() === 'review') {\n <axm-post-questionnaire-content mode=\"review\" [questionsWithAnswers]=\"reviewSnapshot()\"\n [reviewContext]=\"reviewContext()\"></axm-post-questionnaire-content>\n }\n\n <!-- Questionnaire workspace (hidden while in review mode) -->\n <div class=\"__questions ax-flex ax-flex-col ax-flex-1 ax-min-h-0 ax-overflow-hidden\"\n [class.ax-invisible]=\"flowStep() === 'review'\" [style.position]=\"flowStep() === 'review' ? 'fixed' : null\"\n [style.left]=\"flowStep() === 'review' ? '-9999px' : null\" [class.ax-size-0]=\"flowStep() === 'review'\"\n [class.ax-overflow-hidden]=\"flowStep() === 'review'\">\n\n <!-- Questionnaire rendering container -->\n <div class=\"__content ax-flex ax-flex-col ax-flex-1 ax-min-h-0 ax-overflow-y-auto ax-overflow-x-hidden\">\n <axm-questionnaire-viewer #viewer [structure]=\"structure()!\" [config]=\"viewerConfig()\"\n [initialAnswers]=\"effectiveInitialAnswers()\"\n (answersChanged)=\"handleAnswersChanged($event)\"></axm-questionnaire-viewer>\n </div>\n </div>\n }\n }\n </axp-page-content>\n\n <!-- Footer actions (only when content is ready) -->\n @if (structure() && !isLoading() && !loadError()) {\n <axp-page-footer class=\"--animated\">\n <!-- Footer prefix: progress indicator -->\n <axp-layout-prefix>\n @if (flowStep() === 'questions') {\n <div class=\"__footer-progress ax-text-sm ax-font-mono ax-tabular-nums\" dir=\"ltr\">\n @if (showProgressBar()) {\n {{ answeredQuestionsCount() }} / {{ totalQuestionsCount() }}\n }\n </div>\n }\n <!-- @if (showTimer()) {\n <axp-stopwatch [mode]=\"timerTimeLimit() ? 'count-down' : 'count-up'\" [timeLimit]=\"timerTimeLimit()\"\n [format]=\"timerTimeLimit() && timerTimeLimit()! >= 3600 ? 'hh:mm:ss' : 'mm:ss'\" [autoStart]=\"true\"\n [showControls]=\"false\" [value]=\"timerElapsed()\"\n (valueChange)=\"viewerViewModel()!.updateTimerElapsed($event)\"></axp-stopwatch>\n } -->\n </axp-layout-prefix>\n\n <!-- Footer suffix: flow action buttons -->\n <axp-layout-suffix>\n @if (flowStep() === 'pre') {\n <ax-button look=\"solid\" color=\"primary\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.start' | translate | async\"\n (onClick)=\"flowStep.set('questions')\"></ax-button>\n }\n <!-- Question step actions -->\n @else if (flowStep() === 'questions') {\n <!-- Question navigation buttons -->\n @if (showNavigationButtons()) {\n <ax-button look=\"solid\" [disabled]=\"!canNavigatePrevious()\"\n [text]=\"'@general:actions.previous.title' | translate | async\" (onClick)=\"handlePreviousClick()\"></ax-button>\n @if (!isLastStep()) {\n <ax-button look=\"solid\" color=\"primary\" [disabled]=\"!canNavigateNext()\"\n [text]=\"'@general:actions.next.title' | translate | async\" (onClick)=\"handleNextClick()\"></ax-button>\n }\n }\n\n <!-- Final-step actions -->\n @if (isLastStep() && showReviewButton()) {\n <ax-button look=\"solid\" color=\"primary\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.review' | translate | async\"\n [disabled]=\"!areAllRequiredQuestionsAnswered()\" (onClick)=\"handleReviewClick()\"></ax-button>\n }\n @if (isLastStep() && !showReviewButton()) {\n <ax-button look=\"solid\" color=\"primary\" [text]=\"'@general:actions.submit.title' | translate | async\"\n [disabled]=\"isReadonly() || isSubmitting() || !areAllRequiredQuestionsAnswered()\"\n (onClick)=\"handleSubmitClick()\"></ax-button>\n }\n }\n <!-- Review step actions -->\n @else if (flowStep() === 'review') {\n <ax-button look=\"outline\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.edit' | translate | async\"\n (onClick)=\"flowStep.set('questions')\"></ax-button>\n <ax-button look=\"solid\" color=\"primary\" [text]=\"'@general:actions.submit.title' | translate | async\"\n [disabled]=\"isSubmitting()\" (onClick)=\"handleReviewSubmit()\"></ax-button>\n }\n <!-- Results step actions -->\n @else if (flowStep() === 'results') {\n @if (prePostConfig()?.post?.summaryEnabled) {\n <ax-button look=\"solid\" color=\"primary\" [text]=\"'@general:actions.next.title' | translate | async\"\n (onClick)=\"flowStep.set('summary')\"></ax-button>\n } @else {\n <ax-button look=\"solid\" color=\"primary\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.done' | translate | async\"\n (onClick)=\"handleDone()\"></ax-button>\n }\n }\n <!-- Summary step action -->\n @else if (flowStep() === 'summary') {\n <ax-button look=\"solid\" color=\"primary\"\n [text]=\"'@assessment-management:questionnaires.components.questionnaire-viewer.flow.done' | translate | async\"\n (onClick)=\"handleDone()\"></ax-button>\n }\n </axp-layout-suffix>\n </axp-page-footer>\n }\n</axp-page-layout>", styles: ["axm-questionnaire-viewer-page .__questions{min-height:0px;overflow:hidden}axm-questionnaire-viewer-page .__header{z-index:20;display:flex;flex-shrink:0;flex-direction:column;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));position:sticky;top:0;color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-page .__content axm-questionnaire-viewer{display:flex;height:auto;max-height:none;min-height:0px;width:100%;max-width:100%;flex:none;flex-direction:column}axm-questionnaire-viewer-page .__content axm-questionnaire-viewer .__questionnaire-container{min-height:0px;flex:none;overflow:visible}axm-questionnaire-viewer-page .__toolbar-meta{color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-page .__footer-progress{color:rgb(var(--ax-sys-color-on-surface-variant))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutBlockComponent, selector: " axp-page-content, axp-page-footer-container, axp-page-footer, axp-page-header, axp-page-header-container, axp-page-toolbar, axp-layout-content, axp-layout-page-content, axp-layout-sections, axp-layout-body, axp-layout-page-body, axp-layout-prefix, axp-layout-suffix, axp-layout-title-bar, axp-layout-title, axp-layout-title-actions, axp-layout-nav-button, axp-layout-description, axp-layout-breadcrumbs, axp-layout-list-action, " }, { kind: "component", type:
5409
8633
  //AXPStopwatchComponent,
5410
- AXMQuestionnaireViewerComponent, selector: "axm-questionnaire-viewer", inputs: ["questionnaireId", "structure", "config", "initialAnswers"], outputs: ["answersChanged", "questionAnswered"] }, { kind: "component", type: AXMPreQuestionnaireContentComponent, selector: "axm-pre-questionnaire-content", inputs: ["content"] }, { kind: "component", type: AXMPostQuestionnaireContentComponent, selector: "axm-post-questionnaire-content", inputs: ["mode", "questionsWithAnswers", "reviewContext", "summaryContent", "placeholderValues", "locale", "showQuestionNumbers"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
8634
+ AXMQuestionnaireViewerComponent, selector: "axm-questionnaire-viewer", inputs: ["questionnaireId", "structure", "config", "initialAnswers"], outputs: ["answersChanged", "questionAnswered"] }, { kind: "component", type: AXMPreQuestionnaireContentComponent, selector: "axm-pre-questionnaire-content", inputs: ["content"] }, { kind: "component", type: AXMPostQuestionnaireContentComponent, selector: "axm-post-questionnaire-content", inputs: ["mode", "questionsWithAnswers", "reviewContext", "summaryContent", "placeholderValues", "locale", "showQuestionNumbers"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
5411
8635
  }
5412
8636
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerPageComponent, decorators: [{
5413
8637
  type: Component,
@@ -5450,6 +8674,8 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5450
8674
  this.isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
5451
8675
  this.loadError = signal(null, ...(ngDevMode ? [{ debugName: "loadError" }] : /* istanbul ignore next */ []));
5452
8676
  this.isSubmitting = signal(false, ...(ngDevMode ? [{ debugName: "isSubmitting" }] : /* istanbul ignore next */ []));
8677
+ this.sessionId = signal(null, ...(ngDevMode ? [{ debugName: "sessionId" }] : /* istanbul ignore next */ []));
8678
+ this.existingSession = signal(null, ...(ngDevMode ? [{ debugName: "existingSession" }] : /* istanbul ignore next */ []));
5453
8679
  this.flowStep = signal('questions', ...(ngDevMode ? [{ debugName: "flowStep" }] : /* istanbul ignore next */ []));
5454
8680
  this.reviewSnapshot = signal([], ...(ngDevMode ? [{ debugName: "reviewSnapshot" }] : /* istanbul ignore next */ []));
5455
8681
  this.reviewContext = signal(null, ...(ngDevMode ? [{ debugName: "reviewContext" }] : /* istanbul ignore next */ []));
@@ -5517,6 +8743,8 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5517
8743
  this.isLoading.set(false);
5518
8744
  return;
5519
8745
  }
8746
+ this.sessionId.set(sessionId);
8747
+ this.existingSession.set(session);
5520
8748
  const caseId = session.caseId;
5521
8749
  const assessmentCase = await this.entityService
5522
8750
  .withEntity(RootConfig.module.name, RootConfig.entities.assessmentCase.name)
@@ -5558,28 +8786,41 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5558
8786
  //#endregion
5559
8787
  //#region ---- Submit Handler ----
5560
8788
  async saveSessionAndGetSummary(answers) {
5561
- const sessionId = this.route.snapshot.paramMap.get('sessionId') ?? this.route.parent?.snapshot.paramMap.get('sessionId');
8789
+ const sessionIdVal = this.sessionId();
5562
8790
  const structureVal = this.structure();
5563
8791
  const q = this.questionnaire();
5564
- if (!sessionId || !structureVal || !q)
8792
+ const session = this.existingSession();
8793
+ if (!sessionIdVal || !structureVal || !q || !session)
5565
8794
  return null;
5566
- const { answeredQuestionsCount, progressPercentage } = this.questionnaireViewerService.calculateProgressFromStructure(structureVal, answers);
5567
- const session = await this.entityService
8795
+ const vm = this.viewerViewModel();
8796
+ const result = await persistAssessmentSession({
8797
+ entityService: this.entityService,
8798
+ questionnaireViewerService: this.questionnaireViewerService,
8799
+ caseId: session.caseId,
8800
+ sessionId: sessionIdVal,
8801
+ existingSession: session,
8802
+ responderId: session.responderId,
8803
+ role: session.role,
8804
+ structure: structureVal,
8805
+ questionnaireId: q.id,
8806
+ answers,
8807
+ finalize: true,
8808
+ progressMetrics: vm
8809
+ ? {
8810
+ answeredQuestionsCount: vm.answeredQuestionsCount(),
8811
+ progressPercentage: vm.progressPercentage(),
8812
+ }
8813
+ : undefined,
8814
+ timeSpentSeconds: vm?.timerElapsed(),
8815
+ });
8816
+ const updated = await this.entityService
5568
8817
  .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
5569
8818
  .data()
5570
- .byKey(sessionId);
5571
- if (session) {
5572
- await this.entityService
5573
- .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
5574
- .data()
5575
- .update(sessionId, {
5576
- answers,
5577
- progressPercentage,
5578
- answeredQuestionsCount,
5579
- lastSavedAt: new Date(),
5580
- });
8819
+ .byKey(result.sessionId);
8820
+ if (updated) {
8821
+ this.existingSession.set(updated);
5581
8822
  }
5582
- const elapsed = this.viewerViewModel()?.timerElapsed() ?? 0;
8823
+ const elapsed = vm?.timerElapsed() ?? 0;
5583
8824
  return this.questionnaireViewerService.buildSummaryPlaceholderValues(structureVal, answers, {
5584
8825
  questionnaireId: q.id,
5585
8826
  elapsedSeconds: elapsed,
@@ -5594,7 +8835,7 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5594
8835
  this.handleDone();
5595
8836
  return;
5596
8837
  }
5597
- if (cfg?.post.showCalculationResults === true) {
8838
+ if (cfg?.post.showOutcomeResults === true) {
5598
8839
  this.flowStep.set('results');
5599
8840
  return;
5600
8841
  }
@@ -5783,7 +9024,7 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5783
9024
  </ax-footer>
5784
9025
  }
5785
9026
  </div>
5786
- `, isInline: true, styles: ["axm-questionnaire-viewer-standalone-page{display:flex;min-height:0px;width:100%;flex:1 1 0%;flex-direction:column;overflow:hidden}axm-questionnaire-viewer-standalone-page .__body{display:flex;min-height:0px;width:100%;flex:1 1 0%;flex-direction:column;overflow:hidden}axm-questionnaire-viewer-standalone-page .__content-area{min-height:0px;overflow:hidden}axm-questionnaire-viewer-standalone-page .__questions{min-height:0px;overflow:hidden}axm-questionnaire-viewer-standalone-page .__header{z-index:20;display:flex;flex-shrink:0;flex-direction:column;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));color:rgb(var(--ax-sys-color-on-surface-variant));position:sticky;top:0}axm-questionnaire-viewer-standalone-page .__header .__header-top{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.75rem 1rem}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-meta{display:flex;align-items:center;gap:1rem;font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-meta .__meta-item{display:flex;align-items:center;gap:.375rem}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-meta .__meta-item i{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-primary-600),var(--tw-text-opacity, 1))}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-meta .__meta-item span{font-weight:500}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-timer{display:flex;flex-shrink:0;align-items:center}axm-questionnaire-viewer-standalone-page .__content-wrapper{display:flex;min-height:0px;flex:1 1 0%;overflow:hidden}axm-questionnaire-viewer-standalone-page .__content axm-questionnaire-viewer{display:flex;height:auto;max-height:none;min-height:0px;width:100%;max-width:100%;flex:none;flex-direction:column}axm-questionnaire-viewer-standalone-page .__content axm-questionnaire-viewer .__questionnaire-container{min-height:0px;flex:none;overflow:visible}axm-questionnaire-viewer-standalone-page .__toolbar-meta{color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-standalone-page .__footer-progress{color:rgb(var(--ax-sys-color-on-surface-variant))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: AXPStopwatchComponent, selector: "axp-stopwatch", inputs: ["mode", "timeLimit", "format", "autoStart", "showControls", "value"], outputs: ["valueChange", "timeUp"] }, { kind: "component", type: AXMQuestionnaireViewerComponent, selector: "axm-questionnaire-viewer", inputs: ["questionnaireId", "structure", "config", "initialAnswers"], outputs: ["answersChanged", "questionAnswered"] }, { kind: "component", type: AXMPreQuestionnaireContentComponent, selector: "axm-pre-questionnaire-content", inputs: ["content"] }, { kind: "component", type: AXMPostQuestionnaireContentComponent, selector: "axm-post-questionnaire-content", inputs: ["mode", "questionsWithAnswers", "reviewContext", "summaryContent", "placeholderValues", "locale", "showQuestionNumbers"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
9027
+ `, isInline: true, styles: ["axm-questionnaire-viewer-standalone-page{display:flex;min-height:0px;width:100%;flex:1 1 0%;flex-direction:column;overflow:hidden}axm-questionnaire-viewer-standalone-page .__body{display:flex;min-height:0px;width:100%;flex:1 1 0%;flex-direction:column;overflow:hidden}axm-questionnaire-viewer-standalone-page .__content-area{min-height:0px;overflow:hidden}axm-questionnaire-viewer-standalone-page .__questions{min-height:0px;overflow:hidden}axm-questionnaire-viewer-standalone-page .__header{z-index:20;display:flex;flex-shrink:0;flex-direction:column;background-color:rgb(var(--ax-sys-color-lightest-surface));color:rgb(var(--ax-sys-color-on-lightest-surface));border-color:rgb(var(--ax-sys-color-border-lightest-surface));border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));color:rgb(var(--ax-sys-color-on-surface-variant));position:sticky;top:0}axm-questionnaire-viewer-standalone-page .__header .__header-top{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.75rem 1rem}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-meta{display:flex;align-items:center;gap:1rem;font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(82 82 82 / var(--tw-text-opacity, 1))}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-meta .__meta-item{display:flex;align-items:center;gap:.375rem}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-meta .__meta-item i{font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgba(var(--ax-sys-color-primary-600),var(--tw-text-opacity, 1))}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-meta .__meta-item span{font-weight:500}axm-questionnaire-viewer-standalone-page .__header .__header-top .__header-timer{display:flex;flex-shrink:0;align-items:center}axm-questionnaire-viewer-standalone-page .__content-wrapper{display:flex;min-height:0px;flex:1 1 0%;overflow:hidden}axm-questionnaire-viewer-standalone-page .__content axm-questionnaire-viewer{display:flex;height:auto;max-height:none;min-height:0px;width:100%;max-width:100%;flex:none;flex-direction:column}axm-questionnaire-viewer-standalone-page .__content axm-questionnaire-viewer .__questionnaire-container{min-height:0px;flex:none;overflow:visible}axm-questionnaire-viewer-standalone-page .__toolbar-meta{color:rgb(var(--ax-sys-color-on-surface-variant))}axm-questionnaire-viewer-standalone-page .__footer-progress{color:rgb(var(--ax-sys-color-on-surface-variant))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: AXPStopwatchComponent, selector: "axp-stopwatch", inputs: ["mode", "timeLimit", "format", "autoStart", "showControls", "value"], outputs: ["valueChange", "timeUp"] }, { kind: "component", type: AXMQuestionnaireViewerComponent, selector: "axm-questionnaire-viewer", inputs: ["questionnaireId", "structure", "config", "initialAnswers"], outputs: ["answersChanged", "questionAnswered"] }, { kind: "component", type: AXMPreQuestionnaireContentComponent, selector: "axm-pre-questionnaire-content", inputs: ["content"] }, { kind: "component", type: AXMPostQuestionnaireContentComponent, selector: "axm-post-questionnaire-content", inputs: ["mode", "questionsWithAnswers", "reviewContext", "summaryContent", "placeholderValues", "locale", "showQuestionNumbers"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
5787
9028
  }
5788
9029
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerStandalonePageComponent, decorators: [{
5789
9030
  type: Component,
@@ -5920,5 +9161,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5920
9161
  * Generated bundle index. Do not edit.
5921
9162
  */
5922
9163
 
5923
- export { AXMQuestionnaireViewerService as A, AXMAssessmentManagementFeatureDefinitionProvider as B, AXMAssessmentManagementFeatureKeys as C, AXMAssessmentManagementMenuProvider as D, AXMAssessmentManagementModule as E, AXMAssessmentManagementPermissionDefinitionProvider as F, AXMAssessmentManagementPermissionKeys as G, AXMAssessmentManagementSearchCommandProvider as H, AXMQuestionnaireViewerPageComponent as I, AXMQuestionnaireViewerStandalonePageComponent as J, AXPAssessmentManagementMenuKeys as K, DEFAULT_POST_QUESTIONNAIRE_CONFIG as L, DEFAULT_PRE_QUESTIONNAIRE_CONFIG as M, DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY as N, DEFAULT_QUESTION_COMMENT_CONFIG as O, DEFAULT_QUESTION_HINT_CONFIG as P, getQuestionAnswerCommentStoragePath as Q, RootConfig as R, normalizeValidationStrategy as S, normalizeViewMode as T, readHintFieldsFromRecord as U, convertStateToValue as a, convertValueToState as b, createDefaultSection as c, getQuestionnaireItemVisibilityBadgeMode as d, normalizeQuestionHintFromEntityRow as e, mergeQuestionHintForViewer as f, getQuestionnaireItemRequiredBadgeMode as g, collectQuestionnaireItemNames as h, allocateNextQuestionItemName as i, normalizeQuestionHintMode as j, normalizeQuestionHintConfig as k, diffQuestionHintOverride as l, mergeQuestionCommentConfig as m, normalizeQuestionCommentConfig as n, diffQuestionCommentOverride as o, parseQuestionHintPartialFromRecord as p, toPrePostConfig as q, AXMQuestionnaireViewerComponent as r, stripHtmlToPlain as s, toDisplaySettings as t, AXMPreQuestionnaireContentComponent as u, AXMPostQuestionnaireContentComponent as v, normalizeCompletionMode as w, stripQuestionBankInterfaceMetadata as x, resolveWidgetNodeFromMetaDataDefinitionInterface as y, AXMAssessmentManagementEntityProvider as z };
5924
- //# sourceMappingURL=acorex-modules-assessment-management-acorex-modules-assessment-management-o8aFazwA.mjs.map
9164
+ export { AXMQuestionnaireViewerService as A, DEFAULT_QUESTION_HINT_CONFIG as B, diffQuestionCommentOverride as C, DEFAULT_POST_QUESTIONNAIRE_CONFIG as D, diffQuestionHintOverride as E, findDuplicateOutcomeNames as F, flattenQuestionnaireOutcomeItems as G, getQuestionAnswerCommentStoragePath as H, isQuestionnaireOutcomeDisplayColorHex as I, matchesQuestionnaireOutcomeDisplayRule as J, mergeQuestionCommentConfig as K, mergeQuestionHintForViewer as L, normalizeQuestionCommentConfig as M, normalizeQuestionHintConfig as N, normalizeQuestionHintFromEntityRow as O, normalizeQuestionHintMode as P, QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY as Q, RootConfig as R, normalizeQuestionnaireOutcomeDisplayRules as S, normalizeQuestionnaireOutcomesValue as T, normalizeValidationStrategy$1 as U, normalizeViewMode as V, parseQuestionHintPartialFromRecord as W, parseQuestionnaireOutcomeDisplayColor as X, readHintFieldsFromRecord as Y, QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY as a, AXMQuestionnaireViewerComponent as b, AXMPreQuestionnaireContentComponent as c, AXMPostQuestionnaireContentComponent as d, toDisplaySettings as e, AXMAssessmentManagementEntityProvider as f, AXMAssessmentManagementFeatureDefinitionProvider as g, AXMAssessmentManagementFeatureKeys as h, AXMAssessmentManagementMenuProvider as i, AXMAssessmentManagementModule as j, AXMAssessmentManagementPermissionDefinitionProvider as k, AXMAssessmentManagementPermissionKeys as l, mapToOutcomeResultsViewModel as m, normalizeCompletionMode as n, AXMAssessmentManagementSearchCommandProvider as o, persistAssessmentSession as p, AXMQuestionnaireViewerPageComponent as q, resolveWidgetNodeFromMetaDataDefinitionInterface as r, stripQuestionBankInterfaceMetadata as s, toPrePostConfig as t, AXMQuestionnaireViewerStandalonePageComponent as u, AXPAssessmentManagementMenuKeys as v, DEFAULT_PRE_QUESTIONNAIRE_CONFIG as w, DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY as x, DEFAULT_QUESTIONNAIRE_OUTCOMES as y, DEFAULT_QUESTION_COMMENT_CONFIG as z };
9165
+ //# sourceMappingURL=acorex-modules-assessment-management-acorex-modules-assessment-management-DlhSQ0cB.mjs.map