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

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 (89) 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-DqmJVJ0z.mjs} +3599 -373
  3. package/fesm2022/acorex-modules-assessment-management-acorex-modules-assessment-management-DqmJVJ0z.mjs.map +1 -0
  4. package/fesm2022/{acorex-modules-assessment-management-assessment-case.entity-yrc_Yybq.mjs → acorex-modules-assessment-management-assessment-case.entity-DhVwPz0T.mjs} +45 -29
  5. package/fesm2022/acorex-modules-assessment-management-assessment-case.entity-DhVwPz0T.mjs.map +1 -0
  6. package/fesm2022/{acorex-modules-assessment-management-assessment-session.entity-Cwf30iuu.mjs → acorex-modules-assessment-management-assessment-session.entity-BvV2Qgw1.mjs} +19 -3
  7. package/fesm2022/acorex-modules-assessment-management-assessment-session.entity-BvV2Qgw1.mjs.map +1 -0
  8. package/fesm2022/{acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs → acorex-modules-assessment-management-fill-assessment-session.command-4fkaeAG-.mjs} +2 -2
  9. package/fesm2022/acorex-modules-assessment-management-fill-assessment-session.command-4fkaeAG-.mjs.map +1 -0
  10. package/fesm2022/{acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs → acorex-modules-assessment-management-preview-question.command-6IGOdVVZ.mjs} +2 -2
  11. package/fesm2022/acorex-modules-assessment-management-preview-question.command-6IGOdVVZ.mjs.map +1 -0
  12. package/fesm2022/{acorex-modules-assessment-management-preview-questionnaire.command-wqPNgv7q.mjs → acorex-modules-assessment-management-preview-questionnaire.command-CRDBe_BS.mjs} +2 -2
  13. package/fesm2022/{acorex-modules-assessment-management-preview-questionnaire.command-wqPNgv7q.mjs.map → acorex-modules-assessment-management-preview-questionnaire.command-CRDBe_BS.mjs.map} +1 -1
  14. 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-B2ES-zcL.mjs} +2 -2
  15. package/fesm2022/acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-B2ES-zcL.mjs.map +1 -0
  16. package/fesm2022/{acorex-modules-assessment-management-question-bank-item.entity-DUf2ceKY.mjs → acorex-modules-assessment-management-question-bank-item.entity-Cw2WCdYK.mjs} +2 -2
  17. package/fesm2022/{acorex-modules-assessment-management-question-bank-item.entity-DUf2ceKY.mjs.map → acorex-modules-assessment-management-question-bank-item.entity-Cw2WCdYK.mjs.map} +1 -1
  18. package/fesm2022/{acorex-modules-assessment-management-questionnaire-viewer-popup.component-Cy5ElBmU.mjs → acorex-modules-assessment-management-questionnaire-viewer-popup.component-D9I3S-ZT.mjs} +125 -124
  19. package/fesm2022/acorex-modules-assessment-management-questionnaire-viewer-popup.component-D9I3S-ZT.mjs.map +1 -0
  20. package/fesm2022/{acorex-modules-assessment-management-questionnaire.entity-RWSkzNbA.mjs → acorex-modules-assessment-management-questionnaire.entity-D9nHUmQB.mjs} +12 -36
  21. package/fesm2022/acorex-modules-assessment-management-questionnaire.entity-D9nHUmQB.mjs.map +1 -0
  22. package/fesm2022/acorex-modules-assessment-management-view-session-answers.command-BOeO3Dit.mjs +350 -0
  23. package/fesm2022/acorex-modules-assessment-management-view-session-answers.command-BOeO3Dit.mjs.map +1 -0
  24. package/fesm2022/acorex-modules-assessment-management.mjs +1 -1
  25. package/fesm2022/acorex-modules-common.mjs +1 -8
  26. package/fesm2022/acorex-modules-common.mjs.map +1 -1
  27. package/fesm2022/{acorex-modules-human-capital-management-acorex-modules-human-capital-management-BWezAea1.mjs → acorex-modules-human-capital-management-acorex-modules-human-capital-management-uRz1c5Km.mjs} +20 -20
  28. package/fesm2022/{acorex-modules-human-capital-management-acorex-modules-human-capital-management-BWezAea1.mjs.map → acorex-modules-human-capital-management-acorex-modules-human-capital-management-uRz1c5Km.mjs.map} +1 -1
  29. package/fesm2022/{acorex-modules-human-capital-management-approve-leave-request.command-Bh_Bbq5-.mjs → acorex-modules-human-capital-management-approve-leave-request.command-23klKnTy.mjs} +2 -2
  30. package/fesm2022/{acorex-modules-human-capital-management-approve-leave-request.command-Bh_Bbq5-.mjs.map → acorex-modules-human-capital-management-approve-leave-request.command-23klKnTy.mjs.map} +1 -1
  31. package/fesm2022/{acorex-modules-human-capital-management-assign-position-assignment.command-BpDdDG9i.mjs → acorex-modules-human-capital-management-assign-position-assignment.command-BJ8xUiR2.mjs} +2 -2
  32. package/fesm2022/{acorex-modules-human-capital-management-assign-position-assignment.command-BpDdDG9i.mjs.map → acorex-modules-human-capital-management-assign-position-assignment.command-BJ8xUiR2.mjs.map} +1 -1
  33. package/fesm2022/{acorex-modules-human-capital-management-cancel-leave-request.command-CjtKaKFw.mjs → acorex-modules-human-capital-management-cancel-leave-request.command-B3u1gXWI.mjs} +2 -2
  34. package/fesm2022/{acorex-modules-human-capital-management-cancel-leave-request.command-CjtKaKFw.mjs.map → acorex-modules-human-capital-management-cancel-leave-request.command-B3u1gXWI.mjs.map} +1 -1
  35. package/fesm2022/{acorex-modules-human-capital-management-employee.entity-Cx_oY2aK.mjs → acorex-modules-human-capital-management-employee.entity-uhwOOw_y.mjs} +11 -3
  36. package/fesm2022/acorex-modules-human-capital-management-employee.entity-uhwOOw_y.mjs.map +1 -0
  37. package/fesm2022/{acorex-modules-human-capital-management-employment-type.entity-B20Taa5x.mjs → acorex-modules-human-capital-management-employment-type.entity-BQe82rsr.mjs} +2 -2
  38. package/fesm2022/{acorex-modules-human-capital-management-employment-type.entity-B20Taa5x.mjs.map → acorex-modules-human-capital-management-employment-type.entity-BQe82rsr.mjs.map} +1 -1
  39. package/fesm2022/{acorex-modules-human-capital-management-leave-request.entity-BO8QXpZE.mjs → acorex-modules-human-capital-management-leave-request.entity-CE4hs4C8.mjs} +2 -2
  40. package/fesm2022/{acorex-modules-human-capital-management-leave-request.entity-BO8QXpZE.mjs.map → acorex-modules-human-capital-management-leave-request.entity-CE4hs4C8.mjs.map} +1 -1
  41. package/fesm2022/{acorex-modules-human-capital-management-leave-type.entity-CQwSWqSh.mjs → acorex-modules-human-capital-management-leave-type.entity-DJ4EEUKg.mjs} +2 -2
  42. package/fesm2022/{acorex-modules-human-capital-management-leave-type.entity-CQwSWqSh.mjs.map → acorex-modules-human-capital-management-leave-type.entity-DJ4EEUKg.mjs.map} +1 -1
  43. package/fesm2022/{acorex-modules-human-capital-management-lifecycle-event-type.entity-BPWJrvbN.mjs → acorex-modules-human-capital-management-lifecycle-event-type.entity-CB2hlyOP.mjs} +2 -2
  44. package/fesm2022/{acorex-modules-human-capital-management-lifecycle-event-type.entity-BPWJrvbN.mjs.map → acorex-modules-human-capital-management-lifecycle-event-type.entity-CB2hlyOP.mjs.map} +1 -1
  45. package/fesm2022/{acorex-modules-human-capital-management-lifecycle-event.entity-ZZuPu9KQ.mjs → acorex-modules-human-capital-management-lifecycle-event.entity-DWonFZpU.mjs} +2 -2
  46. package/fesm2022/{acorex-modules-human-capital-management-lifecycle-event.entity-ZZuPu9KQ.mjs.map → acorex-modules-human-capital-management-lifecycle-event.entity-DWonFZpU.mjs.map} +1 -1
  47. package/fesm2022/{acorex-modules-human-capital-management-position-assignment.entity-BN1y0qhi.mjs → acorex-modules-human-capital-management-position-assignment.entity-o8PjggiT.mjs} +2 -2
  48. package/fesm2022/{acorex-modules-human-capital-management-position-assignment.entity-BN1y0qhi.mjs.map → acorex-modules-human-capital-management-position-assignment.entity-o8PjggiT.mjs.map} +1 -1
  49. package/fesm2022/{acorex-modules-human-capital-management-reject-leave-request.command-LczqOHP-.mjs → acorex-modules-human-capital-management-reject-leave-request.command-B6sr7oey.mjs} +2 -2
  50. package/fesm2022/{acorex-modules-human-capital-management-reject-leave-request.command-LczqOHP-.mjs.map → acorex-modules-human-capital-management-reject-leave-request.command-B6sr7oey.mjs.map} +1 -1
  51. package/fesm2022/{acorex-modules-human-capital-management-revoke-position-assignment.command-Dm8YacDI.mjs → acorex-modules-human-capital-management-revoke-position-assignment.command-aH7qTRnr.mjs} +2 -2
  52. package/fesm2022/{acorex-modules-human-capital-management-revoke-position-assignment.command-Dm8YacDI.mjs.map → acorex-modules-human-capital-management-revoke-position-assignment.command-aH7qTRnr.mjs.map} +1 -1
  53. package/fesm2022/{acorex-modules-human-capital-management-start-lifecycle-event-flow.command-DPwuyFz2.mjs → acorex-modules-human-capital-management-start-lifecycle-event-flow.command-B-VsXS4c.mjs} +2 -2
  54. package/fesm2022/{acorex-modules-human-capital-management-start-lifecycle-event-flow.command-DPwuyFz2.mjs.map → acorex-modules-human-capital-management-start-lifecycle-event-flow.command-B-VsXS4c.mjs.map} +1 -1
  55. package/fesm2022/acorex-modules-human-capital-management.mjs +1 -1
  56. package/fesm2022/acorex-modules-report-management.mjs +1 -1
  57. package/fesm2022/acorex-modules-report-management.mjs.map +1 -1
  58. package/fesm2022/{acorex-modules-task-management-acorex-modules-task-management--mdNdYIs.mjs → acorex-modules-task-management-acorex-modules-task-management-DAh3IByo.mjs} +37 -19
  59. package/fesm2022/acorex-modules-task-management-acorex-modules-task-management-DAh3IByo.mjs.map +1 -0
  60. package/fesm2022/{acorex-modules-task-management-task-board.page-BIZ1qH7m.mjs → acorex-modules-task-management-task-board.page-C2hB9JPM.mjs} +21 -12
  61. package/fesm2022/acorex-modules-task-management-task-board.page-C2hB9JPM.mjs.map +1 -0
  62. package/fesm2022/acorex-modules-task-management.mjs +1 -1
  63. package/package.json +2 -2
  64. package/types/acorex-modules-assessment-management.d.ts +391 -271
  65. package/types/acorex-modules-common.d.ts +1 -0
  66. package/types/acorex-modules-task-management.d.ts +14 -1
  67. package/fesm2022/acorex-modules-assessment-management-acorex-modules-assessment-management-o8aFazwA.mjs.map +0 -1
  68. package/fesm2022/acorex-modules-assessment-management-assessment-case.entity-yrc_Yybq.mjs.map +0 -1
  69. package/fesm2022/acorex-modules-assessment-management-assessment-session-answers-view.util-BUQa_TSY.mjs +0 -47
  70. package/fesm2022/acorex-modules-assessment-management-assessment-session-answers-view.util-BUQa_TSY.mjs.map +0 -1
  71. package/fesm2022/acorex-modules-assessment-management-assessment-session.entity-Cwf30iuu.mjs.map +0 -1
  72. package/fesm2022/acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs.map +0 -1
  73. package/fesm2022/acorex-modules-assessment-management-index-D8KjfHAH.mjs +0 -1509
  74. package/fesm2022/acorex-modules-assessment-management-index-D8KjfHAH.mjs.map +0 -1
  75. package/fesm2022/acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs.map +0 -1
  76. package/fesm2022/acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-DYyUhSWd.mjs.map +0 -1
  77. package/fesm2022/acorex-modules-assessment-management-questionnaire-calculation.entity-CYF0k42_.mjs +0 -236
  78. package/fesm2022/acorex-modules-assessment-management-questionnaire-calculation.entity-CYF0k42_.mjs.map +0 -1
  79. package/fesm2022/acorex-modules-assessment-management-questionnaire-viewer-popup.component-Cy5ElBmU.mjs.map +0 -1
  80. package/fesm2022/acorex-modules-assessment-management-questionnaire.entity-RWSkzNbA.mjs.map +0 -1
  81. package/fesm2022/acorex-modules-assessment-management-save-questionnaire-questions.command-koGmUbNE.mjs +0 -61
  82. package/fesm2022/acorex-modules-assessment-management-save-questionnaire-questions.command-koGmUbNE.mjs.map +0 -1
  83. package/fesm2022/acorex-modules-assessment-management-view-case-last-session-answers.command-NTuDdoYk.mjs +0 -157
  84. package/fesm2022/acorex-modules-assessment-management-view-case-last-session-answers.command-NTuDdoYk.mjs.map +0 -1
  85. package/fesm2022/acorex-modules-assessment-management-view-session-answers.command-0btGV66_.mjs +0 -172
  86. package/fesm2022/acorex-modules-assessment-management-view-session-answers.command-0btGV66_.mjs.map +0 -1
  87. package/fesm2022/acorex-modules-human-capital-management-employee.entity-Cx_oY2aK.mjs.map +0 -1
  88. package/fesm2022/acorex-modules-task-management-acorex-modules-task-management--mdNdYIs.mjs.map +0 -1
  89. package/fesm2022/acorex-modules-task-management-task-board.page-BIZ1qH7m.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-Cw2WCdYK.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-D9nHUmQB.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-DhVwPz0T.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-BvV2Qgw1.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-B2ES-zcL.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-B2ES-zcL.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-D9I3S-ZT.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,19 @@ class AXMQuestionnaireViewerService {
1137
1403
  saveHandler: config.saveHandler,
1138
1404
  assessmentFillContext: config.assessmentFillContext,
1139
1405
  questionnaireId: config.questionnaireId,
1406
+ outcomeRules: config.outcomeRules,
1407
+ storedOutcomeResults: config.storedOutcomeResults ?? null,
1140
1408
  },
1141
1409
  });
1142
1410
  return result.data || null;
1143
1411
  }
1144
1412
  /**
1145
- * Evaluate all questionnaire calculations for the given questionnaire and answers.
1413
+ * Evaluate all questionnaire outcomes for the given questionnaire and answers.
1146
1414
  * Pass `structure` so expressions can use `answer.byId`, `answer.byTag`, and `answer.allByTag`.
1147
1415
  */
1148
1416
  /**
1149
1417
  * 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.).
1418
+ * non-empty string is evaluated with the same scope as questionnaire outcomes (`answer.*`, `fn.*`, etc.).
1151
1419
  */
