@acorex-platform/module-report-management-client 0.0.30 → 0.0.33

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 (48) hide show
  1. package/fesm2022/{acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-BDCuhd5O.mjs → acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-DENcGULA.mjs} +1018 -386
  2. package/fesm2022/acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-DENcGULA.mjs.map +1 -0
  3. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-query-snapshot-policy.mjs +143 -0
  4. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-query-snapshot-policy.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-query-snapshot.mjs +183 -0
  6. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-query-snapshot.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report-category.mjs +129 -0
  8. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report-category.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report-generation-request.mjs +260 -0
  10. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report-generation-request.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report.mjs +170 -0
  12. package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-acorex-platform-module-report-management-client-contracts-entity-catalog-CgrfJQLl.mjs +83 -0
  14. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-acorex-platform-module-report-management-client-contracts-entity-catalog-CgrfJQLl.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot-policy.entity-D50GUBH6.mjs +83 -0
  16. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot-policy.entity-D50GUBH6.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot.entity-DuR9ZuN2.mjs +123 -0
  18. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot.entity-DuR9ZuN2.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report-category.entity-Dc4uvjsG.mjs +69 -0
  20. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report-category.entity-Dc4uvjsG.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report-generation-request.entity-mJUQuk89.mjs +200 -0
  22. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report-generation-request.entity-mJUQuk89.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report.entity-CMDFRHeK.mjs +110 -0
  24. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report.entity-CMDFRHeK.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog.mjs +2 -0
  26. package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-module-report-management-client-contracts.mjs +23 -1
  28. package/fesm2022/acorex-platform-module-report-management-client-contracts.mjs.map +1 -1
  29. package/fesm2022/{acorex-platform-module-report-management-client-entity-reports-page-component.provider-C6tJPxri.mjs → acorex-platform-module-report-management-client-entity-reports-page-component.provider-B_i4p7tQ.mjs} +3 -3
  30. package/fesm2022/{acorex-platform-module-report-management-client-entity-reports-page-component.provider-C6tJPxri.mjs.map → acorex-platform-module-report-management-client-entity-reports-page-component.provider-B_i4p7tQ.mjs.map} +1 -1
  31. package/fesm2022/{acorex-platform-module-report-management-client-entity-reports-page.component-fBftKdQW.mjs → acorex-platform-module-report-management-client-entity-reports-page.component-D27Gtma6.mjs} +2 -2
  32. package/fesm2022/{acorex-platform-module-report-management-client-entity-reports-page.component-fBftKdQW.mjs.map → acorex-platform-module-report-management-client-entity-reports-page.component-D27Gtma6.mjs.map} +1 -1
  33. package/fesm2022/acorex-platform-module-report-management-client-local-entities.mjs +33 -0
  34. package/fesm2022/acorex-platform-module-report-management-client-local-entities.mjs.map +1 -0
  35. package/fesm2022/{acorex-platform-module-report-management-client-report-popup-viewer.component-B9msMMod.mjs → acorex-platform-module-report-management-client-report-popup-viewer.component-B6aTfRp0.mjs} +2 -2
  36. package/fesm2022/{acorex-platform-module-report-management-client-report-popup-viewer.component-B9msMMod.mjs.map → acorex-platform-module-report-management-client-report-popup-viewer.component-B6aTfRp0.mjs.map} +1 -1
  37. package/fesm2022/acorex-platform-module-report-management-client.mjs +1 -1
  38. package/package.json +29 -1
  39. package/types/acorex-platform-module-report-management-client-contracts-entities-query-snapshot-policy.d.ts +5 -0
  40. package/types/acorex-platform-module-report-management-client-contracts-entities-query-snapshot.d.ts +5 -0
  41. package/types/acorex-platform-module-report-management-client-contracts-entities-report-category.d.ts +5 -0
  42. package/types/acorex-platform-module-report-management-client-contracts-entities-report-generation-request.d.ts +5 -0
  43. package/types/acorex-platform-module-report-management-client-contracts-entities-report.d.ts +5 -0
  44. package/types/acorex-platform-module-report-management-client-contracts-entity-catalog.d.ts +10 -0
  45. package/types/acorex-platform-module-report-management-client-contracts.d.ts +99 -58
  46. package/types/acorex-platform-module-report-management-client-local-entities.d.ts +12 -0
  47. package/types/acorex-platform-module-report-management-client.d.ts +46 -4
  48. package/fesm2022/acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-BDCuhd5O.mjs.map +0 -1
@@ -1,21 +1,24 @@
1
- import { AXPExpressionEvaluatorService, AXP_MODULE_MANIFEST_PROVIDER, AXP_FEATURE_DEFINITION_PROVIDER, provideLazyProvider, applySystemActionDefault, AXPDeviceService } from '@acorex-platform/framework-client/core';
2
- import { RootConfig, AXPInsightsReportingPermissionKeys, AXPReportManagementMenuKeys, AXPReportManagementFeatureKeys, AXMReportManagementQueryKeys, AXMReportManagementCommandKeys } from '@acorex-platform/module-report-management-client/contracts';
1
+ import { AXPExpressionEvaluatorService, provideAppStartUpTask, AXP_APP_STARTUP_PRIORITY, AXP_MODULE_MANIFEST_PROVIDER, AXP_FEATURE_DEFINITION_PROVIDER, provideLazyProvider, applySystemActionDefault, AXPDeviceService } from '@acorex-platform/framework-client/core';
2
+ import { RootConfig, AXPInsightsReportingPermissionKeys, AXPReportManagementMenuKeys, AXPReportManagementFeatureKeys, AXPReportManagementSettings, AXP_REPORT_MANAGEMENT_GENERATION_SETTING_DEFAULTS, buildReportGenerationNotificationCorrelationId, AXMReportManagementQueryKeys, REPORT_GENERATION_NOTIFICATION_DEFINITION_KEY, AXMReportManagementCommandKeys } from '@acorex-platform/module-report-management-client/contracts';
3
3
  import { AXPDomainModule } from '@acorex-platform/framework-client/domain';
4
4
  import { AXPEntityService, resolveEntityPluginDetailPageOrder, AXP_ENTITY_ACTION_PLUGIN, provideEntity } from '@acorex-platform/framework-client/layout/entity';
5
5
  import { AXP_PAGE_COMPONENT_PROVIDER, AXPStateMessageComponent, AXPTextCommandColumnComponent, AXPThemeLayoutStartSideComponent, AXPThemeLayoutHeaderComponent, AXPThemeLayoutBlockComponent, AXPThemeLayoutFooterComponent } from '@acorex-platform/framework-client/layout/components';
6
6
  import { AXPRootLayoutComponent } from '@acorex-platform/framework-client/themes/default';
7
7
  import * as i0 from '@angular/core';
