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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/fesm2022/acorex-platform-module-assessment-management-client-acorex-platform-module-assessment-management-client-jljmsqNu.mjs +10593 -0
  2. package/fesm2022/acorex-platform-module-assessment-management-client-acorex-platform-module-assessment-management-client-jljmsqNu.mjs.map +1 -0
  3. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-case-status.provider-i48XDX-I.mjs +83 -0
  4. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-case-status.provider-i48XDX-I.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-session-answers-view.util-42ZcOY2C.mjs +124 -0
  6. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-session-answers-view.util-42ZcOY2C.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-session-status.provider-Bk9hHj5V.mjs +97 -0
  8. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-session-status.provider-Bk9hHj5V.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-voice.commands-CduVfWQ-.mjs +134 -0
  10. package/fesm2022/acorex-platform-module-assessment-management-client-assessment-voice.commands-CduVfWQ-.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-module-assessment-management-client-contracts.mjs +1171 -0
  12. package/fesm2022/acorex-platform-module-assessment-management-client-contracts.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-module-assessment-management-client-fill-assessment-session.command-VF80DWQf.mjs +270 -0
  14. package/fesm2022/acorex-platform-module-assessment-management-client-fill-assessment-session.command-VF80DWQf.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-module-assessment-management-client-list-question-difficulty-options.query-h3xT-MNd.mjs +59 -0
  16. package/fesm2022/acorex-platform-module-assessment-management-client-list-question-difficulty-options.query-h3xT-MNd.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-module-assessment-management-client-preview-question.command-C1ODQxDn.mjs +123 -0
  18. package/fesm2022/acorex-platform-module-assessment-management-client-preview-question.command-C1ODQxDn.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-module-assessment-management-client-preview-questionnaire.command-DBm7ve4w.mjs +165 -0
  20. package/fesm2022/acorex-platform-module-assessment-management-client-preview-questionnaire.command-DBm7ve4w.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-module-assessment-management-client-preview-session-pdf.command-BsiJU6ep.mjs +178 -0
  22. package/fesm2022/acorex-platform-module-assessment-management-client-preview-session-pdf.command-BsiJU6ep.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-module-assessment-management-client-question-bank-interface-editor-widget-edit.component-Bbl-efrl.mjs +489 -0
  24. package/fesm2022/acorex-platform-module-assessment-management-client-question-bank-interface-editor-widget-edit.component-Bbl-efrl.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-module-assessment-management-client-questionnaire-viewer-popup.component-Djcfg0dv.mjs +609 -0
  26. package/fesm2022/acorex-platform-module-assessment-management-client-questionnaire-viewer-popup.component-Djcfg0dv.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-module-assessment-management-client-review-assessment-session.command-CZ96KQ-l.mjs +228 -0
  28. package/fesm2022/acorex-platform-module-assessment-management-client-review-assessment-session.command-CZ96KQ-l.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-module-assessment-management-client-view-session-answers.command-CCzJoJ4p.mjs +280 -0
  30. package/fesm2022/acorex-platform-module-assessment-management-client-view-session-answers.command-CCzJoJ4p.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-module-assessment-management-client.mjs +2 -0
  32. package/fesm2022/acorex-platform-module-assessment-management-client.mjs.map +1 -0
  33. package/package.json +28 -0
  34. package/types/acorex-platform-module-assessment-management-client-contracts.d.ts +752 -0
  35. package/types/acorex-platform-module-assessment-management-client.d.ts +1279 -0