1152
1420
  async evaluateQuestionnaireVisibilityFlag(isVisible, answers, structure) {
1153
1421
  if (isVisible === false) {
@@ -1172,11 +1440,11 @@ class AXMQuestionnaireViewerService {
1172
1440
  }
1173
1441
  }
1174
1442
  /**
1175
- * Evaluates questionnaire calculations and returns values plus display titles from calculation entities.
1443
+ * Evaluates questionnaire outcome rules and returns values plus display titles.
1176
1444
  */
1177
- async evaluateOutcomes(questionnaireId, answers, structure) {
1178
- const rules = await this.loadCalculationsForQuestionnaire(questionnaireId);
1179
- if (!rules?.length)
1445
+ async evaluateOutcomes(answers, options) {
1446
+ const rules = await this.resolveOutcomeRules(options?.questionnaireId, options?.outcomeRules);
1447
+ if (!rules.length)
1180
1448
  return { values: {}, outcomeTitles: {} };
1181
1449
  const sorted = this.topologicalSort(rules);
1182
1450
  const outcomes = {};
@@ -1185,25 +1453,32 @@ class AXMQuestionnaireViewerService {
1185
1453
  if (rule.name) {
1186
1454
  outcomeTitles[rule.name] = this.resolveOutcomeDisplayTitle(rule);
1187
1455
  }
1188
- const value = await this.computeResult(rule, answers, outcomes, structure);
1456
+ const value = await this.computeResult(rule, answers, outcomes, options?.structure);
1189
1457
  if (rule.name && value !== undefined)
1190
1458
  outcomes[rule.name] = value;
1191
1459
  }
1192
1460
  return { values: outcomes, outcomeTitles };
1193
1461
  }
1194
1462
  /**
1195
- * Progress metrics and optional calculation results for summary / preview.
1463
+ * Progress metrics and optional outcome results for summary / preview.
1196
1464
  */
1197
1465
  async buildSummaryPlaceholderValues(structure, answers, options) {
1198
1466
  const { answeredQuestionsCount } = this.calculateProgressFromStructure(structure, answers);
1199
1467
  const totalQuestions = structure?.sections?.reduce((s, sec) => s + (sec.items?.length ?? 0), 0) ?? 0;
1200
1468
  let outcomes;
1201
1469
  let outcomeTitles;
1202
- if (options?.questionnaireId) {
1203
- const { values: evaluated, outcomeTitles: titles } = await this.evaluateOutcomes(options.questionnaireId, answers, structure);
1470
+ let outcomeSections;
1471
+ if (options?.outcomeRules?.length || options?.questionnaireId) {
1472
+ const { values: evaluated, outcomeTitles: titles } = await this.evaluateOutcomes(answers, {
1473
+ questionnaireId: options?.questionnaireId,
1474
+ outcomeRules: options?.outcomeRules,
1475
+ structure,
1476
+ });
1204
1477
  if (Object.keys(evaluated).length > 0) {
1205
1478
  outcomes = evaluated;
1206
1479
  outcomeTitles = titles;
1480
+ const sections = await this.resolveOutcomeSections(options?.questionnaireId, options?.outcomeRules);
1481
+ outcomeSections = this.buildOutcomeResultSections(sections, evaluated, titles);
1207
1482
  }
1208
1483
  }
1209
1484
  const elapsed = options?.elapsedSeconds;
@@ -1215,6 +1490,30 @@ class AXMQuestionnaireViewerService {
1215
1490
  totalTimeFormatted,
1216
1491
  outcomes,
1217
1492
  outcomeTitles,
1493
+ outcomeSections,
1494
+ };
1495
+ }
1496
+ /**
1497
+ * Builds outcome section rows from stored session values (no expression re-evaluation).
1498
+ */
1499
+ async buildOutcomeResultsFromStoredValues(storedOutcomes, options) {
1500
+ const keys = Object.keys(storedOutcomes);
1501
+ if (!keys.length) {
1502
+ return { outcomes: {}, outcomeTitles: {}, outcomeSections: [] };
1503
+ }
1504
+ const rules = await this.resolveOutcomeRules(options.questionnaireId, undefined);
1505
+ const outcomeTitles = {};
1506
+ for (const rule of rules) {
1507
+ if (rule.name) {
1508
+ outcomeTitles[rule.name] = this.resolveOutcomeDisplayTitle(rule);
1509
+ }
1510
+ }
1511
+ const sections = await this.resolveOutcomeSections(options.questionnaireId, undefined);
1512
+ const outcomeSections = this.buildOutcomeResultSections(sections, storedOutcomes, outcomeTitles);
1513
+ return {
1514
+ outcomes: storedOutcomes,
1515
+ outcomeTitles,
1516
+ outcomeSections,
1218
1517
  };
1219
1518
  }
1220
1519
  //#endregion
@@ -1246,38 +1545,100 @@ class AXMQuestionnaireViewerService {
1246
1545
  return { answeredQuestionsCount: answered, progressPercentage };
1247
1546
  }
1248
1547
  //#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)
1548
+ //#region ---- Questionnaire outcomes (evaluation) ----
1549
+ async resolveOutcomeRules(questionnaireId, inlineRules) {
1550
+ if (inlineRules?.length) {
1551
+ return inlineRules;
1552
+ }
1553
+ if (!questionnaireId) {
1554
+ return [];
1555
+ }
1556
+ const questionnaire = await this.loadQuestionnaire(questionnaireId);
1557
+ return flattenQuestionnaireOutcomeItems(questionnaire?.outcomes);
1558
+ }
1559
+ async resolveOutcomeSections(questionnaireId, inlineRules) {
1560
+ if (inlineRules?.length) {
1561
+ return [{ name: 'outcomes', order: 0, items: inlineRules }];
1562
+ }
1563
+ if (!questionnaireId) {
1564
+ return [];
1565
+ }
1566
+ const questionnaire = await this.loadQuestionnaire(questionnaireId);
1567
+ return normalizeQuestionnaireOutcomesValue(questionnaire?.outcomes).sections;
1568
+ }
1569
+ async loadQuestionnaire(questionnaireId) {
1570
+ return this.entityService
1571
+ .withEntity(RootConfig.module.name, RootConfig.entities.questionnaire.name)
1253
1572
  .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 ?? []);
1573
+ .byKey(questionnaireId);
1574
+ }
1575
+ buildOutcomeResultSections(sections, values, titles) {
1576
+ const sortedSections = sections.slice().sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
1577
+ const groups = [];
1578
+ for (const [index, section] of sortedSections.entries()) {
1579
+ const sectionName = (section.name ?? '').trim() || `section-${index + 1}`;
1580
+ const sectionOrder = section.order ?? index;
1581
+ const items = [];
1582
+ for (const item of section.items ?? []) {
1583
+ if (!item.name || values[item.name] === undefined) {
1584
+ continue;
1585
+ }
1586
+ const value = values[item.name];
1587
+ const display = this.resolveOutcomeDisplay(item, value);
1588
+ items.push({
1589
+ key: item.name,
1590
+ value,
1591
+ label: (titles[item.name] ?? '').trim() || this.humanizeOutcomeKey(item.name),
1592
+ display,
1593
+ });
1594
+ }
1595
+ if (items.length === 0) {
1596
+ continue;
1597
+ }
1598
+ groups.push({
1599
+ trackKey: `${sectionOrder}::${sectionName}`,
1600
+ sectionName,
1601
+ sectionOrder,
1602
+ sectionTitle: section.title ?? { 'en-US': sectionName },
1603
+ sectionDescription: section.description,
1604
+ items,
1605
+ });
1606
+ }
1607
+ return groups;
1608
+ }
1609
+ /**
1610
+ * First matching display rule for an evaluated outcome value.
1611
+ */
1612
+ resolveOutcomeDisplay(rule, value) {
1613
+ const displayRules = normalizeQuestionnaireOutcomeDisplayRules(rule.displayRules);
1614
+ if (!displayRules.length) {
1615
+ return undefined;
1616
+ }
1617
+ for (const displayRule of displayRules) {
1618
+ if (matchesQuestionnaireOutcomeDisplayRule(value, displayRule)) {
1619
+ return {
1620
+ color: displayRule.color,
1621
+ emphasis: displayRule.emphasis ?? 'normal',
1622
+ iconClass: typeof displayRule.icon === 'string' ? displayRule.icon : undefined,
1623
+ };
1624
+ }
1625
+ }
1626
+ return undefined;
1264
1627
  }
1265
1628
  /**
1266
- * Display label for a calculation: entity `title` when set, otherwise a humanized `name`.
1629
+ * Display label for an outcome rule: `title` when set, otherwise a humanized `name`.
1267
1630
  */
1268
1631
  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;
1632
+ const resolved = resolveMultiLanguageString(rule.title, 'en-US').trim();
1633
+ if (resolved.length > 0) {
1634
+ return resolved;
1274
1635
  }
1275
1636
  const name = rule.name?.trim();
1276
1637
  if (name)
1277
- return this.humanizeCalculationKey(name);
1638
+ return this.humanizeOutcomeKey(name);
1278
1639
  return '';
1279
1640
  }
1280
- humanizeCalculationKey(name) {
1641
+ humanizeOutcomeKey(name) {
1281
1642
  return name
1282
1643
  .replace(/_/g, ' ')
1283
1644
  .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
@@ -1287,14 +1648,14 @@ class AXMQuestionnaireViewerService {
1287
1648
  .join(' ');
1288
1649
  }
1289
1650
  async computeResult(rule, answers, outcomes, structure) {
1290
- const exprText = this.calculationExpressionText(rule);
1651
+ const exprText = this.outcomeExpressionText(rule);
1291
1652
  if (!exprText)
1292
1653
  return undefined;
1293
1654
  const raw = await this.evaluateOutcomeExpression(exprText, answers, outcomes, structure);
1294
1655
  return this.coerceOutcome(raw, this.inferResultKind(rule));
1295
1656
  }
1296
1657
  /** Non-empty trimmed `expression`, or empty string if missing. */
1297
- calculationExpressionText(rule) {
1658
+ outcomeExpressionText(rule) {
1298
1659
  const exp = rule.expression;
1299
1660
  return typeof exp === 'string' ? exp.trim() : '';
1300
1661
  }
@@ -1332,17 +1693,17 @@ class AXMQuestionnaireViewerService {
1332
1693
  return raw;
1333
1694
  }
1334
1695
  }
1335
- async evaluateOutcomeExpression(expression, answers, outcomes, structure) {
1696
+ async evaluateOutcomeExpression(expression, answers, outcomes, structure, value) {
1336
1697
  const trimmed = expression?.trim();
1337
1698
  if (!trimmed)
1338
1699
  return undefined;
1339
1700
  try {
1340
- const scope = this.buildExpressionScope(answers, outcomes, structure);
1701
+ const scope = this.buildExpressionScope(answers, outcomes, structure, value);
1341
1702
  const wrapped = trimmed.includes('{{') ? trimmed : `{{ ${trimmed} }}`;
1342
1703
  return await this.expressionEvaluator.evaluate(wrapped, scope);
1343
1704
  }
1344
1705
  catch (err) {
1345
- console.warn('[QuestionnaireViewer] Calculation expression evaluation failed:', expression, err);
1706
+ console.warn('[QuestionnaireViewer] Outcome expression evaluation failed:', expression, err);
1346
1707
  return undefined;
1347
1708
  }
1348
1709
  }
@@ -1376,7 +1737,7 @@ class AXMQuestionnaireViewerService {
1376
1737
  /**
1377
1738
  * Scope: answer.eval (path), answer.byId / byTag / allByTag, formula.eval / outcome.eval, fn.* helpers.
1378
1739
  */
1379
- buildExpressionScope(answers, outcomes, structure) {
1740
+ buildExpressionScope(answers, outcomes, structure, value) {
1380
1741
  const resolveOutcome = (key) => outcomes[key];
1381
1742
  const itemsOrdered = this.flattenQuestionnaireItems(structure ?? null);
1382
1743
  const itemHasTag = (item, normalizedTag) => {
@@ -1472,6 +1833,7 @@ class AXMQuestionnaireViewerService {
1472
1833
  formula: { eval: resolveOutcome },
1473
1834
  outcome: { eval: resolveOutcome },
1474
1835
  fn,
1836
+ ...(value !== undefined ? { value } : {}),
1475
1837
  };
1476
1838
  }
1477
1839
  expressionOutcomeRefs(expression, ruleNames) {
@@ -1513,7 +1875,7 @@ class AXMQuestionnaireViewerService {
1513
1875
  visiting.add(name);
1514
1876
  const rule = nameToRule.get(name);
1515
1877
  if (rule) {
1516
- const exprStr = this.calculationExpressionText(rule);
1878
+ const exprStr = this.outcomeExpressionText(rule);
1517
1879
  const formDeps = this.expressionOutcomeRefs(exprStr, ruleNames);
1518
1880
  for (const d of formDeps)
1519
1881
  visit(d);
@@ -1531,13 +1893,2774 @@ class AXMQuestionnaireViewerService {
1531
1893
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1532
1894
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerService, providedIn: 'root' }); }
1533
1895
  }
1534
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerService, decorators: [{
1535
- type: Injectable,
1896
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerService, decorators: [{
1897
+ type: Injectable,
1898
+ args: [{
1899
+ providedIn: 'root',
1900
+ }]
1901
+ }] });
1902
+
1903
+ //#region ---- Imports ----
1904
+ //#endregion
1905
+ //#region ---- Groups ----
1906
+ const GROUP_IDENTITY = {
1907
+ name: 'identity',
1908
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.identity.title',
1909
+ order: 0,
1910
+ };
1911
+ const GROUP_VALIDATION = {
1912
+ name: 'validation',
1913
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.validation.title',
1914
+ order: 1,
1915
+ };
1916
+ const GROUP_VISIBILITY = {
1917
+ name: 'visibility',
1918
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.visibility.title',
1919
+ order: 2,
1920
+ };
1921
+ const GROUP_HINT = {
1922
+ name: 'hint',
1923
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.hint.title',
1924
+ order: 3,
1925
+ };
1926
+ const GROUP_COMMENT = {
1927
+ name: 'comment',
1928
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.groups.comment.title',
1929
+ order: 4,
1930
+ };
1931
+ //#endregion
1932
+ //#region ---- Shared options ----
1933
+ const HINT_MODE_OPTIONS = [
1934
+ {
1935
+ id: 'off',
1936
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.off',
1937
+ },
1938
+ {
1939
+ id: 'info',
1940
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.info',
1941
+ },
1942
+ {
1943
+ id: 'warning',
1944
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.warning',
1945
+ },
1946
+ {
1947
+ id: 'danger',
1948
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.danger',
1949
+ },
1950
+ {
1951
+ id: 'success',
1952
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.success',
1953
+ },
1954
+ {
1955
+ id: 'neutral',
1956
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.options.neutral',
1957
+ },
1958
+ ];
1959
+ //#endregion
1960
+ //#region ---- Properties by group ----
1961
+ const IDENTITY_PROPS = [
1962
+ {
1963
+ name: 'name',
1964
+ title: '@general:terms.common.name',
1965
+ group: GROUP_IDENTITY,
1966
+ order: 10,
1967
+ schema: {
1968
+ dataType: 'string',
1969
+ defaultValue: '',
1970
+ interface: {
1971
+ name: 'name',
1972
+ path: 'name',
1973
+ type: AXPWidgetsCatalog.text,
1974
+ options: {
1975
+ placeholder: '@general:terms.common.name',
1976
+ },
1977
+ },
1978
+ },
1979
+ visible: true,
1980
+ },
1981
+ {
1982
+ name: 'tags',
1983
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.tags.title',
1984
+ group: GROUP_IDENTITY,
1985
+ order: 20,
1986
+ schema: {
1987
+ dataType: 'array',
1988
+ defaultValue: [],
1989
+ interface: {
1990
+ name: 'tags',
1991
+ path: 'tags',
1992
+ type: 'tag-editor',
1993
+ options: {
1994
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-builder.fields.tags.placeholder',
1995
+ allowDuplicate: false,
1996
+ },
1997
+ },
1998
+ },
1999
+ visible: true,
2000
+ },
2001
+ ];
2002
+ const VALIDATION_PROPS = [
2003
+ {
2004
+ ...createBooleanProperty({
2005
+ name: 'isRequired',
2006
+ path: 'isRequired',
2007
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.is-required.title',
2008
+ group: GROUP_VALIDATION,
2009
+ defaultValue: false,
2010
+ }),
2011
+ order: 10,
2012
+ },
2013
+ ];
2014
+ const VISIBILITY_PROPS = [
2015
+ {
2016
+ ...createBooleanProperty({
2017
+ name: 'isVisible',
2018
+ path: 'isVisible',
2019
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.visible.title',
2020
+ group: GROUP_VISIBILITY,
2021
+ defaultValue: true,
2022
+ }),
2023
+ order: 10,
2024
+ },
2025
+ ];
2026
+ const HINT_PROPS = [
2027
+ {
2028
+ name: 'hintMode',
2029
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint-mode.title',
2030
+ group: GROUP_HINT,
2031
+ order: 10,
2032
+ schema: {
2033
+ dataType: 'string',
2034
+ defaultValue: { id: 'off', title: HINT_MODE_OPTIONS[0].title },
2035
+ interface: {
2036
+ name: 'hintMode',
2037
+ path: 'hint.mode',
2038
+ type: AXPWidgetsCatalog.select,
2039
+ options: {
2040
+ valueField: 'id',
2041
+ textField: 'title',
2042
+ dataSource: [...HINT_MODE_OPTIONS],
2043
+ },
2044
+ },
2045
+ },
2046
+ visible: true,
2047
+ },
2048
+ {
2049
+ name: 'hintContent',
2050
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.hint.title',
2051
+ group: GROUP_HINT,
2052
+ order: 20,
2053
+ schema: {
2054
+ dataType: 'string',
2055
+ defaultValue: '',
2056
+ interface: {
2057
+ name: 'hintContent',
2058
+ path: 'hint.content',
2059
+ type: AXPWidgetsCatalog.richText,
2060
+ options: {},
2061
+ },
2062
+ },
2063
+ visible: "{{ context.eval('hint.mode') && (context.eval('hint.mode').id || context.eval('hint.mode')) != 'off' }}",
2064
+ },
2065
+ ];
2066
+ const COMMENT_PROPS = [
2067
+ {
2068
+ ...createBooleanProperty({
2069
+ name: 'commentEnabled',
2070
+ path: 'comment.enabled',
2071
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.comment-enabled.title',
2072
+ group: GROUP_COMMENT,
2073
+ defaultValue: false,
2074
+ }),
2075
+ order: 10,
2076
+ },
2077
+ {
2078
+ ...createBooleanProperty({
2079
+ name: 'commentRequired',
2080
+ path: 'comment.required',
2081
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.fields.comment-required.title',
2082
+ group: GROUP_COMMENT,
2083
+ defaultValue: false,
2084
+ visible: "{{ context.eval('comment.enabled') === true }}",
2085
+ }),
2086
+ order: 20,
2087
+ },
2088
+ ];
2089
+ //#endregion
2090
+ //#region ---- Hint form context helper ----
2091
+ /**
2092
+ * Builds initial `hint` object for the question property viewer from the effective (merged) hint config.
2093
+ */
2094
+ function createQuestionHintFormContext(hint) {
2095
+ let content = '';
2096
+ if (hint.mode !== 'off') {
2097
+ const hc = hint.content;
2098
+ if (typeof hc === 'string') {
2099
+ content = hc;
2100
+ }
2101
+ else if (hc && typeof hc === 'object') {
2102
+ content = Object.values(hc)[0] ?? '';
2103
+ }
2104
+ }
2105
+ const modeId = hint.mode;
2106
+ const opt = HINT_MODE_OPTIONS.find((o) => o.id === modeId) ?? HINT_MODE_OPTIONS[0];
2107
+ return {
2108
+ content,
2109
+ mode: { id: opt.id, title: opt.title },
2110
+ };
2111
+ }
2112
+ //#endregion
2113
+ //#region ---- Tab definition ----
2114
+ /**
2115
+ * Property viewer tabs for editing a single questionnaire question row (metadata).
2116
+ */
2117
+ const QUESTIONNAIRE_QUESTION_EDIT_TABS = [
2118
+ {
2119
+ name: 'general',
2120
+ title: '@assessment-management:questionnaires.components.questionnaire-builder.tabs.general.title',
2121
+ groups: [
2122
+ {
2123
+ name: GROUP_IDENTITY.name,
2124
+ title: GROUP_IDENTITY.title,
2125
+ isCollapsed: false,
2126
+ props: IDENTITY_PROPS,
2127
+ },
2128
+ {
2129
+ name: GROUP_VALIDATION.name,
2130
+ title: GROUP_VALIDATION.title,
2131
+ isCollapsed: false,
2132
+ props: VALIDATION_PROPS,
2133
+ },
2134
+ {
2135
+ name: GROUP_VISIBILITY.name,
2136
+ title: GROUP_VISIBILITY.title,
2137
+ isCollapsed: false,
2138
+ props: VISIBILITY_PROPS,
2139
+ },
2140
+ {
2141
+ name: GROUP_HINT.name,
2142
+ title: GROUP_HINT.title,
2143
+ isCollapsed: false,
2144
+ props: HINT_PROPS,
2145
+ },
2146
+ {
2147
+ name: GROUP_COMMENT.name,
2148
+ title: GROUP_COMMENT.title,
2149
+ isCollapsed: false,
2150
+ props: COMMENT_PROPS,
2151
+ },
2152
+ ],
2153
+ },
2154
+ ];
2155
+ //#endregion
2156
+
2157
+ //#region ---- Imports ----
2158
+ //#endregion
2159
+ //#region ---- Component ----
2160
+ /**
2161
+ * Reusable Questionnaire Builder Component
2162
+ * Can be used both in dialog and inline contexts
2163
+ */
2164
+ class AXMQuestionnaireQuestionsBuilderComponent {
2165
+ //#endregion
2166
+ //#region ---- Question bank entity data ----
2167
+ questionBankData() {
2168
+ return this.entityService
2169
+ .withEntity(RootConfig.module.name, RootConfig.entities.questionBankItem.name)
2170
+ .data();
2171
+ }
2172
+ questionBankCategoryData() {
2173
+ return this.entityService
2174
+ .withEntity(RootConfig.module.name, `${RootConfig.entities.questionBankItem.name}Category`)
2175
+ .data();
2176
+ }
2177
+ //#endregion
2178
+ //#region ---- Lifecycle ----
2179
+ constructor() {
2180
+ //#region ---- Services & Dependencies ----
2181
+ this.dataSelectorService = inject(AXPDataSelectorService);
2182
+ this.entityService = inject(AXPEntityService);
2183
+ this.entityResolver = inject(AXPEntityDefinitionRegistryService);
2184
+ this.translationService = inject(AXTranslationService);
2185
+ this.propertyViewerService = inject(AXPPropertyViewerService);
2186
+ this.questionnaireViewerService = inject(AXMQuestionnaireViewerService);
2187
+ this.toastService = inject(AXToastService);
2188
+ this.widgetRegistry = inject(AXPWidgetRegistryService);
2189
+ this.commandService = inject(AXPCommandService);
2190
+ //#endregion
2191
+ //#region ---- Inputs & Outputs ----
2192
+ /**
2193
+ * Initial value (QuestionnaireBuilderValue)
2194
+ */
2195
+ this.value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
2196
+ /**
2197
+ * Readonly mode
2198
+ */
2199
+ this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
2200
+ /**
2201
+ * Pre/post config for preview (welcome, review, summary).
2202
+ * When provided, passed to the preview dialog.
2203
+ */
2204
+ this.prePostConfig = input(null, ...(ngDevMode ? [{ debugName: "prePostConfig" }] : /* istanbul ignore next */ []));
2205
+ /**
2206
+ * When false, the header (Add Section, Settings, Preview, Full Screen) is hidden.
2207
+ * Use when actions are provided by the host (e.g. page primary header actions).
2208
+ */
2209
+ this.showHeader = input(true, ...(ngDevMode ? [{ debugName: "showHeader" }] : /* istanbul ignore next */ []));
2210
+ /**
2211
+ * Display settings (view mode, progress bar, timer, etc.) when provided by the host (e.g. entity form).
2212
+ * Used for preview. When not provided, falls back to state/value or defaults.
2213
+ */
2214
+ this.displaySettingsInput = input(null, ...(ngDevMode ? [{ debugName: "displaySettingsInput" }] : /* istanbul ignore next */ []));
2215
+ /**
2216
+ * Persisted questionnaire id (e.g. from entity form) — enables questionnaire outcomes in preview.
2217
+ */
2218
+ this.questionnaireEntityId = input(undefined, ...(ngDevMode ? [{ debugName: "questionnaireEntityId" }] : /* istanbul ignore next */ []));
2219
+ /**
2220
+ * Value change event
2221
+ */
2222
+ this.valueChange = output();
2223
+ //#endregion
2224
+ //#region ---- Class Properties ----
2225
+ // Designer state management - always start with at least one default section
2226
+ this.builderState = signal({
2227
+ sections: [createDefaultSection()],
2228
+ }, ...(ngDevMode ? [{ debugName: "builderState" }] : /* istanbul ignore next */ []));
2229
+ // Selected item tracking
2230
+ this.selectedItemName = signal(null, ...(ngDevMode ? [{ debugName: "selectedItemName" }] : /* istanbul ignore next */ []));
2231
+ this.selectedSectionName = signal(null, ...(ngDevMode ? [{ debugName: "selectedSectionName" }] : /* istanbul ignore next */ []));
2232
+ // Fullscreen directive reference
2233
+ this.fullscreenDirective = viewChild('fullscreen', ...(ngDevMode ? [{ debugName: "fullscreenDirective" }] : /* istanbul ignore next */ []));
2234
+ this.standardRef = viewChild(AXPStandardSectionItemsBuilderComponent, ...(ngDevMode ? [{ debugName: "standardRef" }] : /* istanbul ignore next */ []));
2235
+ //#endregion
2236
+ //#region ---- Computed Properties ----
2237
+ /** Value for {@link AXPStandardSectionItemsBuilderComponent} (sections only; drag/reorder handled there). */
2238
+ this.builderValue = computed(() => ({
2239
+ sections: this.builderState().sections.map((s) => ({
2240
+ id: s.name,
2241
+ name: s.name,
2242
+ order: s.order,
2243
+ title: s.title,
2244
+ description: s.description,
2245
+ tags: s.tags,
2246
+ isVisible: s.isVisible,
2247
+ items: s.items.map((it) => ({
2248
+ id: it.name,
2249
+ name: it.name,
2250
+ order: it.order,
2251
+ questionBankItemId: it.questionBankItemId,
2252
+ isRequired: it.isRequired,
2253
+ isVisible: it.isVisible,
2254
+ interfaceOverride: it.interfaceOverride,
2255
+ hint: it.hint,
2256
+ comment: it.comment,
2257
+ tags: it.tags,
2258
+ question: it.question,
2259
+ isNewlyAdded: it.isNewlyAdded,
2260
+ })),
2261
+ })),
2262
+ }), ...(ngDevMode ? [{ debugName: "builderValue" }] : /* istanbul ignore next */ []));
2263
+ this.isPreviewDisabled = computed(() => this.builderState().sections.length === 0 || this.builderState().sections.every((s) => s.items.length === 0), ...(ngDevMode ? [{ debugName: "isPreviewDisabled" }] : /* istanbul ignore next */ []));
2264
+ this.standardConfig = computed(() => ({
2265
+ showSectionTechnicalName: true,
2266
+ minSectionCount: 1,
2267
+ texts: {
2268
+ addSection: '@assessment-management:questionnaires.components.questionnaire-builder.actions.add-section.title',
2269
+ addItem: '@assessment-management:questionnaires.components.questionnaire-builder.actions.add-question.title',
2270
+ emptySectionsTitle: '@assessment-management:questionnaires.components.questionnaire-builder.empty-states.no-sections.title',
2271
+ emptySectionsDescription: '@assessment-management:questionnaires.components.questionnaire-builder.empty-states.no-sections.description',
2272
+ emptyItemsTitle: '@assessment-management:questionnaires.components.questionnaire-builder.empty-states.empty-section.title',
2273
+ emptyItemsDescription: '@assessment-management:questionnaires.components.questionnaire-builder.empty-states.empty-section.description',
2274
+ defaultSectionBadge: '@assessment-management:questionnaires.components.questionnaire-builder.default-section-badge',
2275
+ },
2276
+ mapItemToView: (item, section) => this.mapQuestionnaireItemToView(item, section),
2277
+ promptAddItems: (sid, v) => this.questionnairePromptAddItems(sid, v),
2278
+ promptEditItem: (item, sid, v) => this.questionnairePromptEditItem(item, sid, v),
2279
+ }), ...(ngDevMode ? [{ debugName: "standardConfig" }] : /* istanbul ignore next */ []));
2280
+ /**
2281
+ * Get current display settings from state or default
2282
+ */
2283
+ this.displaySettings = computed(() => {
2284
+ const fromInput = this.displaySettingsInput();
2285
+ if (fromInput != null) {
2286
+ return fromInput;
2287
+ }
2288
+ const state = this.builderState();
2289
+ if (state.displaySettings) {
2290
+ return state.displaySettings;
2291
+ }
2292
+ const currentValue = convertStateToValue(state, this.value() || undefined);
2293
+ return (currentValue.displaySettings || {
2294
+ viewMode: 'single-page',
2295
+ showProgressBar: true,
2296
+ showTimer: false,
2297
+ showQuestionNumbers: true,
2298
+ });
2299
+ }, ...(ngDevMode ? [{ debugName: "displaySettings" }] : /* istanbul ignore next */ []));
2300
+ // Track last emitted value to prevent circular updates
2301
+ let lastEmittedValue = null;
2302
+ // Sync value input to state (only when value changes externally)
2303
+ effect(() => {
2304
+ const val = this.value();
2305
+ if (val !== null && val !== undefined) {
2306
+ const currentState = untracked(() => this.builderState());
2307
+ const currentValue = convertStateToValue(currentState);
2308
+ const valStr = JSON.stringify(val);
2309
+ const currentStr = JSON.stringify(currentValue);
2310
+ const lastEmittedStr = lastEmittedValue ? JSON.stringify(lastEmittedValue) : null;
2311
+ if (valStr !== currentStr && valStr !== lastEmittedStr) {
2312
+ const newState = convertValueToState(val);
2313
+ untracked(() => {
2314
+ this.builderState.set(newState);
2315
+ this.loadQuestionDetailsForState(newState);
2316
+ });
2317
+ }
2318
+ }
2319
+ else if (val === null || val === undefined) {
2320
+ // If value is null/undefined, ensure we have at least one default section
2321
+ const currentState = untracked(() => this.builderState());
2322
+ if (currentState.sections.length === 0) {
2323
+ const defaultSection = createDefaultSection();
2324
+ defaultSection.order = 0;
2325
+ untracked(() => {
2326
+ this.builderState.set({ sections: [defaultSection] });
2327
+ });
2328
+ }
2329
+ }
2330
+ });
2331
+ // Sync state changes to output
2332
+ effect(() => {
2333
+ const state = this.builderState();
2334
+ const currentValue = this.value();
2335
+ const value = convertStateToValue(state, currentValue || undefined);
2336
+ untracked(() => {
2337
+ lastEmittedValue = value;
2338
+ this.valueChange.emit(value);
2339
+ });
2340
+ });
2341
+ }
2342
+ /**
2343
+ * Load question details for all items in the state
2344
+ */
2345
+ async loadQuestionDetailsForState(state) {
2346
+ const questionIds = new Set();
2347
+ // Collect all question bank item IDs
2348
+ state.sections.forEach((section) => {
2349
+ section.items.forEach((item) => {
2350
+ if (item.questionBankItemId && !item.question) {
2351
+ questionIds.add(item.questionBankItemId);
2352
+ }
2353
+ });
2354
+ });
2355
+ if (questionIds.size === 0)
2356
+ return;
2357
+ // Load all questions in parallel
2358
+ const questionPromises = Array.from(questionIds).map((id) => this.questionBankData()
2359
+ .byKey(id)
2360
+ .catch(() => null));
2361
+ const questions = await Promise.all(questionPromises);
2362
+ // Create a map of question ID to question data
2363
+ const questionMap = new Map();
2364
+ questions.forEach((q) => {
2365
+ if (q) {
2366
+ questionMap.set(q.id, q);
2367
+ }
2368
+ });
2369
+ // Update state with question details
2370
+ this.builderState.update((currentState) => ({
2371
+ ...currentState,
2372
+ sections: currentState.sections.map((section) => ({
2373
+ ...section,
2374
+ items: section.items.map((item) => {
2375
+ if (item.question)
2376
+ return item; // Already has question data
2377
+ const questionData = questionMap.get(item.questionBankItemId);
2378
+ if (questionData) {
2379
+ return {
2380
+ ...item,
2381
+ question: {
2382
+ id: questionData.id,
2383
+ title: questionData.title,
2384
+ question: questionData.question,
2385
+ note: questionData.note,
2386
+ difficulty: questionData.difficulty,
2387
+ interface: questionData.interface,
2388
+ },
2389
+ };
2390
+ }
2391
+ return item;
2392
+ }),
2393
+ })),
2394
+ }));
2395
+ }
2396
+ mapQuestionnaireItemToView(item, _section) {
2397
+ const q = item;
2398
+ const title = this.itemQuestionHeading(q);
2399
+ const note = this.itemQuestionNotePreview(q);
2400
+ const badges = [];
2401
+ const req = q.isRequired;
2402
+ const rawReq = typeof req === 'boolean' || typeof req === 'string' ? req : undefined;
2403
+ const requiredMode = getQuestionnaireItemRequiredBadgeMode(rawReq);
2404
+ if (requiredMode === 'required') {
2405
+ badges.push({
2406
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.required',
2407
+ variant: 'danger',
2408
+ });
2409
+ }
2410
+ else if (requiredMode === 'conditional') {
2411
+ badges.push({
2412
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.conditional-required',
2413
+ variant: 'warning',
2414
+ });
2415
+ }
2416
+ else {
2417
+ badges.push({
2418
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.optional',
2419
+ variant: 'success',
2420
+ });
2421
+ }
2422
+ const rawVis = q.isVisible;
2423
+ const visibilityMode = getQuestionnaireItemVisibilityBadgeMode(typeof rawVis === 'boolean' || typeof rawVis === 'string' ? rawVis : undefined);
2424
+ if (visibilityMode === 'hidden') {
2425
+ badges.push({
2426
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.visibility-hidden',
2427
+ variant: 'danger',
2428
+ });
2429
+ }
2430
+ else if (visibilityMode === 'conditional') {
2431
+ badges.push({
2432
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.visibility-conditional',
2433
+ variant: 'warning',
2434
+ });
2435
+ }
2436
+ else {
2437
+ badges.push({
2438
+ text: '@assessment-management:questionnaires.components.questionnaire-builder.item-badge.visibility-visible',
2439
+ variant: 'success',
2440
+ });
2441
+ }
2442
+ return {
2443
+ icon: this.getQuestionWidgetIcon(q),
2444
+ title,
2445
+ name: q.name,
2446
+ description: note || undefined,
2447
+ badges,
2448
+ };
2449
+ }
2450
+ /**
2451
+ * Standard section-items builder emits here. Uses direct `builderState.update` so the `valueChange`
2452
+ * effect runs in the same turn and hosts (e.g. questions page) can mark dirty.
2453
+ */
2454
+ onBuilderValueChange(next) {
2455
+ this.builderState.update((state) => {
2456
+ const updatedState = {
2457
+ ...state,
2458
+ sections: this.mergeSectionsFromStandardBuilder(next.sections, state.sections),
2459
+ };
2460
+ if (updatedState.sections.length === 0) {
2461
+ const defaultSection = createDefaultSection();
2462
+ defaultSection.order = 0;
2463
+ return { ...updatedState, sections: [defaultSection] };
2464
+ }
2465
+ return updatedState;
2466
+ });
2467
+ }
2468
+ mergeSectionsFromStandardBuilder(incoming, previous) {
2469
+ const prevByName = new Map(previous.map((s) => [s.name, s]));
2470
+ return incoming.map((sec) => {
2471
+ const axSec = sec;
2472
+ const incomingName = String(axSec['name'] ?? axSec['id'] ?? '').trim();
2473
+ const prevSec = prevByName.get(incomingName);
2474
+ if (!prevSec) {
2475
+ return this.normalizeIncomingSection(sec);
2476
+ }
2477
+ const prevItemByName = new Map(prevSec.items.map((i) => [i.name, i]));
2478
+ const merged = this.normalizeIncomingSection(sec);
2479
+ return {
2480
+ ...merged,
2481
+ isNewlyAdded: prevSec.isNewlyAdded ?? merged.isNewlyAdded,
2482
+ items: merged.items.map((it) => {
2483
+ const prev = prevItemByName.get(it.name);
2484
+ if (!prev) {
2485
+ return it;
2486
+ }
2487
+ return {
2488
+ ...prev,
2489
+ ...it,
2490
+ question: it.question ?? prev.question,
2491
+ };
2492
+ }),
2493
+ };
2494
+ });
2495
+ }
2496
+ /** Map platform section rows (id + name) into domain sections (name only). */
2497
+ normalizeIncomingSection(sec) {
2498
+ const ax = sec;
2499
+ const name = String(ax['name'] ?? ax['id'] ?? '').trim() || `section-${Date.now()}`;
2500
+ return {
2501
+ name,
2502
+ title: sec.title,
2503
+ description: sec.description,
2504
+ order: sec.order,
2505
+ tags: sec.tags,
2506
+ isVisible: sec.isVisible,
2507
+ isNewlyAdded: sec.isNewlyAdded,
2508
+ items: sec.items.map((it) => this.normalizeIncomingItem(it)),
2509
+ };
2510
+ }
2511
+ normalizeIncomingItem(it) {
2512
+ const name = String(it['name'] ?? it['id'] ?? '').trim() || `item-${Date.now()}`;
2513
+ const qbi = it.questionBankItemId;
2514
+ return {
2515
+ name,
2516
+ questionBankItemId: qbi,
2517
+ order: it.order,
2518
+ isRequired: it.isRequired,
2519
+ isVisible: it.isVisible,
2520
+ interfaceOverride: it.interfaceOverride,
2521
+ hint: it.hint,
2522
+ comment: it.comment,
2523
+ tags: it.tags,
2524
+ question: it.question,
2525
+ isNewlyAdded: it.isNewlyAdded,
2526
+ };
2527
+ }
2528
+ /** Edit dialog context: default visible; normalize legacy string booleans; expressions pass through (property viewer handles binding). */
2529
+ questionEditFormIsVisible(raw) {
2530
+ if (raw === undefined || raw === null) {
2531
+ return true;
2532
+ }
2533
+ if (typeof raw === 'boolean') {
2534
+ return raw;
2535
+ }
2536
+ if (raw.trim() === '') {
2537
+ return true;
2538
+ }
2539
+ const t = raw.trim().toLowerCase();
2540
+ if (t === 'true' || t === '1') {
2541
+ return true;
2542
+ }
2543
+ if (t === 'false' || t === '0') {
2544
+ return false;
2545
+ }
2546
+ return raw.trim();
2547
+ }
2548
+ questionEditFormIsRequired(item) {
2549
+ const r = item.isRequired;
2550
+ if (typeof r === 'string') {
2551
+ const t = r.trim();
2552
+ if (!t) {
2553
+ return false;
2554
+ }
2555
+ const tl = t.toLowerCase();
2556
+ if (tl === 'true' || tl === '1') {
2557
+ return true;
2558
+ }
2559
+ if (tl === 'false' || tl === '0') {
2560
+ return false;
2561
+ }
2562
+ return t;
2563
+ }
2564
+ return r ?? false;
2565
+ }
2566
+ /** Persist from property viewer: omit when default visible; `false` when hidden; trimmed non-empty string = expression. */
2567
+ persistQuestionIsVisibleFromForm(value) {
2568
+ if (value === undefined || value === null) {
2569
+ return undefined;
2570
+ }
2571
+ if (typeof value === 'boolean') {
2572
+ return value ? undefined : false;
2573
+ }
2574
+ if (typeof value === 'string') {
2575
+ const t = value.trim();
2576
+ return t || undefined;
2577
+ }
2578
+ return undefined;
2579
+ }
2580
+ persistQuestionIsRequiredFromForm(value) {
2581
+ if (value === undefined || value === null) {
2582
+ return false;
2583
+ }
2584
+ if (typeof value === 'boolean') {
2585
+ return value;
2586
+ }
2587
+ if (typeof value === 'string') {
2588
+ const t = value.trim();
2589
+ if (!t) {
2590
+ return false;
2591
+ }
2592
+ return t;
2593
+ }
2594
+ return false;
2595
+ }
2596
+ async questionnairePromptEditItem(item, sectionId, _value) {
2597
+ const qItem = item;
2598
+ const validationFailed = async (titleKey, descKey, fallTitle, fallDesc) => {
2599
+ const title = (await this.translationService.translateAsync(titleKey)) || fallTitle;
2600
+ const content = (await this.translationService.translateAsync(descKey)) || fallDesc;
2601
+ this.toastService.show({ color: 'warning', title, content });
2602
+ throw new Error('QuestionnaireEditQuestionValidation');
2603
+ };
2604
+ try {
2605
+ const bankRow = qItem.questionBankItemId
2606
+ ? await this.entityService
2607
+ .withEntity(RootConfig.module.name, RootConfig.entities.questionBankItem.name)
2608
+ .data()
2609
+ .byKey(qItem.questionBankItemId)
2610
+ .catch(() => null)
2611
+ : null;
2612
+ const bankComment = normalizeQuestionCommentConfig(bankRow?.comment);
2613
+ const mergedCommentForForm = mergeQuestionCommentConfig(bankComment, qItem.comment);
2614
+ const bankHint = normalizeQuestionHintFromEntityRow(bankRow ?? {});
2615
+ const mergedHintForForm = mergeQuestionHintForViewer(bankHint, parseQuestionHintPartialFromRecord(qItem));
2616
+ const result = await this.propertyViewerService
2617
+ .create()
2618
+ .dialog((d) => {
2619
+ d.setTitle('@assessment-management:questionnaires.components.questionnaire-builder.actions.edit-question.title')
2620
+ .setSize('md')
2621
+ .setCloseButton(true)
2622
+ .setMode('advanced')
2623
+ .setTabs(QUESTIONNAIRE_QUESTION_EDIT_TABS)
2624
+ .setContext({
2625
+ name: qItem.name,
2626
+ tags: qItem.tags ?? [],
2627
+ isRequired: this.questionEditFormIsRequired(qItem),
2628
+ isVisible: this.questionEditFormIsVisible(qItem.isVisible),
2629
+ hint: createQuestionHintFormContext(mergedHintForForm),
2630
+ comment: mergedCommentForForm,
2631
+ })
2632
+ .onAction(async (ref) => {
2633
+ const context = ref.context();
2634
+ const rawId = String(context.name ?? '').trim();
2635
+ if (!rawId) {
2636
+ 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.');
2637
+ }
2638
+ if (!this.isValidQuestionnaireItemName(rawId)) {
2639
+ 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).');
2640
+ }
2641
+ const isDuplicate = this.builderState().sections.some((s) => s.items.some((i) => (s.name !== sectionId || i.name !== qItem.name) && i.name === rawId));
2642
+ if (isDuplicate) {
2643
+ 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.');
2644
+ }
2645
+ });
2646
+ })
2647
+ .show();
2648
+ if (!result) {
2649
+ return null;
2650
+ }
2651
+ const formData = result.values;
2652
+ const rawIdOut = String(formData.name ?? '').trim();
2653
+ const tags = this.normalizeTagsFromForm(formData.tags);
2654
+ const hintPersist = this.persistQuestionHintFromForm(bankHint, formData.hint);
2655
+ const commentPersist = this.persistQuestionCommentFromForm(bankComment, formData.comment);
2656
+ const nextItem = {
2657
+ ...qItem,
2658
+ name: rawIdOut,
2659
+ id: rawIdOut,
2660
+ tags,
2661
+ isRequired: this.persistQuestionIsRequiredFromForm(formData.isRequired),
2662
+ isVisible: this.persistQuestionIsVisibleFromForm(formData.isVisible),
2663
+ };
2664
+ if (hintPersist != null) {
2665
+ nextItem.hint = hintPersist;
2666
+ }
2667
+ else {
2668
+ delete nextItem.hint;
2669
+ }
2670
+ if (commentPersist != null) {
2671
+ nextItem.comment = commentPersist;
2672
+ }
2673
+ else {
2674
+ delete nextItem.comment;
2675
+ }
2676
+ return nextItem;
2677
+ }
2678
+ catch (error) {
2679
+ if (error instanceof Error && error.message === 'QuestionnaireEditQuestionValidation') {
2680
+ return null;
2681
+ }
2682
+ console.error('Error editing question:', error);
2683
+ return null;
2684
+ }
2685
+ }
2686
+ //#endregion
2687
+ //#region ---- Section Management ----
2688
+ /** Toolbar / page command: delegates to {@link AXPStandardSectionItemsBuilderComponent#addSection}. */
2689
+ async addSection() {
2690
+ await this.standardRef()?.addSection();
2691
+ }
2692
+ //#endregion
2693
+ //#region ---- Item Management ----
2694
+ async questionnairePromptAddItems(sectionId, value) {
2695
+ const all = value.sections;
2696
+ const section = all.find((x) => x.name === sectionId);
2697
+ if (!section)
2698
+ return null;
2699
+ return (await this.selectQuestionsFromQuestionBankForSection(section, all));
2700
+ }
2701
+ /**
2702
+ * Open question bank selector and return new items (standard builder appends them).
2703
+ */
2704
+ async selectQuestionsFromQuestionBankForSection(section, allSections) {
2705
+ try {
2706
+ const dataSource = new AXDataSource({
2707
+ key: 'id',
2708
+ pageSize: 20,
2709
+ load: async (e) => {
2710
+ const questions = await this.questionBankData().query(e);
2711
+ return {
2712
+ items: questions.items,
2713
+ total: questions.total,
2714
+ };
2715
+ },
2716
+ });
2717
+ // Create category tree data source for filtering
2718
+ const categoryTreeDataSource = {
2719
+ loadRootNodes: async () => {
2720
+ const result = await this.questionBankCategoryData().query({
2721
+ skip: 0,
2722
+ take: 1000,
2723
+ filter: {
2724
+ field: 'parentId',
2725
+ operator: { type: 'isEmpty' },
2726
+ value: true,
2727
+ },
2728
+ });
2729
+ return result.items.map((item) => ({
2730
+ id: item.id,
2731
+ title: item.title || item.name,
2732
+ description: item.description || '',
2733
+ parentId: item.parentId,
2734
+ childrenCount: item.childrenCount || 0,
2735
+ }));
2736
+ },
2737
+ loadChildNodes: async (parentId) => {
2738
+ const result = await this.questionBankCategoryData().query({
2739
+ skip: 0,
2740
+ take: 1000,
2741
+ filter: {
2742
+ field: 'parentId',
2743
+ operator: { type: 'equal' },
2744
+ value: parentId,
2745
+ },
2746
+ });
2747
+ return result.items.map((item) => ({
2748
+ id: item.id,
2749
+ title: item.title || item.name,
2750
+ description: item.description || '',
2751
+ parentId: item.parentId,
2752
+ childrenCount: item.childrenCount || 0,
2753
+ }));
2754
+ },
2755
+ searchNodes: async (searchValue) => {
2756
+ const result = await this.questionBankCategoryData().query({
2757
+ skip: 0,
2758
+ take: 1000,
2759
+ filter: {
2760
+ filters: [
2761
+ {
2762
+ field: 'title',
2763
+ operator: { type: 'contains' },
2764
+ value: searchValue,
2765
+ },
2766
+ {
2767
+ field: 'name',
2768
+ operator: { type: 'contains' },
2769
+ value: searchValue,
2770
+ },
2771
+ ],
2772
+ logic: 'or',
2773
+ },
2774
+ });
2775
+ return result.items.map((item) => ({
2776
+ id: item.id,
2777
+ title: item.title || item.name,
2778
+ description: item.description || '',
2779
+ parentId: item.parentId,
2780
+ childrenCount: item.childrenCount || 0,
2781
+ }));
2782
+ },
2783
+ };
2784
+ const entity = await this.entityResolver.resolve(RootConfig.module.name, RootConfig.entities.questionBankItem.name);
2785
+ if (!entity) {
2786
+ throw new Error('Entity not found: ' + RootConfig.entities.questionBankItem.name);
2787
+ }
2788
+ const columns = entity.columns
2789
+ ?.filter((column) => entity.properties.some((property) => property.name === column.name))
2790
+ .map((column) => {
2791
+ const property = entity.properties.find((prop) => prop.name === column.name);
2792
+ return {
2793
+ name: column.name,
2794
+ title: property?.title ?? '',
2795
+ visible: column.options?.visible !== false,
2796
+ widget: {
2797
+ type: column.showAs?.type ?? property?.schema?.interface?.type ?? 'text-editor',
2798
+ options: column.showAs?.options ?? property?.schema?.interface?.options ?? {},
2799
+ },
2800
+ };
2801
+ }) || [];
2802
+ // Translate titles
2803
+ const selectQuestionsTitle = (await this.translationService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.select-questions')) || 'Select Questions from Question Bank';
2804
+ const categoriesTitle = (await this.translationService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.categories')) || 'Categories';
2805
+ const titleLabel = (await this.translationService.translateAsync('@general:terms.common.title')) || 'Title';
2806
+ const noteLabel = (await this.translationService.translateAsync('@assessment-management:question-bank.entities.question-bank-item.fields.note.title')) || 'Note';
2807
+ // Open data selector with category filter
2808
+ const result = await this.dataSelectorService.openWithCategoryFilter({
2809
+ title: selectQuestionsTitle,
2810
+ dataSource: dataSource,
2811
+ columns: columns,
2812
+ selectionMode: 'multiple',
2813
+ searchFields: [
2814
+ { name: 'title', title: titleLabel },
2815
+ { name: 'note', title: noteLabel },
2816
+ ],
2817
+ allowCreate: 'full',
2818
+ onCreate: (mode) => this.createQuestionBankItem(entity, mode),
2819
+ }, {
2820
+ title: categoriesTitle,
2821
+ dataSource: categoryTreeDataSource,
2822
+ filterField: 'categoryIds',
2823
+ filterOperator: 'contains',
2824
+ width: '350px',
2825
+ });
2826
+ if (result?.items && result.items.length > 0) {
2827
+ const questionPromises = result.items.map((item) => this.questionBankData()
2828
+ .byKey(item.id)
2829
+ .catch(() => null));
2830
+ const questions = await Promise.all(questionPromises);
2831
+ const used = collectQuestionnaireItemNames(allSections);
2832
+ const baseOrder = section.items.length;
2833
+ const newItems = result.items.map((item, index) => {
2834
+ const fullQuestion = questions[index];
2835
+ const qName = allocateNextQuestionItemName(used);
2836
+ used.add(qName);
2837
+ return {
2838
+ name: qName,
2839
+ questionBankItemId: item.id,
2840
+ order: baseOrder + index,
2841
+ isRequired: false,
2842
+ isVisible: undefined,
2843
+ isNewlyAdded: true,
2844
+ question: fullQuestion
2845
+ ? {
2846
+ id: fullQuestion.id,
2847
+ title: fullQuestion.title,
2848
+ question: fullQuestion.question,
2849
+ note: fullQuestion.note,
2850
+ difficulty: fullQuestion.difficulty,
2851
+ }
2852
+ : {
2853
+ id: item.id,
2854
+ title: item.title || 'Question',
2855
+ question: item.question,
2856
+ note: item.note,
2857
+ difficulty: item.difficulty,
2858
+ },
2859
+ };
2860
+ });
2861
+ return newItems;
2862
+ }
2863
+ return null;
2864
+ }
2865
+ catch (error) {
2866
+ console.error('Error loading questions:', error);
2867
+ return null;
2868
+ }
2869
+ }
2870
+ /**
2871
+ * Create a new question bank item via Entity:Create and return it for selection in the data selector.
2872
+ */
2873
+ async createQuestionBankItem(entity, _mode) {
2874
+ const result = await this.commandService.execute('Entity:Create', {
2875
+ __context__: {
2876
+ entity: `${entity.module}.${entity.name}`,
2877
+ entityInfo: {
2878
+ name: entity.name,
2879
+ module: entity.module,
2880
+ title: entity.title,
2881
+ parentKey: entity.parentKey,
2882
+ },
2883
+ data: {},
2884
+ options: {},
2885
+ },
2886
+ });
2887
+ if (result?.success && result?.data) {
2888
+ const item = Array.isArray(result.data) ? result.data[0] : result.data;
2889
+ return item;
2890
+ }
2891
+ return null;
2892
+ }
2893
+ /**
2894
+ * Maps nested `hint` from the question property viewer to persisted questionnaire item `hint` partial (diff from bank).
2895
+ */
2896
+ persistQuestionHintFromForm(bank, hint) {
2897
+ if (!hint) {
2898
+ return undefined;
2899
+ }
2900
+ const modeRaw = hint.mode == null ? undefined : typeof hint.mode === 'string' ? hint.mode : hint.mode.id;
2901
+ const mode = normalizeQuestionHintMode(modeRaw) ?? 'off';
2902
+ const c = hint.content;
2903
+ let content;
2904
+ if (c == null) {
2905
+ content = undefined;
2906
+ }
2907
+ else if (typeof c === 'string') {
2908
+ const t = c.trim();
2909
+ content = t || undefined;
2910
+ }
2911
+ else if (typeof c === 'object') {
2912
+ content = c;
2913
+ }
2914
+ const edited = normalizeQuestionHintConfig({ mode, content });
2915
+ return diffQuestionHintOverride(bank, edited);
2916
+ }
2917
+ /**
2918
+ * Persists questionnaire item `comment` override: only fields that differ from the question bank default.
2919
+ */
2920
+ persistQuestionCommentFromForm(bank, form) {
2921
+ if (!form) {
2922
+ return undefined;
2923
+ }
2924
+ const edited = normalizeQuestionCommentConfig(form);
2925
+ return diffQuestionCommentOverride(bank, edited);
2926
+ }
2927
+ /**
2928
+ * Normalize tag-editor output to trimmed unique strings (supports string[] or { title }[] from tag service).
2929
+ */
2930
+ normalizeTagsFromForm(raw) {
2931
+ if (raw == null || !Array.isArray(raw))
2932
+ return undefined;
2933
+ const seen = new Set();
2934
+ const out = [];
2935
+ for (const entry of raw) {
2936
+ let t = '';
2937
+ if (typeof entry === 'string')
2938
+ t = entry.trim();
2939
+ else if (entry && typeof entry === 'object' && 'title' in entry) {
2940
+ t = String(entry.title).trim();
2941
+ }
2942
+ if (!t)
2943
+ continue;
2944
+ const key = t.toLowerCase();
2945
+ if (seen.has(key))
2946
+ continue;
2947
+ seen.add(key);
2948
+ out.push(t);
2949
+ }
2950
+ return out.length ? out : undefined;
2951
+ }
2952
+ /** Allowed characters for questionnaire item technical name (UUID-safe). */
2953
+ isValidQuestionnaireItemName(name) {
2954
+ return name.length >= 1 && name.length <= 128 && /^[a-zA-Z0-9_-]+$/.test(name);
2955
+ }
2956
+ /**
2957
+ * Plain heading for builder cards: prefer rich `question`, else admin list `title`.
2958
+ */
2959
+ itemQuestionHeading(item) {
2960
+ const q = item.question;
2961
+ if (!q) {
2962
+ return 'Question';
2963
+ }
2964
+ const locale = this.translationService.getActiveLang?.() ?? 'en-US';
2965
+ const raw = q.question;
2966
+ if (raw != null && raw !== '') {
2967
+ const html = typeof raw === 'string' ? raw : resolveMultiLanguageString(raw, locale);
2968
+ const plain = stripHtmlToPlain(html);
2969
+ if (plain.trim()) {
2970
+ return plain;
2971
+ }
2972
+ }
2973
+ const titleResolved = resolveMultiLanguageString(q.title, locale);
2974
+ return titleResolved.trim() || 'Question';
2975
+ }
2976
+ /** Plain preview line for optional note under the heading. */
2977
+ itemQuestionNotePreview(item) {
2978
+ const raw = item.question?.note;
2979
+ if (raw == null || raw === '') {
2980
+ return '';
2981
+ }
2982
+ const locale = this.translationService.getActiveLang?.() ?? 'en-US';
2983
+ const html = typeof raw === 'string' ? raw : resolveMultiLanguageString(raw, locale);
2984
+ return stripHtmlToPlain(html);
2985
+ }
2986
+ /**
2987
+ * Resolve icon based on widget type, with safe fallback.
2988
+ */
2989
+ getQuestionWidgetIcon(item) {
2990
+ const fallbackIcon = 'fa-light fa-question-circle';
2991
+ const widgetType = item.interfaceOverride?.type ?? item.question?.interface?.type;
2992
+ if (!widgetType) {
2993
+ return fallbackIcon;
2994
+ }
2995
+ try {
2996
+ return this.widgetRegistry.resolve(widgetType).icon ?? fallbackIcon;
2997
+ }
2998
+ catch {
2999
+ return fallbackIcon;
3000
+ }
3001
+ }
3002
+ //#endregion
3003
+ //#region ---- Preview ----
3004
+ /**
3005
+ * Open preview dialog with questionnaire viewer
3006
+ */
3007
+ async handlePreview() {
3008
+ if (this.isPreviewDisabled()) {
3009
+ return;
3010
+ }
3011
+ try {
3012
+ const currentValue = convertStateToValue(this.builderState(), this.value() || undefined);
3013
+ // Always use the current displaySettings from the computed property to ensure preview uses latest settings
3014
+ currentValue.displaySettings = this.displaySettings();
3015
+ const previewTitle = (await this.translationService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.actions.preview.title')) || 'Preview';
3016
+ const result = await this.questionnaireViewerService.open({
3017
+ title: previewTitle,
3018
+ structure: currentValue,
3019
+ prePostConfig: this.prePostConfig() ?? undefined,
3020
+ questionnaireId: this.questionnaireEntityId(),
3021
+ });
3022
+ // Handle result if needed (answers, submitted status)
3023
+ if (result) {
3024
+ console.log('Questionnaire preview result:', result);
3025
+ }
3026
+ }
3027
+ catch (error) {
3028
+ console.error('Error opening preview:', error);
3029
+ }
3030
+ }
3031
+ /**
3032
+ * Toggle fullscreen mode using the directive
3033
+ */
3034
+ async handleFullscreen() {
3035
+ const fullscreen = this.fullscreenDirective();
3036
+ if (fullscreen) {
3037
+ await fullscreen.toggle();
3038
+ }
3039
+ }
3040
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3041
+ 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 }); }
3042
+ }
3043
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsBuilderComponent, decorators: [{
3044
+ type: Component,
3045
+ args: [{ selector: 'axm-questionnaire-questions-builder', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
3046
+ CommonModule,
3047
+ FormsModule,
3048
+ AXButtonModule,
3049
+ AXDecoratorModule,
3050
+ AXDropdownModule,
3051
+ AXFormModule,
3052
+ AXSwitchModule,
3053
+ AXLabelModule,
3054
+ AXTranslationModule,
3055
+ AXPStandardSectionItemsBuilderComponent,
3056
+ AXFullScreenDirective,
3057
+ ], 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"] }]
3058
+ }], 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 }] }] } });
3059
+
3060
+ /** Component key for the questionnaire Questions page tab. Register via AXP_PAGE_COMPONENT_PROVIDER. */
3061
+ const QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY = 'questionnaire-questions-page';
3062
+
3063
+ const QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND = 'Questionnaire:Questions:Save';
3064
+ /**
3065
+ * Command to save the questionnaire questions field.
3066
+ * By default uses entity update (patch) with { questions }.
3067
+ * Can be overridden for custom API logic.
3068
+ */
3069
+ class AXMSaveQuestionnaireQuestionsCommand {
3070
+ constructor() {
3071
+ this.entityService = inject(AXPEntityService);
3072
+ this.translationService = inject(AXTranslationService);
3073
+ }
3074
+ async execute(input) {
3075
+ const { id, questions } = input;
3076
+ if (!id) {
3077
+ return {
3078
+ success: false,
3079
+ message: {
3080
+ text: await this.translationService.translateAsync('@general:messages.entity.invalid-data'),
3081
+ },
3082
+ };
3083
+ }
3084
+ try {
3085
+ const entityRef = `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`;
3086
+ const dataAccessor = this.entityService.withEntity(entityRef).data();
3087
+ await dataAccessor.update(id, { questions });
3088
+ return {
3089
+ success: true,
3090
+ data: { id, questions },
3091
+ message: {
3092
+ text: await this.translationService.translateAsync('@general:messages.generic.success.description'),
3093
+ },
3094
+ };
3095
+ }
3096
+ catch (error) {
3097
+ const message = error instanceof Error
3098
+ ? error.message
3099
+ : await this.translationService.translateAsync('@general:messages.generic.error.description');
3100
+ return {
3101
+ success: false,
3102
+ message: { text: message },
3103
+ };
3104
+ }
3105
+ }
3106
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireQuestionsCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3107
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireQuestionsCommand, providedIn: 'root' }); }
3108
+ }
3109
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireQuestionsCommand, decorators: [{
3110
+ type: Injectable,
3111
+ args: [{
3112
+ providedIn: 'root',
3113
+ }]
3114
+ }] });
3115
+
3116
+ var saveQuestionnaireQuestions_command = /*#__PURE__*/Object.freeze({
3117
+ __proto__: null,
3118
+ AXMSaveQuestionnaireQuestionsCommand: AXMSaveQuestionnaireQuestionsCommand,
3119
+ QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND: QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND
3120
+ });
3121
+
3122
+ /** Local command handled in execute(); reverts questions to rootContext (no AXPCommand). */
3123
+ const QUESTIONNAIRE_QUESTIONS_DISCARD_COMMAND = 'Questionnaire:Questions:Discard';
3124
+ const QUESTIONNAIRE_QUESTIONS_ADD_SECTION_COMMAND = 'Questionnaire:Questions:AddSection';
3125
+ const QUESTIONNAIRE_QUESTIONS_PREVIEW_COMMAND = 'Questionnaire:Questions:Preview';
3126
+ //#region ---- Questionnaire Questions Page Component ----
3127
+ /**
3128
+ * Page component that renders the questions property using axm-questionnaire-questions-builder.
3129
+ * Extends AXPPageLayoutBaseComponent to provide a Save button that persists questions via Questionnaire:Questions:Save.
3130
+ * Receives rootContext (entity data).
3131
+ */
3132
+ class AXMQuestionnaireQuestionsPageComponent extends AXPPageLayoutBaseComponent {
3133
+ //#endregion
3134
+ //#region ---- Lifecycle ----
3135
+ constructor() {
3136
+ super();
3137
+ //#region ---- Inputs ----
3138
+ this.rootContext = input(...(ngDevMode ? [undefined, { debugName: "rootContext" }] : /* istanbul ignore next */ []));
3139
+ /** Optional page config (e.g. field, layout) set by the page-component-renderer. */
3140
+ this.pageConfig = input(...(ngDevMode ? [undefined, { debugName: "pageConfig" }] : /* istanbul ignore next */ []));
3141
+ //#endregion
3142
+ //#region ---- Services & Dependencies ----
3143
+ this.commandService = inject(AXPCommandService);
3144
+ this.eventService = inject(AXPBroadcastEventService);
3145
+ this.hostPageLayout = inject(AXPPageLayoutBase, { optional: true, skipSelf: true });
3146
+ //#endregion
3147
+ //#region ---- View References ----
3148
+ this.builderRef = viewChild('builderRef', ...(ngDevMode ? [{ debugName: "builderRef" }] : /* istanbul ignore next */ []));
3149
+ //#endregion
3150
+ //#region ---- State Signals ----
3151
+ /** Latest context (includes edited questions). Used for Save when rootContext is not updated by the parent. */
3152
+ this.localContext = signal(null, ...(ngDevMode ? [{ debugName: "localContext" }] : /* istanbul ignore next */ []));
3153
+ /** When true, the next valueChange from the builder is ignored (avoids undoing discard due to stale emit). */
3154
+ this.ignoreNextValueChange = signal(false, ...(ngDevMode ? [{ debugName: "ignoreNextValueChange" }] : /* istanbul ignore next */ []));
3155
+ /** Stable pre/post config so we don't pass a new object every CD (avoids infinite loop). */
3156
+ this.prePostConfigStable = signal(null, ...(ngDevMode ? [{ debugName: "prePostConfigStable" }] : /* istanbul ignore next */ []));
3157
+ /** Baseline snapshot of questions for dirty checks. */
3158
+ this.baselineQuestions = signal(null, ...(ngDevMode ? [{ debugName: "baselineQuestions" }] : /* istanbul ignore next */ []));
3159
+ /**
3160
+ * When true, the next builder emission should realign the baseline to the builder's canonical shape.
3161
+ * 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.
3162
+ */
3163
+ this.questionsBaselinePendingReconcile = signal(false, ...(ngDevMode ? [{ debugName: "questionsBaselinePendingReconcile" }] : /* istanbul ignore next */ []));
3164
+ this.isDirty = signal(false, ...(ngDevMode ? [{ debugName: "isDirty" }] : /* istanbul ignore next */ []));
3165
+ //#endregion
3166
+ //#region ---- Computed State ----
3167
+ /** Current context: prefer local updates so edits are preserved. */
3168
+ this.context = computed(() => {
3169
+ const local = this.localContext();
3170
+ if (local != null)
3171
+ return local;
3172
+ return this.rootContext() ?? {};
3173
+ }, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
3174
+ /** Questions value passed to the builder (from context). */
3175
+ this.questionsValue = computed(() => {
3176
+ const ctx = this.context();
3177
+ const q = ctx['questions'];
3178
+ return (q != null && typeof q === 'object' ? q : null);
3179
+ }, ...(ngDevMode ? [{ debugName: "questionsValue" }] : /* istanbul ignore next */ []));
3180
+ /** Pre/post config for preview; stable reference to avoid infinite change detection. */
3181
+ this.prePostConfig = computed(() => this.prePostConfigStable(), ...(ngDevMode ? [{ debugName: "prePostConfig" }] : /* istanbul ignore next */ []));
3182
+ /** Display settings from entity (for builder preview). */
3183
+ this.entityDisplaySettings = computed(() => toDisplaySettings((this.rootContext() ?? undefined)) ?? null, ...(ngDevMode ? [{ debugName: "entityDisplaySettings" }] : /* istanbul ignore next */ []));
3184
+ /** Questionnaire entity id for preview outcome evaluation. */
3185
+ this.questionnaireEntityId = computed(() => {
3186
+ const id = this.rootContext()?.['id'];
3187
+ return typeof id === 'string' && id.length > 0 ? id : undefined;
3188
+ }, ...(ngDevMode ? [{ debugName: "questionnaireEntityId" }] : /* istanbul ignore next */ []));
3189
+ let previousRootId = undefined;
3190
+ effect(() => {
3191
+ const ctx = this.rootContext();
3192
+ const currentId = ctx != null && typeof ctx === 'object' ? ctx['id'] : undefined;
3193
+ if (currentId !== previousRootId) {
3194
+ previousRootId = currentId;
3195
+ this.localContext.set(null);
3196
+ const rootQuestions = ctx?.['questions'];
3197
+ this.baselineQuestions.set(this.cloneQuestions(rootQuestions));
3198
+ this.isDirty.set(false);
3199
+ this.questionsBaselinePendingReconcile.set(true);
3200
+ }
3201
+ });
3202
+ let lastPrePostKey = '';
3203
+ effect(() => {
3204
+ const ctx = this.rootContext();
3205
+ const next = toPrePostConfig(ctx ?? undefined);
3206
+ const key = next ? JSON.stringify(next) : '';
3207
+ if (key !== lastPrePostKey) {
3208
+ lastPrePostKey = key;
3209
+ this.prePostConfigStable.set(next);
3210
+ }
3211
+ });
3212
+ }
3213
+ //#endregion
3214
+ //#region ---- Page Actions ----
3215
+ async getPrimaryMenuItems() {
3216
+ const addSectionTitle = (await this.translateService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.actions.add-section.title')) || 'Add Section';
3217
+ const previewTitle = (await this.translateService.translateAsync('@assessment-management:questionnaires.components.questionnaire-builder.actions.preview.title')) || 'Preview';
3218
+ const discardTitle = (await this.translateService.translateAsync('@general:actions.discard.title')) || 'Discard';
3219
+ const saveTitle = (await this.translateService.translateAsync('@general:actions.save.title')) || 'Save';
3220
+ const addSectionAction = {
3221
+ title: addSectionTitle,
3222
+ icon: 'fa-light fa-layer-group',
3223
+ zone: 'header',
3224
+ priority: 'primary',
3225
+ color: 'primary',
3226
+ command: {
3227
+ name: QUESTIONNAIRE_QUESTIONS_ADD_SECTION_COMMAND,
3228
+ options: {},
3229
+ },
3230
+ };
3231
+ const previewAction = {
3232
+ title: previewTitle,
3233
+ icon: 'fa-light fa-eye',
3234
+ zone: 'header',
3235
+ priority: 'primary',
3236
+ color: 'default',
3237
+ command: {
3238
+ name: QUESTIONNAIRE_QUESTIONS_PREVIEW_COMMAND,
3239
+ options: {},
3240
+ },
3241
+ };
3242
+ const discardAction = {
3243
+ title: discardTitle,
3244
+ icon: 'fa-light fa-rotate-left',
3245
+ zone: 'footer',
3246
+ priority: 'secondary',
3247
+ color: 'default',
3248
+ visible: this.isDirty(),
3249
+ command: {
3250
+ name: QUESTIONNAIRE_QUESTIONS_DISCARD_COMMAND,
3251
+ options: {},
3252
+ },
3253
+ };
3254
+ const saveAction = {
3255
+ title: saveTitle,
3256
+ icon: 'fa-light fa-floppy-disk',
3257
+ zone: 'footer',
3258
+ priority: 'primary',
3259
+ color: 'primary',
3260
+ visible: this.isDirty(),
3261
+ command: {
3262
+ name: QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND,
3263
+ options: {},
3264
+ },
3265
+ };
3266
+ return [addSectionAction, previewAction, discardAction, saveAction];
3267
+ }
3268
+ //#endregion
3269
+ //#region ---- Command Handling ----
3270
+ execute(command) {
3271
+ if (command.name === QUESTIONNAIRE_QUESTIONS_DISCARD_COMMAND) {
3272
+ this.ignoreNextValueChange.set(true);
3273
+ this.localContext.set(null);
3274
+ this.isDirty.set(false);
3275
+ this.questionsBaselinePendingReconcile.set(true);
3276
+ this.recompute();
3277
+ return;
3278
+ }
3279
+ if (command.name === QUESTIONNAIRE_QUESTIONS_ADD_SECTION_COMMAND) {
3280
+ void this.builderRef()?.addSection();
3281
+ return;
3282
+ }
3283
+ if (command.name === QUESTIONNAIRE_QUESTIONS_PREVIEW_COMMAND) {
3284
+ this.builderRef()?.handlePreview();
3285
+ return;
3286
+ }
3287
+ if (command.name !== QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND) {
3288
+ return;
3289
+ }
3290
+ // Return result for delegateExecute; cast needed to satisfy base signature
3291
+ return this.handleSaveExecute();
3292
+ }
3293
+ async handleSaveExecute() {
3294
+ const ctx = this.localContext() ?? this.rootContext() ?? {};
3295
+ const id = typeof ctx['id'] === 'string' ? ctx['id'] : undefined;
3296
+ const questions = ctx['questions'];
3297
+ if (!id) {
3298
+ return {
3299
+ success: false,
3300
+ message: {
3301
+ text: await this.translateService.translateAsync('@general:messages.entity.invalid-data'),
3302
+ },
3303
+ };
3304
+ }
3305
+ const input = {
3306
+ id,
3307
+ questions: questions ?? { sections: [] },
3308
+ };
3309
+ const result = await this.commandService.execute(QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND, input);
3310
+ const execResult = (result ?? { success: false });
3311
+ if (execResult.success) {
3312
+ const savedQuestions = questions ?? { sections: [] };
3313
+ this.baselineQuestions.set(this.cloneQuestions(savedQuestions));
3314
+ this.localContext.set(null);
3315
+ this.isDirty.set(false);
3316
+ this.questionsBaselinePendingReconcile.set(true);
3317
+ this.recompute();
3318
+ this.eventService.publish(AXPEntityEventsKeys.REFRESH_LAYOUT, {
3319
+ name: `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`,
3320
+ });
3321
+ // Success toast is shown by details-view when execute() returns success
3322
+ }
3323
+ return execResult;
3324
+ }
3325
+ //#endregion
3326
+ //#region ---- UI Handlers ----
3327
+ /** Updates local context when the builder emits a new questions value. Skip if unchanged to avoid CD loop. */
3328
+ onQuestionsChange(value) {
3329
+ if (this.ignoreNextValueChange()) {
3330
+ this.ignoreNextValueChange.set(false);
3331
+ return;
3332
+ }
3333
+ const current = this.questionsValue();
3334
+ if (this.areQuestionsEqual(current ?? undefined, value ?? undefined)) {
3335
+ if (this.questionsBaselinePendingReconcile()) {
3336
+ const aligned = current ?? value;
3337
+ this.baselineQuestions.set(this.cloneQuestions(aligned ?? undefined));
3338
+ this.questionsBaselinePendingReconcile.set(false);
3339
+ this.isDirty.set(false);
3340
+ }
3341
+ return;
3342
+ }
3343
+ const base = this.localContext() ?? this.rootContext() ?? {};
3344
+ if (this.questionsBaselinePendingReconcile() && this.localContext() == null) {
3345
+ this.baselineQuestions.set(this.cloneQuestions(value));
3346
+ this.isDirty.set(false);
3347
+ this.questionsBaselinePendingReconcile.set(false);
3348
+ this.localContext.set({ ...base, questions: value });
3349
+ this.recompute();
3350
+ return;
3351
+ }
3352
+ this.localContext.set({ ...base, questions: value });
3353
+ const baseline = this.baselineQuestions();
3354
+ const dirty = !this.areQuestionsEqual(baseline, value);
3355
+ this.isDirty.set(dirty);
3356
+ this.recompute();
3357
+ }
3358
+ //#endregion
3359
+ //#region ---- Utility Methods ----
3360
+ areQuestionsEqual(a, b) {
3361
+ return isEqual(a, b);
3362
+ }
3363
+ cloneQuestions(value) {
3364
+ if (isNil(value))
3365
+ return value;
3366
+ return cloneDeep(value);
3367
+ }
3368
+ recompute() {
3369
+ super.recompute();
3370
+ const maybeLayout = this.hostPageLayout;
3371
+ maybeLayout.recompute?.();
3372
+ }
3373
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3374
+ 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: [
3375
+ {
3376
+ provide: AXPPageLayoutBase,
3377
+ useExisting: AXMQuestionnaireQuestionsPageComponent,
3378
+ },
3379
+ ], viewQueries: [{ propertyName: "builderRef", first: true, predicate: ["builderRef"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
3380
+ <axm-questionnaire-questions-builder
3381
+ #builderRef
3382
+ [value]="questionsValue()"
3383
+ [prePostConfig]="prePostConfig()"
3384
+ [displaySettingsInput]="entityDisplaySettings()"
3385
+ [questionnaireEntityId]="questionnaireEntityId()"
3386
+ [showHeader]="false"
3387
+ (valueChange)="onQuestionsChange($event)">
3388
+ </axm-questionnaire-questions-builder>
3389
+ `, 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 }); }
3390
+ }
3391
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponent, decorators: [{
3392
+ type: Component,
3393
+ args: [{
3394
+ selector: 'axm-questionnaire-questions-page',
3395
+ standalone: true,
3396
+ imports: [AXMQuestionnaireQuestionsBuilderComponent],
3397
+ template: `
3398
+ <axm-questionnaire-questions-builder
3399
+ #builderRef
3400
+ [value]="questionsValue()"
3401
+ [prePostConfig]="prePostConfig()"
3402
+ [displaySettingsInput]="entityDisplaySettings()"
3403
+ [questionnaireEntityId]="questionnaireEntityId()"
3404
+ [showHeader]="false"
3405
+ (valueChange)="onQuestionsChange($event)">
3406
+ </axm-questionnaire-questions-builder>
3407
+ `,
3408
+ changeDetection: ChangeDetectionStrategy.OnPush,
3409
+ providers: [
3410
+ {
3411
+ provide: AXPPageLayoutBase,
3412
+ useExisting: AXMQuestionnaireQuestionsPageComponent,
3413
+ },
3414
+ ],
3415
+ }]
3416
+ }], 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 }] }] } });
3417
+
3418
+ var questionnaireQuestionsPage_component = /*#__PURE__*/Object.freeze({
3419
+ __proto__: null,
3420
+ AXMQuestionnaireQuestionsPageComponent: AXMQuestionnaireQuestionsPageComponent
3421
+ });
3422
+
3423
+ //#region ---- Questionnaire Questions Page Component Provider ----
3424
+ class AXMQuestionnaireQuestionsPageComponentProvider {
3425
+ async components() {
3426
+ return [
3427
+ {
3428
+ key: QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY,
3429
+ loader: () => Promise.resolve().then(function () { return questionnaireQuestionsPage_component; }).then((m) => m.AXMQuestionnaireQuestionsPageComponent),
3430
+ },
3431
+ ];
3432
+ }
3433
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponentProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3434
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponentProvider }); }
3435
+ }
3436
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireQuestionsPageComponentProvider, decorators: [{
3437
+ type: Injectable
3438
+ }] });
3439
+
3440
+ var index$1 = /*#__PURE__*/Object.freeze({
3441
+ __proto__: null,
3442
+ AXMQuestionnaireQuestionsPageComponent: AXMQuestionnaireQuestionsPageComponent,
3443
+ AXMQuestionnaireQuestionsPageComponentProvider: AXMQuestionnaireQuestionsPageComponentProvider,
3444
+ AXMSaveQuestionnaireQuestionsCommand: AXMSaveQuestionnaireQuestionsCommand,
3445
+ QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY: QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY,
3446
+ QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND: QUESTIONNAIRE_QUESTIONS_SAVE_COMMAND
3447
+ });
3448
+
3449
+ //#region ---- Questions builder exports ----
3450
+ //#endregion
3451
+
3452
+ //#region ---- Imports ----
3453
+ //#endregion
3454
+ //#region ---- Select options ----
3455
+ /** i18n keys for comparison operator select options. */
3456
+ const OUTCOME_DISPLAY_OPERATOR_LABEL_KEYS = {
3457
+ eq: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.eq',
3458
+ neq: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.neq',
3459
+ gt: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.gt',
3460
+ gte: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.gte',
3461
+ lt: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.lt',
3462
+ lte: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-operator.options.lte',
3463
+ };
3464
+ /** i18n keys for emphasis select options. */
3465
+ const OUTCOME_DISPLAY_EMPHASIS_LABEL_KEYS = {
3466
+ normal: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-emphasis.options.normal',
3467
+ medium: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-emphasis.options.medium',
3468
+ bold: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-emphasis.options.bold',
3469
+ };
3470
+ const OUTCOME_DISPLAY_OPERATOR_SELECT_OPTIONS = Object.keys(OUTCOME_DISPLAY_OPERATOR_LABEL_KEYS).map((id) => ({
3471
+ id,
3472
+ title: OUTCOME_DISPLAY_OPERATOR_LABEL_KEYS[id],
3473
+ }));
3474
+ const OUTCOME_DISPLAY_OPERATOR_STRING_SELECT_OPTIONS = OUTCOME_DISPLAY_OPERATOR_SELECT_OPTIONS.filter((option) => option.id === 'eq' || option.id === 'neq');
3475
+ const OUTCOME_DISPLAY_OPERATOR_BOOLEAN_SELECT_OPTIONS = OUTCOME_DISPLAY_OPERATOR_SELECT_OPTIONS.filter((option) => option.id === 'eq');
3476
+ const OUTCOME_DISPLAY_EMPHASIS_SELECT_OPTIONS = Object.keys(OUTCOME_DISPLAY_EMPHASIS_LABEL_KEYS).map((id) => ({
3477
+ id,
3478
+ title: OUTCOME_DISPLAY_EMPHASIS_LABEL_KEYS[id],
3479
+ }));
3480
+ function toOutcomeDisplayOperatorSelectValue(operator) {
3481
+ return { id: operator, title: OUTCOME_DISPLAY_OPERATOR_LABEL_KEYS[operator] };
3482
+ }
3483
+ function toOutcomeDisplayEmphasisSelectValue(emphasis) {
3484
+ return { id: emphasis, title: OUTCOME_DISPLAY_EMPHASIS_LABEL_KEYS[emphasis] };
3485
+ }
3486
+ //#endregion
3487
+
3488
+ //#region ---- Imports ----
3489
+ //#endregion
3490
+ //#region ---- Result kind labels ----
3491
+ /** i18n keys for select options (property viewer). */
3492
+ const OUTCOME_RESULT_KIND_LABEL_KEYS = {
3493
+ number: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.result-kind.options.number',
3494
+ string: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.result-kind.options.string',
3495
+ boolean: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.result-kind.options.boolean',
3496
+ };
3497
+ /**
3498
+ * Sync labels for list badges — mirrors i18n `result-kind.options` (en-US Title Case).
3499
+ * Select/editor fields still use {@link OUTCOME_RESULT_KIND_LABEL_KEYS} with the translate pipe.
3500
+ */
3501
+ const OUTCOME_RESULT_KIND_LABELS = {
3502
+ number: createMultiLanguageString('Number', 'عدد'),
3503
+ string: createMultiLanguageString('Text', 'متن'),
3504
+ boolean: createMultiLanguageString('Yes / No', 'بله / خیر'),
3505
+ };
3506
+ const OUTCOME_RESULT_KIND_SELECT_OPTIONS = Object.keys(OUTCOME_RESULT_KIND_LABEL_KEYS).map((id) => ({
3507
+ id,
3508
+ title: OUTCOME_RESULT_KIND_LABEL_KEYS[id],
3509
+ }));
3510
+ function resolveOutcomeResultKindLabel(kind, locale) {
3511
+ return resolveMultiLanguageString(OUTCOME_RESULT_KIND_LABELS[kind], locale);
3512
+ }
3513
+ function toOutcomeResultKindSelectValue(kind) {
3514
+ return { id: kind, title: OUTCOME_RESULT_KIND_LABEL_KEYS[kind] };
3515
+ }
3516
+ //#endregion
3517
+
3518
+ //#region ---- Imports ----
3519
+ //#endregion
3520
+ //#region ---- Groups ----
3521
+ const GROUP_BASIC = {
3522
+ name: 'basic-info',
3523
+ title: '@general:terms.interface.basic-info',
3524
+ order: 0,
3525
+ };
3526
+ const GROUP_RESULT = {
3527
+ name: 'result',
3528
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.groups.result',
3529
+ order: 1,
3530
+ };
3531
+ const GROUP_DISPLAY_RULES = {
3532
+ name: 'display-rules',
3533
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.groups.display-rules',
3534
+ order: 2,
3535
+ };
3536
+ const RESULT_KIND_OPTIONS = OUTCOME_RESULT_KIND_SELECT_OPTIONS;
3537
+ const DISPLAY_RULE_GRID_CELL = {
3538
+ alignSelf: 'center',
3539
+ alignItems: 'center',
3540
+ justifyContent: 'center',
3541
+ };
3542
+ const COMPARE_VALUE_PLACEHOLDER = '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-compare-value.placeholder';
3543
+ //#endregion
3544
+ //#region ---- Display rule row layout (result-kind aware) ----
3545
+ function buildDisplayRuleOperatorWidget(resultKind) {
3546
+ const dataSource = resultKind === 'boolean'
3547
+ ? OUTCOME_DISPLAY_OPERATOR_BOOLEAN_SELECT_OPTIONS
3548
+ : resultKind === 'string'
3549
+ ? OUTCOME_DISPLAY_OPERATOR_STRING_SELECT_OPTIONS
3550
+ : OUTCOME_DISPLAY_OPERATOR_SELECT_OPTIONS;
3551
+ return {
3552
+ type: AXPWidgetsCatalog.select,
3553
+ mode: 'edit',
3554
+ path: 'operator',
3555
+ name: 'operator',
3556
+ options: {
3557
+ valueField: 'id',
3558
+ textField: 'title',
3559
+ dataSource: [...dataSource],
3560
+ },
3561
+ };
3562
+ }
3563
+ function buildDisplayRuleCompareValueWidget(resultKind) {
3564
+ if (resultKind === 'boolean') {
3565
+ return {
3566
+ type: AXPWidgetsCatalog.toggle,
3567
+ mode: 'edit',
3568
+ path: 'compareValue',
3569
+ name: 'compareValue',
3570
+ options: {},
3571
+ };
3572
+ }
3573
+ if (resultKind === 'string') {
3574
+ return {
3575
+ type: AXPWidgetsCatalog.text,
3576
+ mode: 'edit',
3577
+ path: 'compareValue',
3578
+ name: 'compareValue',
3579
+ options: {
3580
+ placeholder: COMPARE_VALUE_PLACEHOLDER,
3581
+ },
3582
+ };
3583
+ }
3584
+ return {
3585
+ type: AXPWidgetsCatalog.number,
3586
+ mode: 'edit',
3587
+ path: 'compareValue',
3588
+ name: 'compareValue',
3589
+ options: {
3590
+ placeholder: COMPARE_VALUE_PLACEHOLDER,
3591
+ },
3592
+ };
3593
+ }
3594
+ function buildDisplayRuleRepeaterChildren(resultKind) {
3595
+ return [
3596
+ {
3597
+ type: AXPWidgetsCatalog.grid,
3598
+ mode: 'edit',
3599
+ options: {
3600
+ width: '100%',
3601
+ grid: {
3602
+ default: {
3603
+ columns: 12,
3604
+ gap: '8px',
3605
+ alignItems: 'center',
3606
+ },
3607
+ },
3608
+ },
3609
+ children: [
3610
+ {
3611
+ type: AXPWidgetsCatalog.gridItem,
3612
+ mode: 'edit',
3613
+ options: { colSpan: 3, ...DISPLAY_RULE_GRID_CELL },
3614
+ children: [buildDisplayRuleOperatorWidget(resultKind)],
3615
+ },
3616
+ {
3617
+ type: AXPWidgetsCatalog.gridItem,
3618
+ mode: 'edit',
3619
+ options: { colSpan: 2, ...DISPLAY_RULE_GRID_CELL },
3620
+ children: [buildDisplayRuleCompareValueWidget(resultKind)],
3621
+ },
3622
+ {
3623
+ type: AXPWidgetsCatalog.gridItem,
3624
+ mode: 'edit',
3625
+ options: { colSpan: 3, ...DISPLAY_RULE_GRID_CELL },
3626
+ children: [
3627
+ {
3628
+ type: AXPWidgetsCatalog.themeColorChooser,
3629
+ mode: 'edit',
3630
+ path: 'color',
3631
+ name: 'color',
3632
+ options: {
3633
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-color.placeholder',
3634
+ },
3635
+ },
3636
+ ],
3637
+ },
3638
+ {
3639
+ type: AXPWidgetsCatalog.gridItem,
3640
+ mode: 'edit',
3641
+ options: { colSpan: 2, ...DISPLAY_RULE_GRID_CELL },
3642
+ children: [
3643
+ {
3644
+ type: AXPWidgetsCatalog.select,
3645
+ mode: 'edit',
3646
+ path: 'emphasis',
3647
+ name: 'emphasis',
3648
+ options: {
3649
+ valueField: 'id',
3650
+ textField: 'title',
3651
+ dataSource: [...OUTCOME_DISPLAY_EMPHASIS_SELECT_OPTIONS],
3652
+ },
3653
+ },
3654
+ ],
3655
+ },
3656
+ {
3657
+ type: AXPWidgetsCatalog.gridItem,
3658
+ mode: 'edit',
3659
+ options: { colSpan: 2, ...DISPLAY_RULE_GRID_CELL },
3660
+ children: [
3661
+ {
3662
+ type: AXPWidgetsCatalog.iconChooser,
3663
+ mode: 'edit',
3664
+ path: 'icon',
3665
+ name: 'icon',
3666
+ options: {},
3667
+ },
3668
+ ],
3669
+ },
3670
+ ],
3671
+ },
3672
+ ];
3673
+ }
3674
+ function buildDisplayRulesProps(resultKind) {
3675
+ return [
3676
+ {
3677
+ name: 'displayRules',
3678
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-rules.title',
3679
+ description: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.display-rules.description',
3680
+ group: GROUP_DISPLAY_RULES,
3681
+ order: 10,
3682
+ showLabel: false,
3683
+ schema: {
3684
+ dataType: 'array',
3685
+ defaultValue: [],
3686
+ nullable: true,
3687
+ interface: {
3688
+ name: 'displayRules',
3689
+ path: 'displayRules',
3690
+ type: AXPWidgetsCatalog.repeaterLayout,
3691
+ options: {
3692
+ hasControls: true,
3693
+ allowReorder: true,
3694
+ width: '100%',
3695
+ },
3696
+ children: buildDisplayRuleRepeaterChildren(resultKind),
3697
+ },
3698
+ },
3699
+ visible: true,
3700
+ },
3701
+ ];
3702
+ }
3703
+ //#endregion
3704
+ //#region ---- Properties ----
3705
+ const BASIC_PROPS = [
3706
+ {
3707
+ name: 'name',
3708
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.name.title',
3709
+ group: GROUP_BASIC,
3710
+ order: 10,
3711
+ schema: {
3712
+ dataType: 'string',
3713
+ defaultValue: '',
3714
+ interface: {
3715
+ name: 'name',
3716
+ path: 'name',
3717
+ type: AXPWidgetsCatalog.text,
3718
+ options: {
3719
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.name.placeholder',
3720
+ },
3721
+ },
3722
+ },
3723
+ validations: [{ rule: 'required' }, { rule: 'variable-name' }],
3724
+ visible: true,
3725
+ },
3726
+ {
3727
+ name: 'title',
3728
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.title.title',
3729
+ group: GROUP_BASIC,
3730
+ order: 20,
3731
+ schema: {
3732
+ dataType: 'string',
3733
+ defaultValue: '',
3734
+ interface: {
3735
+ name: 'title',
3736
+ path: 'title',
3737
+ type: AXPWidgetsCatalog.text,
3738
+ options: {
3739
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.title.placeholder',
3740
+ },
3741
+ },
3742
+ },
3743
+ validations: [{ rule: 'required' }],
3744
+ visible: true,
3745
+ },
3746
+ {
3747
+ name: 'description',
3748
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.description.title',
3749
+ group: GROUP_BASIC,
3750
+ order: 30,
3751
+ schema: {
3752
+ dataType: 'string',
3753
+ defaultValue: '',
3754
+ interface: {
3755
+ name: 'description',
3756
+ path: 'description',
3757
+ type: AXPWidgetsCatalog.largeText,
3758
+ options: {
3759
+ rows: 3,
3760
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.description.placeholder',
3761
+ },
3762
+ },
3763
+ },
3764
+ visible: true,
3765
+ },
3766
+ ];
3767
+ const RESULT_PROPS = [
3768
+ {
3769
+ name: 'resultKind',
3770
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.result-kind.title',
3771
+ group: GROUP_RESULT,
3772
+ order: 10,
3773
+ schema: {
3774
+ dataType: 'string',
3775
+ defaultValue: { id: 'number', title: RESULT_KIND_OPTIONS[0].title },
3776
+ interface: {
3777
+ name: 'resultKind',
3778
+ path: 'resultKind',
3779
+ type: AXPWidgetsCatalog.select,
3780
+ options: {
3781
+ valueField: 'id',
3782
+ textField: 'title',
3783
+ dataSource: [...RESULT_KIND_OPTIONS],
3784
+ },
3785
+ },
3786
+ },
3787
+ validations: [{ rule: 'required' }],
3788
+ visible: true,
3789
+ },
3790
+ {
3791
+ name: 'expression',
3792
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.expression.title',
3793
+ group: GROUP_RESULT,
3794
+ order: 20,
3795
+ schema: {
3796
+ dataType: 'string',
3797
+ defaultValue: '',
3798
+ interface: {
3799
+ name: 'expression',
3800
+ path: 'expression',
3801
+ type: 'code-editor',
3802
+ options: {
3803
+ language: 'javascript',
3804
+ lineNumbers: true,
3805
+ height: 'min-h-72',
3806
+ placeholder: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.fields.expression.placeholder',
3807
+ },
3808
+ },
3809
+ },
3810
+ validations: [{ rule: 'required' }],
3811
+ visible: true,
3812
+ },
3813
+ ];
3814
+ //#endregion
3815
+ //#region ---- Tabs ----
3816
+ /** Builds outcome edit tabs; display-rule value widgets match the selected result kind. */
3817
+ function buildQuestionnaireOutcomeEditTabs(resultKind = 'number') {
3818
+ return [
3819
+ {
3820
+ name: 'general',
3821
+ title: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.tabs.general.title',
3822
+ groups: [
3823
+ {
3824
+ name: GROUP_BASIC.name,
3825
+ title: GROUP_BASIC.title,
3826
+ isCollapsed: false,
3827
+ props: BASIC_PROPS,
3828
+ },
3829
+ {
3830
+ name: GROUP_RESULT.name,
3831
+ title: GROUP_RESULT.title,
3832
+ isCollapsed: false,
3833
+ props: RESULT_PROPS,
3834
+ },
3835
+ {
3836
+ name: GROUP_DISPLAY_RULES.name,
3837
+ title: GROUP_DISPLAY_RULES.title,
3838
+ isCollapsed: false,
3839
+ props: buildDisplayRulesProps(resultKind),
3840
+ },
3841
+ ],
3842
+ },
3843
+ ];
3844
+ }
3845
+ //#endregion
3846
+
3847
+ //#region ---- Imports ----
3848
+ //#endregion
3849
+ //#region ---- Mapping ----
3850
+ function outcomeItemToBuilderItem(item, index) {
3851
+ return {
3852
+ id: item.id,
3853
+ name: item.name,
3854
+ order: item.order ?? index,
3855
+ title: item.title,
3856
+ description: item.description,
3857
+ resultKind: item.resultKind,
3858
+ expression: item.expression,
3859
+ displayRules: item.displayRules,
3860
+ isNewlyAdded: item.isNewlyAdded,
3861
+ };
3862
+ }
3863
+ function builderItemToOutcomeItem(item, index) {
3864
+ return {
3865
+ id: String(item.id ?? item['name'] ?? AXPDataGenerator.uuid()),
3866
+ name: String(item['name'] ?? ''),
3867
+ title: item['title'],
3868
+ description: item['description'],
3869
+ resultKind: (item['resultKind'] ?? 'number'),
3870
+ expression: String(item['expression'] ?? ''),
3871
+ displayRules: item['displayRules'],
3872
+ order: item.order ?? index,
3873
+ isNewlyAdded: item['isNewlyAdded'] === true,
3874
+ };
3875
+ }
3876
+ function sectionToBuilderSection(section, index) {
3877
+ const name = section.name.trim() || `section-${index + 1}`;
3878
+ return {
3879
+ id: name,
3880
+ name,
3881
+ order: section.order ?? index,
3882
+ title: section.title,
3883
+ description: section.description,
3884
+ isNewlyAdded: section.isNewlyAdded,
3885
+ items: (section.items ?? []).map((item, itemIndex) => outcomeItemToBuilderItem(item, itemIndex)),
3886
+ };
3887
+ }
3888
+ function builderSectionToOutcomeSection(section, index) {
3889
+ const name = String(section['name'] ?? section.id ?? '').trim() || `section-${index + 1}`;
3890
+ return {
3891
+ name,
3892
+ title: section['title'],
3893
+ description: section['description'],
3894
+ order: section.order ?? index,
3895
+ isNewlyAdded: section['isNewlyAdded'] === true,
3896
+ items: section.items.map((item, itemIndex) => builderItemToOutcomeItem(item, itemIndex)),
3897
+ };
3898
+ }
3899
+ function outcomesValueToBuilderValue(value) {
3900
+ const normalized = normalizeQuestionnaireOutcomesValue(value);
3901
+ return {
3902
+ sections: normalized.sections.map((section, index) => sectionToBuilderSection(section, index)),
3903
+ };
3904
+ }
3905
+ function builderValueToOutcomesValue(value) {
3906
+ return {
3907
+ sections: value.sections.map((section, index) => builderSectionToOutcomeSection(section, index)),
3908
+ };
3909
+ }
3910
+ //#endregion
3911
+
3912
+ //#region ---- Imports ----
3913
+ //#endregion
3914
+ //#region ---- Helpers ----
3915
+ function normalizeOutcomeName(name) {
3916
+ return (name ?? '').trim();
3917
+ }
3918
+ function createDefaultOutcomeSection(order = 0) {
3919
+ return {
3920
+ name: `section-${Date.now()}`,
3921
+ title: 'New Section',
3922
+ order,
3923
+ items: [],
3924
+ isNewlyAdded: true,
3925
+ };
3926
+ }
3927
+ //#endregion
3928
+
3929
+ //#region ---- Imports ----
3930
+ //#endregion
3931
+ //#region ---- Component ----
3932
+ class AXMQuestionnaireOutcomesBuilderComponent {
3933
+ //#endregion
3934
+ //#region ---- Lifecycle ----
3935
+ constructor() {
3936
+ //#region ---- Inputs / Outputs ----
3937
+ this.value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
3938
+ this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
3939
+ this.showHeader = input(true, ...(ngDevMode ? [{ debugName: "showHeader" }] : /* istanbul ignore next */ []));
3940
+ this.valueChange = output();
3941
+ //#endregion
3942
+ //#region ---- Services ----
3943
+ this.propertyViewerService = inject(AXPPropertyViewerService);
3944
+ this.translationService = inject(AXTranslationService);
3945
+ this.toastService = inject(AXToastService);
3946
+ //#endregion
3947
+ //#region ---- View references ----
3948
+ this.standardRef = viewChild(AXPStandardSectionItemsBuilderComponent, ...(ngDevMode ? [{ debugName: "standardRef" }] : /* istanbul ignore next */ []));
3949
+ //#endregion
3950
+ //#region ---- State ----
3951
+ this.builderValue = signal({ sections: [] }, ...(ngDevMode ? [{ debugName: "builderValue" }] : /* istanbul ignore next */ []));
3952
+ //#endregion
3953
+ //#region ---- Computed ----
3954
+ this.standardConfig = computed(() => ({
3955
+ showSectionTechnicalName: true,
3956
+ minSectionCount: 1,
3957
+ texts: {
3958
+ addSection: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-section.title',
3959
+ addItem: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-outcome.title',
3960
+ emptySectionsTitle: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.empty.no-sections.title',
3961
+ emptySectionsDescription: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.empty.no-sections.description',
3962
+ emptyItemsTitle: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.empty.empty-section.title',
3963
+ emptyItemsDescription: '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.empty.empty-section.description',
3964
+ },
3965
+ mapItemToView: (item, _section) => this.mapOutcomeItemToView(item),
3966
+ promptAddItems: (sectionId, value) => this.promptAddOutcome(sectionId, value),
3967
+ promptEditItem: (item, sectionId, value) => this.promptEditOutcome(item, sectionId, value),
3968
+ }), ...(ngDevMode ? [{ debugName: "standardConfig" }] : /* istanbul ignore next */ []));
3969
+ effect(() => {
3970
+ const next = normalizeQuestionnaireOutcomesValue(this.value());
3971
+ const mapped = outcomesValueToBuilderValue(next);
3972
+ untracked(() => this.builderValue.set(this.ensureMinOneSection(mapped)));
3973
+ });
3974
+ }
3975
+ //#endregion
3976
+ //#region ---- Public API ----
3977
+ /** Page header command: delegates to standard builder add-section dialog. */
3978
+ async addSection() {
3979
+ await this.standardRef()?.addSection();
3980
+ }
3981
+ //#endregion
3982
+ //#region ---- Builder handlers ----
3983
+ onBuilderValueChange(next) {
3984
+ const normalized = this.ensureMinOneSection(next);
3985
+ this.builderValue.set(normalized);
3986
+ this.valueChange.emit(builderValueToOutcomesValue(normalized));
3987
+ }
3988
+ //#endregion
3989
+ //#region ---- Item mapping ----
3990
+ mapOutcomeItemToView(item) {
3991
+ const outcome = item;
3992
+ const locale = this.translationService.getActiveLang() ?? 'en-US';
3993
+ const title = resolveMultiLanguageString(outcome.title, locale).trim();
3994
+ const description = resolveMultiLanguageString(outcome.description, locale).trim();
3995
+ const badges = [];
3996
+ const kind = this.coerceResultKind(outcome.resultKind);
3997
+ badges.push({
3998
+ text: resolveOutcomeResultKindLabel(kind, locale),
3999
+ variant: 'neutral',
4000
+ });
4001
+ return {
4002
+ icon: 'fa-light fa-calculator',
4003
+ title: title || outcome.name || '—',
4004
+ name: outcome.name,
4005
+ description: description || undefined,
4006
+ badges,
4007
+ };
4008
+ }
4009
+ //#endregion
4010
+ //#region ---- Prompts ----
4011
+ async promptAddOutcome(sectionId, value) {
4012
+ const section = value.sections.find((s) => s.id === sectionId);
4013
+ if (!section) {
4014
+ return null;
4015
+ }
4016
+ const takenNames = this.collectOutcomeNames(value);
4017
+ const defaultName = this.allocateOutcomeName(takenNames);
4018
+ const item = await this.openOutcomeDialog({
4019
+ mode: 'add',
4020
+ context: {
4021
+ name: defaultName,
4022
+ title: '',
4023
+ description: '',
4024
+ resultKind: toOutcomeResultKindSelectValue('number'),
4025
+ expression: '',
4026
+ displayRules: [],
4027
+ },
4028
+ sectionId,
4029
+ value,
4030
+ excludeItemId: undefined,
4031
+ });
4032
+ return item ? [item] : null;
4033
+ }
4034
+ async promptEditOutcome(item, sectionId, value) {
4035
+ const outcome = item;
4036
+ return this.openOutcomeDialog({
4037
+ mode: 'edit',
4038
+ context: {
4039
+ name: outcome.name,
4040
+ title: outcome.title ?? '',
4041
+ description: outcome.description ?? '',
4042
+ resultKind: toOutcomeResultKindSelectValue(this.coerceResultKind(outcome.resultKind)),
4043
+ expression: outcome.expression ?? '',
4044
+ displayRules: this.toDisplayRulesFormContext(outcome.displayRules),
4045
+ },
4046
+ sectionId,
4047
+ value,
4048
+ excludeItemId: item.id,
4049
+ existingItem: item,
4050
+ });
4051
+ }
4052
+ async openOutcomeDialog(options) {
4053
+ const validationFailed = async (titleKey, descKey, fallTitle, fallDesc) => {
4054
+ const title = (await this.translationService.translateAsync(titleKey)) || fallTitle;
4055
+ const content = (await this.translationService.translateAsync(descKey)) || fallDesc;
4056
+ this.toastService.show({ color: 'warning', title, content });
4057
+ throw new Error('QuestionnaireOutcomeEditValidation');
4058
+ };
4059
+ try {
4060
+ const titleKey = options.mode === 'add'
4061
+ ? '@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-outcome.title'
4062
+ : '@general:actions.edit.title';
4063
+ const initialResultKind = this.coerceResultKind(options.context.resultKind);
4064
+ const result = await this.propertyViewerService
4065
+ .create()
4066
+ .dialog((d) => {
4067
+ d.setTitle(titleKey)
4068
+ .setSize('lg')
4069
+ .setCloseButton(true)
4070
+ .setMode('advanced')
4071
+ .setTabs(buildQuestionnaireOutcomeEditTabs(initialResultKind))
4072
+ .setContext(options.context)
4073
+ .onAction(async (ref) => {
4074
+ const context = ref.context();
4075
+ const rawName = normalizeOutcomeName(context.name);
4076
+ if (!rawName) {
4077
+ 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.');
4078
+ }
4079
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(rawName)) {
4080
+ 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.');
4081
+ }
4082
+ const taken = this.collectOutcomeNames(options.value, options.excludeItemId);
4083
+ if (taken.has(rawName)) {
4084
+ 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.');
4085
+ }
4086
+ const title = context.title;
4087
+ if (isEffectivelyEmptyLocalizedValue(title)) {
4088
+ 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.');
4089
+ }
4090
+ const expression = String(context.expression ?? '').trim();
4091
+ if (!expression) {
4092
+ 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.');
4093
+ }
4094
+ });
4095
+ })
4096
+ .show();
4097
+ if (!result) {
4098
+ return null;
4099
+ }
4100
+ const form = result.values;
4101
+ const name = normalizeOutcomeName(form.name);
4102
+ const title = this.normalizeLocalizedField(form.title);
4103
+ const description = this.normalizeLocalizedField(form.description);
4104
+ const resultKind = this.coerceResultKind(form.resultKind);
4105
+ const expression = String(form.expression ?? '').trim();
4106
+ const displayRules = this.normalizeDisplayRulesFromForm(form.displayRules, resultKind);
4107
+ const id = options.mode === 'edit' && options.existingItem
4108
+ ? String(options.existingItem.id)
4109
+ : AXPDataGenerator.uuid();
4110
+ return {
4111
+ ...(options.existingItem ?? {}),
4112
+ id,
4113
+ name,
4114
+ title,
4115
+ description,
4116
+ resultKind,
4117
+ expression,
4118
+ displayRules,
4119
+ order: options.existingItem?.order ?? 0,
4120
+ isNewlyAdded: options.mode === 'add',
4121
+ };
4122
+ }
4123
+ catch (error) {
4124
+ if (error instanceof Error && error.message === 'QuestionnaireOutcomeEditValidation') {
4125
+ return null;
4126
+ }
4127
+ console.error('Error editing outcome:', error);
4128
+ return null;
4129
+ }
4130
+ }
4131
+ //#endregion
4132
+ //#region ---- Utility ----
4133
+ ensureMinOneSection(value) {
4134
+ if (value.sections.length > 0) {
4135
+ return value;
4136
+ }
4137
+ const section = createDefaultOutcomeSection(0);
4138
+ return outcomesValueToBuilderValue({ sections: [section] });
4139
+ }
4140
+ collectOutcomeNames(value, excludeItemId) {
4141
+ const names = new Set();
4142
+ for (const section of value.sections) {
4143
+ for (const item of section.items) {
4144
+ if (excludeItemId != null && item.id === excludeItemId) {
4145
+ continue;
4146
+ }
4147
+ const name = normalizeOutcomeName(String(item['name'] ?? ''));
4148
+ if (name) {
4149
+ names.add(name);
4150
+ }
4151
+ }
4152
+ }
4153
+ return names;
4154
+ }
4155
+ allocateOutcomeName(taken) {
4156
+ let index = taken.size + 1;
4157
+ let name = `outcome_${index}`;
4158
+ while (taken.has(name)) {
4159
+ index += 1;
4160
+ name = `outcome_${index}`;
4161
+ }
4162
+ return name;
4163
+ }
4164
+ coerceResultKind(value) {
4165
+ if (value === 'number' || value === 'string' || value === 'boolean') {
4166
+ return value;
4167
+ }
4168
+ if (value != null && typeof value === 'object' && 'id' in value) {
4169
+ const id = value.id;
4170
+ if (id === 'number' || id === 'string' || id === 'boolean') {
4171
+ return id;
4172
+ }
4173
+ }
4174
+ return 'number';
4175
+ }
4176
+ coerceDisplayOperator(value, resultKind) {
4177
+ let operator;
4178
+ if (value === 'eq' || value === 'neq' || value === 'gt' || value === 'gte' || value === 'lt' || value === 'lte') {
4179
+ operator = value;
4180
+ }
4181
+ else if (value != null && typeof value === 'object' && 'id' in value) {
4182
+ const id = value.id;
4183
+ if (id === 'eq' || id === 'neq' || id === 'gt' || id === 'gte' || id === 'lt' || id === 'lte') {
4184
+ operator = id;
4185
+ }
4186
+ }
4187
+ if (resultKind === 'boolean') {
4188
+ return operator === 'eq' || operator === 'neq' ? operator : 'eq';
4189
+ }
4190
+ if (resultKind === 'string') {
4191
+ return operator === 'eq' || operator === 'neq' ? operator : 'eq';
4192
+ }
4193
+ return operator ?? 'gte';
4194
+ }
4195
+ coerceDisplayCompareValue(value, resultKind) {
4196
+ if (resultKind === 'boolean') {
4197
+ if (typeof value === 'boolean') {
4198
+ return value;
4199
+ }
4200
+ if (value === 'true') {
4201
+ return true;
4202
+ }
4203
+ if (value === 'false') {
4204
+ return false;
4205
+ }
4206
+ return undefined;
4207
+ }
4208
+ if (resultKind === 'number') {
4209
+ if (typeof value === 'number' && Number.isFinite(value)) {
4210
+ return value;
4211
+ }
4212
+ if (typeof value === 'string') {
4213
+ const trimmed = value.trim();
4214
+ if (!trimmed) {
4215
+ return undefined;
4216
+ }
4217
+ const asNumber = Number(trimmed);
4218
+ return Number.isFinite(asNumber) ? asNumber : undefined;
4219
+ }
4220
+ return undefined;
4221
+ }
4222
+ if (typeof value === 'string') {
4223
+ const trimmed = value.trim();
4224
+ return trimmed.length > 0 ? trimmed : undefined;
4225
+ }
4226
+ if (typeof value === 'number' && Number.isFinite(value)) {
4227
+ return String(value);
4228
+ }
4229
+ if (typeof value === 'boolean') {
4230
+ return value ? 'true' : 'false';
4231
+ }
4232
+ return undefined;
4233
+ }
4234
+ normalizeDisplayColorValue(value) {
4235
+ if (typeof value !== 'string') {
4236
+ return undefined;
4237
+ }
4238
+ const trimmed = value.trim();
4239
+ const parts = trimmed.split(/\s+/);
4240
+ if (parts.length !== 3) {
4241
+ return undefined;
4242
+ }
4243
+ return trimmed;
4244
+ }
4245
+ coerceDisplayEmphasis(value) {
4246
+ if (value === 'normal' || value === 'medium' || value === 'bold') {
4247
+ return value;
4248
+ }
4249
+ if (value != null && typeof value === 'object' && 'id' in value) {
4250
+ const id = value.id;
4251
+ if (id === 'normal' || id === 'medium' || id === 'bold') {
4252
+ return id;
4253
+ }
4254
+ }
4255
+ return 'normal';
4256
+ }
4257
+ normalizeDisplayIconValue(value) {
4258
+ if (value == null || value === '') {
4259
+ return undefined;
4260
+ }
4261
+ if (typeof value === 'string') {
4262
+ const trimmed = value.trim();
4263
+ return trimmed.length > 0 ? trimmed : undefined;
4264
+ }
4265
+ if (typeof value === 'object') {
4266
+ const styleClass = String(get(value, 'styleClass') ?? '').trim();
4267
+ const iconClass = String(get(value, 'iconClass') ?? '').trim();
4268
+ const combined = [styleClass, iconClass].filter(Boolean).join(' ');
4269
+ return combined.length > 0 ? combined : undefined;
4270
+ }
4271
+ return undefined;
4272
+ }
4273
+ toDisplayRulesFormContext(rules) {
4274
+ return normalizeQuestionnaireOutcomeDisplayRules(rules).map((rule) => ({
4275
+ id: rule.id,
4276
+ operator: toOutcomeDisplayOperatorSelectValue(rule.operator),
4277
+ compareValue: rule.compareValue,
4278
+ color: rule.color,
4279
+ emphasis: toOutcomeDisplayEmphasisSelectValue(rule.emphasis ?? 'normal'),
4280
+ icon: rule.icon,
4281
+ }));
4282
+ }
4283
+ normalizeDisplayRulesFromForm(raw, resultKind) {
4284
+ if (!Array.isArray(raw)) {
4285
+ return [];
4286
+ }
4287
+ const rows = [];
4288
+ for (const [index, row] of raw.entries()) {
4289
+ const compareValue = this.coerceDisplayCompareValue(get(row, 'compareValue'), resultKind);
4290
+ const color = this.normalizeDisplayColorValue(get(row, 'color'));
4291
+ if (compareValue === undefined || !color) {
4292
+ continue;
4293
+ }
4294
+ rows.push({
4295
+ id: String(get(row, 'id') ?? AXPDataGenerator.uuid()),
4296
+ order: index,
4297
+ operator: this.coerceDisplayOperator(get(row, 'operator'), resultKind),
4298
+ compareValue,
4299
+ color,
4300
+ emphasis: this.coerceDisplayEmphasis(get(row, 'emphasis')),
4301
+ icon: this.normalizeDisplayIconValue(get(row, 'icon')),
4302
+ });
4303
+ }
4304
+ return normalizeQuestionnaireOutcomeDisplayRules(rows);
4305
+ }
4306
+ normalizeLocalizedField(value) {
4307
+ if (value == null || value === '') {
4308
+ return undefined;
4309
+ }
4310
+ if (typeof value === 'string') {
4311
+ const trimmed = value.trim();
4312
+ return trimmed ? createMultiLanguageString(trimmed, trimmed) : undefined;
4313
+ }
4314
+ if (typeof value === 'object') {
4315
+ return value;
4316
+ }
4317
+ return undefined;
4318
+ }
4319
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4320
+ 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 }); }
4321
+ }
4322
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesBuilderComponent, decorators: [{
4323
+ type: Component,
4324
+ args: [{ selector: 'axm-questionnaire-outcomes-builder', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
4325
+ CommonModule,
4326
+ AXButtonModule,
4327
+ AXDecoratorModule,
4328
+ AXTranslationModule,
4329
+ AXPStandardSectionItemsBuilderComponent,
4330
+ ], 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"] }]
4331
+ }], 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 }] }] } });
4332
+
4333
+ /** Component key for the questionnaire Outcomes page tab. Register via AXP_PAGE_COMPONENT_PROVIDER. */
4334
+ const QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY = 'questionnaire-outcomes-page';
4335
+
4336
+ const QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND = 'Questionnaire:Outcomes:Save';
4337
+ /**
4338
+ * Command to save the questionnaire outcomes field.
4339
+ * Patches entity with { outcomes }.
4340
+ */
4341
+ class AXMSaveQuestionnaireOutcomesCommand {
4342
+ constructor() {
4343
+ this.entityService = inject(AXPEntityService);
4344
+ this.translationService = inject(AXTranslationService);
4345
+ }
4346
+ async execute(input) {
4347
+ const { id, outcomes } = input;
4348
+ if (!id) {
4349
+ return {
4350
+ success: false,
4351
+ message: {
4352
+ text: await this.translationService.translateAsync('@general:messages.entity.invalid-data'),
4353
+ },
4354
+ };
4355
+ }
4356
+ try {
4357
+ const entityRef = `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`;
4358
+ const dataAccessor = this.entityService.withEntity(entityRef).data();
4359
+ await dataAccessor.update(id, { outcomes });
4360
+ return {
4361
+ success: true,
4362
+ data: { id, outcomes },
4363
+ message: {
4364
+ text: await this.translationService.translateAsync('@general:messages.generic.success.description'),
4365
+ },
4366
+ };
4367
+ }
4368
+ catch (error) {
4369
+ const message = error instanceof Error
4370
+ ? error.message
4371
+ : await this.translationService.translateAsync('@general:messages.generic.error.description');
4372
+ return {
4373
+ success: false,
4374
+ message: { text: message },
4375
+ };
4376
+ }
4377
+ }
4378
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireOutcomesCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4379
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireOutcomesCommand, providedIn: 'root' }); }
4380
+ }
4381
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMSaveQuestionnaireOutcomesCommand, decorators: [{
4382
+ type: Injectable,
4383
+ args: [{
4384
+ providedIn: 'root',
4385
+ }]
4386
+ }] });
4387
+
4388
+ var saveQuestionnaireOutcomes_command = /*#__PURE__*/Object.freeze({
4389
+ __proto__: null,
4390
+ AXMSaveQuestionnaireOutcomesCommand: AXMSaveQuestionnaireOutcomesCommand,
4391
+ QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND: QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND
4392
+ });
4393
+
4394
+ const QUESTIONNAIRE_OUTCOMES_DISCARD_COMMAND = 'Questionnaire:Outcomes:Discard';
4395
+ const QUESTIONNAIRE_OUTCOMES_ADD_SECTION_COMMAND = 'Questionnaire:Outcomes:AddSection';
4396
+ //#region ---- Questionnaire Outcomes Page Component ----
4397
+ class AXMQuestionnaireOutcomesPageComponent extends AXPPageLayoutBaseComponent {
4398
+ //#endregion
4399
+ //#region ---- Lifecycle ----
4400
+ constructor() {
4401
+ super();
4402
+ //#region ---- Inputs ----
4403
+ this.rootContext = input(...(ngDevMode ? [undefined, { debugName: "rootContext" }] : /* istanbul ignore next */ []));
4404
+ this.pageConfig = input(...(ngDevMode ? [undefined, { debugName: "pageConfig" }] : /* istanbul ignore next */ []));
4405
+ //#endregion
4406
+ //#region ---- Services ----
4407
+ this.commandService = inject(AXPCommandService);
4408
+ this.eventService = inject(AXPBroadcastEventService);
4409
+ this.hostPageLayout = inject(AXPPageLayoutBase, { optional: true, skipSelf: true });
4410
+ //#endregion
4411
+ //#region ---- View References ----
4412
+ this.builderRef = viewChild('builderRef', ...(ngDevMode ? [{ debugName: "builderRef" }] : /* istanbul ignore next */ []));
4413
+ //#endregion
4414
+ //#region ---- State ----
4415
+ this.localContext = signal(null, ...(ngDevMode ? [{ debugName: "localContext" }] : /* istanbul ignore next */ []));
4416
+ this.ignoreNextValueChange = signal(false, ...(ngDevMode ? [{ debugName: "ignoreNextValueChange" }] : /* istanbul ignore next */ []));
4417
+ this.baselineOutcomes = signal(null, ...(ngDevMode ? [{ debugName: "baselineOutcomes" }] : /* istanbul ignore next */ []));
4418
+ this.outcomesBaselinePendingReconcile = signal(false, ...(ngDevMode ? [{ debugName: "outcomesBaselinePendingReconcile" }] : /* istanbul ignore next */ []));
4419
+ this.isDirty = signal(false, ...(ngDevMode ? [{ debugName: "isDirty" }] : /* istanbul ignore next */ []));
4420
+ //#endregion
4421
+ //#region ---- Computed ----
4422
+ this.context = computed(() => {
4423
+ const local = this.localContext();
4424
+ if (local != null)
4425
+ return local;
4426
+ return this.rootContext() ?? {};
4427
+ }, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
4428
+ this.outcomesValue = computed(() => {
4429
+ const ctx = this.context();
4430
+ return normalizeQuestionnaireOutcomesValue(ctx['outcomes']);
4431
+ }, ...(ngDevMode ? [{ debugName: "outcomesValue" }] : /* istanbul ignore next */ []));
4432
+ let previousRootId = undefined;
4433
+ effect(() => {
4434
+ const ctx = this.rootContext();
4435
+ const currentId = ctx != null && typeof ctx === 'object' ? ctx['id'] : undefined;
4436
+ if (currentId !== previousRootId) {
4437
+ previousRootId = currentId;
4438
+ this.localContext.set(null);
4439
+ const rootOutcomes = normalizeQuestionnaireOutcomesValue(ctx?.['outcomes']);
4440
+ this.baselineOutcomes.set(this.cloneOutcomes(rootOutcomes));
4441
+ this.isDirty.set(false);
4442
+ this.outcomesBaselinePendingReconcile.set(true);
4443
+ }
4444
+ });
4445
+ }
4446
+ //#endregion
4447
+ //#region ---- Page Actions ----
4448
+ async getPrimaryMenuItems() {
4449
+ const addSectionTitle = (await this.translateService.translateAsync('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.actions.add-section.title')) || 'Add Section';
4450
+ const discardTitle = (await this.translateService.translateAsync('@general:actions.discard.title')) || 'Discard';
4451
+ const saveTitle = (await this.translateService.translateAsync('@general:actions.save.title')) || 'Save';
4452
+ const addSectionAction = {
4453
+ title: addSectionTitle,
4454
+ icon: 'fa-light fa-layer-group',
4455
+ zone: 'header',
4456
+ priority: 'primary',
4457
+ color: 'primary',
4458
+ command: {
4459
+ name: QUESTIONNAIRE_OUTCOMES_ADD_SECTION_COMMAND,
4460
+ options: {},
4461
+ },
4462
+ };
4463
+ const discardAction = {
4464
+ title: discardTitle,
4465
+ icon: 'fa-light fa-rotate-left',
4466
+ zone: 'footer',
4467
+ priority: 'secondary',
4468
+ color: 'default',
4469
+ visible: this.isDirty(),
4470
+ command: {
4471
+ name: QUESTIONNAIRE_OUTCOMES_DISCARD_COMMAND,
4472
+ options: {},
4473
+ },
4474
+ };
4475
+ const saveAction = {
4476
+ title: saveTitle,
4477
+ icon: 'fa-light fa-floppy-disk',
4478
+ zone: 'footer',
4479
+ priority: 'primary',
4480
+ color: 'primary',
4481
+ visible: this.isDirty(),
4482
+ command: {
4483
+ name: QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND,
4484
+ options: {},
4485
+ },
4486
+ };
4487
+ return [addSectionAction, discardAction, saveAction];
4488
+ }
4489
+ //#endregion
4490
+ //#region ---- Command Handling ----
4491
+ execute(command) {
4492
+ if (command.name === QUESTIONNAIRE_OUTCOMES_DISCARD_COMMAND) {
4493
+ this.ignoreNextValueChange.set(true);
4494
+ this.localContext.set(null);
4495
+ this.isDirty.set(false);
4496
+ this.outcomesBaselinePendingReconcile.set(true);
4497
+ this.recompute();
4498
+ return;
4499
+ }
4500
+ if (command.name === QUESTIONNAIRE_OUTCOMES_ADD_SECTION_COMMAND) {
4501
+ void this.builderRef()?.addSection();
4502
+ return;
4503
+ }
4504
+ if (command.name !== QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND) {
4505
+ return;
4506
+ }
4507
+ return this.handleSaveExecute();
4508
+ }
4509
+ async handleSaveExecute() {
4510
+ const ctx = this.localContext() ?? this.rootContext() ?? {};
4511
+ const id = typeof ctx['id'] === 'string' ? ctx['id'] : undefined;
4512
+ const outcomes = normalizeQuestionnaireOutcomesValue(ctx['outcomes']);
4513
+ if (!id) {
4514
+ return {
4515
+ success: false,
4516
+ message: {
4517
+ text: await this.translateService.translateAsync('@general:messages.entity.invalid-data'),
4518
+ },
4519
+ };
4520
+ }
4521
+ if (findDuplicateOutcomeNames(outcomes).length > 0) {
4522
+ return {
4523
+ success: false,
4524
+ message: {
4525
+ text: await this.translateService.translateAsync('@assessment-management:questionnaires.components.questionnaire-outcomes-builder.validation.duplicate-name'),
4526
+ },
4527
+ };
4528
+ }
4529
+ const input = { id, outcomes };
4530
+ const result = await this.commandService.execute(QUESTIONNAIRE_OUTCOMES_SAVE_COMMAND, input);
4531
+ const execResult = (result ?? { success: false });
4532
+ if (execResult.success) {
4533
+ this.baselineOutcomes.set(this.cloneOutcomes(outcomes));
4534
+ this.localContext.set(null);
4535
+ this.isDirty.set(false);
4536
+ this.outcomesBaselinePendingReconcile.set(true);
4537
+ this.recompute();
4538
+ this.eventService.publish(AXPEntityEventsKeys.REFRESH_LAYOUT, {
4539
+ name: `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`,
4540
+ });
4541
+ }
4542
+ return execResult;
4543
+ }
4544
+ //#endregion
4545
+ //#region ---- UI Handlers ----
4546
+ onOutcomesChange(value) {
4547
+ if (this.ignoreNextValueChange()) {
4548
+ this.ignoreNextValueChange.set(false);
4549
+ return;
4550
+ }
4551
+ const current = this.outcomesValue();
4552
+ if (this.areOutcomesEqual(current, value)) {
4553
+ if (this.outcomesBaselinePendingReconcile()) {
4554
+ this.baselineOutcomes.set(this.cloneOutcomes(current));
4555
+ this.outcomesBaselinePendingReconcile.set(false);
4556
+ this.isDirty.set(false);
4557
+ }
4558
+ return;
4559
+ }
4560
+ const base = this.localContext() ?? this.rootContext() ?? {};
4561
+ if (this.outcomesBaselinePendingReconcile() && this.localContext() == null) {
4562
+ this.baselineOutcomes.set(this.cloneOutcomes(value));
4563
+ this.isDirty.set(false);
4564
+ this.outcomesBaselinePendingReconcile.set(false);
4565
+ this.localContext.set({ ...base, outcomes: value });
4566
+ this.recompute();
4567
+ return;
4568
+ }
4569
+ this.localContext.set({ ...base, outcomes: value });
4570
+ const baseline = this.baselineOutcomes();
4571
+ this.isDirty.set(!this.areOutcomesEqual(baseline, value));
4572
+ this.recompute();
4573
+ }
4574
+ //#endregion
4575
+ //#region ---- Utility ----
4576
+ areOutcomesEqual(a, b) {
4577
+ return isEqual(normalizeQuestionnaireOutcomesValue(a), normalizeQuestionnaireOutcomesValue(b));
4578
+ }
4579
+ cloneOutcomes(value) {
4580
+ if (isNil(value)) {
4581
+ return { sections: [] };
4582
+ }
4583
+ return cloneDeep(normalizeQuestionnaireOutcomesValue(value));
4584
+ }
4585
+ recompute() {
4586
+ super.recompute();
4587
+ const maybeLayout = this.hostPageLayout;
4588
+ maybeLayout.recompute?.();
4589
+ }
4590
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4591
+ 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: [
4592
+ {
4593
+ provide: AXPPageLayoutBase,
4594
+ useExisting: AXMQuestionnaireOutcomesPageComponent,
4595
+ },
4596
+ ], viewQueries: [{ propertyName: "builderRef", first: true, predicate: ["builderRef"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
4597
+ <axm-questionnaire-outcomes-builder
4598
+ #builderRef
4599
+ [value]="outcomesValue()"
4600
+ [showHeader]="false"
4601
+ (valueChange)="onOutcomesChange($event)">
4602
+ </axm-questionnaire-outcomes-builder>
4603
+ `, isInline: true, dependencies: [{ kind: "component", type: AXMQuestionnaireOutcomesBuilderComponent, selector: "axm-questionnaire-outcomes-builder", inputs: ["value", "readonly", "showHeader"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4604
+ }
4605
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponent, decorators: [{
4606
+ type: Component,
1536
4607
  args: [{
1537
- providedIn: 'root',
4608
+ selector: 'axm-questionnaire-outcomes-page',
4609
+ standalone: true,
4610
+ imports: [AXMQuestionnaireOutcomesBuilderComponent],
4611
+ template: `
4612
+ <axm-questionnaire-outcomes-builder
4613
+ #builderRef
4614
+ [value]="outcomesValue()"
4615
+ [showHeader]="false"
4616
+ (valueChange)="onOutcomesChange($event)">
4617
+ </axm-questionnaire-outcomes-builder>
4618
+ `,
4619
+ changeDetection: ChangeDetectionStrategy.OnPush,
4620
+ providers: [
4621
+ {
4622
+ provide: AXPPageLayoutBase,
4623
+ useExisting: AXMQuestionnaireOutcomesPageComponent,
4624
+ },
4625
+ ],
1538
4626
  }]
4627
+ }], 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 }] }] } });
4628
+
4629
+ var questionnaireOutcomesPage_component = /*#__PURE__*/Object.freeze({
4630
+ __proto__: null,
4631
+ AXMQuestionnaireOutcomesPageComponent: AXMQuestionnaireOutcomesPageComponent
4632
+ });
4633
+
4634
+ //#region ---- Questionnaire Outcomes Page Component Provider ----
4635
+ class AXMQuestionnaireOutcomesPageComponentProvider {
4636
+ async components() {
4637
+ return [
4638
+ {
4639
+ key: QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY,
4640
+ loader: () => Promise.resolve().then(function () { return questionnaireOutcomesPage_component; }).then((m) => m.AXMQuestionnaireOutcomesPageComponent),
4641
+ },
4642
+ ];
4643
+ }
4644
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponentProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4645
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponentProvider }); }
4646
+ }
4647
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomesPageComponentProvider, decorators: [{
4648
+ type: Injectable
1539
4649
  }] });