8
- import { Injectable, inject, Injector, NgModule, computed, effect, untracked, signal, ChangeDetectionStrategy, Component, ViewEncapsulation, viewChild, DestroyRef, input } from '@angular/core';
9
- import { ROUTES, ActivatedRoute } from '@angular/router';
10
- import { AXPTabNavigationService, AXPBackgroundOperationHandler, AXP_BACKGROUND_OPERATION_HANDLERS, AXP_PROTECTED_ROUTE_GUARDS, AXP_MENU_PROVIDER, AXPSettingsService, commandMessageTextForError, AXPCommonSettings } from '@acorex-platform/framework-client/common';
8
+ import { Injectable, inject, DestroyRef, Injector, effect, NgModule, computed, untracked, signal, ChangeDetectionStrategy, Component, ViewEncapsulation, viewChild, input } from '@angular/core';
9
+ import { ROUTES, ActivatedRoute, NavigationStart } from '@angular/router';
10
+ import { AXPTabNavigationService, AXPBackgroundOperationHandler, AXP_BACKGROUND_OPERATION_HANDLERS, AXP_USER_NOTIFICATION_SERVICE, AXP_PROTECTED_ROUTE_GUARDS, AXP_MENU_PROVIDER, AXP_SETTING_DEFINITION_PROVIDER, AXPSettingsService, commandMessageTextForError, AXPCommonSettings } from '@acorex-platform/framework-client/common';
11
11
  import { AXPSessionService, AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex-platform/framework-client/auth';
12
- import { AXPQueryService, AXPRuntimeModule, provideCommandSetups, AXPCommandService } from '@acorex-platform/framework-client/runtime';
12
+ import { AXPQueryService, AXPCommandService, AXPRuntimeModule, provideCommandSetups } from '@acorex-platform/framework-client/runtime';
13
13
  import { AXPCommonPermissionGroupKeys } from '@acorex-platform/module-common-client/contracts';
14
+ import { AXPWidgetsList, runtimeQueryPayloadData, resolveMultiLanguageString, runtimeQueryObjectData, runtimeQueryCollectionData, AXPPlatformScope, runtimeCommandPayloadData, getSystemActions } from '@acorex-platform/framework-shared/core';
14
15
  import { get, isString } from 'lodash-es';
15
- import { AXPopupService } from '@acorex/components/popup';
16
- import { resolveMultiLanguageString, runtimeQueryPayloadData, runtimeQueryObjectData, runtimeQueryCollectionData, AXPPlatformScope, AXPWidgetsList, getSystemActions } from '@acorex-platform/framework-shared/core';
17
16
  import * as i1 from '@acorex/core/translation';
18
17
  import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
18
+ import { AXPInAppInboxConnectorService } from '@acorex-platform/module-communication-management-client';
19
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
20
+ import { map, distinctUntilChanged, Subscription, combineLatest } from 'rxjs';
21
+ import { AXPopupService } from '@acorex/components/popup';
19
22
  import { AXBadgeModule } from '@acorex/components/badge';
20
23
  import * as i1$2 from '@acorex/components/button';
21
24
  import { AXButtonModule } from '@acorex/components/button';
@@ -33,12 +36,11 @@ import * as i2 from '@angular/common';
33
36
  import { CommonModule } from '@angular/common';
34
37
  import * as i2$2 from '@angular/forms';
35
38
  import { FormsModule } from '@angular/forms';
36
- import { Subscription, combineLatest } from 'rxjs';
39
+ import { filter } from 'rxjs/operators';
37
40
  import { AXPPageStatus } from '@acorex-platform/framework-shared/widget-core';
38
41
  import { AXLocaleService } from '@acorex/core/locale';
39
42
  import * as i2$1 from '@acorex-platform/framework-client/layout/widget-core';
40
43
  import { AXPWidgetRegistryService, AXPWidgetCoreModule } from '@acorex-platform/framework-client/layout/widget-core';
41
- import { AXToastService } from '@acorex/components/toast';
42
44
  import { signalStore, withState, withComputed, withMethods, patchState, withHooks } from '@ngrx/signals';
43
45
  import { set } from 'lodash';
44
46
  import { AXBarChartComponent } from '@acorex/charts/bar-chart';
@@ -163,6 +165,79 @@ class AXPReportManagementFeatureDefinitionProvider {
163
165
  }
164
166
  //#endregion
165
167
 
168
+ //#region ----- Report management settings -----
169
+ const DEDUPE_POLICY_ITEMS = [
170
+ {
171
+ value: 'none',
172
+ text: '@report-management:settings.generation.items.dedupe-policy.options.none',
173
+ },
174
+ {
175
+ value: 'in-flight',
176
+ text: '@report-management:settings.generation.items.dedupe-policy.options.in-flight',
177
+ },
178
+ {
179
+ value: 'in-flight-or-cooldown',
180
+ text: '@report-management:settings.generation.items.dedupe-policy.options.in-flight-or-cooldown',
181
+ },
182
+ ];
183
+ class AXMReportManagementSettingProvider {
184
+ async provide(context) {
185
+ const cooldownPolicy = `{{ context.eval('${AXPReportManagementSettings.GenerationDedupePolicy}') === 'in-flight-or-cooldown' }}`;
186
+ context
187
+ .addGroup('report-management', '@report-management:settings.group.title', '@report-management:settings.group.description', 'fa-light fa-chart-line', undefined, 840)
188
+ .addSection('generation', '@report-management:settings.generation.title', '@report-management:settings.generation.description')
189
+ .addSetting({
190
+ key: AXPReportManagementSettings.GenerationDedupePolicy,
191
+ title: '@report-management:settings.generation.items.dedupe-policy.title',
192
+ description: '@report-management:settings.generation.items.dedupe-policy.description',
193
+ scope: 'P',
194
+ overridableAt: [],
195
+ isInherited: false,
196
+ defaultValue: AXP_REPORT_MANAGEMENT_GENERATION_SETTING_DEFAULTS[AXPReportManagementSettings.GenerationDedupePolicy],
197
+ widget: {
198
+ type: AXPWidgetsList.Editors.SelectBox,
199
+ options: {
200
+ valueField: 'value',
201
+ textField: 'text',
202
+ dataSource: DEDUPE_POLICY_ITEMS,
203
+ },
204
+ },
205
+ validationRules: [{ rule: 'required' }],
206
+ })
207
+ .addSetting({
208
+ key: AXPReportManagementSettings.GenerationDedupeCooldownMinutes,
209
+ title: '@report-management:settings.generation.items.dedupe-cooldown-minutes.title',
210
+ description: '@report-management:settings.generation.items.dedupe-cooldown-minutes.description',
211
+ scope: 'P',
212
+ overridableAt: [],
213
+ isInherited: false,
214
+ defaultValue: AXP_REPORT_MANAGEMENT_GENERATION_SETTING_DEFAULTS[AXPReportManagementSettings.GenerationDedupeCooldownMinutes],
215
+ widget: {
216
+ type: AXPWidgetsList.Editors.NumberBox,
217
+ options: { minValue: 0, maxValue: 1440, visibility: cooldownPolicy },
218
+ },
219
+ validationRules: [{ rule: 'required' }],
220
+ })
221
+ .addSetting({
222
+ key: AXPReportManagementSettings.GenerationMaxConcurrentPerUser,
223
+ title: '@report-management:settings.generation.items.max-concurrent-per-user.title',
224
+ description: '@report-management:settings.generation.items.max-concurrent-per-user.description',
225
+ scope: 'P',
226
+ overridableAt: [],
227
+ isInherited: false,
228
+ defaultValue: AXP_REPORT_MANAGEMENT_GENERATION_SETTING_DEFAULTS[AXPReportManagementSettings.GenerationMaxConcurrentPerUser],
229
+ widget: {
230
+ type: AXPWidgetsList.Editors.NumberBox,
231
+ options: { minValue: 0, maxValue: 50 },
232
+ },
233
+ validationRules: [{ rule: 'required' }],
234
+ })
235
+ .endSection()
236
+ .endGroup();
237
+ }
238
+ }
239
+ //#endregion
240
+
166
241
  //#region ----- Types -----
167
242
  //#endregion
168
243
  //#region ----- Date helpers -----
@@ -416,6 +491,558 @@ function createEntityReportsPlugin(_injector) {
416
491
  }
417
492
  //#endregion
418
493
 
494
+ //#region ----- Imports -----
495
+ //#endregion
496
+ //#region ----- Command -----
497
+ /** Opens a report in the full-page report viewer. */
498
+ class AXPOpenReportViewerCommand {
499
+ constructor() {
500
+ //#region ----- Services & Dependencies -----
501
+ this.sessionService = inject(AXPSessionService);
502
+ this.tabNavigation = inject(AXPTabNavigationService);
503
+ this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
504
+ this.translate = inject(AXTranslationService);
505
+ this.inboxConnector = inject(AXPInAppInboxConnectorService, { optional: true });
506
+ }
507
+ //#endregion
508
+ //#region ----- Command Implementation -----
509
+ async execute(input) {
510
+ const rowData = this.resolveRowData(input);
511
+ const options = await this.resolveOptions(input, rowData);
512
+ if (!options.reportId) {
513
+ return {
514
+ success: false,
515
+ message: {
516
+ text: await this.translate.translateAsync('@report-management:report-viewer.messages.error.invalid-popup-target'),
517
+ },
518
+ };
519
+ }
520
+ const runId = typeof options.runId === 'string' ? options.runId.trim() : '';
521
+ if (runId) {
522
+ await this.dismissReportGenerationInboxNotification(runId);
523
+ }
524
+ const appName = this.sessionService.application?.name;
525
+ if (!appName) {
526
+ return {
527
+ success: false,
528
+ message: {
529
+ text: await this.translate.translateAsync('@report-management:report-viewer.messages.error.invalid-popup-target'),
530
+ },
531
+ };
532
+ }
533
+ // Put reportId in the path (not queryParams): tabular navigation drops NavigationExtras.
534
+ const path = runId
535
+ ? `${appName}/${RootConfig.module.route}/viewer/${options.reportId}?runId=${encodeURIComponent(runId)}`
536
+ : `${appName}/${RootConfig.module.route}/viewer/${options.reportId}`;
537
+ await this.tabNavigation.navigate(path);
538
+ return { success: true, kind: 'object', data: undefined };
539
+ }
540
+ //#endregion
541
+ //#region ----- Helpers -----
542
+ async dismissReportGenerationInboxNotification(runId) {
543
+ if (!this.inboxConnector) {
544
+ return;
545
+ }
546
+ try {
547
+ await this.inboxConnector.dismissByCorrelationId(buildReportGenerationNotificationCorrelationId(runId));
548
+ }
549
+ catch {
550
+ // Best-effort — navigation should still proceed.
551
+ }
552
+ }
553
+ resolveRowData(input) {
554
+ const ctx = input;
555
+ if (ctx?.data && typeof ctx.data === 'object') {
556
+ return ctx.data;
557
+ }
558
+ if (input['data'] &&
559
+ typeof input['data'] === 'object') {
560
+ return input['data'];
561
+ }
562
+ return input;
563
+ }
564
+ async resolveOptions(input, rowData) {
565
+ const typedInput = input;
566
+ const rawOptions = {
567
+ reportId: typedInput.reportId,
568
+ runId: typedInput.runId,
569
+ ...(typedInput.options ?? {}),
570
+ };
571
+ const scope = {
572
+ context: {
573
+ eval: (path) => get(rowData, path),
574
+ },
575
+ };
576
+ return (await this.expressionEvaluator.evaluate(rawOptions, scope));
577
+ }
578
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportViewerCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
579
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportViewerCommand, providedIn: 'root' }); }
580
+ }
581
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportViewerCommand, decorators: [{
582
+ type: Injectable,
583
+ args: [{ providedIn: 'root' }]
584
+ }] });
585
+
586
+ var openReportViewer_command = /*#__PURE__*/Object.freeze({
587
+ __proto__: null,
588
+ AXPOpenReportViewerCommand: AXPOpenReportViewerCommand
589
+ });
590
+
591
+ //#region ----- Imports -----
592
+ //#endregion
593
+ //#region ----- Handler -----
594
+ class AXMReportGenerationBackgroundOperationHandler extends AXPBackgroundOperationHandler {
595
+ constructor() {
596
+ super(...arguments);
597
+ this.providerKey = 'report-management';
598
+ this.openViewer = inject(AXPOpenReportViewerCommand);
599
+ this.queryService = inject(AXPQueryService);
600
+ }
601
+ async execute(command, operation) {
602
+ if (command.name !== 'click') {
603
+ return;
604
+ }
605
+ const reportRequestId = get(operation.data, 'reportRequestId') ??
606
+ get(operation.data, 'payload.reportRequestId') ??
607
+ get(operation.data, 'command.payload.reportRequestId');
608
+ if (typeof reportRequestId !== 'string' || !reportRequestId.trim()) {
609
+ return;
610
+ }
611
+ const result = await this.queryService.fetch(AXMReportManagementQueryKeys.ReportGeneration.Get, { id: reportRequestId });
612
+ const request = runtimeQueryPayloadData(result);
613
+ if (!request?.reportId) {
614
+ return;
615
+ }
616
+ if (request.status === 'completed') {
617
+ await this.openViewer.execute({ reportId: request.reportId, runId: request.id });
618
+ return;
619
+ }
620
+ await this.openViewer.execute({ reportId: request.reportId });
621
+ }
622
+ resolveCta(operation) {
623
+ if (operation.status === 'failed' && operation.capabilities.retryable) {
624
+ return {
625
+ text: '@general:background-operation.actions.retry',
626
+ icon: 'fa-light fa-arrow-rotate-right',
627
+ color: 'primary',
628
+ look: 'solid',
629
+ command: { name: 'retry' },
630
+ };
631
+ }
632
+ if (operation.status === 'completed' && operation.capabilities.clickable) {
633
+ return {
634
+ text: '@report-management:report-generation.notifications.actions.open-viewer',
635
+ icon: 'fa-light fa-file-chart-column',
636
+ color: 'primary',
637
+ look: 'solid',
638
+ command: { name: 'click' },
639
+ };
640
+ }
641
+ return null;
642
+ }
643
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationBackgroundOperationHandler, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
644
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationBackgroundOperationHandler }); }
645
+ }
646
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationBackgroundOperationHandler, decorators: [{
647
+ type: Injectable
648
+ }] });
649
+ //#endregion
650
+ //#region ----- Providers -----
651
+ const AXM_REPORT_GENERATION_BACKGROUND_OPERATION_HANDLERS = [
652
+ {
653
+ provide: AXP_BACKGROUND_OPERATION_HANDLERS,
654
+ useExisting: AXMReportGenerationBackgroundOperationHandler,
655
+ multi: true,
656
+ },
657
+ AXMReportGenerationBackgroundOperationHandler,
658
+ ];
659
+
660
+ //#region ----- Imports -----
661
+ //#endregion
662
+ //#region ----- Constants -----
663
+ const TOASTED_KEYS_STORAGE = 'axp:report-generation-inbox-toast-keys';
664
+ //#endregion
665
+ //#region ----- Bridge -----
666
+ /**
667
+ * When a report-generation inbox row arrives (user was not watching inline),
668
+ * mirrors it as a transient toast with an open action. Inbox persistence stays server-owned.
669
+ */
670
+ class AXMReportGenerationInboxToastBridgeService {
671
+ constructor() {
672
+ //#region ----- Dependencies -----
673
+ this.inboxConnector = inject(AXPInAppInboxConnectorService, { optional: true });
674
+ this.userNotificationService = inject(AXP_USER_NOTIFICATION_SERVICE);
675
+ this.commandService = inject(AXPCommandService);
676
+ this.translationService = inject(AXTranslationService);
677
+ this.sessionService = inject(AXPSessionService);
678
+ this.destroyRef = inject(DestroyRef);
679
+ this.injector = inject(Injector);
680
+ //#endregion
681
+ //#region ----- State -----
682
+ this.started = false;
683
+ this.inboxBaselined = false;
684
+ this.boundUserId = null;
685
+ this.toastedKeys = new Set();
686
+ }
687
+ //#endregion
688
+ //#region ----- Lifecycle -----
689
+ start() {
690
+ if (this.started || !this.inboxConnector) {
691
+ return;
692
+ }
693
+ this.started = true;
694
+ this.sessionService.isReadyForAppScopedApi$
695
+ .pipe(map(() => this.sessionService.user?.id?.trim() || null), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef))
696
+ .subscribe((userId) => {
697
+ this.bindSessionUser(userId);
698
+ });
699
+ this.bindSessionUser(this.sessionService.user?.id?.trim() || null);
700
+ effect(() => {
701
+ const userId = this.sessionService.user?.id?.trim();
702
+ const inboxLoading = this.inboxConnector?.loading() ?? false;
703
+ const items = this.readReportGenerationItems();
704
+ if (!this.sessionService.isReadyForAppScopedApi || !userId) {
705
+ return;
706
+ }
707
+ if (!this.inboxBaselined) {
708
+ for (const item of items) {
709
+ this.markToasted(this.toastKey(item));
710
+ }
711
+ if (!inboxLoading) {
712
+ this.inboxBaselined = true;
713
+ }
714
+ return;
715
+ }
716
+ for (const item of items) {
717
+ const key = this.toastKey(item);
718
+ if (this.toastedKeys.has(key)) {
719
+ continue;
720
+ }
721
+ this.markToasted(key);
722
+ void this.showToastForInboxItem(item);
723
+ }
724
+ }, { injector: this.injector });
725
+ }
726
+ //#endregion
727
+ //#region ----- Toast -----
728
+ async showToastForInboxItem(item) {
729
+ const action = item.actions?.[0];
730
+ const actionTitle = action?.title
731
+ ? await this.translationService.translateAsync(action.title)
732
+ : await this.translationService.translateAsync('@report-management:report-generation.notifications.actions.open-viewer');
733
+ const toastActions = action?.command?.name
734
+ ? [
735
+ {
736
+ text: actionTitle,
737
+ color: 'primary',
738
+ onClick: () => this.executeInboxAction(item, action),
739
+ },
740
+ ]
741
+ : undefined;
742
+ await this.userNotificationService.notify({
743
+ correlationId: item.correlationId?.trim() || item.id,
744
+ title: await this.resolveText(item.title),
745
+ body: await this.resolveText(item.body),
746
+ icon: item.iconClass ?? this.resolveIcon(item),
747
+ delivery: {
748
+ toast: {
749
+ color: this.resolveToastColor(item),
750
+ location: 'bottom-center',
751
+ closeButton: true,
752
+ timeOut: 8_000,
753
+ timeOutProgress: true,
754
+ showActions: !!toastActions?.length,
755
+ },
756
+ },
757
+ toastActions,
758
+ });
759
+ }
760
+ async executeInboxAction(item, action) {
761
+ if (!action.command?.name) {
762
+ return;
763
+ }
764
+ await this.commandService.execute(action.command.name, action.command.options ?? {});
765
+ }
766
+ //#endregion
767
+ //#region ----- Helpers -----
768
+ readReportGenerationItems() {
769
+ const userId = this.sessionService.user?.id?.trim();
770
+ return (this.inboxConnector?.inboxList() ?? []).filter((item) => {
771
+ if (item.definitionKey !== REPORT_GENERATION_NOTIFICATION_DEFINITION_KEY) {
772
+ return false;
773
+ }
774
+ const recipientUserId = item.recipientUserId?.trim();
775
+ return !recipientUserId || recipientUserId === userId;
776
+ });
777
+ }
778
+ toastKey(item) {
779
+ return item.correlationId?.trim() || item.id;
780
+ }
781
+ bindSessionUser(userId) {
782
+ if (userId === this.boundUserId) {
783
+ return;
784
+ }
785
+ this.boundUserId = userId;
786
+ this.inboxBaselined = false;
787
+ this.toastedKeys.clear();
788
+ if (userId) {
789
+ this.loadPersistedToastedKeys(userId);
790
+ }
791
+ }
792
+ markToasted(key) {
793
+ const normalized = key.trim();
794
+ if (!normalized) {
795
+ return;
796
+ }
797
+ this.toastedKeys.add(normalized);
798
+ this.persistToastedKeys();
799
+ }
800
+ toastedStorageKey(userId) {
801
+ return `${TOASTED_KEYS_STORAGE}:${userId}`;
802
+ }
803
+ loadPersistedToastedKeys(userId) {
804
+ try {
805
+ const raw = sessionStorage.getItem(this.toastedStorageKey(userId));
806
+ if (!raw) {
807
+ return;
808
+ }
809
+ const parsed = JSON.parse(raw);
810
+ if (!Array.isArray(parsed)) {
811
+ return;
812
+ }
813
+ for (const entry of parsed) {
814
+ if (typeof entry === 'string' && entry.trim()) {
815
+ this.toastedKeys.add(entry.trim());
816
+ }
817
+ }
818
+ }
819
+ catch {
820
+ // Ignore corrupt session storage.
821
+ }
822
+ }
823
+ persistToastedKeys() {
824
+ const userId = this.boundUserId?.trim();
825
+ if (!userId) {
826
+ return;
827
+ }
828
+ try {
829
+ sessionStorage.setItem(this.toastedStorageKey(userId), JSON.stringify([...this.toastedKeys]));
830
+ }
831
+ catch {
832
+ // Ignore quota / privacy errors.
833
+ }
834
+ }
835
+ async resolveText(value) {
836
+ const text = value?.trim();
837
+ if (!text) {
838
+ return '';
839
+ }
840
+ if (text.startsWith('@')) {
841
+ return this.translationService.translateAsync(text);
842
+ }
843
+ return text;
844
+ }
845
+ resolveToastColor(item) {
846
+ switch (item.priority) {
847
+ case 'Danger':
848
+ return 'danger';
849
+ case 'Warning':
850
+ return 'warning';
851
+ default:
852
+ return 'success';
853
+ }
854
+ }
855
+ resolveIcon(item) {
856
+ if (item.priority === 'Danger') {
857
+ return 'fa-light fa-circle-exclamation';
858
+ }
859
+ if (item.priority === 'Warning') {
860
+ return 'fa-light fa-triangle-exclamation';
861
+ }
862
+ return 'fa-light fa-circle-check';
863
+ }
864
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationInboxToastBridgeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
865
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationInboxToastBridgeService, providedIn: 'root' }); }
866
+ }
867
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationInboxToastBridgeService, decorators: [{
868
+ type: Injectable,
869
+ args: [{ providedIn: 'root' }]
870
+ }] });
871
+
872
+ //#region ----- Imports -----
873
+ //#endregion
874
+ //#region ----- Providers -----
875
+ function provideReportGenerationInboxToastBridge() {
876
+ return [
877
+ provideAppStartUpTask(() => {
878
+ const bridge = inject(AXMReportGenerationInboxToastBridgeService);
879
+ const inbox = inject(AXPInAppInboxConnectorService, { optional: true });
880
+ return {
881
+ name: 'report-generation-inbox-toast-bridge',
882
+ priority: AXP_APP_STARTUP_PRIORITY.realtimeHandlers,
883
+ run: async () => {
884
+ if (inbox) {
885
+ await inbox.ensureReady();
886
+ await inbox.refresh();
887
+ }
888
+ bridge.start();
889
+ },
890
+ };
891
+ }),
892
+ ];
893
+ }
894
+ //#endregion
895
+
896
+ function routesFacory() {
897
+ let routes = [
898
+ {
899
+ path: `:app/${RootConfig.module.route}/viewer`,
900
+ canActivate: [...AXP_PROTECTED_ROUTE_GUARDS],
901
+ data: { tab: true, rootLayout: true },
902
+ loadComponent: () => {
903
+ return AXPRootLayoutComponent;
904
+ },
905
+ children: [
906
+ {
907
+ path: ':reportId',
908
+ loadComponent: () => Promise.resolve().then(function () { return reportViewerPage_component; }).then((c) => c.AXPReportViewerPageComponent),
909
+ data: { reuse: true },
910
+ },
911
+ {
912
+ path: '',
913
+ loadComponent: () => Promise.resolve().then(function () { return reportViewerPage_component; }).then((c) => c.AXPReportViewerPageComponent),
914
+ data: { reuse: true },
915
+ },
916
+ ],
917
+ },
918
+ ];
919
+ return routes;
920
+ }
921
+ class AXMReportManagementModule {
922
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
923
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, imports: [AXPDomainModule,
924
+ AXPRuntimeModule] }); }
925
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, providers: [
926
+ {
927
+ provide: ROUTES,
928
+ multi: true,
929
+ useFactory: routesFacory,
930
+ },
931
+ provideEntity([RootConfig.entities.report.fullName, RootConfig.entities.category.fullName]),
932
+ // Module Manifest Provider
933
+ {
934
+ provide: AXP_MODULE_MANIFEST_PROVIDER,
935
+ useValue: ReportManagementManifest,
936
+ multi: true,
937
+ },
938
+ // Provider references (now using tokens)
939
+ {
940
+ provide: AXP_PERMISSION_DEFINITION_PROVIDER,
941
+ useClass: AXMPermissionDefinitionProvider,
942
+ multi: true,
943
+ },
944
+ {
945
+ provide: AXP_MENU_PROVIDER,
946
+ useClass: AXMMenuProvider,
947
+ multi: true,
948
+ },
949
+ {
950
+ provide: AXP_FEATURE_DEFINITION_PROVIDER,
951
+ useClass: AXPReportManagementFeatureDefinitionProvider,
952
+ multi: true,
953
+ },
954
+ {
955
+ provide: AXP_SETTING_DEFINITION_PROVIDER,
956
+ useClass: AXMReportManagementSettingProvider,
957
+ multi: true,
958
+ },
959
+ ...AXM_REPORT_GENERATION_BACKGROUND_OPERATION_HANDLERS,
960
+ ...provideReportGenerationInboxToastBridge(),
961
+ provideCommandSetups([
962
+ {
963
+ key: 'ReportManagement.Report:OpenInPopup',
964
+ command: () => Promise.resolve().then(function () { return openReportInPopup_command; }).then((c) => c.AXPOpenReportInPopupCommand),
965
+ },
966
+ {
967
+ key: 'ReportManagement.Report:OpenViewer',
968
+ command: () => Promise.resolve().then(function () { return openReportViewer_command; }).then((c) => c.AXPOpenReportViewerCommand),
969
+ },
970
+ ]),
971
+ {
972
+ provide: AXP_ENTITY_ACTION_PLUGIN,
973
+ multi: true,
974
+ useFactory: () => createEntityReportsPlugin(inject(Injector)),
975
+ },
976
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-platform-module-report-management-client-entity-reports-page-component.provider-B_i4p7tQ.mjs').then((m) => m.AXMEntityReportsPageComponentProvider)),
977
+ ], imports: [AXPDomainModule,
978
+ AXPRuntimeModule] }); }
979
+ }
980
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, decorators: [{
981
+ type: NgModule,
982
+ args: [{
983
+ imports: [
984
+ AXPDomainModule,
985
+ AXPRuntimeModule,
986
+ // Entity Modules
987
+ ],
988
+ exports: [],
989
+ declarations: [],
990
+ providers: [
991
+ {
992
+ provide: ROUTES,
993
+ multi: true,
994
+ useFactory: routesFacory,
995
+ },
996
+ provideEntity([RootConfig.entities.report.fullName, RootConfig.entities.category.fullName]),
997
+ // Module Manifest Provider
998
+ {
999
+ provide: AXP_MODULE_MANIFEST_PROVIDER,
1000
+ useValue: ReportManagementManifest,
1001
+ multi: true,
1002
+ },
1003
+ // Provider references (now using tokens)
1004
+ {
1005
+ provide: AXP_PERMISSION_DEFINITION_PROVIDER,
1006
+ useClass: AXMPermissionDefinitionProvider,
1007
+ multi: true,
1008
+ },
1009
+ {
1010
+ provide: AXP_MENU_PROVIDER,
1011
+ useClass: AXMMenuProvider,
1012
+ multi: true,
1013
+ },
1014
+ {
1015
+ provide: AXP_FEATURE_DEFINITION_PROVIDER,
1016
+ useClass: AXPReportManagementFeatureDefinitionProvider,
1017
+ multi: true,
1018
+ },
1019
+ {
1020
+ provide: AXP_SETTING_DEFINITION_PROVIDER,
1021
+ useClass: AXMReportManagementSettingProvider,
1022
+ multi: true,
1023
+ },
1024
+ ...AXM_REPORT_GENERATION_BACKGROUND_OPERATION_HANDLERS,
1025
+ ...provideReportGenerationInboxToastBridge(),
1026
+ provideCommandSetups([
1027
+ {
1028
+ key: 'ReportManagement.Report:OpenInPopup',
1029
+ command: () => Promise.resolve().then(function () { return openReportInPopup_command; }).then((c) => c.AXPOpenReportInPopupCommand),
1030
+ },
1031
+ {
1032
+ key: 'ReportManagement.Report:OpenViewer',
1033
+ command: () => Promise.resolve().then(function () { return openReportViewer_command; }).then((c) => c.AXPOpenReportViewerCommand),
1034
+ },
1035
+ ]),
1036
+ {
1037
+ provide: AXP_ENTITY_ACTION_PLUGIN,
1038
+ multi: true,
1039
+ useFactory: () => createEntityReportsPlugin(inject(Injector)),
1040
+ },
1041
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-platform-module-report-management-client-entity-reports-page-component.provider-B_i4p7tQ.mjs').then((m) => m.AXMEntityReportsPageComponentProvider)),
1042
+ ],
1043
+ }]
1044
+ }] });
1045
+
419
1046
  //#endregion