@@ -0,0 +1,59 @@
1
+ import { resolveCollectionSkip, resolveCollectionTake } from '@acorex-platform/framework-shared/runtime';
2
+ import { runtimeQuerySuccessCollection, runtimeQueryFailure } from '@acorex-platform/framework-shared/core';
3
+ import { applyFilterArray, applySortArray } from '@acorex-platform/framework-client/core';
4
+ import * as i0 from '@angular/core';
5
+ import { Injectable } from '@angular/core';
6
+
7
+ //#endregion
8
+ //#region ---- Constants ----
9
+ /** Same rows as the former `question-difficulty-options` registry data source. */
10
+ const QUESTION_DIFFICULTY_OPTIONS = [
11
+ {
12
+ id: 'easy',
13
+ title: '@assessment-management:question-bank.entities.question-bank-item.fields.difficulty.options.easy',
14
+ },
15
+ {
16
+ id: 'medium',
17
+ title: '@assessment-management:question-bank.entities.question-bank-item.fields.difficulty.options.medium',
18
+ },
19
+ {
20
+ id: 'hard',
21
+ title: '@assessment-management:question-bank.entities.question-bank-item.fields.difficulty.options.hard',
22
+ },
23
+ ];
24
+ //#endregion
25
+ //#region ---- Query ----
26
+ /**
27
+ * Static question difficulty catalog — replaces the `question-difficulty-options` DI data source.
28
+ */
29
+ class AXMListQuestionDifficultyOptionsQuery {
30
+ //#region ---- Query Execution ----
31
+ async fetch(input) {
32
+ try {
33
+ let items = [...QUESTION_DIFFICULTY_OPTIONS];
34
+ if (input?.filter) {
35
+ items = applyFilterArray(items, [input.filter]);
36
+ }
37
+ const rawSort = Array.isArray(input?.sort) ? input.sort : input?.sort ? [input.sort] : [];
38
+ const sortOpt = rawSort.length > 0 ? rawSort : [{ field: 'id', dir: 'asc' }];
39
+ items = applySortArray(items, sortOpt);
40
+ const skip = resolveCollectionSkip(input?.skip);
41
+ const take = resolveCollectionTake(input?.take);
42
+ const total = items.length;
43
+ const page = items.slice(skip, skip + take);
44
+ return runtimeQuerySuccessCollection({ items: page, total });
45
+ }
46
+ catch (error) {
47
+ return runtimeQueryFailure(error instanceof Error ? error.message : 'Unknown error occurred');
48
+ }
49
+ }
50
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMListQuestionDifficultyOptionsQuery, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
51
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMListQuestionDifficultyOptionsQuery, providedIn: 'root' }); }
52
+ }
53
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMListQuestionDifficultyOptionsQuery, decorators: [{
54
+ type: Injectable,
55
+ args: [{ providedIn: 'root' }]
56
+ }] });
57
+
58
+ export { AXMListQuestionDifficultyOptionsQuery };
59
+ //# sourceMappingURL=acorex-platform-module-assessment-management-client-list-question-difficulty-options.query-h3xT-MNd.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-module-assessment-management-client-list-question-difficulty-options.query-h3xT-MNd.mjs","sources":["../../../../libs/modules/assessment-management/client/src/lib/features/queries/list-question-difficulty-options.query.ts"],"sourcesContent":["import {\n resolveCollectionSkip,\n resolveCollectionTake,\n type AXPCollectionQuery,\n type AXPCollectionQueryInput,\n type AXPQueryCollectionFetchResultPromise,\n} from '@acorex-platform/framework-shared/runtime';\nimport {\n runtimeQueryFailure,\n runtimeQuerySuccessCollection,\n} from '@acorex-platform/framework-shared/core';\nimport { applyFilterArray, applySortArray } from '@acorex-platform/framework-client/core';\n\nimport { Injectable } from '@angular/core';\n\n//#region ---- Types ----\n\nexport type AXMListQuestionDifficultyOptionsQueryInput = AXPCollectionQueryInput;\n\nexport interface AXMListQuestionDifficultyOptionsQueryItem {\n /** Stored difficulty code. */\n id: string;\n /** Display title (i18n key). */\n title: string;\n}\n\n//#endregion\n\n//#region ---- Constants ----\n\n/** Same rows as the former `question-difficulty-options` registry data source. */\nconst QUESTION_DIFFICULTY_OPTIONS: AXMListQuestionDifficultyOptionsQueryItem[] = [\n {\n id: 'easy',\n title: '@assessment-management:question-bank.entities.question-bank-item.fields.difficulty.options.easy',\n },\n {\n id: 'medium',\n title: '@assessment-management:question-bank.entities.question-bank-item.fields.difficulty.options.medium',\n },\n {\n id: 'hard',\n title: '@assessment-management:question-bank.entities.question-bank-item.fields.difficulty.options.hard',\n },\n];\n\n//#endregion\n\n//#region ---- Query ----\n\n/**\n * Static question difficulty catalog — replaces the `question-difficulty-options` DI data source.\n */\n@Injectable({ providedIn: 'root' })\nexport class AXMListQuestionDifficultyOptionsQuery\n implements\n AXPCollectionQuery<\n AXMListQuestionDifficultyOptionsQueryInput,\n AXMListQuestionDifficultyOptionsQueryItem\n >\n{\n //#region ---- Query Execution ----\n\n async fetch(\n input?: AXMListQuestionDifficultyOptionsQueryInput | null,\n ): AXPQueryCollectionFetchResultPromise<AXMListQuestionDifficultyOptionsQueryItem> {\n try {\n let items = [...QUESTION_DIFFICULTY_OPTIONS];\n\n if (input?.filter) {\n items = applyFilterArray(items, [input.filter]);\n }\n\n const rawSort = Array.isArray(input?.sort) ? input.sort : input?.sort ? [input.sort] : [];\n const sortOpt = rawSort.length > 0 ? rawSort : [{ field: 'id' as const, dir: 'asc' as const }];\n items = applySortArray(items, sortOpt);\n\n const skip = resolveCollectionSkip(input?.skip);\n const take = resolveCollectionTake(input?.take);\n const total = items.length;\n const page = items.slice(skip, skip + take);\n\n return runtimeQuerySuccessCollection({ items: page, total });\n } catch (error) {\n return runtimeQueryFailure(\n error instanceof Error ? error.message : 'Unknown error occurred',\n );\n }\n }\n\n //#endregion\n}\n\n//#endregion\n"],"names":[],"mappings":";;;;;;AA0BA;AAEA;AAEA;AACA,MAAM,2BAA2B,GAAgD;AAC/E,IAAA;AACE,QAAA,EAAE,EAAE,MAAM;AACV,QAAA,KAAK,EAAE,iGAAiG;AACzG,KAAA;AACD,IAAA;AACE,QAAA,EAAE,EAAE,QAAQ;AACZ,QAAA,KAAK,EAAE,mGAAmG;AAC3G,KAAA;AACD,IAAA;AACE,QAAA,EAAE,EAAE,MAAM;AACV,QAAA,KAAK,EAAE,iGAAiG;AACzG,KAAA;CACF;AAED;AAEA;AAEA;;AAEG;MAEU,qCAAqC,CAAA;;IAShD,MAAM,KAAK,CACT,KAAyD,EAAA;AAEzD,QAAA,IAAI;AACF,YAAA,IAAI,KAAK,GAAG,CAAC,GAAG,2BAA2B,CAAC;AAE5C,YAAA,IAAI,KAAK,EAAE,MAAM,EAAE;gBACjB,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACjD;AAEA,YAAA,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;YACzF,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,OAAO,GAAG,CAAC,EAAE,KAAK,EAAE,IAAa,EAAE,GAAG,EAAE,KAAc,EAAE,CAAC;AAC9F,YAAA,KAAK,GAAG,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC;YAEtC,MAAM,IAAI,GAAG,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC;YAC/C,MAAM,IAAI,GAAG,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC;AAC/C,YAAA,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM;AAC1B,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;YAE3C,OAAO,6BAA6B,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC9D;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,OAAO,mBAAmB,CACxB,KAAK,YAAY,KAAK,GAAG,KAAK,CAAC,OAAO,GAAG,wBAAwB,CAClE;QACH;IACF;8GAlCW,qCAAqC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAArC,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,qCAAqC,cADxB,MAAM,EAAA,CAAA,CAAA;;2FACnB,qCAAqC,EAAA,UAAA,EAAA,CAAA;kBADjD,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;;;"}
@@ -0,0 +1,123 @@
1
+ import { AXTranslationService, resolveMultiLanguageString } from '@acorex/core/translation';
2
+ import { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';
3
+ import * as i0 from '@angular/core';
4
+ import { inject, Injectable } from '@angular/core';
5
+ import { RootConfig } from '@acorex-platform/module-assessment-management-client/contracts';
6
+ import { A as AXMQuestionnaireViewerService } from './acorex-platform-module-assessment-management-client-acorex-platform-module-assessment-management-client-jljmsqNu.mjs';
7
+
8
+ //#endregion
9
+ //#region ---- Preview Question Command ----
10
+ /**
11
+ * Command that opens a preview of a single question in a dialog.
12
+ * Used from the questionnaire builder per-question primary action.
13
+ */
14
+ class PreviewQuestionCommand {
15
+ constructor() {
16
+ //#region ---- Services & Dependencies ----
17
+ this.questionnaireViewerService = inject(AXMQuestionnaireViewerService);
18
+ this.entityService = inject(AXPEntityService);
19
+ this.translationService = inject(AXTranslationService);
20
+ }
21
+ //#endregion
22
+ //#region ---- Command Implementation ----
23
+ resolveId(input) {
24
+ const record = input;
25
+ const ctxData = input.data;
26
+ const ctxOptions = input.options;
27
+ const asString = (v) => (typeof v === 'string' && v.length > 0 ? v : undefined);
28
+ return (asString(input.id) ??
29
+ asString(record['questionBankItemId']) ??
30
+ asString(ctxOptions?.['questionBankItemId']) ??
31
+ asString(ctxData?.['id']) ??
32
+ asString(record['id']));
33
+ }
34
+ async execute(input) {
35
+ try {
36
+ const id = this.resolveId(input);
37
+ let structure = input.structure;
38
+ let title = input.title;
39
+ if (id) {
40
+ const item = await this.entityService
41
+ .withEntity(RootConfig.module.name, RootConfig.entities.questionBankItem.name)
42
+ .data()
43
+ .byKey(id);
44
+ if (!item) {
45
+ return {
46
+ success: false,
47
+ message: { text: 'Question not found' },
48
+ };
49
+ }
50
+ const locale = this.translationService.getActiveLang?.() ?? 'en-US';
51
+ const resolvedListTitle = resolveMultiLanguageString(item.title, locale);
52
+ structure = {
53
+ sections: [
54
+ {
55
+ name: `preview-section-${item.id}`,
56
+ title: resolvedListTitle || 'Question',
57
+ description: undefined,
58
+ order: 0,
59
+ items: [
60
+ {
61
+ name: String(item.id),
62
+ questionBankItemId: item.id,
63
+ order: 0,
64
+ isRequired: false,
65
+ interfaceOverride: item.interface,
66
+ },
67
+ ],
68
+ },
69
+ ],
70
+ displaySettings: {
71
+ viewMode: 'single-page',
72
+ showProgressBar: false,
73
+ showQuestionNumbers: false,
74
+ },
75
+ };
76
+ title = resolvedListTitle || undefined;
77
+ }
78
+ if (!structure?.sections?.length) {
79
+ return {
80
+ success: false,
81
+ message: { text: 'No question structure to preview' },
82
+ };
83
+ }
84
+ const totalQuestions = structure.sections?.reduce((s, sec) => s + (sec.items?.length ?? 0), 0) ?? 0;
85
+ if (totalQuestions === 0) {
86
+ return {
87
+ success: false,
88
+ message: { text: 'No question to preview' },
89
+ };
90
+ }
91
+ const previewTitleKey = '@assessment-management:question-bank.entities.question-bank-item.actions.preview.title';
92
+ const defaultTitle = (await this.translationService.translateAsync(previewTitleKey)) || 'Preview';
93
+ const dialogTitle = title?.trim() || defaultTitle;
94
+ await this.questionnaireViewerService.open({
95
+ title: dialogTitle,
96
+ structure,
97
+ config: { previewMode: true },
98
+ });
99
+ return {
100
+ success: true,
101
+ kind: 'object',
102
+ data: undefined,
103
+ message: { text: '' },
104
+ metadata: { refresh: false },
105
+ };
106
+ }
107
+ catch (error) {
108
+ const message = error instanceof Error ? error.message : 'Unknown error';
109
+ return {
110
+ success: false,
111
+ message: { text: `Error previewing question: ${message}` },
112
+ };
113
+ }
114
+ }
115
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PreviewQuestionCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
116
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PreviewQuestionCommand }); }
117
+ }
118
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PreviewQuestionCommand, decorators: [{
119
+ type: Injectable
120
+ }] });
121
+
122
+ export { PreviewQuestionCommand };
123
+ //# sourceMappingURL=acorex-platform-module-assessment-management-client-preview-question.command-C1ODQxDn.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-module-assessment-management-client-preview-question.command-C1ODQxDn.mjs","sources":["../../../../libs/modules/assessment-management/client/src/lib/features/questionnaire-viewer/commands/preview-question.command.ts"],"sourcesContent":["//#region ---- Imports ----\n\nimport { AXPCommand, type AXPCommandExecuteResultPromise } from '@acorex-platform/framework-shared/runtime';\nimport { AXTranslationService, resolveMultiLanguageString } from '@acorex/core/translation';\nimport { AXPRuntimeCommandResult } from '@acorex-platform/framework-shared/core';\nimport { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';\n\nimport { Injectable, inject } from '@angular/core';\nimport { RootConfig } from '@acorex-platform/module-assessment-management-client/contracts';\nimport type { AXMAssessmentManagementQuestionBankItemEntityModel } from '@acorex-platform/module-assessment-management-client/contracts';\nimport { QuestionnaireBuilderValue } from '@acorex-platform/module-assessment-management-client/contracts';\nimport { AXMQuestionnaireViewerService } from '../questionnaire-viewer.service';\n\n//#endregion\n\n//#region ---- Command Input/Output Types ----\n\n/**\n * Input for PreviewQuestion command.\n * Pass id to load the question bank item (title and structure come from the item).\n * Or pass pre-built structure (e.g. from builder) with optional title.\n */\nexport interface PreviewQuestionCommandInput {\n /** Question bank item id. */\n id?: string;\n /** Pre-built structure; when set, id is not required. */\n structure?: QuestionnaireBuilderValue;\n /** Dialog title when using pre-built structure. */\n title?: string;\n data?: Record<string, unknown>;\n options?: Record<string, unknown>;\n}\n\n//#endregion\n\n//#region ---- Preview Question Command ----\n\n/**\n * Command that opens a preview of a single question in a dialog.\n * Used from the questionnaire builder per-question primary action.\n */\n@Injectable()\nexport class PreviewQuestionCommand implements AXPCommand<PreviewQuestionCommandInput, void> {\n //#region ---- Services & Dependencies ----\n\n private readonly questionnaireViewerService = inject(AXMQuestionnaireViewerService);\n private readonly entityService = inject(AXPEntityService);\n private readonly translationService = inject(AXTranslationService);\n\n //#endregion\n\n //#region ---- Command Implementation ----\n\n private resolveId(input: PreviewQuestionCommandInput): string | undefined {\n const record = input as Record<string, unknown>;\n const ctxData = input.data as Record<string, unknown> | undefined;\n const ctxOptions = input.options as Record<string, unknown> | undefined;\n const asString = (v: unknown): string | undefined => (typeof v === 'string' && v.length > 0 ? v : undefined);\n return (\n asString(input.id) ??\n asString(record['questionBankItemId']) ??\n asString(ctxOptions?.['questionBankItemId']) ??\n asString(ctxData?.['id']) ??\n asString(record['id'])\n );\n }\n\n async execute(input: PreviewQuestionCommandInput): AXPCommandExecuteResultPromise<void> {\n try {\n const id = this.resolveId(input);\n let structure: QuestionnaireBuilderValue | undefined = input.structure;\n let title = input.title;\n\n if (id) {\n const item = await this.entityService\n .withEntity(RootConfig.module.name, RootConfig.entities.questionBankItem.name)\n .data<string, AXMAssessmentManagementQuestionBankItemEntityModel>()\n .byKey(id);\n if (!item) {\n return {\n success: false,\n message: { text: 'Question not found' },\n };\n }\n const locale = this.translationService.getActiveLang?.() ?? 'en-US';\n const resolvedListTitle = resolveMultiLanguageString(item.title, locale);\n structure = {\n sections: [\n {\n name: `preview-section-${item.id}`,\n title: resolvedListTitle || 'Question',\n description: undefined,\n order: 0,\n items: [\n {\n name: String(item.id),\n questionBankItemId: item.id,\n order: 0,\n isRequired: false,\n interfaceOverride: item.interface,\n } as QuestionnaireBuilderValue['sections'][0]['items'][0],\n ],\n },\n ],\n displaySettings: {\n viewMode: 'single-page',\n showProgressBar: false,\n showQuestionNumbers: false,\n },\n };\n title = resolvedListTitle || undefined;\n }\n\n if (!structure?.sections?.length) {\n return {\n success: false,\n message: { text: 'No question structure to preview' },\n };\n }\n\n const totalQuestions =\n structure.sections?.reduce((s: number, sec: { items?: unknown[] }) => s + (sec.items?.length ?? 0), 0) ?? 0;\n if (totalQuestions === 0) {\n return {\n success: false,\n message: { text: 'No question to preview' },\n };\n }\n\n const previewTitleKey = '@assessment-management:question-bank.entities.question-bank-item.actions.preview.title';\n const defaultTitle = (await this.translationService.translateAsync(previewTitleKey)) || 'Preview';\n const dialogTitle = title?.trim() || defaultTitle;\n\n await this.questionnaireViewerService.open({\n title: dialogTitle,\n structure,\n config: { previewMode: true },\n });\n\n return {\n success: true,\n kind: 'object',\n data: undefined,\n message: { text: '' },\n metadata: { refresh: false },\n };\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n success: false,\n message: { text: `Error previewing question: ${message}` },\n };\n }\n }\n\n //#endregion\n}\n\n//#endregion\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,165 @@
1
+ import { AXTranslationService, resolveMultiLanguageString } from '@acorex/core/translation';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, Injectable } from '@angular/core';
4
+ import { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';
5
+ import { A as AXMQuestionnaireViewerService, g as formatQuestionnairePreviewDialogTitle } from './acorex-platform-module-assessment-management-client-acorex-platform-module-assessment-management-client-jljmsqNu.mjs';
6
+ import { RootConfig, toPrePostConfig, toDisplaySettings, toPrintSettings } from '@acorex-platform/module-assessment-management-client/contracts';
7
+
8
+ //#endregion
9
+ //#region ---- Preview Questionnaire Command ----
10
+ /**
11
+ * Command that opens a preview of a questionnaire in a dialog.
12
+ */
13
+ class AXPPreviewQuestionnaireCommand {
14
+ constructor() {
15
+ //#region ---- Services & Dependencies ----
16
+ this.entityService = inject(AXPEntityService);
17
+ this.questionnaireViewerService = inject(AXMQuestionnaireViewerService);
18
+ this.translationService = inject(AXTranslationService);
19
+ }
20
+ //#endregion
21
+ //#region ---- Command Implementation ----
22
+ /**
23
+ * Resolves questionnaire id/title from root input or from entity action payload (`options` / row `data`).
24
+ */
25
+ resolvePayload(input) {
26
+ const ctxOpts = input.options;
27
+ const ctxData = input.data;
28
+ const asNonEmptyString = (v) => typeof v === 'string' && v.length > 0 ? v : undefined;
29
+ const asDisplayTitle = (v) => {
30
+ const s = asNonEmptyString(v);
31
+ if (s)
32
+ return s;
33
+ if (v && typeof v === 'object') {
34
+ const o = v;
35
+ const fromTitle = o['title'];
36
+ if (typeof fromTitle === 'string' && fromTitle.trim())
37
+ return fromTitle.trim();
38
+ const fromName = o['name'];
39
+ if (typeof fromName === 'string' && fromName.trim())
40
+ return fromName.trim();
41
+ }
42
+ return undefined;
43
+ };
44
+ const questionnaireId = asNonEmptyString(input.questionnaireId) ??
45
+ asNonEmptyString(ctxOpts?.questionnaireId) ??
46
+ (ctxData?.['id'] != null ? String(ctxData['id']) : undefined);
47
+ if (!questionnaireId) {
48
+ return null;
49
+ }
50
+ const questionnaireTitle = asDisplayTitle(input.questionnaireTitle) ??
51
+ asDisplayTitle(ctxOpts?.questionnaireTitle) ??
52
+ asDisplayTitle(ctxData?.['title']) ??
53
+ asDisplayTitle(ctxData?.['name']);
54
+ return { questionnaireId, questionnaireTitle };
55
+ }
56
+ async execute(input) {
57
+ try {
58
+ const resolved = this.resolvePayload(input);
59
+ if (!resolved) {
60
+ return {
61
+ success: false,
62
+ message: {
63
+ text: 'Questionnaire id is missing (could not resolve from command context).',
64
+ },
65
+ };
66
+ }
67
+ const { questionnaireId, questionnaireTitle } = resolved;
68
+ // Get questionnaire data
69
+ const questionnaire = await this.entityService
70
+ .withEntity(RootConfig.module.name, RootConfig.entities.questionnaire.name)
71
+ .data()
72
+ .byKey(questionnaireId);
73
+ if (!questionnaire) {
74
+ return {
75
+ success: false,
76
+ message: {
77
+ text: `Questionnaire with ID ${questionnaireId} not found`,
78
+ },
79
+ };
80
+ }
81
+ // Check if questionnaire has questions
82
+ if (!questionnaire.questions || !questionnaire.questions.sections || questionnaire.questions.sections.length === 0) {
83
+ return {
84
+ success: false,
85
+ message: {
86
+ text: 'Questionnaire has no questions to preview',
87
+ },
88
+ };
89
+ }
90
+ // Get preview title
91
+ const previewTitleKey = '@assessment-management:questionnaires.entities.questionnaire.actions.preview.title';
92
+ const previewTitleBase = (await this.translationService.translateAsync(previewTitleKey)) || 'Preview';
93
+ const locale = this.translationService.getActiveLang?.() ?? 'en-US';
94
+ const entityTitle = resolveMultiLanguageString(questionnaire.title, locale).trim();
95
+ const displayTitle = questionnaireTitle?.trim() || entityTitle || undefined;
96
+ const previewTitle = formatQuestionnairePreviewDialogTitle(previewTitleBase, displayTitle);
97
+ const cfg = toPrePostConfig(questionnaire);
98
+ const displaySettings = toDisplaySettings(questionnaire);
99
+ const questions = questionnaire.questions;
100
+ const structure = {
101
+ sections: questions?.sections ?? [],
102
+ displaySettings: displaySettings ?? undefined,
103
+ printSettings: toPrintSettings(questionnaire) ?? undefined,
104
+ };
105
+ const result = await this.questionnaireViewerService.open({
106
+ title: previewTitle,
107
+ structure,
108
+ prePostConfig: cfg,
109
+ config: {
110
+ previewMode: true,
111
+ completionMode: 'submit-only',
112
+ viewMode: displaySettings?.viewMode,
113
+ showProgressBar: displaySettings?.showProgressBar,
114
+ timer: { showTimer: displaySettings?.showTimer ?? false },
115
+ showQuestionNumbers: displaySettings?.showQuestionNumbers,
116
+ validationStrategy: displaySettings?.validationStrategy,
117
+ },
118
+ questionnaireId,
119
+ printTemplateId: asEntityId(questionnaire.printTemplateId),
120
+ submitHandler: async (answers) => this.questionnaireViewerService.buildSummaryPlaceholderValues(structure, answers, {
121
+ questionnaireId,
122
+ }),
123
+ });
124
+ if (result?.submitted && result.answers) {
125
+ return {
126
+ success: true,
127
+ kind: 'object',
128
+ data: undefined,
129
+ metadata: { outcome: 'Previewed', refresh: false },
130
+ };
131
+ }
132
+ return {
133
+ success: true,
134
+ kind: 'object',
135
+ data: undefined,
136
+ metadata: { outcome: 'Cancelled', refresh: false },
137
+ };
138
+ }
139
+ catch (error) {
140
+ return {
141
+ success: false,
142
+ message: {
143
+ text: `Error previewing questionnaire: ${error.message || 'Unknown error'}`,
144
+ },
145
+ };
146
+ }
147
+ }
148
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPreviewQuestionnaireCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
149
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPreviewQuestionnaireCommand }); }
150
+ }
151
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPPreviewQuestionnaireCommand, decorators: [{
152
+ type: Injectable
153
+ }] });
154
+ function asEntityId(value) {
155
+ if (typeof value === 'string' && value.trim()) {
156
+ return value.trim();
157
+ }
158
+ if (value && typeof value === 'object' && 'id' in value) {
159
+ return asEntityId(value.id);
160
+ }
161
+ return undefined;
162
+ }
163
+
164
+ export { AXPPreviewQuestionnaireCommand };
165
+ //# sourceMappingURL=acorex-platform-module-assessment-management-client-preview-questionnaire.command-DBm7ve4w.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-module-assessment-management-client-preview-questionnaire.command-DBm7ve4w.mjs","sources":["../../../../libs/modules/assessment-management/client/src/lib/features/questionnaire-viewer/commands/preview-questionnaire.command.ts"],"sourcesContent":["//#region ---- Imports ----\n\nimport { AXPCommand, type AXPCommandExecuteResultPromise } from '@acorex-platform/framework-shared/runtime';\nimport { AXPRuntimeCommandResult } from '@acorex-platform/framework-shared/core';\n\nimport { AXTranslationService, resolveMultiLanguageString } from '@acorex/core/translation';\nimport { Injectable, inject } from '@angular/core';\nimport { AXPEntityService } from '@acorex-platform/framework-client/layout/entity';\nimport { AXMQuestionnaireViewerService } from '../../../features/questionnaire-viewer/questionnaire-viewer.service';\nimport {\n type AXMAssessmentManagementQuestionnaireEntityModel,\n toDisplaySettings,\n toPrePostConfig,\n toPrintSettings,\n} from '@acorex-platform/module-assessment-management-client/contracts';\nimport { RootConfig } from '@acorex-platform/module-assessment-management-client/contracts';\nimport { formatQuestionnairePreviewDialogTitle } from '../utils/questionnaire-preview-title.util';\n\n//#endregion\n\n//#region ---- Command Input/Output Types ----\n\n/**\n * Input for PreviewQuestionnaire command.\n */\nexport interface AXPPreviewQuestionnaireCommandInput {\n /**\n * Questionnaire ID to preview.\n */\n questionnaireId: string;\n\n /**\n * Questionnaire title (for display).\n */\n questionnaireTitle?: string;\n}\n\n/** Entity list passes evaluated action options on `options` (see entity-master-list view model). */\ntype AXPPreviewQuestionnaireCommandInvocation = AXPPreviewQuestionnaireCommandInput & {\n data?: Record<string, unknown>;\n options?: Partial<AXPPreviewQuestionnaireCommandInput> & Record<string, unknown>;\n};\n\n//#endregion\n\n//#region ---- Preview Questionnaire Command ----\n\n/**\n * Command that opens a preview of a questionnaire in a dialog.\n */\n@Injectable()\nexport class AXPPreviewQuestionnaireCommand implements AXPCommand<AXPPreviewQuestionnaireCommandInput, void> {\n //#region ---- Services & Dependencies ----\n\n private readonly entityService = inject(AXPEntityService);\n private readonly questionnaireViewerService = inject(AXMQuestionnaireViewerService);\n private readonly translationService = inject(AXTranslationService);\n\n //#endregion\n\n //#region ---- Command Implementation ----\n\n /**\n * Resolves questionnaire id/title from root input or from entity action payload (`options` / row `data`).\n */\n private resolvePayload(input: AXPPreviewQuestionnaireCommandInvocation): AXPPreviewQuestionnaireCommandInput | null {\n const ctxOpts = input.options;\n const ctxData = input.data;\n\n const asNonEmptyString = (v: unknown): string | undefined =>\n typeof v === 'string' && v.length > 0 ? v : undefined;\n\n const asDisplayTitle = (v: unknown): string | undefined => {\n const s = asNonEmptyString(v);\n if (s) return s;\n if (v && typeof v === 'object') {\n const o = v as Record<string, unknown>;\n const fromTitle = o['title'];\n if (typeof fromTitle === 'string' && fromTitle.trim()) return fromTitle.trim();\n const fromName = o['name'];\n if (typeof fromName === 'string' && fromName.trim()) return fromName.trim();\n }\n return undefined;\n };\n\n const questionnaireId =\n asNonEmptyString(input.questionnaireId) ??\n asNonEmptyString(ctxOpts?.questionnaireId) ??\n (ctxData?.['id'] != null ? String(ctxData['id']) : undefined);\n\n if (!questionnaireId) {\n return null;\n }\n\n const questionnaireTitle =\n asDisplayTitle(input.questionnaireTitle) ??\n asDisplayTitle(ctxOpts?.questionnaireTitle) ??\n asDisplayTitle(ctxData?.['title']) ??\n asDisplayTitle(ctxData?.['name']);\n\n return { questionnaireId, questionnaireTitle };\n }\n\n async execute(input: AXPPreviewQuestionnaireCommandInvocation): AXPCommandExecuteResultPromise<void> {\n try {\n const resolved = this.resolvePayload(input);\n if (!resolved) {\n return {\n success: false,\n message: {\n text: 'Questionnaire id is missing (could not resolve from command context).',\n },\n };\n }\n\n const { questionnaireId, questionnaireTitle } = resolved;\n\n // Get questionnaire data\n const questionnaire = await this.entityService\n .withEntity(RootConfig.module.name, RootConfig.entities.questionnaire.name)\n .data<string, AXMAssessmentManagementQuestionnaireEntityModel>()\n .byKey(questionnaireId);\n\n if (!questionnaire) {\n return {\n success: false,\n message: {\n text: `Questionnaire with ID ${questionnaireId} not found`,\n },\n };\n }\n\n // Check if questionnaire has questions\n if (!questionnaire.questions || !questionnaire.questions.sections || questionnaire.questions.sections.length === 0) {\n return {\n success: false,\n message: {\n text: 'Questionnaire has no questions to preview',\n },\n };\n }\n\n // Get preview title\n const previewTitleKey = '@assessment-management:questionnaires.entities.questionnaire.actions.preview.title';\n const previewTitleBase = (await this.translationService.translateAsync(previewTitleKey)) || 'Preview';\n const locale = this.translationService.getActiveLang?.() ?? 'en-US';\n const entityTitle = resolveMultiLanguageString(questionnaire.title, locale).trim();\n const displayTitle = questionnaireTitle?.trim() || entityTitle || undefined;\n const previewTitle = formatQuestionnairePreviewDialogTitle(previewTitleBase, displayTitle);\n\n const cfg = toPrePostConfig(questionnaire);\n const displaySettings = toDisplaySettings(questionnaire);\n const questions = questionnaire.questions;\n const structure = {\n sections: questions?.sections ?? [],\n displaySettings: displaySettings ?? undefined,\n printSettings: toPrintSettings(questionnaire) ?? undefined,\n };\n const result = await this.questionnaireViewerService.open({\n title: previewTitle,\n structure,\n prePostConfig: cfg,\n config: {\n previewMode: true,\n completionMode: 'submit-only',\n viewMode: displaySettings?.viewMode,\n showProgressBar: displaySettings?.showProgressBar,\n timer: { showTimer: displaySettings?.showTimer ?? false },\n showQuestionNumbers: displaySettings?.showQuestionNumbers,\n validationStrategy: displaySettings?.validationStrategy,\n },\n questionnaireId,\n printTemplateId: asEntityId(questionnaire.printTemplateId),\n submitHandler: async (answers: Record<string, any>) =>\n this.questionnaireViewerService.buildSummaryPlaceholderValues(structure, answers, {\n questionnaireId,\n }),\n });\n\n if (result?.submitted && result.answers) {\n return {\n success: true,\n kind: 'object',\n data: undefined,\n metadata: { outcome: 'Previewed', refresh: false },\n };\n }\n return {\n success: true,\n kind: 'object',\n data: undefined,\n metadata: { outcome: 'Cancelled', refresh: false },\n };\n } catch (error: any) {\n return {\n success: false,\n message: {\n text: `Error previewing questionnaire: ${error.message || 'Unknown error'}`,\n },\n };\n }\n }\n\n //#endregion\n}\n\nfunction asEntityId(value: unknown): string | undefined {\n if (typeof value === 'string' && value.trim()) {\n return value.trim();\n }\n if (value && typeof value === 'object' && 'id' in value) {\n return asEntityId((value as { id: unknown }).id);\n }\n return undefined;\n}\n\n//#endregion\n\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,178 @@
1
+ import { runtimeCommandPayloadData } from '@acorex-platform/framework-shared/core';
2
+ import { AXTranslationService } from '@acorex/core/translation';
3
+ import * as i0 from '@angular/core';
4
+ import { inject, Injectable } from '@angular/core';
5
+ import { get, isString, isEmpty, toString } from 'lodash';
6
+ import { AXPCommandService } from '@acorex-platform/framework-client/runtime';
7
+ import { AXM_ASSESSMENT_SESSION_PREVIEW_PDF_COMMAND_KEY } from '@acorex-platform/module-assessment-management-client/contracts';
8
+
9
+ //#endregion
10
+ //#region ---- Preview session PDF command ----
11
+ class PreviewSessionPdfCommand {
12
+ constructor() {
13
+ this.commandService = inject(AXPCommandService);
14
+ this.translationService = inject(AXTranslationService);
15
+ }
16
+ async execute(input) {
17
+ const previewWindow = input.previewWindow ?? this.openPreviewWindow();
18
+ this.writePreviewPlaceholder(previewWindow);
19
+ try {
20
+ const printHtmlGzip = this.asNonEmptyString(input.printHtmlGzip);
21
+ const sessionId = this.resolveSessionId(input);
22
+ const caseId = this.resolveCaseId(input);
23
+ const questionnaireId = this.asEntityId(input.questionnaireId);
24
+ const printTemplateId = this.asEntityId(input.printTemplateId);
25
+ if (!printHtmlGzip && !sessionId && !caseId) {
26
+ this.closePreviewWindow(previewWindow);
27
+ return {
28
+ success: false,
29
+ message: { text: 'Assessment case ID, session ID, or print HTML is required' },
30
+ };
31
+ }
32
+ const locale = this.asNonEmptyString(input.locale) ??
33
+ this.asNonEmptyString(this.translationService.getActiveLang()) ??
34
+ 'en-US';
35
+ const documentTitle = this.asNonEmptyString(input.title);
36
+ const result = await this.commandService.executeRemote(AXM_ASSESSMENT_SESSION_PREVIEW_PDF_COMMAND_KEY, {
37
+ ...(printHtmlGzip ? { printHtmlGzip } : {}),
38
+ ...(documentTitle ? { title: documentTitle } : {}),
39
+ ...(sessionId ? { sessionId } : {}),
40
+ ...(caseId ? { caseId } : {}),
41
+ ...(questionnaireId ? { questionnaireId } : {}),
42
+ ...(printTemplateId ? { printTemplateId } : {}),
43
+ ...(this.asNonEmptyString(input.paperSize) ? { paperSize: input.paperSize } : {}),
44
+ ...(this.asNonEmptyString(input.orientation) ? { orientation: input.orientation } : {}),
45
+ ...(input.watermark != null ? { watermark: input.watermark } : {}),
46
+ locale,
47
+ });
48
+ const pdf = runtimeCommandPayloadData(result);
49
+ if (!pdf?.base64) {
50
+ this.closePreviewWindow(previewWindow);
51
+ const message = (typeof result?.message?.text === 'string' && result.message.text.trim()) ||
52
+ (await this.translationService.translateAsync('@assessment-management:sessions.actions.preview-pdf.failed')) ||
53
+ 'Failed to preview assessment session PDF.';
54
+ return { success: false, message: { text: message } };
55
+ }
56
+ this.showPdfInBrowser(previewWindow, pdf.base64, pdf.filename || 'questionnaire.pdf');
57
+ return { success: true, kind: 'object', data: undefined };
58
+ }
59
+ catch (error) {
60
+ this.closePreviewWindow(previewWindow);
61
+ return {
62
+ success: false,
63
+ message: {
64
+ text: error instanceof Error ? error.message : 'Failed to preview assessment session PDF.',
65
+ },
66
+ };
67
+ }
68
+ }
69
+ openPreviewWindow() {
70
+ if (typeof window === 'undefined') {
71
+ return null;
72
+ }
73
+ const preview = window.open('about:blank', '_blank');
74
+ this.writePreviewPlaceholder(preview);
75
+ window.focus();
76
+ return preview;
77
+ }
78
+ writePreviewPlaceholder(preview) {
79
+ if (!preview || preview.closed) {
80
+ return;
81
+ }
82
+ try {
83
+ preview.document.title = 'Preview PDF';
84
+ preview.document.body.innerHTML =
85
+ '<p style="font-family:sans-serif;padding:24px;color:#64748b">Generating PDF…</p>';
86
+ }
87
+ catch {
88
+ // about:blank can be briefly cross-origin in some browsers
89
+ }
90
+ }
91
+ showPdfInBrowser(preview, base64, filename) {
92
+ const bytes = Uint8Array.from(atob(base64), (char) => char.charCodeAt(0));
93
+ const blob = new Blob([bytes], { type: 'application/pdf' });
94
+ const url = URL.createObjectURL(blob);
95
+ if (preview && !preview.closed) {
96
+ preview.location.replace(url);
97
+ try {
98
+ preview.focus();
99
+ }
100
+ catch {
101
+ // some browsers block focus from a backgrounded opener
102
+ }
103
+ window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
104
+ return;
105
+ }
106
+ const opened = window.open(url, '_blank', 'noopener');
107
+ if (opened) {
108
+ window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
109
+ return;
110
+ }
111
+ const link = document.createElement('a');
112
+ link.href = url;
113
+ link.download = filename;
114
+ link.rel = 'noopener';
115
+ link.click();
116
+ window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
117
+ }
118
+ closePreviewWindow(preview) {
119
+ if (preview && !preview.closed) {
120
+ preview.close();
121
+ }
122
+ }
123
+ resolveSessionId(input) {
124
+ const candidates = [get(input, 'sessionId'), get(input, 'options.sessionId')];
125
+ for (const candidate of candidates) {
126
+ const resolved = this.asNonEmptyString(candidate);
127
+ if (resolved) {
128
+ return resolved;
129
+ }
130
+ }
131
+ const row = get(input, 'data');
132
+ if (row && this.asNonEmptyString(get(row, 'caseId'))) {
133
+ return this.asNonEmptyString(get(row, 'id'));
134
+ }
135
+ return undefined;
136
+ }
137
+ resolveCaseId(input) {
138
+ const candidates = [get(input, 'caseId'), get(input, 'options.caseId')];
139
+ for (const candidate of candidates) {
140
+ const resolved = this.asNonEmptyString(candidate);
141
+ if (resolved) {
142
+ return resolved;
143
+ }
144
+ }
145
+ const row = get(input, 'data');
146
+ if (row && !this.asNonEmptyString(get(row, 'caseId'))) {
147
+ return this.asNonEmptyString(get(row, 'id'));
148
+ }
149
+ return undefined;
150
+ }
151
+ asEntityId(value) {
152
+ if (isString(value) && !isEmpty(value.trim())) {
153
+ return value.trim();
154
+ }
155
+ if (value && typeof value === 'object' && 'id' in value) {
156
+ return this.asEntityId(value.id);
157
+ }
158
+ return undefined;
159
+ }
160
+ asNonEmptyString(value) {
161
+ if (isString(value) && !isEmpty(value)) {
162
+ return value;
163
+ }
164
+ if (value != null && !isString(value)) {
165
+ const normalized = toString(value).trim();
166
+ return isEmpty(normalized) ? undefined : normalized;
167
+ }
168
+ return undefined;
169
+ }
170
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PreviewSessionPdfCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
171
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PreviewSessionPdfCommand }); }
172
+ }
173
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: PreviewSessionPdfCommand, decorators: [{
174
+ type: Injectable
175
+ }] });
176
+
177
+ export { PreviewSessionPdfCommand };
178
+ //# sourceMappingURL=acorex-platform-module-assessment-management-client-preview-session-pdf.command-BsiJU6ep.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-platform-module-assessment-management-client-preview-session-pdf.command-BsiJU6ep.mjs","sources":["../../../../libs/modules/assessment-management/client/src/lib/features/questionnaire-viewer/commands/preview-session-pdf.command.ts"],"sourcesContent":["//#region ---- Imports ----\n\nimport { AXPCommand, type AXPCommandExecuteResultPromise } from '@acorex-platform/framework-shared/runtime';\nimport { runtimeCommandPayloadData } from '@acorex-platform/framework-shared/core';\nimport { AXTranslationService } from '@acorex/core/translation';\nimport { Injectable, inject } from '@angular/core';\nimport { get, isEmpty, isString, toString } from 'lodash';\nimport { AXPCommandService } from '@acorex-platform/framework-client/runtime';\nimport {\n AXM_ASSESSMENT_SESSION_PREVIEW_PDF_COMMAND_KEY,\n type AXMAssessmentSessionPreviewPdfRequest,\n type AXMAssessmentSessionPreviewPdfResult,\n} from '@acorex-platform/module-assessment-management-client/contracts';\n\n//#endregion\n\n//#region ---- Types ----\n\nexport interface PreviewSessionPdfCommandInput {\n caseId?: string;\n sessionId?: string;\n questionnaireId?: string;\n locale?: string;\n printHtmlGzip?: string;\n title?: string;\n printTemplateId?: string;\n paperSize?: 'A4' | 'Letter' | 'Legal';\n orientation?: 'portrait' | 'landscape';\n watermark?: string;\n}\n\ntype PreviewSessionPdfCommandInvocation = PreviewSessionPdfCommandInput & {\n data?: Record<string, unknown>;\n options?: Partial<PreviewSessionPdfCommandInput>;\n /** Reserved tab from the click handler so popup blockers do not hide the PDF. */\n previewWindow?: Window | null;\n};\n\n//#endregion\n\n//#region ---- Preview session PDF command ----\n\n@Injectable()\nexport class PreviewSessionPdfCommand implements AXPCommand<PreviewSessionPdfCommandInput, void> {\n private readonly commandService = inject(AXPCommandService);\n private readonly translationService = inject(AXTranslationService);\n\n async execute(input: PreviewSessionPdfCommandInvocation): AXPCommandExecuteResultPromise<void> {\n const previewWindow = input.previewWindow ?? this.openPreviewWindow();\n this.writePreviewPlaceholder(previewWindow);\n try {\n const printHtmlGzip = this.asNonEmptyString(input.printHtmlGzip);\n const sessionId = this.resolveSessionId(input);\n const caseId = this.resolveCaseId(input);\n const questionnaireId = this.asEntityId(input.questionnaireId);\n const printTemplateId = this.asEntityId(input.printTemplateId);\n if (!printHtmlGzip && !sessionId && !caseId) {\n this.closePreviewWindow(previewWindow);\n return {\n success: false,\n message: { text: 'Assessment case ID, session ID, or print HTML is required' },\n };\n }\n\n const locale =\n this.asNonEmptyString(input.locale) ??\n this.asNonEmptyString(this.translationService.getActiveLang()) ??\n 'en-US';\n const documentTitle = this.asNonEmptyString(input.title);\n\n const result = await this.commandService.executeRemote<\n AXMAssessmentSessionPreviewPdfRequest,\n AXMAssessmentSessionPreviewPdfResult\n >(AXM_ASSESSMENT_SESSION_PREVIEW_PDF_COMMAND_KEY, {\n ...(printHtmlGzip ? { printHtmlGzip } : {}),\n ...(documentTitle ? { title: documentTitle } : {}),\n ...(sessionId ? { sessionId } : {}),\n ...(caseId ? { caseId } : {}),\n ...(questionnaireId ? { questionnaireId } : {}),\n ...(printTemplateId ? { printTemplateId } : {}),\n ...(this.asNonEmptyString(input.paperSize) ? { paperSize: input.paperSize } : {}),\n ...(this.asNonEmptyString(input.orientation) ? { orientation: input.orientation } : {}),\n ...(input.watermark != null ? { watermark: input.watermark } : {}),\n locale,\n });\n\n const pdf = runtimeCommandPayloadData(result);\n if (!pdf?.base64) {\n this.closePreviewWindow(previewWindow);\n const message =\n (typeof result?.message?.text === 'string' && result.message.text.trim()) ||\n (await this.translationService.translateAsync(\n '@assessment-management:sessions.actions.preview-pdf.failed',\n )) ||\n 'Failed to preview assessment session PDF.';\n return { success: false, message: { text: message } };\n }\n\n this.showPdfInBrowser(previewWindow, pdf.base64, pdf.filename || 'questionnaire.pdf');\n return { success: true, kind: 'object', data: undefined };\n } catch (error: unknown) {\n this.closePreviewWindow(previewWindow);\n return {\n success: false,\n message: {\n text: error instanceof Error ? error.message : 'Failed to preview assessment session PDF.',\n },\n };\n }\n }\n\n private openPreviewWindow(): Window | null {\n if (typeof window === 'undefined') {\n return null;\n }\n const preview = window.open('about:blank', '_blank');\n this.writePreviewPlaceholder(preview);\n window.focus();\n return preview;\n }\n\n private writePreviewPlaceholder(preview: Window | null): void {\n if (!preview || preview.closed) {\n return;\n }\n try {\n preview.document.title = 'Preview PDF';\n preview.document.body.innerHTML =\n '<p style=\"font-family:sans-serif;padding:24px;color:#64748b\">Generating PDF…</p>';\n } catch {\n // about:blank can be briefly cross-origin in some browsers\n }\n }\n\n private showPdfInBrowser(preview: Window | null, base64: string, filename: string): void {\n const bytes = Uint8Array.from(atob(base64), (char) => char.charCodeAt(0));\n const blob = new Blob([bytes], { type: 'application/pdf' });\n const url = URL.createObjectURL(blob);\n if (preview && !preview.closed) {\n preview.location.replace(url);\n try {\n preview.focus();\n } catch {\n // some browsers block focus from a backgrounded opener\n }\n window.setTimeout(() => URL.revokeObjectURL(url), 60_000);\n return;\n }\n const opened = window.open(url, '_blank', 'noopener');\n if (opened) {\n window.setTimeout(() => URL.revokeObjectURL(url), 60_000);\n return;\n }\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n link.rel = 'noopener';\n link.click();\n window.setTimeout(() => URL.revokeObjectURL(url), 60_000);\n }\n\n private closePreviewWindow(preview: Window | null): void {\n if (preview && !preview.closed) {\n preview.close();\n }\n }\n\n private resolveSessionId(input: PreviewSessionPdfCommandInvocation): string | undefined {\n const candidates = [get(input, 'sessionId'), get(input, 'options.sessionId')];\n for (const candidate of candidates) {\n const resolved = this.asNonEmptyString(candidate);\n if (resolved) {\n return resolved;\n }\n }\n const row = get(input, 'data') as Record<string, unknown> | undefined;\n if (row && this.asNonEmptyString(get(row, 'caseId'))) {\n return this.asNonEmptyString(get(row, 'id'));\n }\n return undefined;\n }\n\n private resolveCaseId(input: PreviewSessionPdfCommandInvocation): string | undefined {\n const candidates = [get(input, 'caseId'), get(input, 'options.caseId')];\n for (const candidate of candidates) {\n const resolved = this.asNonEmptyString(candidate);\n if (resolved) {\n return resolved;\n }\n }\n const row = get(input, 'data') as Record<string, unknown> | undefined;\n if (row && !this.asNonEmptyString(get(row, 'caseId'))) {\n return this.asNonEmptyString(get(row, 'id'));\n }\n return undefined;\n }\n\n private asEntityId(value: unknown): string | undefined {\n if (isString(value) && !isEmpty(value.trim())) {\n return value.trim();\n }\n if (value && typeof value === 'object' && 'id' in value) {\n return this.asEntityId((value as { id: unknown }).id);\n }\n return undefined;\n }\n\n private asNonEmptyString(value: unknown): string | undefined {\n if (isString(value) && !isEmpty(value)) {\n return value;\n }\n if (value != null && !isString(value)) {\n const normalized = toString(value).trim();\n return isEmpty(normalized) ? undefined : normalized;\n }\n return undefined;\n }\n}\n\n//#endregion\n"],"names":[],"mappings":"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"}