1540
4650
 
4651
+ var index = /*#__PURE__*/Object.freeze({
4652
+ __proto__: null,
4653
+ AXMQuestionnaireOutcomesPageComponent: AXMQuestionnaireOutcomesPageComponent,
4654
+ AXMQuestionnaireOutcomesPageComponentProvider: AXMQuestionnaireOutcomesPageComponentProvider,
4655
+ QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY: QUESTIONNAIRE_OUTCOMES_PAGE_COMPONENT_KEY
4656
+ });
4657
+
4658
+ //#region ---- Outcomes builder exports ----
4659
+ //#endregion
4660
+
4661
+ //#region ---- Questionnaire builder feature exports ----
4662
+ //#endregion
4663
+
1541
4664
  //#region ---- Imports ----
1542
4665
  //#endregion
1543
4666
  /** Guards concurrent `refreshVisibility` runs; incremented on each refresh. */
@@ -1558,7 +4681,7 @@ function persistedItemName(item, index) {
1558
4681
  const id = typeof legacy.id === 'string' ? legacy.id.trim() : '';
1559
4682
  return id || `item-${index}`;
1560
4683
  }
1561
- function normalizeValidationStrategy$1(value) {
4684
+ function normalizeValidationStrategy(value) {
1562
4685
  if (value === 'step' || value === 'end') {
1563
4686
  return value;
1564
4687
  }
@@ -1566,7 +4689,7 @@ function normalizeValidationStrategy$1(value) {
1566
4689
  typeof value === 'object' &&
1567
4690
  'id' in value &&
1568
4691
  typeof value.id === 'string') {
1569
- return normalizeValidationStrategy$1(value.id);
4692
+ return normalizeValidationStrategy(value.id);
1570
4693
  }
1571
4694
  return 'step';
1572
4695
  }
@@ -1963,8 +5086,8 @@ const AXMQuestionnaireViewerViewModel = signalStore(withState(() => ({
1963
5086
  const displaySettings = structure?.displaySettings;
1964
5087
  const displaySettingsValue = displaySettings?.validationStrategy;
1965
5088
  return configValue !== undefined
1966
- ? normalizeValidationStrategy$1(configValue)
1967
- : normalizeValidationStrategy$1(displaySettingsValue);
5089
+ ? normalizeValidationStrategy(configValue)
5090
+ : normalizeValidationStrategy(displaySettingsValue);
1968
5091
  }),
1969
5092
  //#endregion
1970
5093
  //#region ---- Navigation ----
@@ -3483,7 +6606,7 @@ class AXMQuestionnaireViewerQuestionComponent {
3483
6606
  </div>
3484
6607
  }
3485
6608
  </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 }); }
