@acorex-platform/module-report-management-client 0.0.38 → 0.0.46
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.
- package/fesm2022/acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-D5dCG62j.mjs +1545 -0
- package/fesm2022/acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-D5dCG62j.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-query-snapshot-policy.mjs +14 -0
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-query-snapshot-policy.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-query-snapshot.mjs +14 -0
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-query-snapshot.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report-category.mjs +13 -0
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report-category.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report-generation-request.mjs +14 -0
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report-generation-request.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report.mjs +14 -0
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entities-report.mjs.map +1 -1
- package/fesm2022/{acorex-platform-module-report-management-client-contracts-entity-catalog-acorex-platform-module-report-management-client-contracts-entity-catalog-CgrfJQLl.mjs → acorex-platform-module-report-management-client-contracts-entity-catalog-acorex-platform-module-report-management-client-contracts-entity-catalog-CboBAIna.mjs} +6 -6
- package/fesm2022/{acorex-platform-module-report-management-client-contracts-entity-catalog-acorex-platform-module-report-management-client-contracts-entity-catalog-CgrfJQLl.mjs.map → acorex-platform-module-report-management-client-contracts-entity-catalog-acorex-platform-module-report-management-client-contracts-entity-catalog-CboBAIna.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot-policy.entity-D50GUBH6.mjs → acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot-policy.entity-BUY06Dz0.mjs} +16 -2
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot-policy.entity-BUY06Dz0.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot.entity-DuR9ZuN2.mjs → acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot.entity-CnnX3xZt.mjs} +16 -2
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot.entity-CnnX3xZt.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-report-management-client-contracts-entity-catalog-report-category.entity-Dc4uvjsG.mjs → acorex-platform-module-report-management-client-contracts-entity-catalog-report-category.entity-BQFdNXAm.mjs} +15 -2
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report-category.entity-BQFdNXAm.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-report-management-client-contracts-entity-catalog-report-generation-request.entity-mJUQuk89.mjs → acorex-platform-module-report-management-client-contracts-entity-catalog-report-generation-request.entity-D381cWLE.mjs} +16 -2
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report-generation-request.entity-D381cWLE.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-report-management-client-contracts-entity-catalog-report.entity-CMDFRHeK.mjs → acorex-platform-module-report-management-client-contracts-entity-catalog-report.entity-Bwui0M8X.mjs} +16 -2
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report.entity-Bwui0M8X.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog.mjs +1 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts.mjs +21 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts.mjs.map +1 -1
- package/fesm2022/{acorex-platform-module-report-management-client-entity-reports-page-component.provider-B0dU2K_E.mjs → acorex-platform-module-report-management-client-entity-reports-page-component.provider-0eSOzb0y.mjs} +3 -3
- package/fesm2022/{acorex-platform-module-report-management-client-entity-reports-page-component.provider-B0dU2K_E.mjs.map → acorex-platform-module-report-management-client-entity-reports-page-component.provider-0eSOzb0y.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-module-report-management-client-entity-reports-page.component-D4SAGGn-.mjs → acorex-platform-module-report-management-client-entity-reports-page.component-CHBZbbaH.mjs} +4 -4
- package/fesm2022/{acorex-platform-module-report-management-client-entity-reports-page.component-D4SAGGn-.mjs.map → acorex-platform-module-report-management-client-entity-reports-page.component-CHBZbbaH.mjs.map} +1 -1
- package/fesm2022/acorex-platform-module-report-management-client-report-layout-switcher.util-BGhHRYfe.mjs +2266 -0
- package/fesm2022/acorex-platform-module-report-management-client-report-layout-switcher.util-BGhHRYfe.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-report-management-client-report-popup-viewer.component-Dg3IxjGy.mjs → acorex-platform-module-report-management-client-report-popup-viewer.component-BJq1BeUz.mjs} +5 -4
- package/fesm2022/{acorex-platform-module-report-management-client-report-popup-viewer.component-Dg3IxjGy.mjs.map → acorex-platform-module-report-management-client-report-popup-viewer.component-BJq1BeUz.mjs.map} +1 -1
- package/fesm2022/acorex-platform-module-report-management-client-report-viewer-page.component-YUdzA4B9.mjs +934 -0
- package/fesm2022/acorex-platform-module-report-management-client-report-viewer-page.component-YUdzA4B9.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-report-management-client.mjs +1 -1
- package/package.json +1 -1
- package/types/acorex-platform-module-report-management-client-contracts.d.ts +8 -2
- package/types/acorex-platform-module-report-management-client.d.ts +16 -831
- package/fesm2022/acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-DUdUNlnp.mjs +0 -4864
- package/fesm2022/acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-DUdUNlnp.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot-policy.entity-D50GUBH6.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-query-snapshot.entity-DuR9ZuN2.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report-category.entity-Dc4uvjsG.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report-generation-request.entity-mJUQuk89.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-report-management-client-contracts-entity-catalog-report.entity-CMDFRHeK.mjs.map +0 -1
|
@@ -0,0 +1,1545 @@
|
|
|
1
|
+
import { AXPExpressionEvaluatorService, provideAppStartUpTask, AXP_APP_STARTUP_PRIORITY, AXP_FEATURE_DEFINITION_PROVIDER, provideLazyProvider } from '@acorex-platform/framework-client/core';
|
|
2
|
+
import { AXPDomainModule } from '@acorex-platform/framework-client/domain';
|
|
3
|
+
import { AXPEntityService, resolveEntityPluginDetailPageOrder, AXP_ENTITY_ACTION_PLUGIN, provideEntity } from '@acorex-platform/framework-client/layout/entity';
|
|
4
|
+
import { AXP_PAGE_COMPONENT_PROVIDER } from '@acorex-platform/framework-client/layout/components';
|
|
5
|
+
import { AXPRootLayoutComponent } from '@acorex-platform/framework-client/themes/default';
|
|
6
|
+
import * as i0 from '@angular/core';
|
|
7
|
+
import { Injectable, inject, DestroyRef, Injector, effect, NgModule } from '@angular/core';
|
|
8
|
+
import { ROUTES } from '@angular/router';
|
|
9
|
+
import { AXPInsightsReportingPermissionKeys, RootConfig, AXPReportManagementMenuKeys, AXPReportManagementFeatureKeys, AXPReportManagementSettings, AXP_REPORT_MANAGEMENT_GENERATION_SETTING_DEFAULTS, buildReportGenerationNotificationCorrelationId, AXMReportManagementQueryKeys, REPORT_GENERATION_NOTIFICATION_DEFINITION_KEY } from '@acorex-platform/module-report-management-client/contracts';
|
|
10
|
+
import { AXPTabNavigationService, AXPBackgroundOperationHandler, AXP_BACKGROUND_OPERATION_HANDLERS, AXP_USER_NOTIFICATION_SERVICE, AXP_PROTECTED_ROUTE_GUARDS, AXP_MENU_PROVIDER, AXP_SETTING_DEFINITION_PROVIDER } from '@acorex-platform/framework-client/common';
|
|
11
|
+
import { AXPSessionService, AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex-platform/framework-client/auth';
|
|
12
|
+
import { AXPQueryService, AXPCommandService, AXPRuntimeModule, provideCommandSetups } from '@acorex-platform/framework-client/runtime';
|
|
13
|
+
import { AXPCommonPermissionGroupKeys } from '@acorex-platform/module-common-client/contracts';
|
|
14
|
+
import { AXPWidgetsList, runtimeQueryPayloadData, resolveMultiLanguageString, runtimeQueryObjectData, runtimeQueryCollectionData } from '@acorex-platform/framework-shared/core';
|
|
15
|
+
import { get, isString } from 'lodash-es';
|
|
16
|
+
import { AXTranslationService } from '@acorex/core/translation';
|
|
17
|
+
import { AXPInAppInboxConnectorService } from '@acorex-platform/module-communication-management-client';
|
|
18
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
19
|
+
import { map, distinctUntilChanged } from 'rxjs';
|
|
20
|
+
import { AXPopupService } from '@acorex/components/popup';
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region ----- Permission Definition Provider -----
|
|
24
|
+
/**
|
|
25
|
+
* Registers report capabilities into the shared Insights & Reporting group.
|
|
26
|
+
*/
|
|
27
|
+
class AXMPermissionDefinitionProvider {
|
|
28
|
+
async define(context) {
|
|
29
|
+
const keys = AXPInsightsReportingPermissionKeys;
|
|
30
|
+
const groups = AXPCommonPermissionGroupKeys;
|
|
31
|
+
const P = '@common:insights-reporting.permissions';
|
|
32
|
+
const G = '@common:permission-groups';
|
|
33
|
+
context.addGroup(groups.InsightsReporting.Root, `${G}.insights-reporting.title`, `${G}.insights-reporting.description`, { order: 100 });
|
|
34
|
+
context
|
|
35
|
+
.addGroup(groups.InsightsReporting.Reports, `${G}.insights-reporting-reports.title`, `${G}.insights-reporting-reports.description`, { parent: groups.InsightsReporting.Root, order: 10 })
|
|
36
|
+
.addPermission(keys.Access, `${P}.access.title`, `${P}.access.description`)
|
|
37
|
+
.endPermission()
|
|
38
|
+
.addPermission(keys.MaintainReports, `${P}.maintain-reports.title`, `${P}.maintain-reports.description`)
|
|
39
|
+
.endPermission()
|
|
40
|
+
.addPermission(keys.Setup, `${P}.setup.title`, `${P}.setup.description`)
|
|
41
|
+
.endPermission()
|
|
42
|
+
.endGroup();
|
|
43
|
+
}
|
|
44
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMPermissionDefinitionProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
45
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMPermissionDefinitionProvider }); }
|
|
46
|
+
}
|
|
47
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMPermissionDefinitionProvider, decorators: [{
|
|
48
|
+
type: Injectable
|
|
49
|
+
}] });
|
|
50
|
+
|
|
51
|
+
class AXMMenuProvider {
|
|
52
|
+
constructor() {
|
|
53
|
+
this.entityService = inject(AXPEntityService);
|
|
54
|
+
}
|
|
55
|
+
async provide(context) {
|
|
56
|
+
const module = RootConfig.module;
|
|
57
|
+
context.addItems([
|
|
58
|
+
{
|
|
59
|
+
name: AXPReportManagementMenuKeys.Reports,
|
|
60
|
+
text: RootConfig.entities.report.titlePlural,
|
|
61
|
+
path: this.entityService.withEntity(module.name, RootConfig.entities.report.name).createPath(),
|
|
62
|
+
icon: RootConfig.entities.report.icon,
|
|
63
|
+
priority: 8300,
|
|
64
|
+
policy: {
|
|
65
|
+
features: [module.name],
|
|
66
|
+
permissions: [AXPInsightsReportingPermissionKeys.MaintainReports],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//#region ---- Imports ----
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region ---- Feature Definition Provider ----
|
|
76
|
+
/**
|
|
77
|
+
* Report Management Feature Definition Provider.
|
|
78
|
+
* Provides feature definitions for the Report Management module.
|
|
79
|
+
*/
|
|
80
|
+
class AXPReportManagementFeatureDefinitionProvider {
|
|
81
|
+
async provide(context) {
|
|
82
|
+
// Report Builder feature
|
|
83
|
+
context.addFeature(AXPReportManagementFeatureKeys.ReportBuilder, {
|
|
84
|
+
title: '@report-management:features.report-builder.title',
|
|
85
|
+
description: '@report-management:features.report-builder.description',
|
|
86
|
+
defaultValue: true,
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
});
|
|
89
|
+
// Saved Reports feature
|
|
90
|
+
context.addFeature(AXPReportManagementFeatureKeys.SavedReports, {
|
|
91
|
+
title: '@report-management:features.saved-reports.title',
|
|
92
|
+
description: '@report-management:features.saved-reports.description',
|
|
93
|
+
defaultValue: true,
|
|
94
|
+
type: 'boolean',
|
|
95
|
+
});
|
|
96
|
+
// Scheduled Reports feature
|
|
97
|
+
context.addFeature(AXPReportManagementFeatureKeys.ScheduledReports, {
|
|
98
|
+
title: '@report-management:features.scheduled-reports.title',
|
|
99
|
+
description: '@report-management:features.scheduled-reports.description',
|
|
100
|
+
defaultValue: true,
|
|
101
|
+
type: 'boolean',
|
|
102
|
+
});
|
|
103
|
+
// Background Reports feature
|
|
104
|
+
context.addFeature(AXPReportManagementFeatureKeys.BackgroundReports, {
|
|
105
|
+
title: '@report-management:features.background-reports.title',
|
|
106
|
+
description: '@report-management:features.background-reports.description',
|
|
107
|
+
defaultValue: true,
|
|
108
|
+
type: 'boolean',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
|
|
114
|
+
//#region ----- Report management settings -----
|
|
115
|
+
const DEDUPE_POLICY_ITEMS = [
|
|
116
|
+
{
|
|
117
|
+
value: 'none',
|
|
118
|
+
text: '@report-management:settings.generation.items.dedupe-policy.options.none',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
value: 'in-flight',
|
|
122
|
+
text: '@report-management:settings.generation.items.dedupe-policy.options.in-flight',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
value: 'in-flight-or-cooldown',
|
|
126
|
+
text: '@report-management:settings.generation.items.dedupe-policy.options.in-flight-or-cooldown',
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
class AXMReportManagementSettingProvider {
|
|
130
|
+
async provide(context) {
|
|
131
|
+
const cooldownPolicy = `{{ context.eval('${AXPReportManagementSettings.GenerationDedupePolicy}') === 'in-flight-or-cooldown' }}`;
|
|
132
|
+
context
|
|
133
|
+
.addGroup('report-management', '@report-management:settings.group.title', '@report-management:settings.group.description', 'fa-light fa-chart-line', undefined, 840)
|
|
134
|
+
.addSection('generation', '@report-management:settings.generation.title', '@report-management:settings.generation.description')
|
|
135
|
+
.addSetting({
|
|
136
|
+
key: AXPReportManagementSettings.GenerationDedupePolicy,
|
|
137
|
+
title: '@report-management:settings.generation.items.dedupe-policy.title',
|
|
138
|
+
description: '@report-management:settings.generation.items.dedupe-policy.description',
|
|
139
|
+
scope: 'P',
|
|
140
|
+
overridableAt: [],
|
|
141
|
+
isInherited: false,
|
|
142
|
+
defaultValue: AXP_REPORT_MANAGEMENT_GENERATION_SETTING_DEFAULTS[AXPReportManagementSettings.GenerationDedupePolicy],
|
|
143
|
+
widget: {
|
|
144
|
+
type: AXPWidgetsList.Editors.SelectBox,
|
|
145
|
+
options: {
|
|
146
|
+
valueField: 'value',
|
|
147
|
+
textField: 'text',
|
|
148
|
+
dataSource: DEDUPE_POLICY_ITEMS,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
validationRules: [{ rule: 'required' }],
|
|
152
|
+
})
|
|
153
|
+
.addSetting({
|
|
154
|
+
key: AXPReportManagementSettings.GenerationDedupeCooldownMinutes,
|
|
155
|
+
title: '@report-management:settings.generation.items.dedupe-cooldown-minutes.title',
|
|
156
|
+
description: '@report-management:settings.generation.items.dedupe-cooldown-minutes.description',
|
|
157
|
+
scope: 'P',
|
|
158
|
+
overridableAt: [],
|
|
159
|
+
isInherited: false,
|
|
160
|
+
defaultValue: AXP_REPORT_MANAGEMENT_GENERATION_SETTING_DEFAULTS[AXPReportManagementSettings.GenerationDedupeCooldownMinutes],
|
|
161
|
+
widget: {
|
|
162
|
+
type: AXPWidgetsList.Editors.NumberBox,
|
|
163
|
+
options: { minValue: 0, maxValue: 1440, visibility: cooldownPolicy },
|
|
164
|
+
},
|
|
165
|
+
validationRules: [{ rule: 'required' }],
|
|
166
|
+
})
|
|
167
|
+
.addSetting({
|
|
168
|
+
key: AXPReportManagementSettings.GenerationMaxConcurrentPerUser,
|
|
169
|
+
title: '@report-management:settings.generation.items.max-concurrent-per-user.title',
|
|
170
|
+
description: '@report-management:settings.generation.items.max-concurrent-per-user.description',
|
|
171
|
+
scope: 'P',
|
|
172
|
+
overridableAt: [],
|
|
173
|
+
isInherited: false,
|
|
174
|
+
defaultValue: AXP_REPORT_MANAGEMENT_GENERATION_SETTING_DEFAULTS[AXPReportManagementSettings.GenerationMaxConcurrentPerUser],
|
|
175
|
+
widget: {
|
|
176
|
+
type: AXPWidgetsList.Editors.NumberBox,
|
|
177
|
+
options: { minValue: 0, maxValue: 50 },
|
|
178
|
+
},
|
|
179
|
+
validationRules: [{ rule: 'required' }],
|
|
180
|
+
})
|
|
181
|
+
.endSection()
|
|
182
|
+
.endGroup();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
|
|
187
|
+
//#region ----- Types -----
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region ----- Date helpers -----
|
|
190
|
+
/** Local calendar date as `yyyy-MM-dd` (session "today" for report ranges). */
|
|
191
|
+
function formatReportDateOnly(date = new Date()) {
|
|
192
|
+
const year = date.getFullYear();
|
|
193
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
194
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
195
|
+
return `${year}-${month}-${day}`;
|
|
196
|
+
}
|
|
197
|
+
function resolveTodayDateRange(date = new Date()) {
|
|
198
|
+
const day = formatReportDateOnly(date);
|
|
199
|
+
return { from: day, to: day };
|
|
200
|
+
}
|
|
201
|
+
function isReportDateRangeObject(value) {
|
|
202
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const raw = value;
|
|
206
|
+
const from = raw['from'] ?? raw['start'];
|
|
207
|
+
const to = raw['to'] ?? raw['end'];
|
|
208
|
+
return typeof from === 'string' && typeof to === 'string' && !!from.trim() && !!to.trim();
|
|
209
|
+
}
|
|
210
|
+
function toReportDateRangeFilterValue(range) {
|
|
211
|
+
return {
|
|
212
|
+
operation: { type: 'between' },
|
|
213
|
+
value: { from: range.from, to: range.to },
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Converts report default / binding values into filter-form context shape.
|
|
218
|
+
* - `'today'` and `{ from, to }` → `{ operation: between, value }`
|
|
219
|
+
* - existing filter objects are left unchanged
|
|
220
|
+
*/
|
|
221
|
+
function resolveReportFilterDefaultValue(value, now = new Date()) {
|
|
222
|
+
if (value === 'today') {
|
|
223
|
+
return toReportDateRangeFilterValue(resolveTodayDateRange(now));
|
|
224
|
+
}
|
|
225
|
+
if (isReportDateRangeObject(value)) {
|
|
226
|
+
return toReportDateRangeFilterValue({
|
|
227
|
+
from: String(value.from ?? value.start),
|
|
228
|
+
to: String(value.to ?? value.end),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return value;
|
|
232
|
+
}
|
|
233
|
+
function collectReportParameterDefaults(parameterGroups, now = new Date()) {
|
|
234
|
+
const defaults = {};
|
|
235
|
+
for (const group of parameterGroups ?? []) {
|
|
236
|
+
for (const param of group.parameters ?? []) {
|
|
237
|
+
const rawDefault = param.widget?.defaultValue ?? param.widget?.options?.['defaultValue'];
|
|
238
|
+
if (rawDefault === undefined) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
defaults[param.path] = resolveReportFilterDefaultValue(rawDefault, now);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return defaults;
|
|
245
|
+
}
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region ----- Parameter resolution -----
|
|
248
|
+
/**
|
|
249
|
+
* Resolves special parameter tokens after expression evaluation.
|
|
250
|
+
* - `dateRange: 'today'` → `{ from, to }` for the local calendar day
|
|
251
|
+
*/
|
|
252
|
+
function resolveReportParameterValues(parameters, now = new Date()) {
|
|
253
|
+
if (!parameters) {
|
|
254
|
+
return {};
|
|
255
|
+
}
|
|
256
|
+
const resolved = {};
|
|
257
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
258
|
+
if (value === 'today') {
|
|
259
|
+
resolved[key] = resolveTodayDateRange(now);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
resolved[key] = value;
|
|
263
|
+
}
|
|
264
|
+
return resolved;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Maps entity-binding parameter declarations into OpenInPopup command parameters.
|
|
268
|
+
* Field paths become `context.eval` expressions; `today` and `{from,to}` stay literal.
|
|
269
|
+
*/
|
|
270
|
+
function mapEntityBindingParameters(parameters) {
|
|
271
|
+
if (!parameters) {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
return Object.fromEntries(Object.entries(parameters).map(([key, value]) => [key, toBindingParameterExpression(value)]));
|
|
275
|
+
}
|
|
276
|
+
function toBindingParameterExpression(value) {
|
|
277
|
+
if (value === 'today') {
|
|
278
|
+
return 'today';
|
|
279
|
+
}
|
|
280
|
+
if (isReportDateRangeObject(value)) {
|
|
281
|
+
return {
|
|
282
|
+
from: String(value.from ?? value.start),
|
|
283
|
+
to: String(value.to ?? value.end),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (typeof value !== 'string') {
|
|
287
|
+
return value;
|
|
288
|
+
}
|
|
289
|
+
const trimmed = value.trim();
|
|
290
|
+
if (!trimmed || trimmed.includes('{{') || trimmed.includes('context.eval')) {
|
|
291
|
+
return value;
|
|
292
|
+
}
|
|
293
|
+
return `{{ context.eval('${trimmed}') }}`;
|
|
294
|
+
}
|
|
295
|
+
//#endregion
|
|
296
|
+
//#region ----- Viewer query params -----
|
|
297
|
+
/**
|
|
298
|
+
* Flattens popup/filter parameters into report-viewer URL query params
|
|
299
|
+
* (`field.operation.type` + `field.value` / nested `from`/`to`).
|
|
300
|
+
*/
|
|
301
|
+
function flattenReportParametersToQueryParams(parameters) {
|
|
302
|
+
const result = {};
|
|
303
|
+
for (const [field, rawValue] of Object.entries(parameters)) {
|
|
304
|
+
if (rawValue === null || rawValue === undefined || rawValue === '') {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (isReportDateRangeObject(rawValue)) {
|
|
308
|
+
const from = String(rawValue.from ?? rawValue.start);
|
|
309
|
+
const to = String(rawValue.to ?? rawValue.end);
|
|
310
|
+
result[`${field}.operation.type`] = 'between';
|
|
311
|
+
result[`${field}.value.from`] = from;
|
|
312
|
+
result[`${field}.value.to`] = to;
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (typeof rawValue === 'object' && !Array.isArray(rawValue) && 'operation' in rawValue) {
|
|
316
|
+
const filterObj = rawValue;
|
|
317
|
+
if (filterObj.operation?.type) {
|
|
318
|
+
result[`${field}.operation.type`] = String(filterObj.operation.type);
|
|
319
|
+
}
|
|
320
|
+
if (isReportDateRangeObject(filterObj.value)) {
|
|
321
|
+
result[`${field}.value.from`] = String(filterObj.value.from ?? filterObj.value.start);
|
|
322
|
+
result[`${field}.value.to`] = String(filterObj.value.to ?? filterObj.value.end);
|
|
323
|
+
}
|
|
324
|
+
else if (filterObj.value !== undefined && filterObj.value !== null && filterObj.value !== '') {
|
|
325
|
+
result[`${field}.value`] = Array.isArray(filterObj.value)
|
|
326
|
+
? filterObj.value.map(String).join(',')
|
|
327
|
+
: String(filterObj.value);
|
|
328
|
+
}
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
result[`${field}.operation.type`] = Array.isArray(rawValue) ? 'in' : 'equal';
|
|
332
|
+
result[`${field}.value`] = Array.isArray(rawValue) ? rawValue.map(String).join(',') : String(rawValue);
|
|
333
|
+
}
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
//#endregion
|
|
337
|
+
|
|
338
|
+
//#region ----- Imports -----
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region ----- Constants -----
|
|
341
|
+
const OPEN_REPORT_IN_POPUP_COMMAND = 'ReportManagement.Report:OpenInPopup';
|
|
342
|
+
const ENTITY_REPORTS_PAGE_COMPONENT_KEY = 'entity-reports-page';
|
|
343
|
+
//#endregion
|
|
344
|
+
//#region ----- Display helpers -----
|
|
345
|
+
function normalizeReportEntityDisplays(display) {
|
|
346
|
+
if (!display) {
|
|
347
|
+
return new Set(['page']);
|
|
348
|
+
}
|
|
349
|
+
const list = Array.isArray(display) ? display : [display];
|
|
350
|
+
return new Set(list);
|
|
351
|
+
}
|
|
352
|
+
function bindingShowsOnPage(binding) {
|
|
353
|
+
const displays = normalizeReportEntityDisplays(binding.display);
|
|
354
|
+
// Default / empty → page. Explicit lists without `page` hide from the Reports page.
|
|
355
|
+
return displays.has('page') || displays.size === 0;
|
|
356
|
+
}
|
|
357
|
+
function resolveBindingTitle(report, binding) {
|
|
358
|
+
if (typeof binding.title === 'string' && binding.title.trim()) {
|
|
359
|
+
return binding.title.trim();
|
|
360
|
+
}
|
|
361
|
+
return report.title;
|
|
362
|
+
}
|
|
363
|
+
function resolveBindingDescription(report, binding) {
|
|
364
|
+
if (typeof binding.description === 'string' && binding.description.trim()) {
|
|
365
|
+
return binding.description.trim();
|
|
366
|
+
}
|
|
367
|
+
if (typeof report.description === 'string') {
|
|
368
|
+
const trimmed = report.description.trim();
|
|
369
|
+
return trimmed || undefined;
|
|
370
|
+
}
|
|
371
|
+
return report.description;
|
|
372
|
+
}
|
|
373
|
+
function buildOpenInPopupCommandOptions(report, binding) {
|
|
374
|
+
return {
|
|
375
|
+
reportId: report.id,
|
|
376
|
+
layoutId: binding.layoutId,
|
|
377
|
+
title: resolveBindingTitle(report, binding),
|
|
378
|
+
parameters: mapEntityBindingParameters(binding.parameters),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function extractEntityBindings(report, entityType) {
|
|
382
|
+
const bindings = report.entities ?? [];
|
|
383
|
+
return bindings
|
|
384
|
+
.filter((binding) => binding?.entity === entityType)
|
|
385
|
+
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
386
|
+
}
|
|
387
|
+
//#endregion
|
|
388
|
+
|
|
389
|
+
//#region ----- Imports -----
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region ----- Plugin factory -----
|
|
392
|
+
/**
|
|
393
|
+
* Entity `reports` plugin — contributes a Reports detail page for definitions
|
|
394
|
+
* bound via `entities[]`. Reports open from that page (not entity Actions).
|
|
395
|
+
*
|
|
396
|
+
* Takes {@link Injector} for factory signature parity with other entity plugins.
|
|
397
|
+
*/
|
|
398
|
+
function createEntityReportsPlugin(_injector) {
|
|
399
|
+
return {
|
|
400
|
+
name: 'reports',
|
|
401
|
+
order: 55,
|
|
402
|
+
apply: async (ctx, options) => {
|
|
403
|
+
const opts = options ?? {};
|
|
404
|
+
const entityType = `${ctx.entity.module}.${ctx.entity.name}`;
|
|
405
|
+
const pageTitle = opts.title?.trim() || '@report-management:entity-reports.page.title';
|
|
406
|
+
//#region ----- Reports detail page -----
|
|
407
|
+
ctx.entity.pages ??= [];
|
|
408
|
+
const pageExists = ctx.entity.pages.some((p) => p.componentKey === ENTITY_REPORTS_PAGE_COMPONENT_KEY);
|
|
409
|
+
const pageOrder = resolveEntityPluginDetailPageOrder({ relatedEntities: ctx.entity.relatedEntities, pages: ctx.entity.pages }, 'reports', {
|
|
410
|
+
componentKey: ENTITY_REPORTS_PAGE_COMPONENT_KEY,
|
|
411
|
+
skipPage: pageExists ? { componentKey: ENTITY_REPORTS_PAGE_COMPONENT_KEY } : undefined,
|
|
412
|
+
});
|
|
413
|
+
const page = {
|
|
414
|
+
componentKey: ENTITY_REPORTS_PAGE_COMPONENT_KEY,
|
|
415
|
+
title: pageTitle,
|
|
416
|
+
icon: opts.icon ?? 'fa-light fa-chart-mixed',
|
|
417
|
+
layout: { order: pageOrder },
|
|
418
|
+
options: { entityType },
|
|
419
|
+
};
|
|
420
|
+
if (!pageExists) {
|
|
421
|
+
ctx.entity.pages.push(page);
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
const pages = ctx.entity.pages;
|
|
425
|
+
const index = pages.findIndex((p) => p.componentKey === ENTITY_REPORTS_PAGE_COMPONENT_KEY);
|
|
426
|
+
if (index >= 0) {
|
|
427
|
+
pages[index] = { ...pages[index], ...page };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
//#endregion
|
|
431
|
+
ctx.entity.extensions ??= {};
|
|
432
|
+
ctx.entity.extensions.reports = {
|
|
433
|
+
entityType,
|
|
434
|
+
};
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
//#endregion
|
|
439
|
+
|
|
440
|
+
//#region ----- Imports -----
|
|
441
|
+
//#endregion
|
|
442
|
+
//#region ----- Command -----
|
|
443
|
+
/** Opens a report in the full-page report viewer. */
|
|
444
|
+
class AXPOpenReportViewerCommand {
|
|
445
|
+
constructor() {
|
|
446
|
+
//#region ----- Services & Dependencies -----
|
|
447
|
+
this.sessionService = inject(AXPSessionService);
|
|
448
|
+
this.tabNavigation = inject(AXPTabNavigationService);
|
|
449
|
+
this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
|
|
450
|
+
this.translate = inject(AXTranslationService);
|
|
451
|
+
this.inboxConnector = inject(AXPInAppInboxConnectorService, { optional: true });
|
|
452
|
+
}
|
|
453
|
+
//#endregion
|
|
454
|
+
//#region ----- Command Implementation -----
|
|
455
|
+
async execute(input) {
|
|
456
|
+
const rowData = this.resolveRowData(input);
|
|
457
|
+
const options = await this.resolveOptions(input, rowData);
|
|
458
|
+
if (!options.reportId) {
|
|
459
|
+
return {
|
|
460
|
+
success: false,
|
|
461
|
+
message: {
|
|
462
|
+
text: await this.translate.translateAsync('@report-management:report-viewer.messages.error.invalid-popup-target'),
|
|
463
|
+
},
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
const runId = typeof options.runId === 'string' ? options.runId.trim() : '';
|
|
467
|
+
if (runId) {
|
|
468
|
+
await this.dismissReportGenerationInboxNotification(runId);
|
|
469
|
+
}
|
|
470
|
+
const appName = this.sessionService.application?.name;
|
|
471
|
+
if (!appName) {
|
|
472
|
+
return {
|
|
473
|
+
success: false,
|
|
474
|
+
message: {
|
|
475
|
+
text: await this.translate.translateAsync('@report-management:report-viewer.messages.error.invalid-popup-target'),
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
// Put reportId in the path (not queryParams): tabular navigation drops NavigationExtras.
|
|
480
|
+
const path = runId
|
|
481
|
+
? `${appName}/${RootConfig.module.route}/viewer/${options.reportId}?runId=${encodeURIComponent(runId)}`
|
|
482
|
+
: `${appName}/${RootConfig.module.route}/viewer/${options.reportId}`;
|
|
483
|
+
await this.tabNavigation.navigate(path);
|
|
484
|
+
return { success: true, kind: 'object', data: undefined };
|
|
485
|
+
}
|
|
486
|
+
//#endregion
|
|
487
|
+
//#region ----- Helpers -----
|
|
488
|
+
async dismissReportGenerationInboxNotification(runId) {
|
|
489
|
+
if (!this.inboxConnector) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
try {
|
|
493
|
+
await this.inboxConnector.dismissByCorrelationId(buildReportGenerationNotificationCorrelationId(runId));
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
// Best-effort — navigation should still proceed.
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
resolveRowData(input) {
|
|
500
|
+
const ctx = input;
|
|
501
|
+
if (ctx?.data && typeof ctx.data === 'object') {
|
|
502
|
+
return ctx.data;
|
|
503
|
+
}
|
|
504
|
+
if (input['data'] &&
|
|
505
|
+
typeof input['data'] === 'object') {
|
|
506
|
+
return input['data'];
|
|
507
|
+
}
|
|
508
|
+
return input;
|
|
509
|
+
}
|
|
510
|
+
async resolveOptions(input, rowData) {
|
|
511
|
+
const typedInput = input;
|
|
512
|
+
const rawOptions = {
|
|
513
|
+
reportId: typedInput.reportId,
|
|
514
|
+
runId: typedInput.runId,
|
|
515
|
+
...(typedInput.options ?? {}),
|
|
516
|
+
};
|
|
517
|
+
const scope = {
|
|
518
|
+
context: {
|
|
519
|
+
eval: (path) => get(rowData, path),
|
|
520
|
+
},
|
|
521
|
+
};
|
|
522
|
+
return (await this.expressionEvaluator.evaluate(rawOptions, scope));
|
|
523
|
+
}
|
|
524
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportViewerCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
525
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportViewerCommand, providedIn: 'root' }); }
|
|
526
|
+
}
|
|
527
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportViewerCommand, decorators: [{
|
|
528
|
+
type: Injectable,
|
|
529
|
+
args: [{ providedIn: 'root' }]
|
|
530
|
+
}] });
|
|
531
|
+
|
|
532
|
+
var openReportViewer_command = /*#__PURE__*/Object.freeze({
|
|
533
|
+
__proto__: null,
|
|
534
|
+
AXPOpenReportViewerCommand: AXPOpenReportViewerCommand
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
//#region ----- Imports -----
|
|
538
|
+
//#endregion
|
|
539
|
+
//#region ----- Handler -----
|
|
540
|
+
class AXMReportGenerationBackgroundOperationHandler extends AXPBackgroundOperationHandler {
|
|
541
|
+
constructor() {
|
|
542
|
+
super(...arguments);
|
|
543
|
+
this.providerKey = 'report-management';
|
|
544
|
+
this.openViewer = inject(AXPOpenReportViewerCommand);
|
|
545
|
+
this.queryService = inject(AXPQueryService);
|
|
546
|
+
}
|
|
547
|
+
async execute(command, operation) {
|
|
548
|
+
if (command.name !== 'click') {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const reportRequestId = get(operation.data, 'reportRequestId') ??
|
|
552
|
+
get(operation.data, 'payload.reportRequestId') ??
|
|
553
|
+
get(operation.data, 'command.payload.reportRequestId');
|
|
554
|
+
if (typeof reportRequestId !== 'string' || !reportRequestId.trim()) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
const result = await this.queryService.fetch(AXMReportManagementQueryKeys.ReportGeneration.Get, { id: reportRequestId });
|
|
558
|
+
const request = runtimeQueryPayloadData(result);
|
|
559
|
+
if (!request?.reportId) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (request.status === 'completed') {
|
|
563
|
+
await this.openViewer.execute({ reportId: request.reportId, runId: request.id });
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
await this.openViewer.execute({ reportId: request.reportId });
|
|
567
|
+
}
|
|
568
|
+
resolveCta(operation) {
|
|
569
|
+
if (operation.status === 'failed' && operation.capabilities.retryable) {
|
|
570
|
+
return {
|
|
571
|
+
text: '@general:background-operation.actions.retry',
|
|
572
|
+
icon: 'fa-light fa-arrow-rotate-right',
|
|
573
|
+
color: 'primary',
|
|
574
|
+
look: 'solid',
|
|
575
|
+
command: { name: 'retry' },
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
if (operation.status === 'completed' && operation.capabilities.clickable) {
|
|
579
|
+
return {
|
|
580
|
+
text: '@report-management:report-generation.notifications.actions.open-viewer',
|
|
581
|
+
icon: 'fa-light fa-file-chart-column',
|
|
582
|
+
color: 'primary',
|
|
583
|
+
look: 'solid',
|
|
584
|
+
command: { name: 'click' },
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationBackgroundOperationHandler, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
590
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationBackgroundOperationHandler }); }
|
|
591
|
+
}
|
|
592
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationBackgroundOperationHandler, decorators: [{
|
|
593
|
+
type: Injectable
|
|
594
|
+
}] });
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region ----- Providers -----
|
|
597
|
+
const AXM_REPORT_GENERATION_BACKGROUND_OPERATION_HANDLERS = [
|
|
598
|
+
{
|
|
599
|
+
provide: AXP_BACKGROUND_OPERATION_HANDLERS,
|
|
600
|
+
useExisting: AXMReportGenerationBackgroundOperationHandler,
|
|
601
|
+
multi: true,
|
|
602
|
+
},
|
|
603
|
+
AXMReportGenerationBackgroundOperationHandler,
|
|
604
|
+
];
|
|
605
|
+
|
|
606
|
+
//#region ----- Imports -----
|
|
607
|
+
//#endregion
|
|
608
|
+
//#region ----- Constants -----
|
|
609
|
+
const TOASTED_KEYS_STORAGE = 'axp:report-generation-inbox-toast-keys';
|
|
610
|
+
//#endregion
|
|
611
|
+
//#region ----- Bridge -----
|
|
612
|
+
/**
|
|
613
|
+
* When a report-generation inbox row arrives (user was not watching inline),
|
|
614
|
+
* mirrors it as a transient toast with an open action. Inbox persistence stays server-owned.
|
|
615
|
+
*/
|
|
616
|
+
class AXMReportGenerationInboxToastBridgeService {
|
|
617
|
+
constructor() {
|
|
618
|
+
//#region ----- Dependencies -----
|
|
619
|
+
this.inboxConnector = inject(AXPInAppInboxConnectorService, { optional: true });
|
|
620
|
+
this.userNotificationService = inject(AXP_USER_NOTIFICATION_SERVICE);
|
|
621
|
+
this.commandService = inject(AXPCommandService);
|
|
622
|
+
this.translationService = inject(AXTranslationService);
|
|
623
|
+
this.sessionService = inject(AXPSessionService);
|
|
624
|
+
this.destroyRef = inject(DestroyRef);
|
|
625
|
+
this.injector = inject(Injector);
|
|
626
|
+
//#endregion
|
|
627
|
+
//#region ----- State -----
|
|
628
|
+
this.started = false;
|
|
629
|
+
this.inboxBaselined = false;
|
|
630
|
+
this.boundUserId = null;
|
|
631
|
+
this.toastedKeys = new Set();
|
|
632
|
+
}
|
|
633
|
+
//#endregion
|
|
634
|
+
//#region ----- Lifecycle -----
|
|
635
|
+
start() {
|
|
636
|
+
if (this.started || !this.inboxConnector) {
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
this.started = true;
|
|
640
|
+
this.sessionService.isReadyForAppScopedApi$
|
|
641
|
+
.pipe(map(() => this.sessionService.user?.id?.trim() || null), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef))
|
|
642
|
+
.subscribe((userId) => {
|
|
643
|
+
this.bindSessionUser(userId);
|
|
644
|
+
});
|
|
645
|
+
this.bindSessionUser(this.sessionService.user?.id?.trim() || null);
|
|
646
|
+
effect(() => {
|
|
647
|
+
const userId = this.sessionService.user?.id?.trim();
|
|
648
|
+
const inboxLoading = this.inboxConnector?.loading() ?? false;
|
|
649
|
+
const items = this.readReportGenerationItems();
|
|
650
|
+
if (!this.sessionService.isReadyForAppScopedApi || !userId) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (!this.inboxBaselined) {
|
|
654
|
+
for (const item of items) {
|
|
655
|
+
this.markToasted(this.toastKey(item));
|
|
656
|
+
}
|
|
657
|
+
if (!inboxLoading) {
|
|
658
|
+
this.inboxBaselined = true;
|
|
659
|
+
}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
for (const item of items) {
|
|
663
|
+
const key = this.toastKey(item);
|
|
664
|
+
if (this.toastedKeys.has(key)) {
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
this.markToasted(key);
|
|
668
|
+
void this.showToastForInboxItem(item);
|
|
669
|
+
}
|
|
670
|
+
}, { injector: this.injector });
|
|
671
|
+
}
|
|
672
|
+
//#endregion
|
|
673
|
+
//#region ----- Toast -----
|
|
674
|
+
async showToastForInboxItem(item) {
|
|
675
|
+
const action = item.actions?.[0];
|
|
676
|
+
const actionTitle = action?.title
|
|
677
|
+
? await this.translationService.translateAsync(action.title)
|
|
678
|
+
: await this.translationService.translateAsync('@report-management:report-generation.notifications.actions.open-viewer');
|
|
679
|
+
const toastActions = action?.command?.name
|
|
680
|
+
? [
|
|
681
|
+
{
|
|
682
|
+
text: actionTitle,
|
|
683
|
+
color: 'primary',
|
|
684
|
+
onClick: () => this.executeInboxAction(item, action),
|
|
685
|
+
},
|
|
686
|
+
]
|
|
687
|
+
: undefined;
|
|
688
|
+
await this.userNotificationService.notify({
|
|
689
|
+
correlationId: item.correlationId?.trim() || item.id,
|
|
690
|
+
title: await this.resolveText(item.title),
|
|
691
|
+
body: await this.resolveText(item.body),
|
|
692
|
+
icon: item.iconClass ?? this.resolveIcon(item),
|
|
693
|
+
delivery: {
|
|
694
|
+
toast: {
|
|
695
|
+
color: this.resolveToastColor(item),
|
|
696
|
+
location: 'bottom-center',
|
|
697
|
+
closeButton: true,
|
|
698
|
+
timeOut: 8_000,
|
|
699
|
+
timeOutProgress: true,
|
|
700
|
+
showActions: !!toastActions?.length,
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
toastActions,
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
async executeInboxAction(item, action) {
|
|
707
|
+
if (!action.command?.name) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
await this.commandService.execute(action.command.name, action.command.options ?? {});
|
|
711
|
+
}
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region ----- Helpers -----
|
|
714
|
+
readReportGenerationItems() {
|
|
715
|
+
const userId = this.sessionService.user?.id?.trim();
|
|
716
|
+
return (this.inboxConnector?.inboxList() ?? []).filter((item) => {
|
|
717
|
+
if (item.definitionKey !== REPORT_GENERATION_NOTIFICATION_DEFINITION_KEY) {
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
const recipientUserId = item.recipientUserId?.trim();
|
|
721
|
+
return !recipientUserId || recipientUserId === userId;
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
toastKey(item) {
|
|
725
|
+
return item.correlationId?.trim() || item.id;
|
|
726
|
+
}
|
|
727
|
+
bindSessionUser(userId) {
|
|
728
|
+
if (userId === this.boundUserId) {
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
this.boundUserId = userId;
|
|
732
|
+
this.inboxBaselined = false;
|
|
733
|
+
this.toastedKeys.clear();
|
|
734
|
+
if (userId) {
|
|
735
|
+
this.loadPersistedToastedKeys(userId);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
markToasted(key) {
|
|
739
|
+
const normalized = key.trim();
|
|
740
|
+
if (!normalized) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
this.toastedKeys.add(normalized);
|
|
744
|
+
this.persistToastedKeys();
|
|
745
|
+
}
|
|
746
|
+
toastedStorageKey(userId) {
|
|
747
|
+
return `${TOASTED_KEYS_STORAGE}:${userId}`;
|
|
748
|
+
}
|
|
749
|
+
loadPersistedToastedKeys(userId) {
|
|
750
|
+
try {
|
|
751
|
+
const raw = sessionStorage.getItem(this.toastedStorageKey(userId));
|
|
752
|
+
if (!raw) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const parsed = JSON.parse(raw);
|
|
756
|
+
if (!Array.isArray(parsed)) {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
for (const entry of parsed) {
|
|
760
|
+
if (typeof entry === 'string' && entry.trim()) {
|
|
761
|
+
this.toastedKeys.add(entry.trim());
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
catch {
|
|
766
|
+
// Ignore corrupt session storage.
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
persistToastedKeys() {
|
|
770
|
+
const userId = this.boundUserId?.trim();
|
|
771
|
+
if (!userId) {
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
try {
|
|
775
|
+
sessionStorage.setItem(this.toastedStorageKey(userId), JSON.stringify([...this.toastedKeys]));
|
|
776
|
+
}
|
|
777
|
+
catch {
|
|
778
|
+
// Ignore quota / privacy errors.
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
async resolveText(value) {
|
|
782
|
+
const text = value?.trim();
|
|
783
|
+
if (!text) {
|
|
784
|
+
return '';
|
|
785
|
+
}
|
|
786
|
+
if (text.startsWith('@')) {
|
|
787
|
+
return this.translationService.translateAsync(text);
|
|
788
|
+
}
|
|
789
|
+
return text;
|
|
790
|
+
}
|
|
791
|
+
resolveToastColor(item) {
|
|
792
|
+
switch (item.priority) {
|
|
793
|
+
case 'Danger':
|
|
794
|
+
return 'danger';
|
|
795
|
+
case 'Warning':
|
|
796
|
+
return 'warning';
|
|
797
|
+
default:
|
|
798
|
+
return 'success';
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
resolveIcon(item) {
|
|
802
|
+
if (item.priority === 'Danger') {
|
|
803
|
+
return 'fa-light fa-circle-exclamation';
|
|
804
|
+
}
|
|
805
|
+
if (item.priority === 'Warning') {
|
|
806
|
+
return 'fa-light fa-triangle-exclamation';
|
|
807
|
+
}
|
|
808
|
+
return 'fa-light fa-circle-check';
|
|
809
|
+
}
|
|
810
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationInboxToastBridgeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
811
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationInboxToastBridgeService, providedIn: 'root' }); }
|
|
812
|
+
}
|
|
813
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportGenerationInboxToastBridgeService, decorators: [{
|
|
814
|
+
type: Injectable,
|
|
815
|
+
args: [{ providedIn: 'root' }]
|
|
816
|
+
}] });
|
|
817
|
+
|
|
818
|
+
//#region ----- Imports -----
|
|
819
|
+
//#endregion
|
|
820
|
+
//#region ----- Providers -----
|
|
821
|
+
function provideReportGenerationInboxToastBridge() {
|
|
822
|
+
return [
|
|
823
|
+
provideAppStartUpTask(() => {
|
|
824
|
+
const bridge = inject(AXMReportGenerationInboxToastBridgeService);
|
|
825
|
+
const inbox = inject(AXPInAppInboxConnectorService, { optional: true });
|
|
826
|
+
return {
|
|
827
|
+
name: 'report-generation-inbox-toast-bridge',
|
|
828
|
+
priority: AXP_APP_STARTUP_PRIORITY.realtimeHandlers,
|
|
829
|
+
run: async () => {
|
|
830
|
+
if (inbox) {
|
|
831
|
+
await inbox.ensureReady();
|
|
832
|
+
await inbox.refresh();
|
|
833
|
+
}
|
|
834
|
+
bridge.start();
|
|
835
|
+
},
|
|
836
|
+
};
|
|
837
|
+
}),
|
|
838
|
+
];
|
|
839
|
+
}
|
|
840
|
+
//#endregion
|
|
841
|
+
|
|
842
|
+
function routesFacory() {
|
|
843
|
+
let routes = [
|
|
844
|
+
{
|
|
845
|
+
path: `:app/${RootConfig.module.route}/viewer`,
|
|
846
|
+
canActivate: [...AXP_PROTECTED_ROUTE_GUARDS],
|
|
847
|
+
data: { tab: true, rootLayout: true },
|
|
848
|
+
loadComponent: () => {
|
|
849
|
+
return AXPRootLayoutComponent;
|
|
850
|
+
},
|
|
851
|
+
children: [
|
|
852
|
+
{
|
|
853
|
+
path: ':reportId',
|
|
854
|
+
loadComponent: () => import('./acorex-platform-module-report-management-client-report-viewer-page.component-YUdzA4B9.mjs').then((c) => c.AXPReportViewerPageComponent),
|
|
855
|
+
data: { reuse: true },
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
path: '',
|
|
859
|
+
loadComponent: () => import('./acorex-platform-module-report-management-client-report-viewer-page.component-YUdzA4B9.mjs').then((c) => c.AXPReportViewerPageComponent),
|
|
860
|
+
data: { reuse: true },
|
|
861
|
+
},
|
|
862
|
+
],
|
|
863
|
+
},
|
|
864
|
+
];
|
|
865
|
+
return routes;
|
|
866
|
+
}
|
|
867
|
+
class AXMReportManagementModule {
|
|
868
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
869
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, imports: [AXPDomainModule,
|
|
870
|
+
AXPRuntimeModule] }); }
|
|
871
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, providers: [
|
|
872
|
+
{
|
|
873
|
+
provide: ROUTES,
|
|
874
|
+
multi: true,
|
|
875
|
+
useFactory: routesFacory,
|
|
876
|
+
},
|
|
877
|
+
provideEntity([RootConfig.entities.report.fullName, RootConfig.entities.category.fullName]),
|
|
878
|
+
// Provider references (now using tokens)
|
|
879
|
+
{
|
|
880
|
+
provide: AXP_PERMISSION_DEFINITION_PROVIDER,
|
|
881
|
+
useClass: AXMPermissionDefinitionProvider,
|
|
882
|
+
multi: true,
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
provide: AXP_MENU_PROVIDER,
|
|
886
|
+
useClass: AXMMenuProvider,
|
|
887
|
+
multi: true,
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
provide: AXP_FEATURE_DEFINITION_PROVIDER,
|
|
891
|
+
useClass: AXPReportManagementFeatureDefinitionProvider,
|
|
892
|
+
multi: true,
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
provide: AXP_SETTING_DEFINITION_PROVIDER,
|
|
896
|
+
useClass: AXMReportManagementSettingProvider,
|
|
897
|
+
multi: true,
|
|
898
|
+
},
|
|
899
|
+
...AXM_REPORT_GENERATION_BACKGROUND_OPERATION_HANDLERS,
|
|
900
|
+
...provideReportGenerationInboxToastBridge(),
|
|
901
|
+
provideCommandSetups([
|
|
902
|
+
{
|
|
903
|
+
key: 'ReportManagement.Report:OpenInPopup',
|
|
904
|
+
command: () => Promise.resolve().then(function () { return openReportInPopup_command; }).then((c) => c.AXPOpenReportInPopupCommand),
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
key: 'ReportManagement.Report:OpenViewer',
|
|
908
|
+
command: () => Promise.resolve().then(function () { return openReportViewer_command; }).then((c) => c.AXPOpenReportViewerCommand),
|
|
909
|
+
},
|
|
910
|
+
]),
|
|
911
|
+
{
|
|
912
|
+
provide: AXP_ENTITY_ACTION_PLUGIN,
|
|
913
|
+
multi: true,
|
|
914
|
+
useFactory: () => createEntityReportsPlugin(inject(Injector)),
|
|
915
|
+
},
|
|
916
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-platform-module-report-management-client-entity-reports-page-component.provider-0eSOzb0y.mjs').then((m) => m.AXMEntityReportsPageComponentProvider)),
|
|
917
|
+
], imports: [AXPDomainModule,
|
|
918
|
+
AXPRuntimeModule] }); }
|
|
919
|
+
}
|
|
920
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMReportManagementModule, decorators: [{
|
|
921
|
+
type: NgModule,
|
|
922
|
+
args: [{
|
|
923
|
+
imports: [
|
|
924
|
+
AXPDomainModule,
|
|
925
|
+
AXPRuntimeModule,
|
|
926
|
+
// Entity Modules
|
|
927
|
+
],
|
|
928
|
+
exports: [],
|
|
929
|
+
declarations: [],
|
|
930
|
+
providers: [
|
|
931
|
+
{
|
|
932
|
+
provide: ROUTES,
|
|
933
|
+
multi: true,
|
|
934
|
+
useFactory: routesFacory,
|
|
935
|
+
},
|
|
936
|
+
provideEntity([RootConfig.entities.report.fullName, RootConfig.entities.category.fullName]),
|
|
937
|
+
// Provider references (now using tokens)
|
|
938
|
+
{
|
|
939
|
+
provide: AXP_PERMISSION_DEFINITION_PROVIDER,
|
|
940
|
+
useClass: AXMPermissionDefinitionProvider,
|
|
941
|
+
multi: true,
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
provide: AXP_MENU_PROVIDER,
|
|
945
|
+
useClass: AXMMenuProvider,
|
|
946
|
+
multi: true,
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
provide: AXP_FEATURE_DEFINITION_PROVIDER,
|
|
950
|
+
useClass: AXPReportManagementFeatureDefinitionProvider,
|
|
951
|
+
multi: true,
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
provide: AXP_SETTING_DEFINITION_PROVIDER,
|
|
955
|
+
useClass: AXMReportManagementSettingProvider,
|
|
956
|
+
multi: true,
|
|
957
|
+
},
|
|
958
|
+
...AXM_REPORT_GENERATION_BACKGROUND_OPERATION_HANDLERS,
|
|
959
|
+
...provideReportGenerationInboxToastBridge(),
|
|
960
|
+
provideCommandSetups([
|
|
961
|
+
{
|
|
962
|
+
key: 'ReportManagement.Report:OpenInPopup',
|
|
963
|
+
command: () => Promise.resolve().then(function () { return openReportInPopup_command; }).then((c) => c.AXPOpenReportInPopupCommand),
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
key: 'ReportManagement.Report:OpenViewer',
|
|
967
|
+
command: () => Promise.resolve().then(function () { return openReportViewer_command; }).then((c) => c.AXPOpenReportViewerCommand),
|
|
968
|
+
},
|
|
969
|
+
]),
|
|
970
|
+
{
|
|
971
|
+
provide: AXP_ENTITY_ACTION_PLUGIN,
|
|
972
|
+
multi: true,
|
|
973
|
+
useFactory: () => createEntityReportsPlugin(inject(Injector)),
|
|
974
|
+
},
|
|
975
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-platform-module-report-management-client-entity-reports-page-component.provider-0eSOzb0y.mjs').then((m) => m.AXMEntityReportsPageComponentProvider)),
|
|
976
|
+
],
|
|
977
|
+
}]
|
|
978
|
+
}] });
|
|
979
|
+
|
|
980
|
+
//#endregion
|
|
981
|
+
|
|
982
|
+
//#region ---- Imports ----
|
|
983
|
+
//#endregion
|
|
984
|
+
/**
|
|
985
|
+
* Report Viewer Service
|
|
986
|
+
*
|
|
987
|
+
* Handles report definition retrieval, caching, and layout actions.
|
|
988
|
+
*/
|
|
989
|
+
class AXPReportViewerService {
|
|
990
|
+
constructor() {
|
|
991
|
+
//#region ---- Dependencies ----
|
|
992
|
+
this.entityService = inject(AXPEntityService);
|
|
993
|
+
this.reportDefinitionService = this.entityService
|
|
994
|
+
.withEntity(RootConfig.module.name, RootConfig.entities.report.name)
|
|
995
|
+
.data();
|
|
996
|
+
//#endregion
|
|
997
|
+
//#region ---- Cache Storage ----
|
|
998
|
+
/** Cache for individual report definitions by id */
|
|
999
|
+
this.definitionsById = new Map();
|
|
1000
|
+
/** Pending API requests to prevent duplicate calls */
|
|
1001
|
+
this.pendingReportRequests = new Map();
|
|
1002
|
+
}
|
|
1003
|
+
//#endregion
|
|
1004
|
+
//#region ---- Public API: Report Definitions ----
|
|
1005
|
+
/**
|
|
1006
|
+
* Reports bound to a host entity type via `entities[].entity`.
|
|
1007
|
+
* Falls back to an in-memory filter when the nested-array query returns nothing
|
|
1008
|
+
* (or is unsupported by the current query backend).
|
|
1009
|
+
*/
|
|
1010
|
+
async getReportsByEntityType(entityType) {
|
|
1011
|
+
if (!entityType?.trim()) {
|
|
1012
|
+
return [];
|
|
1013
|
+
}
|
|
1014
|
+
let items = [];
|
|
1015
|
+
try {
|
|
1016
|
+
const result = await this.reportDefinitionService.query({
|
|
1017
|
+
skip: 0,
|
|
1018
|
+
take: 1000,
|
|
1019
|
+
filter: {
|
|
1020
|
+
logic: 'and',
|
|
1021
|
+
filters: [
|
|
1022
|
+
{
|
|
1023
|
+
field: 'entities.entity',
|
|
1024
|
+
operator: { type: 'equal' },
|
|
1025
|
+
value: entityType,
|
|
1026
|
+
},
|
|
1027
|
+
],
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
items = (result.items ?? []);
|
|
1031
|
+
}
|
|
1032
|
+
catch {
|
|
1033
|
+
items = [];
|
|
1034
|
+
}
|
|
1035
|
+
if (items.length === 0) {
|
|
1036
|
+
try {
|
|
1037
|
+
const fallback = await this.reportDefinitionService.query({
|
|
1038
|
+
skip: 0,
|
|
1039
|
+
take: 1000,
|
|
1040
|
+
});
|
|
1041
|
+
items = (fallback.items ?? []).filter((report) => (report.entities ?? []).some((binding) => binding?.entity === entityType));
|
|
1042
|
+
}
|
|
1043
|
+
catch {
|
|
1044
|
+
items = [];
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
const bound = [];
|
|
1048
|
+
for (const report of items) {
|
|
1049
|
+
this.definitionsById.set(report.id, report);
|
|
1050
|
+
for (const binding of extractEntityBindings(report, entityType)) {
|
|
1051
|
+
bound.push({ report, binding });
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return bound.sort((a, b) => (a.binding.order ?? 0) - (b.binding.order ?? 0) ||
|
|
1055
|
+
resolveMultiLanguageString(a.report.title, 'en-US').localeCompare(resolveMultiLanguageString(b.report.title, 'en-US')));
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Get single report definition by ID with O(1) lookup
|
|
1059
|
+
*/
|
|
1060
|
+
async getReportById(id) {
|
|
1061
|
+
if (this.definitionsById.has(id)) {
|
|
1062
|
+
return this.definitionsById.get(id);
|
|
1063
|
+
}
|
|
1064
|
+
if (this.pendingReportRequests.has(id)) {
|
|
1065
|
+
return this.pendingReportRequests.get(id);
|
|
1066
|
+
}
|
|
1067
|
+
const requestPromise = this.loadReport(id);
|
|
1068
|
+
this.pendingReportRequests.set(id, requestPromise);
|
|
1069
|
+
return requestPromise;
|
|
1070
|
+
}
|
|
1071
|
+
//#endregion
|
|
1072
|
+
//#region ---- Public API: Layout Actions ----
|
|
1073
|
+
/**
|
|
1074
|
+
* Get general actions list for a layout type.
|
|
1075
|
+
* Override in connectivity layer (mock/API) to provide general actions.
|
|
1076
|
+
*/
|
|
1077
|
+
getGeneralActionsList(layoutType) {
|
|
1078
|
+
return [];
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Get merged actions for a layout definition.
|
|
1082
|
+
* Combines general actions with layout-specific actions.
|
|
1083
|
+
*/
|
|
1084
|
+
getMergedActions(layoutDefinition) {
|
|
1085
|
+
const generalActions = this.getGeneralActionsList(layoutDefinition.layout.type);
|
|
1086
|
+
const customActions = layoutDefinition.actions || [];
|
|
1087
|
+
const customActionsMap = new Map();
|
|
1088
|
+
customActions.forEach((action) => {
|
|
1089
|
+
if (action.name) {
|
|
1090
|
+
customActionsMap.set(action.name, action);
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
const merged = [];
|
|
1094
|
+
const usedNames = new Set();
|
|
1095
|
+
generalActions.forEach((action) => {
|
|
1096
|
+
if (action.name && customActionsMap.has(action.name)) {
|
|
1097
|
+
merged.push(customActionsMap.get(action.name));
|
|
1098
|
+
usedNames.add(action.name);
|
|
1099
|
+
}
|
|
1100
|
+
else {
|
|
1101
|
+
merged.push(action);
|
|
1102
|
+
if (action.name) {
|
|
1103
|
+
usedNames.add(action.name);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
customActions.forEach((action) => {
|
|
1108
|
+
if (action.name && !usedNames.has(action.name)) {
|
|
1109
|
+
merged.push(action);
|
|
1110
|
+
usedNames.add(action.name);
|
|
1111
|
+
}
|
|
1112
|
+
else if (!action.name) {
|
|
1113
|
+
merged.push(action);
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
return merged;
|
|
1117
|
+
}
|
|
1118
|
+
getPrimaryActions(layoutDefinition) {
|
|
1119
|
+
return this.getMergedActions(layoutDefinition).filter((action) => action.priority === 'primary');
|
|
1120
|
+
}
|
|
1121
|
+
getSecondaryActions(layoutDefinition) {
|
|
1122
|
+
return this.getMergedActions(layoutDefinition).filter((action) => action.priority === 'secondary');
|
|
1123
|
+
}
|
|
1124
|
+
//#endregion
|
|
1125
|
+
//#region ---- Private: Data Loading ----
|
|
1126
|
+
/**
|
|
1127
|
+
* Load single report from entity service and cache result.
|
|
1128
|
+
* Can be overridden in extended services (e.g., API layer).
|
|
1129
|
+
*/
|
|
1130
|
+
async loadReport(id) {
|
|
1131
|
+
try {
|
|
1132
|
+
const result = (await this.reportDefinitionService.byKey(id));
|
|
1133
|
+
if (result) {
|
|
1134
|
+
this.definitionsById.set(id, result);
|
|
1135
|
+
return result;
|
|
1136
|
+
}
|
|
1137
|
+
return undefined;
|
|
1138
|
+
}
|
|
1139
|
+
finally {
|
|
1140
|
+
this.pendingReportRequests.delete(id);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
//#endregion
|
|
1144
|
+
//#region ---- Cache Management ----
|
|
1145
|
+
isReportCached(reportId) {
|
|
1146
|
+
return this.definitionsById.has(reportId) || this.pendingReportRequests.has(reportId);
|
|
1147
|
+
}
|
|
1148
|
+
clearAllCache() {
|
|
1149
|
+
this.definitionsById.clear();
|
|
1150
|
+
this.pendingReportRequests.clear();
|
|
1151
|
+
}
|
|
1152
|
+
clearReportsCache() {
|
|
1153
|
+
this.definitionsById.clear();
|
|
1154
|
+
this.pendingReportRequests.clear();
|
|
1155
|
+
}
|
|
1156
|
+
//#endregion
|
|
1157
|
+
//#region ---- Action Handlers (Override in Connectivity Layer) ----
|
|
1158
|
+
async executeAction(commandName, options) {
|
|
1159
|
+
console.log(`Execute action: ${commandName} - should be implemented in connectivity layer`, options);
|
|
1160
|
+
}
|
|
1161
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPReportViewerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1162
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPReportViewerService, providedIn: 'root' }); }
|
|
1163
|
+
}
|
|
1164
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPReportViewerService, decorators: [{
|
|
1165
|
+
type: Injectable,
|
|
1166
|
+
args: [{
|
|
1167
|
+
providedIn: 'root',
|
|
1168
|
+
}]
|
|
1169
|
+
}] });
|
|
1170
|
+
|
|
1171
|
+
function AXPieChartReportMapper(data) {
|
|
1172
|
+
return Object.entries(data)
|
|
1173
|
+
.map(([key, value]) => ({
|
|
1174
|
+
id: key,
|
|
1175
|
+
label: key,
|
|
1176
|
+
value: Number(value),
|
|
1177
|
+
}))
|
|
1178
|
+
.filter((item) => Number.isFinite(item.value) && item.value > 0);
|
|
1179
|
+
}
|
|
1180
|
+
function AXBarChartReportMapper(data) {
|
|
1181
|
+
return Object.entries(data)
|
|
1182
|
+
.map(([key, value]) => ({
|
|
1183
|
+
id: key,
|
|
1184
|
+
label: key,
|
|
1185
|
+
value: Number(value),
|
|
1186
|
+
}))
|
|
1187
|
+
.filter((item) => Number.isFinite(item.value));
|
|
1188
|
+
}
|
|
1189
|
+
function AXLineChartReportMapper(data) {
|
|
1190
|
+
const entries = Object.entries(data);
|
|
1191
|
+
if (entries.length === 0) {
|
|
1192
|
+
return [];
|
|
1193
|
+
}
|
|
1194
|
+
const dataPoints = entries.map(([key, value]) => ({
|
|
1195
|
+
x: key,
|
|
1196
|
+
y: value,
|
|
1197
|
+
}));
|
|
1198
|
+
return [
|
|
1199
|
+
{
|
|
1200
|
+
id: 'series-1',
|
|
1201
|
+
label: 'Data Series',
|
|
1202
|
+
tooltipLabel: 'Countries Info',
|
|
1203
|
+
lineColor: '#d81159',
|
|
1204
|
+
fillColor: '#d8115920',
|
|
1205
|
+
data: dataPoints,
|
|
1206
|
+
},
|
|
1207
|
+
];
|
|
1208
|
+
}
|
|
1209
|
+
//#region ---- Helpers ----
|
|
1210
|
+
/**
|
|
1211
|
+
* Determines whether a filter value should be treated as empty.
|
|
1212
|
+
* This handles deep objects, arrays, and primitives so that
|
|
1213
|
+
* values like { from: null, to: null } are considered empty.
|
|
1214
|
+
*/
|
|
1215
|
+
function isEmptyFilterValue(value) {
|
|
1216
|
+
if (value === null || value === undefined || value === '') {
|
|
1217
|
+
return true;
|
|
1218
|
+
}
|
|
1219
|
+
if (Array.isArray(value)) {
|
|
1220
|
+
return value.length === 0 || value.every((item) => isEmptyFilterValue(item));
|
|
1221
|
+
}
|
|
1222
|
+
if (typeof value === 'object') {
|
|
1223
|
+
const entries = Object.entries(value);
|
|
1224
|
+
if (entries.length === 0) {
|
|
1225
|
+
return true;
|
|
1226
|
+
}
|
|
1227
|
+
// Consider object empty if all nested values are empty
|
|
1228
|
+
return entries.every(([, v]) => isEmptyFilterValue(v));
|
|
1229
|
+
}
|
|
1230
|
+
return false;
|
|
1231
|
+
}
|
|
1232
|
+
//#endregion
|
|
1233
|
+
/**
|
|
1234
|
+
* Converts AXPContextData (filter context) to AXPFilterClause format
|
|
1235
|
+
*
|
|
1236
|
+
* Input format:
|
|
1237
|
+
* {
|
|
1238
|
+
* "fieldName": {
|
|
1239
|
+
* "operation": { "type": "between" },
|
|
1240
|
+
* "value": { "from": "...", "to": "..." }
|
|
1241
|
+
* }
|
|
1242
|
+
* }
|
|
1243
|
+
*
|
|
1244
|
+
* Output format:
|
|
1245
|
+
* {
|
|
1246
|
+
* logic: 'and',
|
|
1247
|
+
* filters: [
|
|
1248
|
+
* {
|
|
1249
|
+
* field: 'fieldName',
|
|
1250
|
+
* operator: { type: 'between' },
|
|
1251
|
+
* value: { from: "...", to: "..." }
|
|
1252
|
+
* }
|
|
1253
|
+
* ]
|
|
1254
|
+
* }
|
|
1255
|
+
*/
|
|
1256
|
+
function convertContextDataToFilterClause(context) {
|
|
1257
|
+
const filters = [];
|
|
1258
|
+
// Iterate through all keys in context
|
|
1259
|
+
for (const [fieldName, fieldData] of Object.entries(context)) {
|
|
1260
|
+
if (fieldName.endsWith('EditorObj') || fieldName.startsWith('__')) {
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
// Drill-down and URL shortcuts may pass plain parameter values (e.g. { brand: 'id' })
|
|
1264
|
+
if (fieldData === null || fieldData === undefined || typeof fieldData !== 'object' || Array.isArray(fieldData)) {
|
|
1265
|
+
if (!isEmptyFilterValue(fieldData)) {
|
|
1266
|
+
filters.push({
|
|
1267
|
+
field: fieldName,
|
|
1268
|
+
operator: {
|
|
1269
|
+
type: Array.isArray(fieldData) ? 'in' : 'equal',
|
|
1270
|
+
},
|
|
1271
|
+
value: fieldData,
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
// Plain `{ from, to }` / `{ start, end }` objects (e.g. dateRange from OpenInPopup)
|
|
1277
|
+
if (isReportDateRangeObject(fieldData) && !('operation' in fieldData)) {
|
|
1278
|
+
// Date-range widgets use `between`; CDK operator union is narrower than runtime.
|
|
1279
|
+
filters.push({
|
|
1280
|
+
field: fieldName,
|
|
1281
|
+
operator: { type: 'between' },
|
|
1282
|
+
value: {
|
|
1283
|
+
from: String(fieldData.from ?? fieldData.start),
|
|
1284
|
+
to: String(fieldData.to ?? fieldData.end),
|
|
1285
|
+
},
|
|
1286
|
+
});
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1289
|
+
// Skip structured filter objects without operation metadata
|
|
1290
|
+
if (!('operation' in fieldData)) {
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
const operation = fieldData.operation;
|
|
1294
|
+
const value = fieldData.value;
|
|
1295
|
+
// Skip if operation type is missing
|
|
1296
|
+
if (!operation || !operation.type) {
|
|
1297
|
+
continue;
|
|
1298
|
+
}
|
|
1299
|
+
// Skip if value is effectively empty (null/undefined/empty string,
|
|
1300
|
+
// empty object/array, or deep object where all nested values are empty)
|
|
1301
|
+
if (isEmptyFilterValue(value)) {
|
|
1302
|
+
continue;
|
|
1303
|
+
}
|
|
1304
|
+
// Create filter clause
|
|
1305
|
+
const filter = {
|
|
1306
|
+
field: fieldName,
|
|
1307
|
+
operator: {
|
|
1308
|
+
type: operation.type,
|
|
1309
|
+
},
|
|
1310
|
+
value: value,
|
|
1311
|
+
};
|
|
1312
|
+
filters.push(filter);
|
|
1313
|
+
}
|
|
1314
|
+
// Return filter clause with AND logic
|
|
1315
|
+
if (filters.length === 0) {
|
|
1316
|
+
return {
|
|
1317
|
+
logic: 'and',
|
|
1318
|
+
filters: [],
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
return {
|
|
1322
|
+
logic: 'and',
|
|
1323
|
+
filters: filters,
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
function asPdfSource(record) {
|
|
1327
|
+
if (!record) {
|
|
1328
|
+
return null;
|
|
1329
|
+
}
|
|
1330
|
+
const url = typeof record['url'] === 'string' ? record['url'] : undefined;
|
|
1331
|
+
const base64 = typeof record['base64'] === 'string' ? record['base64'] : undefined;
|
|
1332
|
+
const blob = record['blob'] instanceof Blob ? record['blob'] : undefined;
|
|
1333
|
+
const filename = typeof record['filename'] === 'string' ? record['filename'] : undefined;
|
|
1334
|
+
if (url == null && base64 == null && blob == null) {
|
|
1335
|
+
return null;
|
|
1336
|
+
}
|
|
1337
|
+
return { url, base64, blob, filename };
|
|
1338
|
+
}
|
|
1339
|
+
function queryResultToPdfSource(queryResult) {
|
|
1340
|
+
if (!queryResult?.success) {
|
|
1341
|
+
return null;
|
|
1342
|
+
}
|
|
1343
|
+
const fromObject = asPdfSource((runtimeQueryPayloadData(queryResult) ?? runtimeQueryObjectData(queryResult)));
|
|
1344
|
+
if (fromObject) {
|
|
1345
|
+
return fromObject;
|
|
1346
|
+
}
|
|
1347
|
+
const page = runtimeQueryCollectionData(queryResult);
|
|
1348
|
+
return asPdfSource(page?.items?.[0]);
|
|
1349
|
+
}
|
|
1350
|
+
function queryResultToChartSeries(queryResult, mapping) {
|
|
1351
|
+
if (!queryResult?.success) {
|
|
1352
|
+
return {};
|
|
1353
|
+
}
|
|
1354
|
+
const objectData = runtimeQueryObjectData(queryResult);
|
|
1355
|
+
if (objectData && !('items' in objectData)) {
|
|
1356
|
+
const series = {};
|
|
1357
|
+
for (const [key, value] of Object.entries(objectData)) {
|
|
1358
|
+
const n = Number(value);
|
|
1359
|
+
if (Number.isFinite(n)) {
|
|
1360
|
+
series[key] = n;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (Object.keys(series).length > 0) {
|
|
1364
|
+
return series;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
const page = runtimeQueryCollectionData(queryResult);
|
|
1368
|
+
const rows = (page?.items ?? []);
|
|
1369
|
+
if (rows.length === 0) {
|
|
1370
|
+
return {};
|
|
1371
|
+
}
|
|
1372
|
+
const firstRow = rows[0] ?? {};
|
|
1373
|
+
const keys = Object.keys(firstRow);
|
|
1374
|
+
const xField = mapping?.xField ?? keys[0] ?? 'x';
|
|
1375
|
+
const yFieldsRaw = mapping?.yFields;
|
|
1376
|
+
const yField = Array.isArray(yFieldsRaw)
|
|
1377
|
+
? yFieldsRaw[0]
|
|
1378
|
+
: yFieldsRaw
|
|
1379
|
+
? String(yFieldsRaw)
|
|
1380
|
+
: (keys[1] ?? 'value');
|
|
1381
|
+
const chartData = {};
|
|
1382
|
+
for (const row of rows) {
|
|
1383
|
+
const key = String(row[xField] ?? '');
|
|
1384
|
+
if (!key) {
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
const value = Number(row[yField] ?? 0);
|
|
1388
|
+
if (Number.isFinite(value)) {
|
|
1389
|
+
chartData[key] = value;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
return chartData;
|
|
1393
|
+
}
|
|
1394
|
+
//#endregion
|
|
1395
|
+
|
|
1396
|
+
//#region ----- Imports -----
|
|
1397
|
+
//#endregion
|
|
1398
|
+
//#region ----- Command -----
|
|
1399
|
+
/** Opens a report in a results-only popup. Pass `runId` to show a frozen generation run. */
|
|
1400
|
+
class AXPOpenReportInPopupCommand {
|
|
1401
|
+
constructor() {
|
|
1402
|
+
//#region ----- Services & Dependencies -----
|
|
1403
|
+
this.popupService = inject(AXPopupService);
|
|
1404
|
+
this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
|
|
1405
|
+
this.translate = inject(AXTranslationService);
|
|
1406
|
+
this.reportViewerService = inject(AXPReportViewerService);
|
|
1407
|
+
this.inboxConnector = inject(AXPInAppInboxConnectorService, { optional: true });
|
|
1408
|
+
}
|
|
1409
|
+
//#endregion
|
|
1410
|
+
//#region ----- Command Implementation -----
|
|
1411
|
+
async execute(input) {
|
|
1412
|
+
const rowData = this.resolveRowData(input);
|
|
1413
|
+
const options = await this.resolveOptions(input, rowData);
|
|
1414
|
+
if (!options.reportId) {
|
|
1415
|
+
return {
|
|
1416
|
+
success: false,
|
|
1417
|
+
message: {
|
|
1418
|
+
text: await this.translate.translateAsync('@report-management:report-viewer.messages.error.invalid-popup-target'),
|
|
1419
|
+
},
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
const runId = typeof options.runId === 'string' ? options.runId.trim() : '';
|
|
1423
|
+
if (runId) {
|
|
1424
|
+
await this.dismissReportGenerationInboxNotification(runId);
|
|
1425
|
+
}
|
|
1426
|
+
const popupTitle = await this.resolvePopupTitle(options, rowData, runId);
|
|
1427
|
+
const parameters = resolveReportParameterValues(options.parameters);
|
|
1428
|
+
const comp = (await import('./acorex-platform-module-report-management-client-report-popup-viewer.component-BJq1BeUz.mjs')).AXPReportPopupViewerComponent;
|
|
1429
|
+
this.popupService.open(comp, {
|
|
1430
|
+
title: popupTitle,
|
|
1431
|
+
size: 'lg',
|
|
1432
|
+
maximizable: true,
|
|
1433
|
+
data: {
|
|
1434
|
+
reportId: options.reportId,
|
|
1435
|
+
layoutId: options.layoutId,
|
|
1436
|
+
runId,
|
|
1437
|
+
parameters,
|
|
1438
|
+
hideColumnFields: options.hideColumnFields ?? [],
|
|
1439
|
+
},
|
|
1440
|
+
});
|
|
1441
|
+
return {
|
|
1442
|
+
success: true,
|
|
1443
|
+
kind: 'object',
|
|
1444
|
+
data: undefined,
|
|
1445
|
+
metadata: { refresh: false },
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
//#endregion
|
|
1449
|
+
//#region ----- Helpers -----
|
|
1450
|
+
resolveRowData(input) {
|
|
1451
|
+
const ctx = input;
|
|
1452
|
+
if (ctx?.data && typeof ctx.data === 'object') {
|
|
1453
|
+
return ctx.data;
|
|
1454
|
+
}
|
|
1455
|
+
if (input['data'] &&
|
|
1456
|
+
typeof input['data'] === 'object') {
|
|
1457
|
+
return input['data'];
|
|
1458
|
+
}
|
|
1459
|
+
return input;
|
|
1460
|
+
}
|
|
1461
|
+
async resolveOptions(input, rowData) {
|
|
1462
|
+
const typedInput = input;
|
|
1463
|
+
const rawOptions = {
|
|
1464
|
+
reportId: typedInput.reportId,
|
|
1465
|
+
layoutId: typedInput.layoutId,
|
|
1466
|
+
runId: typedInput.runId,
|
|
1467
|
+
title: typedInput.title,
|
|
1468
|
+
parameters: typedInput.parameters,
|
|
1469
|
+
hideColumnFields: typedInput.hideColumnFields,
|
|
1470
|
+
...(typedInput.options ?? {}),
|
|
1471
|
+
};
|
|
1472
|
+
const scope = {
|
|
1473
|
+
context: {
|
|
1474
|
+
eval: (path) => get(rowData, path),
|
|
1475
|
+
},
|
|
1476
|
+
};
|
|
1477
|
+
return (await this.expressionEvaluator.evaluate(rawOptions, scope));
|
|
1478
|
+
}
|
|
1479
|
+
async resolvePopupTitle(options, rowData, runId) {
|
|
1480
|
+
let baseTitle = await this.resolveExplicitPopupTitle(options.title, rowData);
|
|
1481
|
+
if (!baseTitle && options.reportId) {
|
|
1482
|
+
const report = await this.reportViewerService.getReportById(options.reportId);
|
|
1483
|
+
if (report?.title) {
|
|
1484
|
+
baseTitle =
|
|
1485
|
+
this.translate.resolve(report.title) ||
|
|
1486
|
+
resolveMultiLanguageString(report.title, this.translate.getActiveLang() || 'en-US') ||
|
|
1487
|
+
'';
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
if (!baseTitle) {
|
|
1491
|
+
baseTitle = await this.translate.translateAsync('@report-management:report-viewer.components.report-popup-viewer.title');
|
|
1492
|
+
}
|
|
1493
|
+
if (!runId) {
|
|
1494
|
+
return baseTitle;
|
|
1495
|
+
}
|
|
1496
|
+
return this.translate.translateAsync('@report-management:report-viewer.components.report-popup-viewer.generated-title', { params: { title: baseTitle } });
|
|
1497
|
+
}
|
|
1498
|
+
async resolveExplicitPopupTitle(title, rowData) {
|
|
1499
|
+
if (title && typeof title === 'object') {
|
|
1500
|
+
const resolved = this.translate.resolve(title);
|
|
1501
|
+
if (resolved) {
|
|
1502
|
+
return resolved;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
if (!isString(title) || !title.trim()) {
|
|
1506
|
+
return '';
|
|
1507
|
+
}
|
|
1508
|
+
const scope = {
|
|
1509
|
+
context: {
|
|
1510
|
+
eval: (path) => get(rowData, path),
|
|
1511
|
+
},
|
|
1512
|
+
};
|
|
1513
|
+
const evaluated = await this.expressionEvaluator.evaluate(title, scope);
|
|
1514
|
+
return isString(evaluated) ? evaluated : title;
|
|
1515
|
+
}
|
|
1516
|
+
async dismissReportGenerationInboxNotification(runId) {
|
|
1517
|
+
if (!this.inboxConnector) {
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
try {
|
|
1521
|
+
await this.inboxConnector.dismissByCorrelationId(buildReportGenerationNotificationCorrelationId(runId));
|
|
1522
|
+
}
|
|
1523
|
+
catch {
|
|
1524
|
+
// Best-effort — opening the report should still proceed.
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportInPopupCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1528
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportInPopupCommand, providedIn: 'root' }); }
|
|
1529
|
+
}
|
|
1530
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPOpenReportInPopupCommand, decorators: [{
|
|
1531
|
+
type: Injectable,
|
|
1532
|
+
args: [{ providedIn: 'root' }]
|
|
1533
|
+
}] });
|
|
1534
|
+
|
|
1535
|
+
var openReportInPopup_command = /*#__PURE__*/Object.freeze({
|
|
1536
|
+
__proto__: null,
|
|
1537
|
+
AXPOpenReportInPopupCommand: AXPOpenReportInPopupCommand
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* Generated bundle index. Do not edit.
|
|
1542
|
+
*/
|
|
1543
|
+
|
|
1544
|
+
export { AXPReportViewerService as A, ENTITY_REPORTS_PAGE_COMPONENT_KEY as E, OPEN_REPORT_IN_POPUP_COMMAND as O, AXPOpenReportViewerCommand as a, collectReportParameterDefaults as b, convertContextDataToFilterClause as c, queryResultToChartSeries as d, AXLineChartReportMapper as e, AXPieChartReportMapper as f, AXBarChartReportMapper as g, bindingShowsOnPage as h, buildOpenInPopupCommandOptions as i, resolveBindingTitle as j, resolveBindingDescription as k, AXMPermissionDefinitionProvider as l, AXMReportManagementModule as m, AXPOpenReportInPopupCommand as n, queryResultToPdfSource as q, resolveReportFilterDefaultValue as r };
|
|
1545
|
+
//# sourceMappingURL=acorex-platform-module-report-management-client-acorex-platform-module-report-management-client-D5dCG62j.mjs.map
|