420
1047
 
421
1048
  //#region ---- Imports ----
@@ -843,6 +1470,7 @@ class AXPOpenReportInPopupCommand {
843
1470
  this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
844
1471
  this.translate = inject(AXTranslationService);
845
1472
  this.reportViewerService = inject(AXPReportViewerService);
1473
+ this.inboxConnector = inject(AXPInAppInboxConnectorService, { optional: true });
846
1474
  }
847
1475
  //#endregion
848
1476
  //#region ----- Command Implementation -----
@@ -858,9 +1486,12 @@ class AXPOpenReportInPopupCommand {
858
1486
  };
859
1487
  }
860
1488
  const runId = typeof options.runId === 'string' ? options.runId.trim() : '';
1489
+ if (runId) {
1490
+ await this.dismissReportGenerationInboxNotification(runId);
1491
+ }
861
1492
  const popupTitle = await this.resolvePopupTitle(options, rowData, runId);
862
1493
  const parameters = resolveReportParameterValues(options.parameters);
863
- const comp = (await import('./acorex-platform-module-report-management-client-report-popup-viewer.component-B9msMMod.mjs')).AXPReportPopupViewerComponent;
1494
+ const comp = (await import('./acorex-platform-module-report-management-client-report-popup-viewer.component-B6aTfRp0.mjs')).AXPReportPopupViewerComponent;
864
1495
  this.popupService.open(comp, {
865
1496
  title: popupTitle,
866
1497
  size: 'lg',
@@ -941,295 +1572,37 @@ class AXPOpenReportInPopupCommand {
941
1572
  return '';
942
1573
  }
943
1574
  const scope = {
944
- context: {
945
- eval: (path) => get(rowData, path),
946
- },
947
- };
948
- const evaluated = await this.expressionEvaluator.evaluate(title, scope);
949
- return isString(evaluated) ? evaluated : title;
950
- }
951
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportInPopupCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
952
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportInPopupCommand, providedIn: 'root' }); }
953
- }
954
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportInPopupCommand, decorators: [{
955
- type: Injectable,
956
- args: [{ providedIn: 'root' }]
957
- }] });
958
-
959
- var openReportInPopup_command = /*#__PURE__*/Object.freeze({
960
- __proto__: null,
961
- AXPOpenReportInPopupCommand: AXPOpenReportInPopupCommand
962
- });
963
-
964
- //#region ----- Imports -----
965
- //#endregion
966
- //#region ----- Command -----
967
- /** Opens a report in the full-page report viewer. */
968
- class AXPOpenReportViewerCommand {
969
- constructor() {
970
- //#region ----- Services & Dependencies -----
971
- this.sessionService = inject(AXPSessionService);
972
- this.tabNavigation = inject(AXPTabNavigationService);
973
- this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
974
- this.translate = inject(AXTranslationService);
975
- }
976
- //#endregion
977
- //#region ----- Command Implementation -----
978
- async execute(input) {
979
- const rowData = this.resolveRowData(input);
980
- const options = await this.resolveOptions(input, rowData);
981
- if (!options.reportId) {
982
- return {
983
- success: false,
984
- message: {
985
- text: await this.translate.translateAsync('@report-management:report-viewer.messages.error.invalid-popup-target'),
986
- },
987
- };
988
- }
989
- const appName = this.sessionService.application?.name;
990
- if (!appName) {
991
- return {
992
- success: false,
993
- message: {
994
- text: await this.translate.translateAsync('@report-management:report-viewer.messages.error.invalid-popup-target'),
995
- },
996
- };
997
- }
998
- // Put reportId in the path (not queryParams): tabular navigation drops NavigationExtras.
999
- const runId = typeof options.runId === 'string' ? options.runId.trim() : '';
1000
- const path = runId
1001
- ? `${appName}/${RootConfig.module.route}/viewer/${options.reportId}?runId=${encodeURIComponent(runId)}`
1002
- : `${appName}/${RootConfig.module.route}/viewer/${options.reportId}`;
1003
- await this.tabNavigation.navigate(path);
1004
- return { success: true, kind: 'object', data: undefined };
1005
- }
1006
- //#endregion
1007
- //#region ----- Helpers -----
1008
- resolveRowData(input) {
1009
- const ctx = input;
1010
- if (ctx?.data && typeof ctx.data === 'object') {
1011
- return ctx.data;
1012
- }
1013
- if (input['data'] &&
1014
- typeof input['data'] === 'object') {
1015
- return input['data'];
1016
- }
1017
- return input;
1018
- }
1019
- async resolveOptions(input, rowData) {
1020
- const typedInput = input;
1021
- const rawOptions = {
1022
- reportId: typedInput.reportId,
1023
- runId: typedInput.runId,
1024
- ...(typedInput.options ?? {}),
1025
- };
1026
- const scope = {
1027
- context: {
1028
- eval: (path) => get(rowData, path),
1029
- },
1030
- };
1031
- return (await this.expressionEvaluator.evaluate(rawOptions, scope));
1032
- }
1033
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportViewerCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1034
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportViewerCommand, providedIn: 'root' }); }
1035
- }
1036
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportViewerCommand, decorators: [{
1037
- type: Injectable,
1038
- args: [{ providedIn: 'root' }]
1039
- }] });
1040
-
1041
- var openReportViewer_command = /*#__PURE__*/Object.freeze({
1042
- __proto__: null,
1043
- AXPOpenReportViewerCommand: AXPOpenReportViewerCommand
1044
- });
1045
-
1046
- //#region ----- Imports -----
1047
- //#endregion
1048
- //#region ----- Handler -----
1049
- class AXMReportGenerationBackgroundOperationHandler extends AXPBackgroundOperationHandler {
1050
- constructor() {
1051
- super(...arguments);
1052
- this.providerKey = 'report-management';
1053
- this.openPopup = inject(AXPOpenReportInPopupCommand);
1054
- this.openViewer = inject(AXPOpenReportViewerCommand);
1055
- this.queryService = inject(AXPQueryService);
1056
- }
1057
- async execute(command, operation) {
1058
- if (command.name !== 'click') {
1059
- return;
1060
- }
1061
- const reportRequestId = get(operation.data, 'reportRequestId') ??
1062
- get(operation.data, 'payload.reportRequestId') ??
1063
- get(operation.data, 'command.payload.reportRequestId');
1064
- if (typeof reportRequestId !== 'string' || !reportRequestId.trim()) {
1065
- return;
1066
- }
1067
- const result = await this.queryService.fetch(AXMReportManagementQueryKeys.ReportGeneration.Get, { id: reportRequestId });
1068
- const request = runtimeQueryPayloadData(result);
1069
- if (!request?.reportId) {
1070
- return;
1071
- }
1072
- if (request.status === 'completed') {
1073
- await this.openPopup.execute({ reportId: request.reportId, runId: request.id });
1074
- return;
1075
- }
1076
- await this.openViewer.execute({ reportId: request.reportId });
1077
- }
1078
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationBackgroundOperationHandler, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1079
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationBackgroundOperationHandler }); }
1080
- }
1081
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationBackgroundOperationHandler, decorators: [{
1082
- type: Injectable
1083
- }] });
1084
- //#endregion
1085
- //#region ----- Providers -----
1086
- const AXM_REPORT_GENERATION_BACKGROUND_OPERATION_HANDLERS = [
1087
- {
1088
- provide: AXP_BACKGROUND_OPERATION_HANDLERS,
1089
- useExisting: AXMReportGenerationBackgroundOperationHandler,
1090
- multi: true,
1091
- },
1092
- AXMReportGenerationBackgroundOperationHandler,
1093
- ];
1094
-
1095
- function routesFacory() {
1096
- let routes = [
1097
- {
1098
- path: `:app/${RootConfig.module.route}/viewer`,
1099
- canActivate: [...AXP_PROTECTED_ROUTE_GUARDS],
1100
- data: { tab: true, rootLayout: true },
1101
- loadComponent: () => {
1102
- return AXPRootLayoutComponent;
1103
- },
1104
- children: [
1105
- {
1106
- path: ':reportId',
1107
- loadComponent: () => Promise.resolve().then(function () { return reportViewerPage_component; }).then((c) => c.AXPReportViewerPageComponent),
1108
- data: { reuse: true },
1109
- },
1110
- {
1111
- path: '',
1112
- loadComponent: () => Promise.resolve().then(function () { return reportViewerPage_component; }).then((c) => c.AXPReportViewerPageComponent),
1113
- data: { reuse: true },
1114
- },
1115
- ],
1116
- },
1117
- ];
1118
- return routes;
1119
- }
1120
- class AXMReportManagementModule {
1121
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1122
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, imports: [AXPDomainModule,
1123
- AXPRuntimeModule] }); }
1124
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, providers: [
1125
- {
1126
- provide: ROUTES,
1127
- multi: true,
1128
- useFactory: routesFacory,
1129
- },
1130
- provideEntity([RootConfig.entities.report.fullName, RootConfig.entities.category.fullName]),
1131
- // Module Manifest Provider
1132
- {
1133
- provide: AXP_MODULE_MANIFEST_PROVIDER,
1134
- useValue: ReportManagementManifest,
1135
- multi: true,
1136
- },
1137
- // Provider references (now using tokens)
1138
- {
1139
- provide: AXP_PERMISSION_DEFINITION_PROVIDER,
1140
- useClass: AXMPermissionDefinitionProvider,
1141
- multi: true,
1142
- },
1143
- {
1144
- provide: AXP_MENU_PROVIDER,
1145
- useClass: AXMMenuProvider,
1146
- multi: true,
1147
- },
1148
- {
1149
- provide: AXP_FEATURE_DEFINITION_PROVIDER,
1150
- useClass: AXPReportManagementFeatureDefinitionProvider,
1151
- multi: true,
1152
- },
1153
- ...AXM_REPORT_GENERATION_BACKGROUND_OPERATION_HANDLERS,
1154
- provideCommandSetups([
1155
- {
1156
- key: 'ReportManagement.Report:OpenInPopup',
1157
- command: () => Promise.resolve().then(function () { return openReportInPopup_command; }).then((c) => c.AXPOpenReportInPopupCommand),
1158
- },
1159
- {
1160
- key: 'ReportManagement.Report:OpenViewer',
1161
- command: () => Promise.resolve().then(function () { return openReportViewer_command; }).then((c) => c.AXPOpenReportViewerCommand),
1162
- },
1163
- ]),
1164
- {
1165
- provide: AXP_ENTITY_ACTION_PLUGIN,
1166
- multi: true,
1167
- useFactory: () => createEntityReportsPlugin(inject(Injector)),
1575
+ context: {
1576
+ eval: (path) => get(rowData, path),
1168
1577
  },
1169
- provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-platform-module-report-management-client-entity-reports-page-component.provider-C6tJPxri.mjs').then((m) => m.AXMEntityReportsPageComponentProvider)),
1170
- ], imports: [AXPDomainModule,
1171
- AXPRuntimeModule] }); }
1578
+ };
1579
+ const evaluated = await this.expressionEvaluator.evaluate(title, scope);
1580
+ return isString(evaluated) ? evaluated : title;
1581
+ }
1582
+ async dismissReportGenerationInboxNotification(runId) {
1583
+ if (!this.inboxConnector) {
1584
+ return;
1585
+ }
1586
+ try {
1587
+ await this.inboxConnector.dismissByCorrelationId(buildReportGenerationNotificationCorrelationId(runId));
1588
+ }
1589
+ catch {
1590
+ // Best-effort — opening the report should still proceed.
1591
+ }
1592
+ }
1593
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportInPopupCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1594
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportInPopupCommand, providedIn: 'root' }); }
1172
1595
  }