6609
+ `, 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
6610
  }
3488
6611
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerQuestionComponent, decorators: [{
3489
6612
  type: Component,
@@ -3685,7 +6808,7 @@ class AXMQuestionnaireViewerSectionHeaderComponent {
3685
6808
  }
3686
6809
  }
3687
6810
  </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 }); }
6811
+ `, 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
6812
  }
3690
6813
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerSectionHeaderComponent, decorators: [{
3691
6814
  type: Component,
@@ -3918,7 +7041,7 @@ class AXMQuestionnaireViewerSideMenuViewComponent {
3918
7041
  return null;
3919
7042
  }
3920
7043
  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 }); }
7044
+ 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
7045
  }
3923
7046
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerSideMenuViewComponent, decorators: [{
3924
7047
  type: Component,
@@ -4183,9 +7306,9 @@ class AXMQuestionnaireViewerComponent {
4183
7306
  this.answersChanged.emit(answers);
4184
7307
  }
4185
7308
  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:
7309
+ 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
7310
  // 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 }); }
7311
+ 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
7312
  }
4190
7313
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerComponent, decorators: [{
4191
7314
  type: Component,
@@ -4255,7 +7378,6 @@ class AXMQuestionnaireEvaluatorScopeProvider {
4255
7378
  async provide(context) {
4256
7379
  const questionnaireEntity = `${RootConfig.module.name}.${RootConfig.entities.questionnaire.name}`;
4257
7380
  const questionBankEntity = `${RootConfig.module.name}.${RootConfig.entities.questionBankItem.name}`;
4258
- const questionnaireCalculationEntity = `${RootConfig.module.name}.${RootConfig.entities.questionnaireCalculation.name}`;
4259
7381
  const filterDefinitionsImpl = async (questionnaireId) => {
4260
7382
  if (!questionnaireId) {
4261
7383
  return [];
@@ -4346,27 +7468,20 @@ class AXMQuestionnaireEvaluatorScopeProvider {
4346
7468
  return [];
4347
7469
  }
4348
7470
  };
4349
- /** Filter definitions from questionnaire calculations (for automation condition: rules.ruleName). */
7471
+ /** Filter definitions from embedded questionnaire outcomes (for automation condition: rules.ruleName). */
4350
7472
  const forRules = async (questionnaireId) => {
4351
7473
  if (!questionnaireId) {
4352
7474
  return [];
4353
7475
  }
4354
7476
  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 ?? []);
7477
+ const dataAccessor = this.entityService.withEntity(questionnaireEntity).data();
7478
+ const questionnaire = await dataAccessor.byKey(questionnaireId);
7479
+ const rules = flattenQuestionnaireOutcomeItems(questionnaire?.outcomes);
7480
+ const locale = this.translationService.getActiveLang() ?? 'en-US';
4366
7481
  return rules.map((rule, order) => ({
4367
7482
  id: rule.id,
4368
7483
  field: `rules.${rule.name}`,
4369
- title: rule.title ?? rule.name,
7484
+ title: resolveMultiLanguageString(rule.title, locale) || rule.name,
4370
7485
  operator: { type: 'equal' },
4371
7486
  widget: {
4372
7487
  type: 'string-filter',
@@ -4389,7 +7504,7 @@ class AXMQuestionnaireEvaluatorScopeProvider {
4389
7504
  context.addScope('questionnaire', {
4390
7505
  /** Filter definitions from questionnaire answers (e.g. visibility / automation conditions). */
4391
7506
  answers: filterDefinitionsImpl,
4392
- /** Filter definitions from questionnaire calculations for automation condition builder (rules.ruleName). */
7507
+ /** Filter definitions from questionnaire outcomes for automation condition builder (rules.ruleName). */
4393
7508
  rules: forRules,
4394
7509
  });
4395
7510
  // Automation condition builder: provide rules for refType = AssessmentManagement.Questionnaire
@@ -4422,31 +7537,27 @@ class AXMQuestionnaireViewerFeatureModule {
4422
7537
  provideCommandSetups([
4423
7538
  {
4424
7539
  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),
7540
+ command: () => import('./acorex-modules-assessment-management-preview-questionnaire.command-CRDBe_BS.mjs').then((c) => c.PreviewQuestionnaireCommand),
4426
7541
  },
4427
7542
  {
4428
7543
  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),
7544
+ command: () => import('./acorex-modules-assessment-management-preview-question.command-6IGOdVVZ.mjs').then((c) => c.PreviewQuestionCommand),
4430
7545
  },
4431
7546
  {
4432
7547
  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),
7548
+ command: () => import('./acorex-modules-assessment-management-preview-question.command-6IGOdVVZ.mjs').then((c) => c.PreviewQuestionCommand),
4434
7549
  },
4435
7550
  {
4436
7551
  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),
7552
+ command: () => import('./acorex-modules-assessment-management-view-session-answers.command-BOeO3Dit.mjs').then((c) => c.ViewSessionAnswersCommand),
4438
7553
  },
4439
7554
  {
4440
7555
  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),
7556
+ command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-4fkaeAG-.mjs').then((c) => c.FillAssessmentSessionCommand),
4442
7557
  },
4443
7558
  {
4444
7559
  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),
4446
- },
4447
- {
4448
- 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),
7560
+ command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-4fkaeAG-.mjs').then((c) => c.FillAssessmentSessionCommand),
4450
7561
  },
4451
7562
  ]),
4452
7563
  ], imports: [AXPWidgetCoreModule,
@@ -4473,31 +7584,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4473
7584
  provideCommandSetups([
4474
7585
  {
4475
7586
  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),
7587
+ command: () => import('./acorex-modules-assessment-management-preview-questionnaire.command-CRDBe_BS.mjs').then((c) => c.PreviewQuestionnaireCommand),
4477
7588
  },
4478
7589
  {
4479
7590
  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),
7591
+ command: () => import('./acorex-modules-assessment-management-preview-question.command-6IGOdVVZ.mjs').then((c) => c.PreviewQuestionCommand),
4481
7592
  },
4482
7593
  {
4483
7594
  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),
7595
+ command: () => import('./acorex-modules-assessment-management-preview-question.command-6IGOdVVZ.mjs').then((c) => c.PreviewQuestionCommand),
4485
7596
  },
4486
7597
  {
4487
7598
  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),
7599
+ command: () => import('./acorex-modules-assessment-management-view-session-answers.command-BOeO3Dit.mjs').then((c) => c.ViewSessionAnswersCommand),
4489
7600
  },
4490
7601
  {
4491
7602
  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),
7603
+ command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-4fkaeAG-.mjs').then((c) => c.FillAssessmentSessionCommand),
4493
7604
  },
4494
7605
  {
4495
7606
  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),
4497
- },
4498
- {
4499
- 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),
7607
+ command: () => import('./acorex-modules-assessment-management-fill-assessment-session.command-4fkaeAG-.mjs').then((c) => c.FillAssessmentSessionCommand),
4501
7608
  },
4502
7609
  ]),
4503
7610
  ],
@@ -4550,8 +7657,10 @@ class AXMAssessmentManagementModule {
4550
7657
  provideLazyProvider(AXP_PERMISSION_DEFINITION_PROVIDER, () => Promise.resolve().then(function () { return permissionDefinition_provider; }).then((m) => m.AXMAssessmentManagementPermissionDefinitionProvider)),
4551
7658
  provideLazyProvider(AXP_MENU_PROVIDER, () => Promise.resolve().then(function () { return menu_provider; }).then((m) => m.AXMAssessmentManagementMenuProvider)),
4552
7659
  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)),
7660
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index$1; }).then((m) => m.AXMQuestionnaireQuestionsPageComponentProvider)),
7661
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index; }).then((m) => m.AXMQuestionnaireOutcomesPageComponentProvider)),
7662
+ provideCommand('Questionnaire:Questions:Save', () => Promise.resolve().then(function () { return saveQuestionnaireQuestions_command; }).then((m) => m.AXMSaveQuestionnaireQuestionsCommand)),
7663
+ provideCommand('Questionnaire:Outcomes:Save', () => Promise.resolve().then(function () { return saveQuestionnaireOutcomes_command; }).then((m) => m.AXMSaveQuestionnaireOutcomesCommand)),
4555
7664
  provideLazyProvider(AXP_SEARCH_PROVIDER, () => Promise.resolve().then(function () { return searchCommand_provider; }).then((m) => m.AXMAssessmentManagementSearchCommandProvider)),