1173
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, decorators: [{
1174
- type: NgModule,
1175
- args: [{
1176
- imports: [
1177
- AXPDomainModule,
1178
- AXPRuntimeModule,
1179
- // Entity Modules
1180
- ],
1181
- exports: [],
1182
- declarations: [],
1183
- providers: [
1184
- {
1185
- provide: ROUTES,
1186
- multi: true,
1187
- useFactory: routesFacory,
1188
- },
1189
- provideEntity([RootConfig.entities.report.fullName, RootConfig.entities.category.fullName]),
1190
- // Module Manifest Provider
1191
- {
1192
- provide: AXP_MODULE_MANIFEST_PROVIDER,
1193
- useValue: ReportManagementManifest,
1194
- multi: true,
1195
- },
1196
- // Provider references (now using tokens)
1197
- {
1198
- provide: AXP_PERMISSION_DEFINITION_PROVIDER,
1199
- useClass: AXMPermissionDefinitionProvider,
1200
- multi: true,
1201
- },
1202
- {
1203
- provide: AXP_MENU_PROVIDER,
1204
- useClass: AXMMenuProvider,
1205
- multi: true,
1206
- },
1207
- {
1208
- provide: AXP_FEATURE_DEFINITION_PROVIDER,
1209
- useClass: AXPReportManagementFeatureDefinitionProvider,
1210
- multi: true,
1211
- },
1212
- ...AXM_REPORT_GENERATION_BACKGROUND_OPERATION_HANDLERS,
1213
- provideCommandSetups([
1214
- {
1215
- key: 'ReportManagement.Report:OpenInPopup',
1216
- command: () => Promise.resolve().then(function () { return openReportInPopup_command; }).then((c) => c.AXPOpenReportInPopupCommand),
1217
- },
1218
- {
1219
- key: 'ReportManagement.Report:OpenViewer',
1220
- command: () => Promise.resolve().then(function () { return openReportViewer_command; }).then((c) => c.AXPOpenReportViewerCommand),
1221
- },
1222
- ]),
1223
- {
1224
- provide: AXP_ENTITY_ACTION_PLUGIN,
1225
- multi: true,
1226
- useFactory: () => createEntityReportsPlugin(inject(Injector)),
1227
- },
1228
- provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-platform-module-report-management-client-entity-reports-page-component.provider-C6tJPxri.mjs').then((m) => m.AXMEntityReportsPageComponentProvider)),
1229
- ],
1230
- }]
1596
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportInPopupCommand, decorators: [{
1597
+ type: Injectable,
1598
+ args: [{ providedIn: 'root' }]
1231
1599
  }] });
1232
1600
 
1601
+ var openReportInPopup_command = /*#__PURE__*/Object.freeze({
1602
+ __proto__: null,
1603
+ AXPOpenReportInPopupCommand: AXPOpenReportInPopupCommand
1604
+ });
1605
+
1233
1606
  //#region ---- Report Viewer User Settings Service ----