4556
7665
  ], imports: [AXMQuestionnaireBuilderFeatureModule,
4557
7666
  AXMQuestionnaireViewerFeatureModule] }); }
@@ -4583,8 +7692,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4583
7692
  provideLazyProvider(AXP_PERMISSION_DEFINITION_PROVIDER, () => Promise.resolve().then(function () { return permissionDefinition_provider; }).then((m) => m.AXMAssessmentManagementPermissionDefinitionProvider)),
4584
7693
  provideLazyProvider(AXP_MENU_PROVIDER, () => Promise.resolve().then(function () { return menu_provider; }).then((m) => m.AXMAssessmentManagementMenuProvider)),
4585
7694
  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)),
7695
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index$1; }).then((m) => m.AXMQuestionnaireQuestionsPageComponentProvider)),
7696
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index; }).then((m) => m.AXMQuestionnaireOutcomesPageComponentProvider)),
7697
+ provideCommand('Questionnaire:Questions:Save', () => Promise.resolve().then(function () { return saveQuestionnaireQuestions_command; }).then((m) => m.AXMSaveQuestionnaireQuestionsCommand)),
7698
+ provideCommand('Questionnaire:Outcomes:Save', () => Promise.resolve().then(function () { return saveQuestionnaireOutcomes_command; }).then((m) => m.AXMSaveQuestionnaireOutcomesCommand)),
4588
7699
  provideLazyProvider(AXP_SEARCH_PROVIDER, () => Promise.resolve().then(function () { return searchCommand_provider; }).then((m) => m.AXMAssessmentManagementSearchCommandProvider)),
4589
7700
  ],
4590
7701
  }]
@@ -4593,109 +7704,136 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4593
7704
  //#region ---- QuestionBankItem Model ----
4594
7705
  //#endregion
4595
7706
 
7707
+ //#region ---- Types ----
7708
+ //#endregion
7709
+
7710
+ //#region ---- Types ----
7711
+ //#endregion
7712
+
4596
7713
  //#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
- };
7714
+ //#endregion
7715
+ //#region ---- Persistence ----
4611
7716
  /**
4612
- * Maps entity pre/post to viewer config (defaults when omitted).
7717
+ * Loads a session by id using list query first, then byKey.
4613
7718
  */
4614
- function toPrePostConfig(q) {
4615
- if (!q) {
4616
- return null;
7719
+ async function loadSessionById(entityService, sessionId) {
7720
+ const sessionAccessor = entityService
7721
+ .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
7722
+ .data();
7723
+ const listMatch = await sessionAccessor.query({
7724
+ skip: 0,
7725
+ take: 1,
7726
+ filter: {
7727
+ field: 'id',
7728
+ operator: { type: 'equal' },
7729
+ value: sessionId,
7730
+ },
7731
+ });
7732
+ const fromQuery = first(listMatch.items);
7733
+ if (fromQuery) {
7734
+ return fromQuery;
4617
7735
  }
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 };
7736
+ return defaultTo(await sessionAccessor.byKey(sessionId), null);
4631
7737
  }
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
7738
  /**
4636
- * Coerces stored view mode (string or SelectBox `{ id }`) to a known value.
7739
+ * Loads assessment case by id using query first, then byKey.
4637
7740
  */
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);
7741
+ async function loadCaseById(entityService, caseId) {
7742
+ const caseAccessor = entityService
7743
+ .withEntity(RootConfig.module.name, RootConfig.entities.assessmentCase.name)
7744
+ .data();
7745
+ const listMatch = await caseAccessor.query({
7746
+ skip: 0,
7747
+ take: 1,
7748
+ filter: {
7749
+ field: 'id',
7750
+ operator: { type: 'equal' },
7751
+ value: caseId,
7752
+ },
7753
+ });
7754
+ const fromQuery = first(listMatch.items);
7755
+ if (fromQuery) {
7756
+ return fromQuery;
4644
7757
  }
4645
- return 'single-page';
7758
+ return defaultTo(await caseAccessor.byKey(caseId), null);
4646
7759
  }
4647
7760
  /**
4648
- * Coerces stored validation strategy (string or SelectBox `{ id }`) to a known value.
7761
+ * Bumps case audit metadata by performing a domain update (history middleware sets auditInfo.updated.at).
4649
7762
  */
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';
7763
+ async function touchAssessmentCaseActivity(entityService, caseId) {
7764
+ const assessmentCase = await loadCaseById(entityService, caseId);
7765
+ if (!assessmentCase) {
7766
+ return;
7767
+ }
7768
+ const caseAccessor = entityService
7769
+ .withEntity(RootConfig.module.name, RootConfig.entities.assessmentCase.name)
7770
+ .data();
7771
+ const caseStatus = assessmentCase.statusId;
7772
+ const nextCaseStatus = caseStatus === AXPSystemStatusType.Closed || caseStatus === AXPSystemStatusType.Cancelled
7773
+ ? caseStatus
7774
+ : AXPSystemStatusType.InProgress;
7775
+ await caseAccessor.update(caseId, { statusId: nextCaseStatus });
4658
7776
  }
4659
7777
  /**
4660
- * Coerces stored completion mode (string or SelectBox `{ id }`) to a known value.
7778
+ * Upserts assessment session answers and status; moves case to in-progress when saving or submitting.
4661
7779
  */
4662
- function normalizeCompletionMode(value) {
4663
- if (typeof value === 'string' && COMPLETION_MODE_VALUES.includes(value)) {
4664
- return value;
7780
+ async function persistAssessmentSession(input) {
7781
+ const { entityService, questionnaireViewerService, caseId, sessionId, existingSession, responderId, role, structure, answers, finalize, progressMetrics, timeSpentSeconds, } = input;
7782
+ const calculatedProgress = questionnaireViewerService.calculateProgressFromStructure(structure, answers);
7783
+ const answeredQuestionsCount = defaultTo(get(progressMetrics, 'answeredQuestionsCount'), calculatedProgress.answeredQuestionsCount);
7784
+ const progressPercentage = defaultTo(get(progressMetrics, 'progressPercentage'), calculatedProgress.progressPercentage);
7785
+ const now = new Date();
7786
+ const sessionAccessor = entityService
7787
+ .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
7788
+ .data();
7789
+ let resolvedSessionId = sessionId;
7790
+ let currentSession = existingSession;
7791
+ if (resolvedSessionId && !currentSession) {
7792
+ currentSession = await loadSessionById(entityService, resolvedSessionId);
7793
+ }
7794
+ const sessionStatusId = finalize
7795
+ ? 'submitted'
7796
+ : get(currentSession, 'statusId') === 'submitted'
7797
+ ? 'submitted'
7798
+ : AXPSystemStatusType.InProgress;
7799
+ const sessionData = {
7800
+ caseId,
7801
+ responderId,
7802
+ role,
7803
+ answers,
7804
+ progressPercentage,
7805
+ answeredQuestionsCount,
7806
+ lastSavedAt: now,
7807
+ statusId: sessionStatusId,
7808
+ };
7809
+ if (isEmpty(get(currentSession, 'startedAt'))) {
7810
+ sessionData['startedAt'] = now;
4665
7811
  }
4666
- if (value != null && typeof value === 'object' && 'id' in value && typeof value.id === 'string') {
4667
- return normalizeCompletionMode(value.id);
7812
+ if (timeSpentSeconds != null && timeSpentSeconds >= 0) {
7813
+ sessionData['timeSpent'] = timeSpentSeconds;
4668
7814
  }
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;
7815
+ const questionnaireId = input.questionnaireId;
7816
+ if (questionnaireId) {
7817
+ const { values } = await questionnaireViewerService.evaluateOutcomes(answers, {
7818
+ questionnaireId,
7819
+ structure,
7820
+ });
7821
+ if (isObject(values) && !isEmpty(values)) {
7822
+ sessionData['outcomes'] = values;
7823
+ }
4677
7824
  }
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
- };
7825
+ if (resolvedSessionId) {
7826
+ await sessionAccessor.update(resolvedSessionId, sessionData);
7827
+ }
7828
+ else {
7829
+ sessionData['startedAt'] = now;
7830
+ resolvedSessionId = await sessionAccessor.create(sessionData);
7831
+ }
7832
+ await touchAssessmentCaseActivity(entityService, caseId);
7833
+ return { sessionId: resolvedSessionId };
4687
7834
  }
4688
7835
  //#endregion
4689
7836
 
4690
- //#region ---- Types ----
4691
- //#endregion
4692
-
4693
- //#region ---- Types ----
4694
- //#endregion
4695
-
4696
- //#region ---- Types ----
4697
- //#endregion
4698
-
4699
7837
  //#region ---- Imports ----
4700
7838
  //#endregion
4701
7839
  //#region ---- Component ----
@@ -4720,7 +7858,7 @@ class AXMPreQuestionnaireContentComponent {
4720
7858
  [innerHTML]="content() | translate | async | safe: 'html'"
4721
7859
  ></div>
4722
7860
  </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 }); }