1234
1607
  class AXPReportViewerUserSettingsService {
1235
1608
  constructor() {
@@ -1532,6 +1905,7 @@ function applyGroupByDrillLinksToColumns(columns, groupByDefinitions) {
1532
1905
  }
1533
1906
  //#endregion
1534
1907
 
1908
+ const OPEN_REPORT_VIEWER_COMMAND = 'ReportManagement.Report:OpenViewer';
1535
1909
  const AXPReportViewerViewModel = signalStore(withState(() => ({
1536
1910
  reportDefinition: null,
1537
1911
  currentLayout: null,
@@ -1549,6 +1923,9 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1549
1923
  frozen: false,
1550
1924
  freeze: null,
1551
1925
  backgroundQueued: false,
1926
+ backgroundGenerationRequestId: null,
1927
+ /** When true, completion is delivered inline on the viewer (no server inbox toast). */
1928
+ inlineGenerationWatchActive: false,
1552
1929
  parametersReadonly: false,
1553
1930
  selectedLayoutIds: [],
1554
1931
  })), withComputed((store, widgetRegistryService = inject(AXPWidgetRegistryService)) => ({
@@ -1602,7 +1979,7 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1602
1979
  return true;
1603
1980
  }
1604
1981
  if ((store.reportDefinition()?.generation?.mode ?? 'interactive') === 'background') {
1605
- if (store.backgroundQueued()) {
1982
+ if (store.backgroundQueued() && !store.frozen()) {
1606
1983
  return false;
1607
1984
  }
1608
1985
  return store.selectedLayoutIds().length > 0;
@@ -1735,6 +2112,12 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1735
2112
  isBackgroundReportMode: computed(() => (store.reportDefinition()?.generation?.mode ?? 'interactive') === 'background'),
1736
2113
  hasSelectedBackgroundLayouts: computed(() => store.selectedLayoutIds().length > 0),
1737
2114
  areParametersReadonly: computed(() => store.parametersReadonly() || store.frozen()),
2115
+ isBackgroundGenerateDisabled: computed(() => {
2116
+ if ((store.reportDefinition()?.generation?.mode ?? 'interactive') !== 'background') {
2117
+ return false;
2118
+ }
2119
+ return store.backgroundQueued() || store.selectedLayoutIds().length === 0;
2120
+ }),
1738
2121
  canSwitchLayout: computed(() => {
1739
2122
  if (store.frozen() && (store.freeze()?.dataSources?.length ?? 0) <= 1) {
1740
2123
  return false;
@@ -1743,8 +2126,207 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1743
2126
  }),
1744
2127
  generationMode: computed(() => store.reportDefinition()?.generation?.mode ?? 'interactive'),
1745
2128
  //#endregion
1746
- })), withMethods((store, reportDefinitionService = inject(AXPReportViewerService), reportUserSettingsService = inject(AXPReportViewerUserSettingsService), queryService = inject(AXPQueryService), commandService = inject(AXPCommandService), toastService = inject(AXToastService), translationService = inject(AXTranslationService)) => {
2129
+ })), withMethods((store, reportDefinitionService = inject(AXPReportViewerService), reportUserSettingsService = inject(AXPReportViewerUserSettingsService), queryService = inject(AXPQueryService), commandService = inject(AXPCommandService), userNotificationService = inject(AXP_USER_NOTIFICATION_SERVICE), translationService = inject(AXTranslationService), inboxConnector = inject(AXPInAppInboxConnectorService, {
2130
+ optional: true,
2131
+ })) => {
1747
2132
  let activeFetchCount = 0;
2133
+ let activeGenerationWatchId = null;
2134
+ const BACKGROUND_GENERATION_POLL_MS = 3_000;
2135
+ const dismissReportGenerationInboxNotification = async (runId) => {
2136
+ const normalized = runId.trim();
2137
+ if (!normalized || !inboxConnector) {
2138
+ return;
2139
+ }
2140
+ try {
2141
+ await inboxConnector.dismissByCorrelationId(buildReportGenerationNotificationCorrelationId(normalized));
2142
+ }
2143
+ catch {
2144
+ // Best-effort — the report viewer should still open.
2145
+ }
2146
+ };
2147
+ const openGeneratedReportFromNotification = async (runId) => {
2148
+ const report = store.reportDefinition();
2149
+ const normalizedRunId = runId.trim();
2150
+ if (!report?.id || !normalizedRunId) {
2151
+ return;
2152
+ }
2153
+ await dismissReportGenerationInboxNotification(normalizedRunId);
2154
+ await commandService.execute(OPEN_REPORT_VIEWER_COMMAND, {
2155
+ reportId: report.id,
2156
+ runId: normalizedRunId,
2157
+ });
2158
+ };
2159
+ const showGenerationNotification = async (options) => {
2160
+ const runId = options.runId?.trim();
2161
+ const showOpenAction = options.showOpenAction === true && !!runId;
2162
+ const toastActions = showOpenAction
2163
+ ? [
2164
+ {
2165
+ text: await translationService.translateAsync('@report-management:report-generation.notifications.actions.open-viewer'),
2166
+ color: 'primary',
2167
+ onClick: () => openGeneratedReportFromNotification(runId),
2168
+ },
2169
+ ]
2170
+ : undefined;
2171
+ await userNotificationService.notify({
2172
+ correlationId: runId
2173
+ ? buildReportGenerationNotificationCorrelationId(runId)
2174
+ : `report-generation-ui:${options.titleKey}`,
2175
+ title: await translationService.translateAsync(options.titleKey),
2176
+ body: await translationService.translateAsync(options.bodyKey),
2177
+ icon: options.icon,
2178
+ delivery: {
2179
+ toast: {
2180
+ color: options.color,
2181
+ location: 'bottom-center',
2182
+ closeButton: true,
2183
+ timeOut: showOpenAction ? 8_000 : 3_000,
2184
+ timeOutProgress: true,
2185
+ showActions: showOpenAction,
2186
+ },
2187
+ },
2188
+ toastActions,
2189
+ });
2190
+ };
2191
+ const showGenerationReadyNotification = async (runId) => {
2192
+ await showGenerationNotification({
2193
+ color: 'success',
2194
+ icon: 'fa-light fa-circle-check',
2195
+ titleKey: '@report-management:report-generation.notifications.complete.title',
2196
+ bodyKey: '@report-management:report-generation.notifications.complete.body',
2197
+ runId,
2198
+ showOpenAction: false,
2199
+ });
2200
+ };
2201
+ const showGenerationFailedNotification = async () => {
2202
+ await showGenerationNotification({
2203
+ color: 'danger',
2204
+ icon: 'fa-light fa-circle-exclamation',
2205
+ titleKey: '@report-management:report-generation.notifications.failed.title',
2206
+ bodyKey: '@report-management:report-generation.notifications.failed.body',
2207
+ });
2208
+ };
2209
+ const api = {
2210
+ loadFrozenRun: async (_runId) => { },
2211
+ startBackgroundGenerationWatch: async (_requestId) => { },
2212
+ };
2213
+ api.loadFrozenRun = async (runId) => {
2214
+ patchState(store, { status: AXPPageStatus.Processing, error: null });
2215
+ try {
2216
+ const result = await queryService.fetch(AXMReportManagementQueryKeys.ReportGeneration.GetViewerFreeze, { id: runId });
2217
+ const freeze = runtimeQueryPayloadData(result);
2218
+ if (!result?.success || !freeze) {
2219
+ patchState(store, {
2220
+ error: commandMessageTextForError(result?.message?.text) ||
2221
+ '@report-management:report-generation.errors.freeze-not-found',
2222
+ });
2223
+ return;
2224
+ }
2225
+ let report = store.reportDefinition();
2226
+ if (freeze.definition) {
2227
+ report = {
2228
+ id: freeze.reportId,
2229
+ title: freeze.definition.title,
2230
+ categoryIds: report?.categoryIds ?? [],
2231
+ parameterGroups: report?.parameterGroups ?? [],
2232
+ layouts: freeze.definition.layouts,
2233
+ defaultLayoutId: freeze.definition.defaultLayoutId,
2234
+ generation: report?.generation,
2235
+ };
2236
+ }
2237
+ else if (!report) {
2238
+ report = (await reportDefinitionService.getReportById(freeze.reportId)) ?? null;
2239
+ }
2240
+ if (!report) {
2241
+ patchState(store, {
2242
+ error: '@report-management:report-viewer.messages.error.failed-to-load',
2243
+ });
2244
+ return;
2245
+ }
2246
+ const defaultLayout = report.layouts.find((layout) => layout.id === freeze.definition?.defaultLayoutId) ||
2247
+ report.layouts.find((layout) => layout.id === freeze.dataSources[0]?.layoutId) ||
2248
+ report.layouts[0];
2249
+ patchState(store, {
2250
+ reportDefinition: report,
2251
+ currentLayout: defaultLayout,
2252
+ filterContext: freeze.parameters ?? {},
2253
+ lastAppliedFilterContext: freeze.parameters ?? {},
2254
+ allowFetchData: true,
2255
+ shouldExecuteReport: true,
2256
+ hasBeenExecuted: true,
2257
+ executionRequestId: store.executionRequestId() + 1,
2258
+ runId,
2259
+ frozen: true,
2260
+ freeze,
2261
+ backgroundQueued: false,
2262
+ backgroundGenerationRequestId: null,
2263
+ inlineGenerationWatchActive: false,
2264
+ parametersReadonly: true,
2265
+ status: AXPPageStatus.Idle,
2266
+ });
2267
+ }
2268
+ catch (error) {
2269
+ patchState(store, {
2270
+ error: error instanceof Error
2271
+ ? error.message
2272
+ : '@report-management:report-generation.errors.freeze-not-found',
2273
+ });
2274
+ }
2275
+ finally {
2276
+ patchState(store, { status: AXPPageStatus.Idle });
2277
+ }
2278
+ };
2279
+ api.startBackgroundGenerationWatch = async (requestId) => {
2280
+ activeGenerationWatchId = requestId;
2281
+ while (activeGenerationWatchId === requestId) {
2282
+ if (!store.backgroundQueued() || store.backgroundGenerationRequestId() !== requestId) {
2283
+ return;
2284
+ }
2285
+ await new Promise((resolve) => setTimeout(resolve, BACKGROUND_GENERATION_POLL_MS));
2286
+ if (activeGenerationWatchId !== requestId) {
2287
+ return;
2288
+ }
2289
+ try {
2290
+ const result = await queryService.fetch(AXMReportManagementQueryKeys.ReportGeneration.Get, { id: requestId });
2291
+ const request = runtimeQueryPayloadData(result);
2292
+ if (!request || activeGenerationWatchId !== requestId) {
2293
+ continue;
2294
+ }
2295
+ if (request.status === 'completed') {
2296
+ activeGenerationWatchId = null;
2297
+ const inlineWatchActive = store.inlineGenerationWatchActive();
2298
+ patchState(store, {
2299
+ backgroundQueued: false,
2300
+ inlineGenerationWatchActive: false,
2301
+ ...(inlineWatchActive ? {} : { backgroundGenerationRequestId: null }),
2302
+ });
2303
+ if (!inlineWatchActive) {
2304
+ return;
2305
+ }
2306
+ await api.loadFrozenRun(requestId);
2307
+ if (!store.error()) {
2308
+ await showGenerationReadyNotification(requestId);
2309
+ }
2310
+ return;
2311
+ }
2312
+ if (request.status === 'failed' || request.status === 'cancelled') {
2313
+ activeGenerationWatchId = null;
2314
+ patchState(store, {
2315
+ backgroundQueued: false,
2316
+ backgroundGenerationRequestId: null,
2317
+ inlineGenerationWatchActive: false,
2318
+ status: AXPPageStatus.Error,
2319
+ error: '@report-management:report-generation.notifications.failed.body',
2320
+ });
2321
+ await showGenerationFailedNotification();
2322
+ return;
2323
+ }
2324
+ }
2325
+ catch {
2326
+ // Keep polling while the run is still active.
2327
+ }
2328
+ }
2329
+ };
1748
2330
  const generateInBackground = async () => {
1749
2331
  const report = store.reportDefinition();
1750
2332
  const isBackgroundMode = (report?.generation?.mode ?? 'interactive') === 'background';
@@ -1762,6 +2344,16 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1762
2344
  return;
1763
2345
  }
1764
2346
  patchState(store, { status: AXPPageStatus.Submitting, error: null });
2347
+ if (store.frozen()) {
2348
+ patchState(store, {
2349
+ frozen: false,
2350
+ freeze: null,
2351
+ runId: null,
2352
+ hasBeenExecuted: false,
2353
+ allowFetchData: false,
2354
+ parametersReadonly: false,
2355
+ });
2356
+ }
1765
2357
  try {
1766
2358
  const result = await commandService.executeRemote(AXMReportManagementCommandKeys.ReportGeneration.Create, {
1767
2359
  reportId: report.id,
@@ -1769,6 +2361,7 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1769
2361
  layoutId: layoutIds[0],
1770
2362
  parameters: store.filterContext(),
1771
2363
  trigger: 'manual',
2364
+ suppressNotificationOnComplete: true,
1772
2365
  });
1773
2366
  if (!result?.success) {
1774
2367
  patchState(store, {
@@ -1778,17 +2371,47 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1778
2371
  });
1779
2372
  return;
1780
2373
  }
2374
+ const createResult = runtimeCommandPayloadData(result);
2375
+ const requestId = createResult?.id?.trim();
2376
+ if (!createResult || !requestId) {
2377
+ patchState(store, {
2378
+ status: AXPPageStatus.Error,
2379
+ error: '@report-management:report-generation.errors.create-failed',
2380
+ });
2381
+ return;
2382
+ }
2383
+ if (createResult.status === 'completed') {
2384
+ patchState(store, { status: AXPPageStatus.Idle, error: null });
2385
+ await api.loadFrozenRun(requestId);
2386
+ if (!store.error()) {
2387
+ await showGenerationReadyNotification(requestId);
2388
+ }
2389
+ return;
2390
+ }
1781
2391
  patchState(store, {
1782
2392
  status: AXPPageStatus.Submitted,
1783
2393
  backgroundQueued: true,
1784
- allowFetchData: false,
1785
- hasBeenExecuted: false,
1786
- });
1787
- toastService.show({
1788
- color: 'success',
1789
- title: await translationService.translateAsync('@report-management:report-generation.messages.queued-title'),
1790
- content: await translationService.translateAsync('@report-management:report-generation.messages.queued-body'),
2394
+ backgroundGenerationRequestId: requestId,
2395
+ inlineGenerationWatchActive: true,
2396
+ error: null,
1791
2397
  });
2398
+ if (createResult.deduplicated) {
2399
+ await showGenerationNotification({
2400
+ color: 'warning',
2401
+ icon: 'fa-light fa-triangle-exclamation',
2402
+ titleKey: '@report-management:report-generation.messages.deduplicated-title',
2403
+ bodyKey: '@report-management:report-generation.messages.deduplicated-body',
2404
+ });
2405
+ }
2406
+ else {
2407
+ await showGenerationNotification({
2408
+ color: 'success',
2409
+ icon: 'fa-light fa-clock',
2410
+ titleKey: '@report-management:report-generation.messages.queued-title',
2411
+ bodyKey: '@report-management:report-generation.messages.queued-body',
2412
+ });
2413
+ }
2414
+ void api.startBackgroundGenerationWatch(requestId);
1792
2415
  }
1793
2416
  catch (error) {
1794
2417
  patchState(store, {
@@ -1799,12 +2422,34 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1799
2422
  });
1800
2423
  }
1801
2424
  };
2425
+ const releaseInlineNotificationWatch = async () => {
2426
+ const requestId = store.backgroundGenerationRequestId()?.trim();
2427
+ activeGenerationWatchId = null;
2428
+ patchState(store, {
2429
+ inlineGenerationWatchActive: false,
2430
+ backgroundQueued: false,
2431
+ });
2432
+ if (!requestId) {
2433
+ return;
2434
+ }
2435
+ try {
2436
+ await commandService.executeRemote(AXMReportManagementCommandKeys.ReportGeneration.ReleaseInlineWatch, { id: requestId });
2437
+ }
2438
+ catch {
2439
+ // Best-effort — completion notify re-reads the flag from storage.
2440
+ }
2441
+ };
1802
2442
  const executeReport = async () => {
1803
2443
  try {
1804
2444
  if (store.status() === AXPPageStatus.Processing ||
1805
2445
  store.status() === AXPPageStatus.Submitting) {
1806
2446
  return;
1807
2447
  }
2448
+ const report = store.reportDefinition();
2449
+ if ((report?.generation?.mode ?? 'interactive') === 'background') {
2450
+ await generateInBackground();
2451
+ return;
2452
+ }
1808
2453
  if (store.frozen()) {
1809
2454
  patchState(store, {
1810
2455
  allowFetchData: true,
@@ -1814,7 +2459,6 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1814
2459
  });
1815
2460
  return;
1816
2461
  }
1817
- const report = store.reportDefinition();
1818
2462
  const layout = store.currentLayout();
1819
2463
  if (!report || !layout) {
1820
2464
  patchState(store, {
@@ -1822,10 +2466,6 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1822
2466
  });
1823
2467
  return;
1824
2468
  }
1825
- if ((report.generation?.mode ?? 'interactive') === 'background') {
1826
- await generateInBackground();
1827
- return;
1828
- }
1829
2469
  patchState(store, {
1830
2470
  status: AXPPageStatus.Submitting,
1831
2471
  error: null,
@@ -1956,6 +2596,7 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1956
2596
  return {
1957
2597
  //#region ---- Report Loading ----
1958
2598
  async loadReport(reportId) {
2599
+ await releaseInlineNotificationWatch();
1959
2600
  patchState(store, {
1960
2601
  status: AXPPageStatus.Processing,
1961
2602
  error: null,
@@ -1986,6 +2627,8 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
1986
2627
  frozen: false,
1987
2628
  freeze: null,
1988
2629
  backgroundQueued: false,
2630
+ backgroundGenerationRequestId: null,
2631
+ inlineGenerationWatchActive: false,
1989
2632
  parametersReadonly: false,
1990
2633
  selectedLayoutIds: report.layouts.map((layout) => layout.id),
1991
2634
  });
@@ -2087,76 +2730,14 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
2087
2730
  executeReport,
2088
2731
  fetchData,
2089
2732
  generateInBackground,
2733
+ releaseInlineNotificationWatch,
2090
2734
  nudgeFetch() {
2091
2735
  if (!store.allowFetchData()) {
2092
2736
  return;
2093
2737
  }
2094
2738
  patchState(store, { executionRequestId: store.executionRequestId() + 1 });
2095
2739
  },
2096
- async loadFrozenRun(runId) {
2097
- patchState(store, { status: AXPPageStatus.Processing, error: null });
2098
- try {
2099
- const result = await queryService.fetch(AXMReportManagementQueryKeys.ReportGeneration.GetViewerFreeze, { id: runId });
2100
- const freeze = runtimeQueryPayloadData(result);
2101
- if (!result?.success || !freeze) {
2102
- patchState(store, {
2103
- error: commandMessageTextForError(result?.message?.text) ||
2104
- '@report-management:report-generation.errors.freeze-not-found',
2105
- });
2106
- return;
2107
- }
2108
- let report = store.reportDefinition();
2109
- if (freeze.definition) {
2110
- report = {
2111
- id: freeze.reportId,
2112
- title: freeze.definition.title,
2113
- categoryIds: report?.categoryIds ?? [],
2114
- parameterGroups: report?.parameterGroups ?? [],
2115
- layouts: freeze.definition.layouts,
2116
- defaultLayoutId: freeze.definition.defaultLayoutId,
2117
- generation: report?.generation,
2118
- };
2119
- }
2120
- else if (!report) {
2121
- report = (await reportDefinitionService.getReportById(freeze.reportId)) ?? null;
2122
- }
2123
- if (!report) {
2124
- patchState(store, {
2125
- error: '@report-management:report-viewer.messages.error.failed-to-load',
2126
- });
2127
- return;
2128
- }
2129
- const defaultLayout = report.layouts.find((layout) => layout.id === freeze.definition?.defaultLayoutId) ||
2130
- report.layouts.find((layout) => layout.id === freeze.dataSources[0]?.layoutId) ||
2131
- report.layouts[0];
2132
- patchState(store, {
2133
- reportDefinition: report,
2134
- currentLayout: defaultLayout,
2135
- filterContext: freeze.parameters ?? {},
2136
- lastAppliedFilterContext: freeze.parameters ?? {},
2137
- allowFetchData: true,
2138
- shouldExecuteReport: true,
2139
- hasBeenExecuted: true,
2140
- executionRequestId: store.executionRequestId() + 1,
2141
- runId,
2142
- frozen: true,
2143
- freeze,
2144
- backgroundQueued: false,
2145
- parametersReadonly: true,
2146
- status: AXPPageStatus.Idle,
2147
- });
2148
- }
2149
- catch (error) {
2150
- patchState(store, {
2151
- error: error instanceof Error
2152
- ? error.message
2153
- : '@report-management:report-generation.errors.freeze-not-found',
2154
- });
2155
- }
2156
- finally {
2157
- patchState(store, { status: AXPPageStatus.Idle });
2158
- }
2159
- },
2740
+ loadFrozenRun: api.loadFrozenRun,
2160
2741
  //#endregion
2161
2742
  //#region ---- Drill View ----
2162
2743
  /**
@@ -2264,6 +2845,8 @@ const AXPReportViewerViewModel = signalStore(withState(() => ({
2264
2845
  frozen: false,
2265
2846
  freeze: null,
2266
2847
  backgroundQueued: false,
2848
+ backgroundGenerationRequestId: null,
2849
+ inlineGenerationWatchActive: false,
2267
2850
  parametersReadonly: false,
2268
2851
  });
2269
2852
  },
@@ -3290,14 +3873,50 @@ class AXPReportViewerPageComponent extends AXPPageLayoutBaseComponent {
3290
3873
  });
3291
3874
  }, /* @ts-ignore */
3292
3875
  ...(ngDevMode ? [{ debugName: "watchNavigationLoading" }] : /* istanbul ignore next */ []));
3876
+ this.syncFrozenRunUrl = effect(() => {
3877
+ const runId = this.vm.runId();
3878
+ const frozen = this.vm.isFrozen();
3879
+ const queued = this.vm.isBackgroundQueued();
3880
+ if (queued) {
3881
+ untracked(() => {
3882
+ const currentRunId = this.route.snapshot.queryParamMap.get('runId')?.trim();
3883
+ if (!currentRunId) {
3884
+ return;
3885
+ }
3886
+ void this.router.navigate([], {
3887
+ relativeTo: this.route,
3888
+ queryParams: { runId: null },
3889
+ queryParamsHandling: 'merge',
3890
+ replaceUrl: true,
3891
+ });
3892
+ });
3893
+ return;
3894
+ }
3895
+ if (!frozen || !runId) {
3896
+ return;
3897
+ }
3898
+ untracked(() => {
3899
+ const currentRunId = this.route.snapshot.queryParamMap.get('runId')?.trim();
3900
+ if (currentRunId === runId) {
3901
+ return;
3902
+ }
3903
+ void this.router.navigate([], {
3904
+ relativeTo: this.route,
3905
+ queryParams: { runId },
3906
+ queryParamsHandling: 'merge',
3907
+ replaceUrl: true,
3908
+ });
3909
+ this.recompute();
3910
+ });
3911
+ }, /* @ts-ignore */
3912
+ ...(ngDevMode ? [{ debugName: "syncFrozenRunUrl" }] : /* istanbul ignore next */ []));
3293
3913
  //#endregion
3294
3914
  this.loadingDialogRef = null;
3295
3915
  this.isDialogOpening = false;
3296
3916
  this.lastReportRouteKey = null;
3297
3917
  //#region ---- Computed Properties ----
3298
3918
  this.showFiltersPanel = () => this.deviceService.isLarge();
3299
- this.showReportDisplay = () => !this.vm.isBackgroundReportMode() &&
3300
- !this.vm.isBackgroundQueued() &&
3919
+ this.showReportDisplay = () => (!this.vm.isBackgroundReportMode() || this.vm.isFrozen()) &&
3301
3920
  (this.deviceService.isLarge() || (this.deviceService.isSmall() && this.vm.hasBeenExecuted()));
3302
3921
  //#endregion
3303
3922
  //#region ---- Layout Configurations ----
@@ -3316,17 +3935,31 @@ class AXPReportViewerPageComponent extends AXPPageLayoutBaseComponent {
3316
3935
  },
3317
3936
  });
3318
3937
  this.layoutPageIcon = reportLayoutPageIcon;
3319
- this.destroyRef.onDestroy(() => this.closeContentLoading());
3938
+ this.destroyRef.onDestroy(() => {
3939
+ this.closeContentLoading();
3940
+ void this.vm.releaseInlineNotificationWatch();
3941
+ });
3320
3942
  }
3321
3943
  reportsListPath() {
3322
3944
  return this.entityService
3323
3945
  .withEntity(RootConfig.module.name, RootConfig.entities.report.name)
3324
3946
  .createPath();
3325
3947
  }
3948
+ isReportViewerUrl(url) {
3949
+ return url.includes(`/${RootConfig.module.route}/viewer`);
3950
+ }
3326
3951
  //#endregion
3327
3952
  //#region ---- Lifecycle ----
3328
3953
  async ngOnInit() {
3329
3954
  await super.ngOnInit();
3955
+ this.unsubscriber
3956
+ .takeUntilDestroy(this.router.events.pipe(filter((event) => event instanceof NavigationStart)))
3957
+ .subscribe((event) => {
3958
+ const navigation = event;
3959
+ if (this.isReportViewerUrl(this.router.url) && !this.isReportViewerUrl(navigation.url)) {
3960
+ void this.vm.releaseInlineNotificationWatch();
3961
+ }
3962
+ });
3330
3963
  // Reload when reportId or runId changes. Ignore other query params (live filters).
3331
3964
  this.unsubscriber
3332
3965
  .takeUntilDestroy(combineLatest([this.route.paramMap, this.route.queryParamMap]))
@@ -3754,7 +4387,7 @@ class AXPReportViewerPageComponent extends AXPPageLayoutBaseComponent {
3754
4387
  this.vm.updateFilterContext(event);
3755
4388
  }
3756
4389
  async handleExecuteReport() {
3757
- if (this.vm.isFrozen()) {
4390
+ if (this.vm.isFrozen() && !this.vm.isBackgroundReportMode()) {
3758
4391
  return;
3759
4392
  }
3760
4393
  const isValid = await this.filtersForm()?.validate();
@@ -3762,9 +4395,10 @@ class AXPReportViewerPageComponent extends AXPPageLayoutBaseComponent {
3762
4395
  return;
3763
4396
  }
3764
4397
  await this.vm.executeReport();
4398
+ this.recompute();
3765
4399
  }
3766
4400
  async handleGenerateInBackground() {
3767
- if (this.vm.isFrozen()) {
4401
+ if (this.vm.isFrozen() && !this.vm.isBackgroundReportMode()) {
3768
4402
  return;
3769
4403
  }
3770
4404
  const isValid = await this.filtersForm()?.validate();
@@ -3915,8 +4549,7 @@ class AXPReportViewerPageComponent extends AXPPageLayoutBaseComponent {
3915
4549
  return reportDefinition?.description || null;
3916
4550
  }
3917
4551
  getPageIcon() {
3918
- if (this.vm.isBackgroundReportMode() ||
3919
- this.vm.isBackgroundQueued() ||
4552
+ if ((this.vm.isBackgroundReportMode() && !this.vm.isFrozen()) ||
3920
4553
  !this.vm.hasMultipleLayouts() ||
3921
4554
  !this.showReportDisplay()) {
3922
4555
  return null;
@@ -3930,8 +4563,7 @@ class AXPReportViewerPageComponent extends AXPPageLayoutBaseComponent {
3930
4563
  });
3931
4564
  }
3932
4565
  async getTitleActions() {
3933
- if (this.vm.isBackgroundReportMode() ||
3934
- this.vm.isBackgroundQueued() ||
4566
+ if ((this.vm.isBackgroundReportMode() && !this.vm.isFrozen()) ||
3935
4567
  !this.vm.hasMultipleLayouts() ||
3936
4568
  !this.showReportDisplay()) {
3937
4569
  return [];
@@ -4063,7 +4695,7 @@ class AXPReportViewerPageComponent extends AXPPageLayoutBaseComponent {
4063
4695
  provide: AXPPageLayoutBase,
4064
4696
  useExisting: AXPReportViewerPageComponent,
4065
4697
  },
4066
- ], viewQueries: [{ propertyName: "filtersForm", first: true, predicate: ["filtersForm"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout *translate=\"let t\">\n <!-- Left Panel: Filters (Always visible for desktop) -->\n @if (showFiltersPanel()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>\n {{ t('@report-management:report-viewer.components.filters.title') | async }}\n </axp-layout-title>\n </axp-layout-header>\n <axp-layout-content>\n @if (vm.reportDefinition()) {\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'card'\"\n (contextChange)=\"handleFiltersChange($event)\" (contextInitiated)=\"handleFiltersInitiated($event)\">\n </axp-layout-renderer>\n <!-- ----- Background report layout multi-select ----- -->\n @if (vm.isBackgroundReportMode() && !vm.isBackgroundQueued() && vm.availableLayouts().length > 0) {\n <div class=\"mt-4 pt-4 border-t\">\n <div class=\"mb-2\">\n <span class=\"text-sm font-medium\">\n {{ t('@report-management:report-generation.components.background-layouts.title') | async }}\n </span>\n </div>\n <p class=\"text-xs opacity-70 mb-3\">\n {{ t('@report-management:report-generation.components.background-layouts.description') | async }}\n </p>\n <div class=\"flex flex-col gap-2\">\n @for (layout of vm.availableLayouts(); track layout.id) {\n <label class=\"flex items-center gap-2 p-2 rounded border cursor-pointer hover:bg-neutral-50\">\n <ax-check-box\n [ngModel]=\"vm.isBackgroundLayoutSelected(layout.id)\"\n (ngModelChange)=\"handleBackgroundLayoutToggle(layout.id, $event)\"\n ></ax-check-box>\n <ax-icon [icon]=\"layoutPageIcon(layout.icon, layout)\"></ax-icon>\n <span class=\"text-sm\">{{ getLayoutSelectionLabel(layout) | translate | async }}</span>\n </label>\n }\n </div>\n </div>\n }\n }\n </axp-layout-content>\n <axp-layout-footer class=\"p-4 border-t\" *translate=\"let t\">\n @if (vm.hasUnappliedFilters()) {\n <div class=\"w-full warning-lightest-surface mb-2 p-2 rounded border border-warning-200\">\n <div class=\"flex items-center gap-2 text-warning-800\">\n <ax-icon class=\"fa-light fa-exclamation-triangle text-sm\"></ax-icon>\n <span class=\"text-xs\">{{\n t('@report-management:report-viewer.components.filters.messages.warning.unapplied') | async\n }}</span>\n </div>\n </div>\n }\n @if (vm.isBackgroundReportMode() && !vm.hasSelectedBackgroundLayouts() && !vm.isBackgroundQueued()) {\n <div class=\"w-full warning-lightest-surface mb-2 p-2 rounded border border-warning-200\">\n <div class=\"flex items-center gap-2 text-warning-800\">\n <ax-icon class=\"fa-light fa-exclamation-triangle text-sm\"></ax-icon>\n <span class=\"text-xs\">{{\n t('@report-management:report-generation.errors.no-layouts-selected') | async\n }}</span>\n </div>\n </div>\n }\n <ax-button class=\"w-full\" [disabled]=\"!vm.canExecute() || vm.areParametersReadonly() || vm.isBackgroundQueued()\"\n [text]=\"(vm.isBackgroundReportMode()\n ? t('@report-management:report-generation.actions.generate-in-background')\n : t('@report-management:report-viewer.actions.execute.title')) | async\" [color]=\"'primary'\"\n (onClick)=\"execute({ name: 'execute-report' })\">\n <ax-prefix>\n <ax-icon [icon]=\"vm.isBackgroundReportMode() ? 'fa-light fa-clock' : 'fa-light fa-play'\"></ax-icon>\n </ax-prefix>\n </ax-button>\n <!-- ----- Secondary background generate for choose mode ----- -->\n @if (canGenerateInBackground() && vm.generationMode() === 'choose') {\n <ax-button class=\"w-full mt-2\" [disabled]=\"vm.areParametersReadonly()\"\n [text]=\"t('@report-management:report-generation.actions.generate-in-background') | async\" [color]=\"'default'\"\n (onClick)=\"execute({ name: 'generate-in-background' })\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-clock\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </axp-layout-footer>\n </axp-layout-start-side>\n }\n <!-- Main Content: Report Display -->\n <axp-page-content class=\"overflow-auto\">\n @if (vm.isFrozen()) {\n <!-- ----- Frozen run banner ----- -->\n <div class=\"warning-lightest-surface mb-3 p-3 rounded border border-warning-200 text-warning-800 text-sm\">\n {{ t('@report-management:report-generation.banners.frozen') | async }}\n </div>\n }\n @if (vm.hasError()) {\n <!-- Error State -->\n <axp-state-message icon=\"fa-light fa-exclamation-triangle\"\n [title]=\"'@report-management:report-viewer.messages.error.fetch-title'\"\n [description]=\"vm.error() || '@report-management:report-viewer.messages.error.unexpected'\">\n <div slot=\"actions\">\n <ax-button look=\"outline\" color=\"primary\"\n [text]=\"t('@report-management:report-viewer.actions.retry.title') | async\" (onClick)=\"vm.clearError()\">\n <ax-prefix>\n <ax-icon class=\"fa-light fa-refresh\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </axp-state-message>\n } @else if (!vm.reportDefinition()) {\n <!-- No Report Selected State -->\n <axp-state-message icon=\"fa-light fa-chart-line\"\n [title]=\"'@report-management:report-viewer.components.report-viewer.empty-states.no-report.title'\"\n [description]=\"'@report-management:report-viewer.components.report-viewer.empty-states.no-report.description'\">\n <div slot=\"actions\">\n <ax-button look=\"outline\" color=\"primary\"\n [text]=\"t('@report-management:report-viewer.actions.browse-reports.title') | async\">\n <ax-prefix>\n <ax-icon class=\"fa-light fa-folder-open\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </axp-state-message>\n <!-- ----- Background report message state (no live layout preview) ----- -->\n } @else if (vm.isBackgroundReportMode() || vm.isBackgroundQueued()) {\n <!-- ----- Mobile filters and layout selection for background reports ----- -->\n @if (deviceService.isSmall() && !vm.isBackgroundQueued()) {\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'fieldset'\"\n (contextChange)=\"handleFiltersChange($event)\" (contextInitiated)=\"handleFiltersInitiated($event)\">\n </axp-layout-renderer>\n @if (vm.availableLayouts().length > 0) {\n <div class=\"mt-4 pt-4 border-t\">\n <div class=\"flex items-center justify-between gap-2 mb-2\">\n <span class=\"text-sm font-medium\">\n {{ t('@report-management:report-generation.components.background-layouts.title') | async }}\n </span>\n </div>\n <p class=\"text-xs opacity-70 mb-3\">\n {{ t('@report-management:report-generation.components.background-layouts.description') | async }}\n </p>\n <div class=\"flex flex-col gap-2\">\n @for (layout of vm.availableLayouts(); track layout.id) {\n <label class=\"flex items-center gap-2 p-2 rounded border cursor-pointer\">\n <ax-check-box\n [ngModel]=\"vm.isBackgroundLayoutSelected(layout.id)\"\n (ngModelChange)=\"handleBackgroundLayoutToggle(layout.id, $event)\"\n ></ax-check-box>\n <ax-icon [icon]=\"layoutPageIcon(layout.icon, layout)\"></ax-icon>\n <span class=\"text-sm\">{{ getLayoutSelectionLabel(layout) | translate | async }}</span>\n </label>\n }\n </div>\n </div>\n }\n }\n @if (vm.isBackgroundQueued()) {\n <axp-state-message icon=\"fa-light fa-clock\"\n [title]=\"'@report-management:report-generation.empty-states.background.queued-title'\"\n [description]=\"'@report-management:report-generation.empty-states.background.queued-description'\">\n </axp-state-message>\n } @else {\n <axp-state-message icon=\"fa-light fa-clock\"\n [title]=\"'@report-management:report-generation.empty-states.background.title'\"\n [description]=\"'@report-management:report-generation.empty-states.background.description'\">\n </axp-state-message>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"reportDisplay\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<!-- Desktop: Report Display Area -->\n<ng-template #reportDisplay>\n @if (showReportDisplay()) {\n <!-- Desktop & Mobile: Report Display Area -->\n @if (vm.isTableLayout()) {\n <axp-report-table-view> </axp-report-table-view>\n } @else if (vm.isChartLayout()) {\n <axp-report-chart-view> </axp-report-chart-view>\n } @else if (vm.isPdfLayout()) {\n <axp-report-pdf-view> </axp-report-pdf-view>\n }\n } @else if (vm.reportDefinition()) {\n <!-- Mobile: Show filters -->\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'fieldset'\">\n </axp-layout-renderer>\n }\n</ng-template>\n\n<!-- Mobile: Execute Button -->\n@if (deviceService.isSmall() && ((!vm.isBackgroundReportMode() && !vm.hasBeenExecuted()) || (vm.isBackgroundReportMode() && !vm.isBackgroundQueued()))) {\n<axp-page-footer class=\"p-2 border-t\">\n <ax-button class=\"w-full\" [disabled]=\"!vm.canExecute() || vm.areParametersReadonly()\"\n [text]=\"(vm.isBackgroundReportMode()\n ? '@report-management:report-generation.actions.generate-in-background'\n : '@report-management:report-viewer.actions.execute.title') | translate | async\" [color]=\"'primary'\"\n (onClick)=\"execute({ name: 'execute-report' })\">\n <ax-prefix>\n <ax-icon [icon]=\"vm.isBackgroundReportMode() ? 'fa-light fa-clock' : 'fa-light fa-play'\"></ax-icon>\n </ax-prefix>\n </ax-button>\n</axp-page-footer>\n}\n", styles: ["@layer properties;.axp-report-viewer-page{display:flex;height:100%;width:100%;flex-direction:column}.axp-report-viewer-page axp-layout-start-side{height:100%;width:calc(var(--spacing, .25rem) * 80);border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}@media(width>=1920px){.axp-report-viewer-page axp-layout-start-side{width:calc(var(--spacing, .25rem) * 96)}}.axp-report-viewer-page axp-layout-start-side>axp-layout-header{height:73px;border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.axp-report-viewer-page axp-layout-start-side axp-layout-content{overflow-x:auto;padding:calc(var(--spacing, .25rem) * 4)}.axp-report-viewer-page .__mobile-filters{display:flex;height:100%;flex-direction:column;overflow-y:auto}.axp-report-viewer-page .__mobile-filters .__mobile-filters-content{flex:1}.axp-report-viewer-page .__mobile-filters .__mobile-filters-footer{position:sticky;bottom:0;border-top-style:var(--tw-border-style);border-top-width:1px;background-color:var(--color-lightest);color:var(--color-on-lightest);border-color:var(--color-border-lightest);padding-top:calc(var(--spacing, .25rem) * 2)}.axp-report-viewer-page axp-page-content{display:flex;height:100%;flex-direction:column}.axp-report-viewer-page axp-report-table-view{flex:1;overflow:hidden}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: AXCheckBoxModule }, { kind: "component", type: i4.AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$3.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: AXLoadingModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i1.AXTranslatorDirective, selector: "[translate]" }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "ngmodule", type: AXLoadingDialogModule }, { kind: "component", type:
4698
+ ], viewQueries: [{ propertyName: "filtersForm", first: true, predicate: ["filtersForm"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<axp-page-layout *translate=\"let t\">\n <!-- Left Panel: Filters (Always visible for desktop) -->\n @if (showFiltersPanel()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>\n {{ t('@report-management:report-viewer.components.filters.title') | async }}\n </axp-layout-title>\n </axp-layout-header>\n <axp-layout-content>\n @if (vm.reportDefinition()) {\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'card'\"\n (contextChange)=\"handleFiltersChange($event)\" (contextInitiated)=\"handleFiltersInitiated($event)\">\n </axp-layout-renderer>\n <!-- ----- Background report layout multi-select ----- -->\n @if (vm.isBackgroundReportMode() && !vm.isBackgroundQueued() && vm.availableLayouts().length > 0) {\n <div class=\"mt-4 pt-4 border-t\">\n <div class=\"mb-2\">\n <span class=\"text-sm font-medium\">\n {{ t('@report-management:report-generation.components.background-layouts.title') | async }}\n </span>\n </div>\n <p class=\"text-xs opacity-70 mb-3\">\n {{ t('@report-management:report-generation.components.background-layouts.description') | async }}\n </p>\n <div class=\"flex flex-col gap-2\">\n @for (layout of vm.availableLayouts(); track layout.id) {\n <label class=\"flex items-center gap-2 p-2 rounded border cursor-pointer hover:bg-neutral-50\">\n <ax-check-box\n [ngModel]=\"vm.isBackgroundLayoutSelected(layout.id)\"\n (ngModelChange)=\"handleBackgroundLayoutToggle(layout.id, $event)\"\n ></ax-check-box>\n <ax-icon [icon]=\"layoutPageIcon(layout.icon, layout)\"></ax-icon>\n <span class=\"text-sm\">{{ getLayoutSelectionLabel(layout) | translate | async }}</span>\n </label>\n }\n </div>\n </div>\n }\n }\n </axp-layout-content>\n <axp-layout-footer class=\"p-4 border-t\" *translate=\"let t\">\n @if (vm.hasUnappliedFilters()) {\n <div class=\"w-full warning-lightest-surface mb-2 p-2 rounded border border-warning-200\">\n <div class=\"flex items-center gap-2 text-warning-800\">\n <ax-icon class=\"fa-light fa-exclamation-triangle text-sm\"></ax-icon>\n <span class=\"text-xs\">{{\n t('@report-management:report-viewer.components.filters.messages.warning.unapplied') | async\n }}</span>\n </div>\n </div>\n }\n @if (vm.isBackgroundReportMode() && !vm.hasSelectedBackgroundLayouts() && !vm.isBackgroundQueued()) {\n <div class=\"w-full warning-lightest-surface mb-2 p-2 rounded border border-warning-200\">\n <div class=\"flex items-center gap-2 text-warning-800\">\n <ax-icon class=\"fa-light fa-exclamation-triangle text-sm\"></ax-icon>\n <span class=\"text-xs\">{{\n t('@report-management:report-generation.errors.no-layouts-selected') | async\n }}</span>\n </div>\n </div>\n }\n <ax-button class=\"w-full\" [disabled]=\"vm.isBackgroundReportMode() ? vm.isBackgroundGenerateDisabled() : (!vm.canExecute() || vm.areParametersReadonly())\"\n [text]=\"(vm.isBackgroundReportMode()\n ? t('@report-management:report-generation.actions.generate-in-background')\n : t('@report-management:report-viewer.actions.execute.title')) | async\" [color]=\"'primary'\"\n (onClick)=\"execute({ name: 'execute-report' })\">\n <ax-prefix>\n <ax-icon [icon]=\"vm.isBackgroundReportMode() ? 'fa-light fa-clock' : 'fa-light fa-play'\"></ax-icon>\n </ax-prefix>\n </ax-button>\n <!-- ----- Secondary background generate for choose mode ----- -->\n @if (canGenerateInBackground() && vm.generationMode() === 'choose') {\n <ax-button class=\"w-full mt-2\" [disabled]=\"vm.areParametersReadonly()\"\n [text]=\"t('@report-management:report-generation.actions.generate-in-background') | async\" [color]=\"'default'\"\n (onClick)=\"execute({ name: 'generate-in-background' })\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-clock\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </axp-layout-footer>\n </axp-layout-start-side>\n }\n <!-- Main Content: Report Display -->\n <axp-page-content class=\"overflow-auto\">\n @if (vm.isFrozen()) {\n <!-- ----- Frozen run banner ----- -->\n <div class=\"warning-lightest-surface mb-3 p-3 rounded border border-warning-200 text-warning-800 text-sm\">\n {{ t('@report-management:report-generation.banners.frozen') | async }}\n </div>\n }\n @if (vm.hasError()) {\n <!-- Error State -->\n <axp-state-message icon=\"fa-light fa-exclamation-triangle\"\n [title]=\"'@report-management:report-viewer.messages.error.fetch-title'\"\n [description]=\"vm.error() || '@report-management:report-viewer.messages.error.unexpected'\">\n <div slot=\"actions\">\n <ax-button look=\"outline\" color=\"primary\"\n [text]=\"t('@report-management:report-viewer.actions.retry.title') | async\" (onClick)=\"vm.clearError()\">\n <ax-prefix>\n <ax-icon class=\"fa-light fa-refresh\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </axp-state-message>\n } @else if (!vm.reportDefinition()) {\n <!-- No Report Selected State -->\n <axp-state-message icon=\"fa-light fa-chart-line\"\n [title]=\"'@report-management:report-viewer.components.report-viewer.empty-states.no-report.title'\"\n [description]=\"'@report-management:report-viewer.components.report-viewer.empty-states.no-report.description'\">\n <div slot=\"actions\">\n <ax-button look=\"outline\" color=\"primary\"\n [text]=\"t('@report-management:report-viewer.actions.browse-reports.title') | async\">\n <ax-prefix>\n <ax-icon class=\"fa-light fa-folder-open\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </axp-state-message>\n <!-- ----- Background report message state (no live layout preview) ----- -->\n } @else if (vm.isBackgroundReportMode() && !vm.isFrozen()) {\n <!-- ----- Mobile filters and layout selection for background reports ----- -->\n @if (deviceService.isSmall() && !vm.isBackgroundQueued()) {\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'fieldset'\"\n (contextChange)=\"handleFiltersChange($event)\" (contextInitiated)=\"handleFiltersInitiated($event)\">\n </axp-layout-renderer>\n @if (vm.availableLayouts().length > 0) {\n <div class=\"mt-4 pt-4 border-t\">\n <div class=\"flex items-center justify-between gap-2 mb-2\">\n <span class=\"text-sm font-medium\">\n {{ t('@report-management:report-generation.components.background-layouts.title') | async }}\n </span>\n </div>\n <p class=\"text-xs opacity-70 mb-3\">\n {{ t('@report-management:report-generation.components.background-layouts.description') | async }}\n </p>\n <div class=\"flex flex-col gap-2\">\n @for (layout of vm.availableLayouts(); track layout.id) {\n <label class=\"flex items-center gap-2 p-2 rounded border cursor-pointer\">\n <ax-check-box\n [ngModel]=\"vm.isBackgroundLayoutSelected(layout.id)\"\n (ngModelChange)=\"handleBackgroundLayoutToggle(layout.id, $event)\"\n ></ax-check-box>\n <ax-icon [icon]=\"layoutPageIcon(layout.icon, layout)\"></ax-icon>\n <span class=\"text-sm\">{{ getLayoutSelectionLabel(layout) | translate | async }}</span>\n </label>\n }\n </div>\n </div>\n }\n }\n @if (vm.isBackgroundQueued()) {\n <axp-state-message icon=\"fa-light fa-clock\"\n [title]=\"'@report-management:report-generation.empty-states.background.queued-title'\"\n [description]=\"'@report-management:report-generation.empty-states.background.queued-description'\">\n </axp-state-message>\n } @else {\n <axp-state-message icon=\"fa-light fa-clock\"\n [title]=\"'@report-management:report-generation.empty-states.background.title'\"\n [description]=\"'@report-management:report-generation.empty-states.background.description'\">\n </axp-state-message>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"reportDisplay\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<!-- Desktop: Report Display Area -->\n<ng-template #reportDisplay>\n @if (showReportDisplay()) {\n <!-- Desktop & Mobile: Report Display Area -->\n @if (vm.isTableLayout()) {\n <axp-report-table-view> </axp-report-table-view>\n } @else if (vm.isChartLayout()) {\n <axp-report-chart-view> </axp-report-chart-view>\n } @else if (vm.isPdfLayout()) {\n <axp-report-pdf-view> </axp-report-pdf-view>\n }\n } @else if (vm.reportDefinition()) {\n <!-- Mobile: Show filters -->\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'fieldset'\">\n </axp-layout-renderer>\n }\n</ng-template>\n\n<!-- Mobile: Execute Button -->\n@if (deviceService.isSmall() && ((!vm.isBackgroundReportMode() && !vm.hasBeenExecuted()) || (vm.isBackgroundReportMode() && !vm.isBackgroundQueued()))) {\n<axp-page-footer class=\"p-2 border-t\">\n <ax-button class=\"w-full\" [disabled]=\"vm.isBackgroundReportMode() ? vm.isBackgroundGenerateDisabled() : (!vm.canExecute() || vm.areParametersReadonly())\"\n [text]=\"(vm.isBackgroundReportMode()\n ? '@report-management:report-generation.actions.generate-in-background'\n : '@report-management:report-viewer.actions.execute.title') | translate | async\" [color]=\"'primary'\"\n (onClick)=\"execute({ name: 'execute-report' })\">\n <ax-prefix>\n <ax-icon [icon]=\"vm.isBackgroundReportMode() ? 'fa-light fa-clock' : 'fa-light fa-play'\"></ax-icon>\n </ax-prefix>\n </ax-button>\n</axp-page-footer>\n}\n", styles: ["@layer properties;.axp-report-viewer-page{display:flex;height:100%;width:100%;flex-direction:column}.axp-report-viewer-page axp-layout-start-side{height:100%;width:calc(var(--spacing, .25rem) * 80);border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}@media(width>=1920px){.axp-report-viewer-page axp-layout-start-side{width:calc(var(--spacing, .25rem) * 96)}}.axp-report-viewer-page axp-layout-start-side>axp-layout-header{height:73px;border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.axp-report-viewer-page axp-layout-start-side axp-layout-content{overflow-x:auto;padding:calc(var(--spacing, .25rem) * 4)}.axp-report-viewer-page .__mobile-filters{display:flex;height:100%;flex-direction:column;overflow-y:auto}.axp-report-viewer-page .__mobile-filters .__mobile-filters-content{flex:1}.axp-report-viewer-page .__mobile-filters .__mobile-filters-footer{position:sticky;bottom:0;border-top-style:var(--tw-border-style);border-top-width:1px;background-color:var(--color-lightest);color:var(--color-on-lightest);border-color:var(--color-border-lightest);padding-top:calc(var(--spacing, .25rem) * 2)}.axp-report-viewer-page axp-page-content{display:flex;height:100%;flex-direction:column}.axp-report-viewer-page axp-report-table-view{flex:1;overflow:hidden}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: AXCheckBoxModule }, { kind: "component", type: i4.AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$3.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: AXLoadingModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i1.AXTranslatorDirective, selector: "[translate]" }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "ngmodule", type: AXLoadingDialogModule }, { kind: "component", type:
4067
4699
  //
4068
4700
  AXPLayoutRendererComponent, selector: "axp-layout-renderer", inputs: ["layout", "context", "look", "mode"], outputs: ["contextChange", "contextInitiated", "validityChange"] }, { kind: "component", type: AXPPageLayoutComponent, selector: "axp-page-layout" }, { kind: "component", type: AXPThemeLayoutStartSideComponent, selector: "axp-layout-page-start-side, axp-layout-start-side" }, { kind: "component", type: AXPThemeLayoutHeaderComponent, selector: "axp-layout-header" }, { 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: AXPReportTableViewComponent, selector: "axp-report-table-view" }, { kind: "component", type: AXPThemeLayoutFooterComponent, selector: "axp-layout-footer, axp-layout-page-footer" }, { kind: "component", type: AXPReportChartViewComponent, selector: "axp-report-chart-view" }, { kind: "component", type: AXPReportPdfViewComponent, selector: "axp-report-pdf-view" }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
4069
4701
  }
@@ -4097,7 +4729,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
4097
4729
  provide: AXPPageLayoutBase,
4098
4730
  useExisting: AXPReportViewerPageComponent,
4099
4731
  },
4100
- ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'axp-report-viewer-page' }, template: "<axp-page-layout *translate=\"let t\">\n <!-- Left Panel: Filters (Always visible for desktop) -->\n @if (showFiltersPanel()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>\n {{ t('@report-management:report-viewer.components.filters.title') | async }}\n </axp-layout-title>\n </axp-layout-header>\n <axp-layout-content>\n @if (vm.reportDefinition()) {\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'card'\"\n (contextChange)=\"handleFiltersChange($event)\" (contextInitiated)=\"handleFiltersInitiated($event)\">\n </axp-layout-renderer>\n <!-- ----- Background report layout multi-select ----- -->\n @if (vm.isBackgroundReportMode() && !vm.isBackgroundQueued() && vm.availableLayouts().length > 0) {\n <div class=\"mt-4 pt-4 border-t\">\n <div class=\"mb-2\">\n <span class=\"text-sm font-medium\">\n {{ t('@report-management:report-generation.components.background-layouts.title') | async }}\n </span>\n </div>\n <p class=\"text-xs opacity-70 mb-3\">\n {{ t('@report-management:report-generation.components.background-layouts.description') | async }}\n </p>\n <div class=\"flex flex-col gap-2\">\n @for (layout of vm.availableLayouts(); track layout.id) {\n <label class=\"flex items-center gap-2 p-2 rounded border cursor-pointer hover:bg-neutral-50\">\n <ax-check-box\n [ngModel]=\"vm.isBackgroundLayoutSelected(layout.id)\"\n (ngModelChange)=\"handleBackgroundLayoutToggle(layout.id, $event)\"\n ></ax-check-box>\n <ax-icon [icon]=\"layoutPageIcon(layout.icon, layout)\"></ax-icon>\n <span class=\"text-sm\">{{ getLayoutSelectionLabel(layout) | translate | async }}</span>\n </label>\n }\n </div>\n </div>\n }\n }\n </axp-layout-content>\n <axp-layout-footer class=\"p-4 border-t\" *translate=\"let t\">\n @if (vm.hasUnappliedFilters()) {\n <div class=\"w-full warning-lightest-surface mb-2 p-2 rounded border border-warning-200\">\n <div class=\"flex items-center gap-2 text-warning-800\">\n <ax-icon class=\"fa-light fa-exclamation-triangle text-sm\"></ax-icon>\n <span class=\"text-xs\">{{\n t('@report-management:report-viewer.components.filters.messages.warning.unapplied') | async\n }}</span>\n </div>\n </div>\n }\n @if (vm.isBackgroundReportMode() && !vm.hasSelectedBackgroundLayouts() && !vm.isBackgroundQueued()) {\n <div class=\"w-full warning-lightest-surface mb-2 p-2 rounded border border-warning-200\">\n <div class=\"flex items-center gap-2 text-warning-800\">\n <ax-icon class=\"fa-light fa-exclamation-triangle text-sm\"></ax-icon>\n <span class=\"text-xs\">{{\n t('@report-management:report-generation.errors.no-layouts-selected') | async\n }}</span>\n </div>\n </div>\n }\n <ax-button class=\"w-full\" [disabled]=\"!vm.canExecute() || vm.areParametersReadonly() || vm.isBackgroundQueued()\"\n [text]=\"(vm.isBackgroundReportMode()\n ? t('@report-management:report-generation.actions.generate-in-background')\n : t('@report-management:report-viewer.actions.execute.title')) | async\" [color]=\"'primary'\"\n (onClick)=\"execute({ name: 'execute-report' })\">\n <ax-prefix>\n <ax-icon [icon]=\"vm.isBackgroundReportMode() ? 'fa-light fa-clock' : 'fa-light fa-play'\"></ax-icon>\n </ax-prefix>\n </ax-button>\n <!-- ----- Secondary background generate for choose mode ----- -->\n @if (canGenerateInBackground() && vm.generationMode() === 'choose') {\n <ax-button class=\"w-full mt-2\" [disabled]=\"vm.areParametersReadonly()\"\n [text]=\"t('@report-management:report-generation.actions.generate-in-background') | async\" [color]=\"'default'\"\n (onClick)=\"execute({ name: 'generate-in-background' })\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-clock\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </axp-layout-footer>\n </axp-layout-start-side>\n }\n <!-- Main Content: Report Display -->\n <axp-page-content class=\"overflow-auto\">\n @if (vm.isFrozen()) {\n <!-- ----- Frozen run banner ----- -->\n <div class=\"warning-lightest-surface mb-3 p-3 rounded border border-warning-200 text-warning-800 text-sm\">\n {{ t('@report-management:report-generation.banners.frozen') | async }}\n </div>\n }\n @if (vm.hasError()) {\n <!-- Error State -->\n <axp-state-message icon=\"fa-light fa-exclamation-triangle\"\n [title]=\"'@report-management:report-viewer.messages.error.fetch-title'\"\n [description]=\"vm.error() || '@report-management:report-viewer.messages.error.unexpected'\">\n <div slot=\"actions\">\n <ax-button look=\"outline\" color=\"primary\"\n [text]=\"t('@report-management:report-viewer.actions.retry.title') | async\" (onClick)=\"vm.clearError()\">\n <ax-prefix>\n <ax-icon class=\"fa-light fa-refresh\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </axp-state-message>\n } @else if (!vm.reportDefinition()) {\n <!-- No Report Selected State -->\n <axp-state-message icon=\"fa-light fa-chart-line\"\n [title]=\"'@report-management:report-viewer.components.report-viewer.empty-states.no-report.title'\"\n [description]=\"'@report-management:report-viewer.components.report-viewer.empty-states.no-report.description'\">\n <div slot=\"actions\">\n <ax-button look=\"outline\" color=\"primary\"\n [text]=\"t('@report-management:report-viewer.actions.browse-reports.title') | async\">\n <ax-prefix>\n <ax-icon class=\"fa-light fa-folder-open\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </axp-state-message>\n <!-- ----- Background report message state (no live layout preview) ----- -->\n } @else if (vm.isBackgroundReportMode() || vm.isBackgroundQueued()) {\n <!-- ----- Mobile filters and layout selection for background reports ----- -->\n @if (deviceService.isSmall() && !vm.isBackgroundQueued()) {\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'fieldset'\"\n (contextChange)=\"handleFiltersChange($event)\" (contextInitiated)=\"handleFiltersInitiated($event)\">\n </axp-layout-renderer>\n @if (vm.availableLayouts().length > 0) {\n <div class=\"mt-4 pt-4 border-t\">\n <div class=\"flex items-center justify-between gap-2 mb-2\">\n <span class=\"text-sm font-medium\">\n {{ t('@report-management:report-generation.components.background-layouts.title') | async }}\n </span>\n </div>\n <p class=\"text-xs opacity-70 mb-3\">\n {{ t('@report-management:report-generation.components.background-layouts.description') | async }}\n </p>\n <div class=\"flex flex-col gap-2\">\n @for (layout of vm.availableLayouts(); track layout.id) {\n <label class=\"flex items-center gap-2 p-2 rounded border cursor-pointer\">\n <ax-check-box\n [ngModel]=\"vm.isBackgroundLayoutSelected(layout.id)\"\n (ngModelChange)=\"handleBackgroundLayoutToggle(layout.id, $event)\"\n ></ax-check-box>\n <ax-icon [icon]=\"layoutPageIcon(layout.icon, layout)\"></ax-icon>\n <span class=\"text-sm\">{{ getLayoutSelectionLabel(layout) | translate | async }}</span>\n </label>\n }\n </div>\n </div>\n }\n }\n @if (vm.isBackgroundQueued()) {\n <axp-state-message icon=\"fa-light fa-clock\"\n [title]=\"'@report-management:report-generation.empty-states.background.queued-title'\"\n [description]=\"'@report-management:report-generation.empty-states.background.queued-description'\">\n </axp-state-message>\n } @else {\n <axp-state-message icon=\"fa-light fa-clock\"\n [title]=\"'@report-management:report-generation.empty-states.background.title'\"\n [description]=\"'@report-management:report-generation.empty-states.background.description'\">\n </axp-state-message>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"reportDisplay\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<!-- Desktop: Report Display Area -->\n<ng-template #reportDisplay>\n @if (showReportDisplay()) {\n <!-- Desktop & Mobile: Report Display Area -->\n @if (vm.isTableLayout()) {\n <axp-report-table-view> </axp-report-table-view>\n } @else if (vm.isChartLayout()) {\n <axp-report-chart-view> </axp-report-chart-view>\n } @else if (vm.isPdfLayout()) {\n <axp-report-pdf-view> </axp-report-pdf-view>\n }\n } @else if (vm.reportDefinition()) {\n <!-- Mobile: Show filters -->\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'fieldset'\">\n </axp-layout-renderer>\n }\n</ng-template>\n\n<!-- Mobile: Execute Button -->\n@if (deviceService.isSmall() && ((!vm.isBackgroundReportMode() && !vm.hasBeenExecuted()) || (vm.isBackgroundReportMode() && !vm.isBackgroundQueued()))) {\n<axp-page-footer class=\"p-2 border-t\">\n <ax-button class=\"w-full\" [disabled]=\"!vm.canExecute() || vm.areParametersReadonly()\"\n [text]=\"(vm.isBackgroundReportMode()\n ? '@report-management:report-generation.actions.generate-in-background'\n : '@report-management:report-viewer.actions.execute.title') | translate | async\" [color]=\"'primary'\"\n (onClick)=\"execute({ name: 'execute-report' })\">\n <ax-prefix>\n <ax-icon [icon]=\"vm.isBackgroundReportMode() ? 'fa-light fa-clock' : 'fa-light fa-play'\"></ax-icon>\n </ax-prefix>\n </ax-button>\n</axp-page-footer>\n}\n", styles: ["@layer properties;.axp-report-viewer-page{display:flex;height:100%;width:100%;flex-direction:column}.axp-report-viewer-page axp-layout-start-side{height:100%;width:calc(var(--spacing, .25rem) * 80);border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}@media(width>=1920px){.axp-report-viewer-page axp-layout-start-side{width:calc(var(--spacing, .25rem) * 96)}}.axp-report-viewer-page axp-layout-start-side>axp-layout-header{height:73px;border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.axp-report-viewer-page axp-layout-start-side axp-layout-content{overflow-x:auto;padding:calc(var(--spacing, .25rem) * 4)}.axp-report-viewer-page .__mobile-filters{display:flex;height:100%;flex-direction:column;overflow-y:auto}.axp-report-viewer-page .__mobile-filters .__mobile-filters-content{flex:1}.axp-report-viewer-page .__mobile-filters .__mobile-filters-footer{position:sticky;bottom:0;border-top-style:var(--tw-border-style);border-top-width:1px;background-color:var(--color-lightest);color:var(--color-on-lightest);border-color:var(--color-border-lightest);padding-top:calc(var(--spacing, .25rem) * 2)}.axp-report-viewer-page axp-page-content{display:flex;height:100%;flex-direction:column}.axp-report-viewer-page axp-report-table-view{flex:1;overflow:hidden}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
4732
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'axp-report-viewer-page' }, template: "<axp-page-layout *translate=\"let t\">\n <!-- Left Panel: Filters (Always visible for desktop) -->\n @if (showFiltersPanel()) {\n <axp-layout-start-side>\n <axp-layout-header>\n <axp-layout-title>\n {{ t('@report-management:report-viewer.components.filters.title') | async }}\n </axp-layout-title>\n </axp-layout-header>\n <axp-layout-content>\n @if (vm.reportDefinition()) {\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'card'\"\n (contextChange)=\"handleFiltersChange($event)\" (contextInitiated)=\"handleFiltersInitiated($event)\">\n </axp-layout-renderer>\n <!-- ----- Background report layout multi-select ----- -->\n @if (vm.isBackgroundReportMode() && !vm.isBackgroundQueued() && vm.availableLayouts().length > 0) {\n <div class=\"mt-4 pt-4 border-t\">\n <div class=\"mb-2\">\n <span class=\"text-sm font-medium\">\n {{ t('@report-management:report-generation.components.background-layouts.title') | async }}\n </span>\n </div>\n <p class=\"text-xs opacity-70 mb-3\">\n {{ t('@report-management:report-generation.components.background-layouts.description') | async }}\n </p>\n <div class=\"flex flex-col gap-2\">\n @for (layout of vm.availableLayouts(); track layout.id) {\n <label class=\"flex items-center gap-2 p-2 rounded border cursor-pointer hover:bg-neutral-50\">\n <ax-check-box\n [ngModel]=\"vm.isBackgroundLayoutSelected(layout.id)\"\n (ngModelChange)=\"handleBackgroundLayoutToggle(layout.id, $event)\"\n ></ax-check-box>\n <ax-icon [icon]=\"layoutPageIcon(layout.icon, layout)\"></ax-icon>\n <span class=\"text-sm\">{{ getLayoutSelectionLabel(layout) | translate | async }}</span>\n </label>\n }\n </div>\n </div>\n }\n }\n </axp-layout-content>\n <axp-layout-footer class=\"p-4 border-t\" *translate=\"let t\">\n @if (vm.hasUnappliedFilters()) {\n <div class=\"w-full warning-lightest-surface mb-2 p-2 rounded border border-warning-200\">\n <div class=\"flex items-center gap-2 text-warning-800\">\n <ax-icon class=\"fa-light fa-exclamation-triangle text-sm\"></ax-icon>\n <span class=\"text-xs\">{{\n t('@report-management:report-viewer.components.filters.messages.warning.unapplied') | async\n }}</span>\n </div>\n </div>\n }\n @if (vm.isBackgroundReportMode() && !vm.hasSelectedBackgroundLayouts() && !vm.isBackgroundQueued()) {\n <div class=\"w-full warning-lightest-surface mb-2 p-2 rounded border border-warning-200\">\n <div class=\"flex items-center gap-2 text-warning-800\">\n <ax-icon class=\"fa-light fa-exclamation-triangle text-sm\"></ax-icon>\n <span class=\"text-xs\">{{\n t('@report-management:report-generation.errors.no-layouts-selected') | async\n }}</span>\n </div>\n </div>\n }\n <ax-button class=\"w-full\" [disabled]=\"vm.isBackgroundReportMode() ? vm.isBackgroundGenerateDisabled() : (!vm.canExecute() || vm.areParametersReadonly())\"\n [text]=\"(vm.isBackgroundReportMode()\n ? t('@report-management:report-generation.actions.generate-in-background')\n : t('@report-management:report-viewer.actions.execute.title')) | async\" [color]=\"'primary'\"\n (onClick)=\"execute({ name: 'execute-report' })\">\n <ax-prefix>\n <ax-icon [icon]=\"vm.isBackgroundReportMode() ? 'fa-light fa-clock' : 'fa-light fa-play'\"></ax-icon>\n </ax-prefix>\n </ax-button>\n <!-- ----- Secondary background generate for choose mode ----- -->\n @if (canGenerateInBackground() && vm.generationMode() === 'choose') {\n <ax-button class=\"w-full mt-2\" [disabled]=\"vm.areParametersReadonly()\"\n [text]=\"t('@report-management:report-generation.actions.generate-in-background') | async\" [color]=\"'default'\"\n (onClick)=\"execute({ name: 'generate-in-background' })\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-clock\"></ax-icon>\n </ax-prefix>\n </ax-button>\n }\n </axp-layout-footer>\n </axp-layout-start-side>\n }\n <!-- Main Content: Report Display -->\n <axp-page-content class=\"overflow-auto\">\n @if (vm.isFrozen()) {\n <!-- ----- Frozen run banner ----- -->\n <div class=\"warning-lightest-surface mb-3 p-3 rounded border border-warning-200 text-warning-800 text-sm\">\n {{ t('@report-management:report-generation.banners.frozen') | async }}\n </div>\n }\n @if (vm.hasError()) {\n <!-- Error State -->\n <axp-state-message icon=\"fa-light fa-exclamation-triangle\"\n [title]=\"'@report-management:report-viewer.messages.error.fetch-title'\"\n [description]=\"vm.error() || '@report-management:report-viewer.messages.error.unexpected'\">\n <div slot=\"actions\">\n <ax-button look=\"outline\" color=\"primary\"\n [text]=\"t('@report-management:report-viewer.actions.retry.title') | async\" (onClick)=\"vm.clearError()\">\n <ax-prefix>\n <ax-icon class=\"fa-light fa-refresh\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </axp-state-message>\n } @else if (!vm.reportDefinition()) {\n <!-- No Report Selected State -->\n <axp-state-message icon=\"fa-light fa-chart-line\"\n [title]=\"'@report-management:report-viewer.components.report-viewer.empty-states.no-report.title'\"\n [description]=\"'@report-management:report-viewer.components.report-viewer.empty-states.no-report.description'\">\n <div slot=\"actions\">\n <ax-button look=\"outline\" color=\"primary\"\n [text]=\"t('@report-management:report-viewer.actions.browse-reports.title') | async\">\n <ax-prefix>\n <ax-icon class=\"fa-light fa-folder-open\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </axp-state-message>\n <!-- ----- Background report message state (no live layout preview) ----- -->\n } @else if (vm.isBackgroundReportMode() && !vm.isFrozen()) {\n <!-- ----- Mobile filters and layout selection for background reports ----- -->\n @if (deviceService.isSmall() && !vm.isBackgroundQueued()) {\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'fieldset'\"\n (contextChange)=\"handleFiltersChange($event)\" (contextInitiated)=\"handleFiltersInitiated($event)\">\n </axp-layout-renderer>\n @if (vm.availableLayouts().length > 0) {\n <div class=\"mt-4 pt-4 border-t\">\n <div class=\"flex items-center justify-between gap-2 mb-2\">\n <span class=\"text-sm font-medium\">\n {{ t('@report-management:report-generation.components.background-layouts.title') | async }}\n </span>\n </div>\n <p class=\"text-xs opacity-70 mb-3\">\n {{ t('@report-management:report-generation.components.background-layouts.description') | async }}\n </p>\n <div class=\"flex flex-col gap-2\">\n @for (layout of vm.availableLayouts(); track layout.id) {\n <label class=\"flex items-center gap-2 p-2 rounded border cursor-pointer\">\n <ax-check-box\n [ngModel]=\"vm.isBackgroundLayoutSelected(layout.id)\"\n (ngModelChange)=\"handleBackgroundLayoutToggle(layout.id, $event)\"\n ></ax-check-box>\n <ax-icon [icon]=\"layoutPageIcon(layout.icon, layout)\"></ax-icon>\n <span class=\"text-sm\">{{ getLayoutSelectionLabel(layout) | translate | async }}</span>\n </label>\n }\n </div>\n </div>\n }\n }\n @if (vm.isBackgroundQueued()) {\n <axp-state-message icon=\"fa-light fa-clock\"\n [title]=\"'@report-management:report-generation.empty-states.background.queued-title'\"\n [description]=\"'@report-management:report-generation.empty-states.background.queued-description'\">\n </axp-state-message>\n } @else {\n <axp-state-message icon=\"fa-light fa-clock\"\n [title]=\"'@report-management:report-generation.empty-states.background.title'\"\n [description]=\"'@report-management:report-generation.empty-states.background.description'\">\n </axp-state-message>\n }\n } @else {\n <ng-container *ngTemplateOutlet=\"reportDisplay\"></ng-container>\n }\n </axp-page-content>\n</axp-page-layout>\n\n<!-- Desktop: Report Display Area -->\n<ng-template #reportDisplay>\n @if (showReportDisplay()) {\n <!-- Desktop & Mobile: Report Display Area -->\n @if (vm.isTableLayout()) {\n <axp-report-table-view> </axp-report-table-view>\n } @else if (vm.isChartLayout()) {\n <axp-report-chart-view> </axp-report-chart-view>\n } @else if (vm.isPdfLayout()) {\n <axp-report-pdf-view> </axp-report-pdf-view>\n }\n } @else if (vm.reportDefinition()) {\n <!-- Mobile: Show filters -->\n <axp-layout-renderer #filtersForm [layout]=\"vm.formDefinition()\" [context]=\"vm.filterContext()\" [look]=\"'fieldset'\">\n </axp-layout-renderer>\n }\n</ng-template>\n\n<!-- Mobile: Execute Button -->\n@if (deviceService.isSmall() && ((!vm.isBackgroundReportMode() && !vm.hasBeenExecuted()) || (vm.isBackgroundReportMode() && !vm.isBackgroundQueued()))) {\n<axp-page-footer class=\"p-2 border-t\">\n <ax-button class=\"w-full\" [disabled]=\"vm.isBackgroundReportMode() ? vm.isBackgroundGenerateDisabled() : (!vm.canExecute() || vm.areParametersReadonly())\"\n [text]=\"(vm.isBackgroundReportMode()\n ? '@report-management:report-generation.actions.generate-in-background'\n : '@report-management:report-viewer.actions.execute.title') | translate | async\" [color]=\"'primary'\"\n (onClick)=\"execute({ name: 'execute-report' })\">\n <ax-prefix>\n <ax-icon [icon]=\"vm.isBackgroundReportMode() ? 'fa-light fa-clock' : 'fa-light fa-play'\"></ax-icon>\n </ax-prefix>\n </ax-button>\n</axp-page-footer>\n}\n", styles: ["@layer properties;.axp-report-viewer-page{display:flex;height:100%;width:100%;flex-direction:column}.axp-report-viewer-page axp-layout-start-side{height:100%;width:calc(var(--spacing, .25rem) * 80);border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}@media(width>=1920px){.axp-report-viewer-page axp-layout-start-side{width:calc(var(--spacing, .25rem) * 96)}}.axp-report-viewer-page axp-layout-start-side>axp-layout-header{height:73px;border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.axp-report-viewer-page axp-layout-start-side axp-layout-content{overflow-x:auto;padding:calc(var(--spacing, .25rem) * 4)}.axp-report-viewer-page .__mobile-filters{display:flex;height:100%;flex-direction:column;overflow-y:auto}.axp-report-viewer-page .__mobile-filters .__mobile-filters-content{flex:1}.axp-report-viewer-page .__mobile-filters .__mobile-filters-footer{position:sticky;bottom:0;border-top-style:var(--tw-border-style);border-top-width:1px;background-color:var(--color-lightest);color:var(--color-on-lightest);border-color:var(--color-border-lightest);padding-top:calc(var(--spacing, .25rem) * 2)}.axp-report-viewer-page axp-page-content{display:flex;height:100%;flex-direction:column}.axp-report-viewer-page axp-report-table-view{flex:1;overflow:hidden}@property --tw-border-style{syntax: \"*\"; inherits: false; initial-value: solid;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style: solid}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
4101
4733
  }], ctorParameters: () => [], propDecorators: { reportId: [{ type: i0.Input, args: [{ isSignal: true, alias: "reportId", required: false }] }], reportParameters: [{ type: i0.Input, args: [{ isSignal: true, alias: "reportParameters", required: false }] }], filtersForm: [{ type: i0.ViewChild, args: ['filtersForm', { isSignal: true }] }] } });
4102
4734
 
4103
4735
  var reportViewerPage_component = /*#__PURE__*/Object.freeze({
@@ -4229,4 +4861,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
4229
4861
  */
4230
4862
 
4231
4863
  export { AXPReportViewerViewModel as A, mapEntityBindingParameters as B, queryResultToChartSeries as C, queryResultToPdfSource as D, ENTITY_REPORTS_PAGE_COMPONENT_KEY as E, resolveReportParameterValues as F, resolveTodayDateRange as G, toReportDateRangeFilterValue as H, OPEN_REPORT_IN_POPUP_COMMAND$1 as O, AXPOpenReportViewerCommand as a, resolveReportFilterDefaultValue as b, reportLayoutTitleKey as c, AXPReportTableViewComponent as d, AXPReportChartViewComponent as e, AXPReportPdfViewComponent as f, AXPReportViewerService as g, bindingShowsOnPage as h, isDistinctLayoutTitle as i, buildOpenInPopupCommandOptions as j, resolveBindingTitle as k, resolveBindingDescription as l, AXBarChartReportMapper as m, AXLineChartReportMapper as n, AXMPermissionDefinitionProvider as o, AXMReportManagementModule as p, AXPOpenReportInPopupCommand as q, reportLayoutPageIcon as r, AXPReportBarChartViewComponent as s, AXPReportViewerPageComponent as t, AXPieChartReportMapper as u, collectReportParameterDefaults as v, convertContextDataToFilterClause as w, flattenReportParametersToQueryParams as x, formatReportDateOnly as y, isReportDateRangeObject as z };
4232
- //# sourceMappingURL=acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-BDCuhd5O.mjs.map
4864
+ //# sourceMappingURL=acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-DENcGULA.mjs.map