7861
+ `, 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
7862
  }
4725
7863
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPreQuestionnaireContentComponent, decorators: [{
4726
7864
  type: Component,
@@ -4743,6 +7881,166 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4743
7881
  }]
4744
7882
  }], propDecorators: { content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }] } });
4745
7883
 
7884
+ //#region ---- Imports ----
7885
+ //#endregion
7886
+ //#region ---- Presentation ----
7887
+ /**
7888
+ * Maps questionnaire outcome display metadata to platform value chip classes/styles.
7889
+ */
7890
+ function mapOutcomeDisplayToValuePresentation(display) {
7891
+ const classes = ['__outcome-value'];
7892
+ if (display?.emphasis === 'medium') {
7893
+ classes.push('__emphasis-medium');
7894
+ }
7895
+ else if (display?.emphasis === 'bold') {
7896
+ classes.push('__emphasis-bold');
7897
+ }
7898
+ if (!display?.color) {
7899
+ classes.push('__tone-default');
7900
+ return { classes: classes.join(' ') };
7901
+ }
7902
+ const parsed = parseQuestionnaireOutcomeDisplayColor(display.color);
7903
+ if (!parsed) {
7904
+ classes.push('__tone-default');
7905
+ return { classes: classes.join(' ') };
7906
+ }
7907
+ if (isQuestionnaireOutcomeDisplayColorHex(parsed.background)) {
7908
+ return {
7909
+ classes: classes.join(' '),
7910
+ color: parsed.foreground,
7911
+ backgroundColor: parsed.background,
7912
+ borderColor: parsed.border,
7913
+ };
7914
+ }
7915
+ return {
7916
+ classes: [...classes, parsed.foreground, parsed.background, parsed.border].join(' '),
7917
+ };
7918
+ }
7919
+ function mapOutcomeRow(entry) {
7920
+ return {
7921
+ key: entry.key,
7922
+ label: entry.label,
7923
+ value: entry.value,
7924
+ iconClass: entry.display?.iconClass,
7925
+ valuePresentation: mapOutcomeDisplayToValuePresentation(entry.display),
7926
+ };
7927
+ }
7928
+ function resolveSectionText(value, locale, fallback) {
7929
+ const resolved = resolveMultiLanguageString(value, locale).trim();
7930
+ return resolved.length > 0 ? resolved : fallback;
7931
+ }
7932
+ //#endregion
7933
+ //#region ---- View model ----
7934
+ /**
7935
+ * Maps post-submit placeholder values or snapshot sections to {@link AXPOutcomeResultsViewModel}.
7936
+ */
7937
+ function mapToOutcomeResultsViewModel(source, options) {
7938
+ const locale = options?.locale ?? 'en-US';
7939
+ const sections = source?.outcomeSections;
7940
+ if (Array.isArray(sections) && sections.length > 0) {
7941
+ return {
7942
+ sections: sections.map((group) => mapSectionGroup(group, locale)),
7943
+ rows: sections.length === 1 ? sections[0].items.map(mapOutcomeRow) : undefined,
7944
+ };
7945
+ }
7946
+ const outcomes = source?.outcomes;
7947
+ const titles = source?.outcomeTitles;
7948
+ if (!outcomes || typeof outcomes !== 'object') {
7949
+ return { rows: [] };
7950
+ }
7951
+ const rows = Object.entries(outcomes).map(([key, value]) => mapOutcomeRow({
7952
+ key,
7953
+ value,
7954
+ label: (titles?.[key] ?? '').trim() || humanizeOutcomeKey(key),
7955
+ }));
7956
+ return { rows };
7957
+ }
7958
+ function mapSectionGroup(group, locale) {
7959
+ const fallbackTitle = group.sectionName || group.trackKey;
7960
+ return {
7961
+ trackKey: group.trackKey,
7962
+ title: resolveSectionText(group.sectionTitle, locale, fallbackTitle),
7963
+ description: group.sectionDescription
7964
+ ? resolveSectionText(group.sectionDescription, locale, '')
7965
+ : undefined,
7966
+ items: group.items.map(mapOutcomeRow),
7967
+ };
7968
+ }
7969
+ function humanizeOutcomeKey(key) {
7970
+ return key
7971
+ .replace(/_/g, ' ')
7972
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
7973
+ .trim()
7974
+ .split(/\s+/)
7975
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
7976
+ .join(' ');
7977
+ }
7978
+ //#endregion
7979
+
7980
+ //#region ---- Imports ----
7981
+ //#endregion
7982
+ //#region ---- Component ----
7983
+ /**
7984
+ * Post-submit outcome results step; delegates presentation to {@link AXPOutcomeResultsViewerComponent}.
7985
+ */
7986
+ class AXMQuestionnaireOutcomeResultsComponent {
7987
+ constructor() {
7988
+ //#region ---- Services & Dependencies ----
7989
+ this.translationService = inject(AXTranslationService);
7990
+ //#endregion
7991
+ //#region ---- Inputs ----
7992
+ /** Evaluated outcomes, titles, and optional grouped sections from the viewer service. */
7993
+ this.placeholderValues = input(null, ...(ngDevMode ? [{ debugName: "placeholderValues" }] : /* istanbul ignore next */ []));
7994
+ //#endregion
7995
+ //#region ---- Computed ----
7996
+ this.viewModel = computed(() => {
7997
+ const locale = this.translationService.getActiveLang?.() ?? 'en-US';
7998
+ return mapToOutcomeResultsViewModel(this.placeholderValues(), { locale });
7999
+ }, ...(ngDevMode ? [{ debugName: "viewModel" }] : /* istanbul ignore next */ []));
8000
+ }
8001
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomeResultsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8002
+ 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: `
8003
+ <axp-outcome-results-viewer
8004
+ [model]="viewModel()"
8005
+ [title]="
8006
+ ('@assessment-management:questionnaires.components.questionnaire-viewer.flow.outcomes-section-title'
8007
+ | translate
8008
+ | async) ?? ''
8009
+ "
8010
+ [emptyMessage]="
8011
+ ('@assessment-management:questionnaires.components.questionnaire-viewer.flow.no-outcomes'
8012
+ | translate
8013
+ | async) ?? ''
8014
+ "
8015
+ />
8016
+ `, 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 }); }
8017
+ }
8018
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireOutcomeResultsComponent, decorators: [{
8019
+ type: Component,
8020
+ args: [{
8021
+ selector: 'axm-questionnaire-outcome-results',
8022
+ template: `
8023
+ <axp-outcome-results-viewer
8024
+ [model]="viewModel()"
8025
+ [title]="
8026
+ ('@assessment-management:questionnaires.components.questionnaire-viewer.flow.outcomes-section-title'
8027
+ | translate
8028
+ | async) ?? ''
8029
+ "
8030
+ [emptyMessage]="
8031
+ ('@assessment-management:questionnaires.components.questionnaire-viewer.flow.no-outcomes'
8032
+ | translate
8033
+ | async) ?? ''
8034
+ "
8035
+ />
8036
+ `,
8037
+ changeDetection: ChangeDetectionStrategy.OnPush,
8038
+ imports: [AXTranslationModule, AsyncPipe, AXPOutcomeResultsViewerComponent],
8039
+ encapsulation: ViewEncapsulation.None,
8040
+ host: { class: 'axm-questionnaire-outcome-results ax-block' },
8041
+ }]
8042
+ }], propDecorators: { placeholderValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderValues", required: false }] }] } });
8043
+
4746
8044
  //#region ---- Imports ----
4747
8045
  //#endregion
4748
8046
  //#region ---- Component ----
@@ -4758,7 +8056,7 @@ class AXMPostQuestionnaireContentComponent {
4758
8056
  this.translationService = inject(AXTranslationService);
4759
8057
  //#endregion
4760
8058
  //#region ---- Inputs ----
4761
- /** Display mode: review, summary (rich text), or results (calculation list). */
8059
+ /** Display mode: review, summary (rich text), or results (outcome list). */
4762
8060
  this.mode = input('review', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
4763
8061
  /** Questions with answers for review mode. */
4764
8062
  this.questionsWithAnswers = input([], ...(ngDevMode ? [{ debugName: "questionsWithAnswers" }] : /* istanbul ignore next */ []));
@@ -4766,7 +8064,7 @@ class AXMPostQuestionnaireContentComponent {
4766
8064
  this.reviewContext = input(null, ...(ngDevMode ? [{ debugName: "reviewContext" }] : /* istanbul ignore next */ []));
4767
8065
  /** Summary content (rich text). Supports multi-language and placeholders. */
4768
8066
  this.summaryContent = input(null, ...(ngDevMode ? [{ debugName: "summaryContent" }] : /* istanbul ignore next */ []));
4769
- /** Placeholder values: totals, time, and calculation outcomes. */
8067
+ /** Placeholder values: totals, time, and outcome values. */
4770
8068
  this.placeholderValues = input(null, ...(ngDevMode ? [{ debugName: "placeholderValues" }] : /* istanbul ignore next */ []));
4771
8069
  /** Locale for multi-language content. Defaults to active lang. */
4772
8070
  this.locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
@@ -4807,19 +8105,6 @@ class AXMPostQuestionnaireContentComponent {
4807
8105
  }
4808
8106
  return groups;
4809
8107
  }, ...(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
8108
  this.summaryContentHtml = computed(() => {
4824
8109
  const content = this.summaryContent();
4825
8110
  const values = this.placeholderValues();
@@ -4836,16 +8121,6 @@ class AXMPostQuestionnaireContentComponent {
4836
8121
  }
4837
8122
  //#endregion
4838
8123
  //#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
8124
  formatAnswer(answer) {
4850
8125
  if (answer == null || answer === '')
4851
8126
  return '—';
@@ -4934,43 +8209,7 @@ class AXMPostQuestionnaireContentComponent {
4934
8209
  </div>
4935
8210
  </div>
4936
8211
  } @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>
8212
+ <axm-questionnaire-outcome-results [placeholderValues]="placeholderValues()" />
4974
8213
  } @else {
4975
8214
  <div
4976
8215
  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 +8217,7 @@ class AXMPostQuestionnaireContentComponent {
4978
8217
  <div class="ax-max-w-2xl ax-w-full ax-prose ax-text-on-surface" [innerHTML]="summaryContentHtml()"></div>
4979
8218
  </div>
4980
8219
  }
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 }); }
8220
+ `, 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
8221
  }
4983
8222
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMPostQuestionnaireContentComponent, decorators: [{
4984
8223
  type: Component,
@@ -5056,43 +8295,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5056
8295
  </div>
5057
8296
  </div>
5058
8297
  } @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>
8298
+ <axm-questionnaire-outcome-results [placeholderValues]="placeholderValues()" />
5096
8299
  } @else {
5097
8300
  <div
5098
8301
  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 +8307,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5104
8307
  CommonModule,
5105
8308
  AXTranslationModule,
5106
8309
  AXPWidgetCoreModule,
8310
+ AXMQuestionnaireOutcomeResultsComponent,
5107
8311
  AXMQuestionnaireViewerQuestionComponent,
5108
8312
  AXMQuestionnaireViewerSectionHeaderComponent,
5109
8313
  ], 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 +8337,7 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5133
8337
  this.isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
5134
8338
  this.loadError = signal(null, ...(ngDevMode ? [{ debugName: "loadError" }] : /* istanbul ignore next */ []));
5135
8339
  this.currentSessionId = signal(null, ...(ngDevMode ? [{ debugName: "currentSessionId" }] : /* istanbul ignore next */ []));
8340
+ this.existingSession = signal(null, ...(ngDevMode ? [{ debugName: "existingSession" }] : /* istanbul ignore next */ []));
5136
8341
  this.flowStep = signal('questions', ...(ngDevMode ? [{ debugName: "flowStep" }] : /* istanbul ignore next */ []));
5137
8342
  this.reviewSnapshot = signal([], ...(ngDevMode ? [{ debugName: "reviewSnapshot" }] : /* istanbul ignore next */ []));
5138
8343
  this.reviewContext = signal(null, ...(ngDevMode ? [{ debugName: "reviewContext" }] : /* istanbul ignore next */ []));
@@ -5219,9 +8424,12 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5219
8424
  .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
5220
8425
  .data()
5221
8426
  .byKey(existingSessionId);
5222
- if (session?.answers) {
5223
- answers = session.answers;
8427
+ if (session) {
8428
+ if (session.answers) {
8429
+ answers = session.answers;
8430
+ }
5224
8431
  this.currentSessionId.set(existingSessionId);
8432
+ this.existingSession.set(session);
5225
8433
  }
5226
8434
  }
5227
8435
  this.questionnaire.set(questionnaire);
@@ -5253,7 +8461,7 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5253
8461
  this.handleDone();
5254
8462
  return;
5255
8463
  }
5256
- if (cfg?.post.showCalculationResults === true) {
8464
+ if (cfg?.post.showOutcomeResults === true) {
5257
8465
  this.flowStep.set('results');
5258
8466
  return;
5259
8467
  }
@@ -5269,7 +8477,6 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5269
8477
  const q = this.questionnaire();
5270
8478
  if (!caseIdVal || !structureVal || !q)
5271
8479
  return null;
5272
- const { answeredQuestionsCount, progressPercentage } = this.questionnaireViewerService.calculateProgressFromStructure(structureVal, answers);
5273
8480
  const responderId = this.authSession.user?.id;
5274
8481
  if (!responderId) {
5275
8482
  this.toastService.show({
@@ -5279,39 +8486,41 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5279
8486
  });
5280
8487
  return null;
5281
8488
  }
5282
- const now = new Date();
5283
- const sessionIdVal = this.currentSessionId();
5284
- const role = 'responder';
5285
- const sessionData = {
8489
+ const vm = this.viewerViewModel();
8490
+ const result = await persistAssessmentSession({
8491
+ entityService: this.entityService,
8492
+ questionnaireViewerService: this.questionnaireViewerService,
5286
8493
  caseId: caseIdVal,
8494
+ sessionId: this.currentSessionId(),
8495
+ existingSession: this.existingSession(),
5287
8496
  responderId,
5288
- role,
8497
+ role: 'responder',
8498
+ structure: structureVal,
8499
+ questionnaireId: q.id,
5289
8500
  answers,
5290
- progressPercentage,
5291
- answeredQuestionsCount,
5292
- lastSavedAt: now,
5293
- };
5294
- const sessionDataAccessor = this.entityService
8501
+ finalize: true,
8502
+ progressMetrics: vm
8503
+ ? {
8504
+ answeredQuestionsCount: vm.answeredQuestionsCount(),
8505
+ progressPercentage: vm.progressPercentage(),
8506
+ }
8507
+ : undefined,
8508
+ timeSpentSeconds: vm?.timerElapsed(),
8509
+ });
8510
+ this.currentSessionId.set(result.sessionId);
8511
+ const session = await this.entityService
5295
8512
  .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
- });
8513
+ .data()
8514
+ .byKey(result.sessionId);
8515
+ if (session) {
8516
+ this.existingSession.set(session);
5313
8517
  }
5314
- const elapsed = this.viewerViewModel()?.timerElapsed() ?? 0;
8518
+ this.toastService.show({
8519
+ color: 'success',
8520
+ title: await this.translateService.translateAsync('@assessment-management:sessions.states.submitted'),
8521
+ content: '',
8522
+ });
8523
+ const elapsed = vm?.timerElapsed() ?? 0;
5315
8524
  return this.questionnaireViewerService.buildSummaryPlaceholderValues(structureVal, answers, {
5316
8525
  questionnaireId: q.id,
5317
8526
  elapsedSeconds: elapsed,
@@ -5405,9 +8614,9 @@ class AXMQuestionnaireViewerPageComponent extends AXPPageLayoutBaseComponent {
5405
8614
  this.router.navigateByUrl(this.getCasesListPath());
5406
8615
  }
5407
8616
  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:
8617
+ 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
8618
  //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 }); }
8619
+ 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
8620
  }
5412
8621
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerPageComponent, decorators: [{
5413
8622
  type: Component,
@@ -5450,6 +8659,8 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5450
8659
  this.isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
5451
8660
  this.loadError = signal(null, ...(ngDevMode ? [{ debugName: "loadError" }] : /* istanbul ignore next */ []));
5452
8661
  this.isSubmitting = signal(false, ...(ngDevMode ? [{ debugName: "isSubmitting" }] : /* istanbul ignore next */ []));
8662
+ this.sessionId = signal(null, ...(ngDevMode ? [{ debugName: "sessionId" }] : /* istanbul ignore next */ []));
8663
+ this.existingSession = signal(null, ...(ngDevMode ? [{ debugName: "existingSession" }] : /* istanbul ignore next */ []));
5453
8664
  this.flowStep = signal('questions', ...(ngDevMode ? [{ debugName: "flowStep" }] : /* istanbul ignore next */ []));
5454
8665
  this.reviewSnapshot = signal([], ...(ngDevMode ? [{ debugName: "reviewSnapshot" }] : /* istanbul ignore next */ []));
5455
8666
  this.reviewContext = signal(null, ...(ngDevMode ? [{ debugName: "reviewContext" }] : /* istanbul ignore next */ []));
@@ -5517,6 +8728,8 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5517
8728
  this.isLoading.set(false);
5518
8729
  return;
5519
8730
  }
8731
+ this.sessionId.set(sessionId);
8732
+ this.existingSession.set(session);
5520
8733
  const caseId = session.caseId;
5521
8734
  const assessmentCase = await this.entityService
5522
8735
  .withEntity(RootConfig.module.name, RootConfig.entities.assessmentCase.name)
@@ -5558,28 +8771,41 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5558
8771
  //#endregion
5559
8772
  //#region ---- Submit Handler ----
5560
8773
  async saveSessionAndGetSummary(answers) {
5561
- const sessionId = this.route.snapshot.paramMap.get('sessionId') ?? this.route.parent?.snapshot.paramMap.get('sessionId');
8774
+ const sessionIdVal = this.sessionId();
5562
8775
  const structureVal = this.structure();
5563
8776
  const q = this.questionnaire();
5564
- if (!sessionId || !structureVal || !q)
8777
+ const session = this.existingSession();
8778
+ if (!sessionIdVal || !structureVal || !q || !session)
5565
8779
  return null;
5566
- const { answeredQuestionsCount, progressPercentage } = this.questionnaireViewerService.calculateProgressFromStructure(structureVal, answers);
5567
- const session = await this.entityService
8780
+ const vm = this.viewerViewModel();
8781
+ const result = await persistAssessmentSession({
8782
+ entityService: this.entityService,
8783
+ questionnaireViewerService: this.questionnaireViewerService,
8784
+ caseId: session.caseId,
8785
+ sessionId: sessionIdVal,
8786
+ existingSession: session,
8787
+ responderId: session.responderId,
8788
+ role: session.role,
8789
+ structure: structureVal,
8790
+ questionnaireId: q.id,
8791
+ answers,
8792
+ finalize: true,
8793
+ progressMetrics: vm
8794
+ ? {
8795
+ answeredQuestionsCount: vm.answeredQuestionsCount(),
8796
+ progressPercentage: vm.progressPercentage(),
8797
+ }
8798
+ : undefined,
8799
+ timeSpentSeconds: vm?.timerElapsed(),
8800
+ });
8801
+ const updated = await this.entityService
5568
8802
  .withEntity(RootConfig.module.name, RootConfig.entities.assessmentSession.name)
5569
8803
  .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
- });
8804
+ .byKey(result.sessionId);
8805
+ if (updated) {
8806
+ this.existingSession.set(updated);
5581
8807
  }
5582
- const elapsed = this.viewerViewModel()?.timerElapsed() ?? 0;
8808
+ const elapsed = vm?.timerElapsed() ?? 0;
5583
8809
  return this.questionnaireViewerService.buildSummaryPlaceholderValues(structureVal, answers, {
5584
8810
  questionnaireId: q.id,
5585
8811
  elapsedSeconds: elapsed,
@@ -5594,7 +8820,7 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5594
8820
  this.handleDone();
5595
8821
  return;
5596
8822
  }
5597
- if (cfg?.post.showCalculationResults === true) {
8823
+ if (cfg?.post.showOutcomeResults === true) {
5598
8824
  this.flowStep.set('results');
5599
8825
  return;
5600
8826
  }
@@ -5783,7 +9009,7 @@ class AXMQuestionnaireViewerStandalonePageComponent {
5783
9009
  </ax-footer>
5784
9010
  }
5785
9011
  </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 }); }
9012
+ `, 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
9013
  }
5788
9014
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMQuestionnaireViewerStandalonePageComponent, decorators: [{
5789
9015
  type: Component,
@@ -5920,5 +9146,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5920
9146
  * Generated bundle index. Do not edit.
5921
9147
  */
5922
9148
 
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
9149
+ export { AXMQuestionnaireViewerService as A, diffQuestionCommentOverride as B, diffQuestionHintOverride as C, DEFAULT_POST_QUESTIONNAIRE_CONFIG as D, findDuplicateOutcomeNames as E, flattenQuestionnaireOutcomeItems as F, getQuestionAnswerCommentStoragePath as G, isQuestionnaireOutcomeDisplayColorHex as H, matchesQuestionnaireOutcomeDisplayRule as I, mergeQuestionCommentConfig as J, mergeQuestionHintForViewer as K, normalizeQuestionCommentConfig as L, normalizeQuestionHintConfig as M, normalizeQuestionHintFromEntityRow as N, normalizeQuestionHintMode as O, normalizeQuestionnaireOutcomeDisplayRules as P, QUESTIONNAIRE_QUESTIONS_PAGE_COMPONENT_KEY as Q, RootConfig as R, normalizeQuestionnaireOutcomesValue as S, normalizeValidationStrategy$1 as T, normalizeViewMode as U, parseQuestionHintPartialFromRecord as V, parseQuestionnaireOutcomeDisplayColor as W, readHintFieldsFromRecord as X, 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, AXMAssessmentManagementSearchCommandProvider as m, normalizeCompletionMode as n, AXMQuestionnaireViewerPageComponent as o, persistAssessmentSession as p, AXMQuestionnaireViewerStandalonePageComponent as q, resolveWidgetNodeFromMetaDataDefinitionInterface as r, stripQuestionBankInterfaceMetadata as s, toPrePostConfig as t, AXPAssessmentManagementMenuKeys as u, DEFAULT_PRE_QUESTIONNAIRE_CONFIG as v, DEFAULT_QUESTIONNAIRE_ENTITY_DISPLAY as w, DEFAULT_QUESTIONNAIRE_OUTCOMES as x, DEFAULT_QUESTION_COMMENT_CONFIG as y, DEFAULT_QUESTION_HINT_CONFIG as z };
9150
+ //# sourceMappingURL=acorex-modules-assessment-management-acorex-modules-assessment-management-DqmJVJ0z.mjs.map