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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2472 @@
1
+ import { AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex-platform/framework-client/auth';
2
+ import { AXP_MENU_PROVIDER, AXP_SEARCH_PROVIDER, AXP_STATUS_PROVIDERS, AXPSearchCommandProvider, createEntityCommandOptions } from '@acorex-platform/framework-client/common';
3
+ import { AXP_MODULE_MANIFEST_PROVIDER, AXP_CATALOG_SCOPE_DEFINITION_PROVIDER, provideLazyProvider, AXP_FEATURE_DEFINITION_PROVIDER, AXPDataGenerator } from '@acorex-platform/framework-client/core';
4
+ import { AXP_PAGE_COMPONENT_PROVIDER, AXPStandardSectionItemsBuilderComponent } from '@acorex-platform/framework-client/layout/components';
5
+ import { AXPMiddlewareAbortError, AXP_ENTITY_STORAGE_MIDDLEWARE, AXPEntityService } from '@acorex-platform/framework-client/layout/entity';
6
+ import { provideCommand, provideCommandSetups, AXPCommandService } from '@acorex-platform/framework-client/runtime';
7
+ import * as i0 from '@angular/core';
8
+ import { Injectable, NgModule, inject, input, output, viewChild, signal, computed, effect, ChangeDetectionStrategy, Component, untracked } from '@angular/core';
9
+ import * as contracts from '@acorex-platform/module-asset-management-client/contracts';
10
+ import { ASSET_CATALOG_SCOPE_BOOKING, RootConfig, AXMAssetManagementCommandKeys, AXMAssetManagementFeatureKeys, AXPAssetsMaintenancePermissionKeys, AXMAssetManagementMenuKeys, AXM_ASSET_STATUS_DEFINITION_KEY, AXMAssetStatus, ASSET_TYPE_COMPONENTS_PAGE_COMPONENT_KEY, AXMAssetLifecycleStatus } from '@acorex-platform/module-asset-management-client/contracts';
11
+ export * from '@acorex-platform/module-asset-management-client/contracts';
12
+ import { AXPCommonMenuKeys, AXPCommonPermissionGroupKeys } from '@acorex-platform/module-common-client/contracts';
13
+ import { AXPStatusProvider, AXPWidgetsCatalog } from '@acorex-platform/framework-shared/core';
14
+ import { merge, get, isEqual, cloneDeep, isString, defaultTo } from 'lodash';
15
+ import * as i1 from '@acorex/components/button';
16
+ import { AXButtonModule } from '@acorex/components/button';
17
+ import * as i2 from '@acorex/components/decorators';
18
+ import { AXDecoratorModule } from '@acorex/components/decorators';
19
+ import * as i4 from '@acorex/core/translation';
20
+ import { AXTranslationService, resolveMultiLanguageString, AXTranslationModule } from '@acorex/core/translation';
21
+ import { AXPLayoutBuilderService } from '@acorex-platform/framework-client/layout/builder';
22
+ import * as i3 from '@angular/common';
23
+ import { CommonModule } from '@angular/common';
24
+ import { AXPPageLayoutBaseComponent, AXPPageLayoutBase } from '@acorex-platform/framework-client/layout/views';
25
+
26
+ function _mergeNamespaces(n, m) {
27
+ m.forEach(function (e) {
28
+ e && typeof e !== 'string' && !Array.isArray(e) && Object.keys(e).forEach(function (k) {
29
+ if (k !== 'default' && !(k in n)) {
30
+ var d = Object.getOwnPropertyDescriptor(e, k);
31
+ Object.defineProperty(n, k, d.get ? d : {
32
+ enumerable: true,
33
+ get: function () { return e[k]; }
34
+ });
35
+ }
36
+ });
37
+ });
38
+ return Object.freeze(n);
39
+ }
40
+
41
+ //#endregion
42
+ //#region ---- Constants ----
43
+ const QUESTIONNAIRE = 'AssessmentManagement.Questionnaire';
44
+ const QUESTION_BANK_ITEM = 'AssessmentManagement.QuestionBankItem';
45
+ const WORKFLOW_DEFINITION = 'WorkflowManagement.WorkflowDefinition';
46
+ const ASSET_CATALOG_TARGETS = [QUESTIONNAIRE, QUESTION_BANK_ITEM, WORKFLOW_DEFINITION];
47
+ //#endregion
48
+ //#region ---- Provider ----
49
+ class AXMAssetManagementCatalogScopeDefinitionProvider {
50
+ async items() {
51
+ return [
52
+ {
53
+ name: ASSET_CATALOG_SCOPE_BOOKING,
54
+ title: '@asset-management:catalog-scopes.booking.title',
55
+ description: '@asset-management:catalog-scopes.booking.description',
56
+ targets: [...ASSET_CATALOG_TARGETS],
57
+ icon: 'fa-light fa-calendar-check',
58
+ cssClass: 'primary-surface',
59
+ order: 25,
60
+ },
61
+ ];
62
+ }
63
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementCatalogScopeDefinitionProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
64
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementCatalogScopeDefinitionProvider }); }
65
+ }
66
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementCatalogScopeDefinitionProvider, decorators: [{
67
+ type: Injectable
68
+ }] });
69
+
70
+ //#region ---- Imports ----
71
+ //#endregion
72
+ //#region ---- Manifest Definition ----
73
+ /**
74
+ * Asset Management Module Manifest.
75
+ * Defines module metadata, features, and provider references.
76
+ */
77
+ const AssetManagementManifest = {
78
+ name: RootConfig.module.name,
79
+ version: '1.0.0',
80
+ title: RootConfig.module.title,
81
+ icon: RootConfig.module.icon,
82
+ i18n: RootConfig.config.i18n,
83
+ type: 'domain',
84
+ domain: 'asset-maintenance',
85
+ required: false,
86
+ // Module dependencies (optional)
87
+ };
88
+ //#endregion
89
+
90
+ //#region ---- Imports ----
91
+ //#endregion
92
+ //#region ---- Asset System Type Has Child Middleware ----
93
+ /**
94
+ * Enriches AssetSystemType query results with `hasChild` for hierarchical tree views.
95
+ * Uses `childrenCount` from the storage query pipeline when available.
96
+ */
97
+ const assetSystemTypeHasChildMiddleware = {
98
+ target: {
99
+ ops: ['query', 'queryAll'],
100
+ entity: `${RootConfig.module.name}.${RootConfig.entities.assetSystemType.name}`,
101
+ order: 10,
102
+ },
103
+ execute: async (ctx, next) => {
104
+ await next();
105
+ if (!ctx.result?.items?.length) {
106
+ return;
107
+ }
108
+ ctx.result = {
109
+ ...ctx.result,
110
+ items: ctx.result.items.map((item) => ({
111
+ ...item,
112
+ hasChild: (item.childrenCount ?? 0) > 0,
113
+ })),
114
+ };
115
+ },
116
+ };
117
+ //#endregion
118
+
119
+ //#region ---- Imports ----
120
+ //#endregion
121
+ //#region ---- Persist validation ----
122
+ function hasPartyId(value) {
123
+ return typeof value === 'string' && value.trim().length > 0;
124
+ }
125
+ function assertRentalPartyForDirection(data) {
126
+ if (data.rentalDirection === 'rentTo' && !hasPartyId(data.rentedToId)) {
127
+ throw new Error('@asset-management:assetRentalHistories.entities.assetRentalHistory.validations.rented-to-required');
128
+ }
129
+ if (data.rentalDirection === 'rentFrom' && !hasPartyId(data.rentedFromId)) {
130
+ throw new Error('@asset-management:assetRentalHistories.entities.assetRentalHistory.validations.rented-from-required');
131
+ }
132
+ if (!hasPartyId(data.rentedToId) && !hasPartyId(data.rentedFromId)) {
133
+ throw new Error('@asset-management:assetRentalHistories.entities.assetRentalHistory.validations.rental-party-required');
134
+ }
135
+ }
136
+ //#endregion
137
+
138
+ //#region ---- Imports ----
139
+ //#endregion
140
+ //#region ---- Asset Rental History Party Middleware ----
141
+ /**
142
+ * Ensures the supplier party matching rentalDirection is set, and clears the opposite party.
143
+ */
144
+ const assetRentalHistoryPartyMiddleware = {
145
+ target: {
146
+ ops: ['create', 'update'],
147
+ entity: `${RootConfig.module.name}.${RootConfig.entities.assetRentalHistory.name}`,
148
+ order: 8,
149
+ },
150
+ execute: async (ctx, next) => {
151
+ if (!ctx.data || typeof ctx.data !== 'object') {
152
+ await next();
153
+ return;
154
+ }
155
+ const incoming = ctx.data;
156
+ let previous = (ctx.previous ?? {});
157
+ if (ctx.op === 'update' && ctx.id != null && !ctx.previous) {
158
+ previous = (await ctx.backend.getOne(ctx.entityName, ctx.id).catch(() => ({})));
159
+ }
160
+ const merged = {
161
+ ...previous,
162
+ ...incoming,
163
+ };
164
+ if (merged.rentalDirection === 'rentTo') {
165
+ incoming.rentedFromId = null;
166
+ incoming.rentedFrom = undefined;
167
+ merged.rentedFromId = null;
168
+ merged.rentedFrom = undefined;
169
+ }
170
+ else if (merged.rentalDirection === 'rentFrom') {
171
+ incoming.rentedToId = null;
172
+ incoming.rentedTo = undefined;
173
+ merged.rentedToId = null;
174
+ merged.rentedTo = undefined;
175
+ }
176
+ try {
177
+ assertRentalPartyForDirection(merged);
178
+ }
179
+ catch (error) {
180
+ throw new AXPMiddlewareAbortError(error instanceof Error ? error.message : String(error), {
181
+ code: 'RENTAL_PARTY_REQUIRED',
182
+ status: 400,
183
+ });
184
+ }
185
+ await next();
186
+ },
187
+ };
188
+ //#endregion
189
+
190
+ //#region ---- Middleware Exports ----
191
+ //#endregion
192
+
193
+ //#region ---- Imports ----
194
+ //#endregion
195
+ //#region ---- Module ----
196
+ class AXMAssetManagementModule {
197
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
198
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementModule }); }
199
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementModule, providers: [
200
+ // Module Manifest Provider
201
+ {
202
+ provide: AXP_MODULE_MANIFEST_PROVIDER,
203
+ useValue: AssetManagementManifest,
204
+ multi: true,
205
+ },
206
+ {
207
+ provide: AXP_CATALOG_SCOPE_DEFINITION_PROVIDER,
208
+ useClass: AXMAssetManagementCatalogScopeDefinitionProvider,
209
+ multi: true,
210
+ },
211
+ // Feature Definition Provider (lazy-loaded)
212
+ provideLazyProvider(AXP_FEATURE_DEFINITION_PROVIDER, () => Promise.resolve().then(function () { return featureDefinition_provider; }).then((m) => m.AXMAssetManagementFeatureDefinitionProvider)),
213
+ // Permission Definition Provider (lazy-loaded)
214
+ provideLazyProvider(AXP_PERMISSION_DEFINITION_PROVIDER, () => Promise.resolve().then(function () { return permissionDefinition_provider; }).then((m) => m.AXMAssetManagementPermissionDefinitionProvider)),
215
+ // Menu Provider (lazy-loaded)
216
+ provideLazyProvider(AXP_MENU_PROVIDER, () => Promise.resolve().then(function () { return menu_provider; }).then((m) => m.AXMAssetManagementMenuProvider)),
217
+ // Entity Provider (lazy-loaded)
218
+ // Search Provider (lazy-loaded)
219
+ provideLazyProvider(AXP_SEARCH_PROVIDER, () => Promise.resolve().then(function () { return searchCommand_provider; }).then((m) => m.AXMAssetManagementSearchCommandProvider)),
220
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index; }).then((m) => m.AXMAssetTypeComponentsPageComponentProvider)),
221
+ provideCommand('AssetType:Components:Save', () => Promise.resolve().then(function () { return saveAssetTypeComponents_command; }).then((m) => m.AXMSaveAssetTypeComponentsCommand)),
222
+ provideCommand(AXMAssetManagementCommandKeys.AssetRentalHistory.RentFrom, () => Promise.resolve().then(function () { return createAssetRentalHistoryRent_commands; }).then((m) => m.AXMCreateAssetRentalHistoryRentFromCommand)),
223
+ provideCommand(AXMAssetManagementCommandKeys.AssetRentalHistory.RentTo, () => Promise.resolve().then(function () { return createAssetRentalHistoryRent_commands; }).then((m) => m.AXMCreateAssetRentalHistoryRentToCommand)),
224
+ provideCommandSetups([
225
+ {
226
+ key: AXMAssetManagementCommandKeys.WorkflowActivity.RecalculateOperationalStatus,
227
+ command: () => import('./acorex-platform-module-asset-management-client-recalculate-asset-operational-status.activity-ueTauO_k.mjs').then((c) => c.RecalculateAssetOperationalStatusActivity),
228
+ },
229
+ ]),
230
+ // provideLazyProvider(AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, () =>
231
+ // import('./features/asset-status.rules').then((m) => m.AXMAssetManagementAssetStatusRulesExpressionScopeProvider),
232
+ // ),
233
+ // Status Providers
234
+ provideLazyProvider(AXP_STATUS_PROVIDERS, () => Promise.resolve().then(function () { return assetStatus_provider; }).then((m) => m.AXMAssetManagementAssetStatusProvider)),
235
+ provideLazyProvider(AXP_STATUS_PROVIDERS, () => import('./acorex-platform-module-asset-management-client-asset-rental-history-status.provider-DwhQPb0-.mjs').then((m) => m.AXMAssetManagementAssetRentalHistoryStatusProvider)),
236
+ provideLazyProvider(AXP_STATUS_PROVIDERS, () => import('./acorex-platform-module-asset-management-client-asset-lifecycle.provider-BDaCpGsv.mjs').then((m) => m.AXMAssetManagementAssetLifecycleStatusProvider)),
237
+ {
238
+ provide: AXP_ENTITY_STORAGE_MIDDLEWARE,
239
+ useValue: assetSystemTypeHasChildMiddleware,
240
+ multi: true,
241
+ },
242
+ {
243
+ provide: AXP_ENTITY_STORAGE_MIDDLEWARE,
244
+ useValue: assetRentalHistoryPartyMiddleware,
245
+ multi: true,
246
+ },
247
+ ] }); }
248
+ }
249
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementModule, decorators: [{
250
+ type: NgModule,
251
+ args: [{
252
+ imports: [],
253
+ providers: [
254
+ // Module Manifest Provider
255
+ {
256
+ provide: AXP_MODULE_MANIFEST_PROVIDER,
257
+ useValue: AssetManagementManifest,
258
+ multi: true,
259
+ },
260
+ {
261
+ provide: AXP_CATALOG_SCOPE_DEFINITION_PROVIDER,
262
+ useClass: AXMAssetManagementCatalogScopeDefinitionProvider,
263
+ multi: true,
264
+ },
265
+ // Feature Definition Provider (lazy-loaded)
266
+ provideLazyProvider(AXP_FEATURE_DEFINITION_PROVIDER, () => Promise.resolve().then(function () { return featureDefinition_provider; }).then((m) => m.AXMAssetManagementFeatureDefinitionProvider)),
267
+ // Permission Definition Provider (lazy-loaded)
268
+ provideLazyProvider(AXP_PERMISSION_DEFINITION_PROVIDER, () => Promise.resolve().then(function () { return permissionDefinition_provider; }).then((m) => m.AXMAssetManagementPermissionDefinitionProvider)),
269
+ // Menu Provider (lazy-loaded)
270
+ provideLazyProvider(AXP_MENU_PROVIDER, () => Promise.resolve().then(function () { return menu_provider; }).then((m) => m.AXMAssetManagementMenuProvider)),
271
+ // Entity Provider (lazy-loaded)
272
+ // Search Provider (lazy-loaded)
273
+ provideLazyProvider(AXP_SEARCH_PROVIDER, () => Promise.resolve().then(function () { return searchCommand_provider; }).then((m) => m.AXMAssetManagementSearchCommandProvider)),
274
+ provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => Promise.resolve().then(function () { return index; }).then((m) => m.AXMAssetTypeComponentsPageComponentProvider)),
275
+ provideCommand('AssetType:Components:Save', () => Promise.resolve().then(function () { return saveAssetTypeComponents_command; }).then((m) => m.AXMSaveAssetTypeComponentsCommand)),
276
+ provideCommand(AXMAssetManagementCommandKeys.AssetRentalHistory.RentFrom, () => Promise.resolve().then(function () { return createAssetRentalHistoryRent_commands; }).then((m) => m.AXMCreateAssetRentalHistoryRentFromCommand)),
277
+ provideCommand(AXMAssetManagementCommandKeys.AssetRentalHistory.RentTo, () => Promise.resolve().then(function () { return createAssetRentalHistoryRent_commands; }).then((m) => m.AXMCreateAssetRentalHistoryRentToCommand)),
278
+ provideCommandSetups([
279
+ {
280
+ key: AXMAssetManagementCommandKeys.WorkflowActivity.RecalculateOperationalStatus,
281
+ command: () => import('./acorex-platform-module-asset-management-client-recalculate-asset-operational-status.activity-ueTauO_k.mjs').then((c) => c.RecalculateAssetOperationalStatusActivity),
282
+ },
283
+ ]),
284
+ // provideLazyProvider(AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, () =>
285
+ // import('./features/asset-status.rules').then((m) => m.AXMAssetManagementAssetStatusRulesExpressionScopeProvider),
286
+ // ),
287
+ // Status Providers
288
+ provideLazyProvider(AXP_STATUS_PROVIDERS, () => Promise.resolve().then(function () { return assetStatus_provider; }).then((m) => m.AXMAssetManagementAssetStatusProvider)),
289
+ provideLazyProvider(AXP_STATUS_PROVIDERS, () => import('./acorex-platform-module-asset-management-client-asset-rental-history-status.provider-DwhQPb0-.mjs').then((m) => m.AXMAssetManagementAssetRentalHistoryStatusProvider)),
290
+ provideLazyProvider(AXP_STATUS_PROVIDERS, () => import('./acorex-platform-module-asset-management-client-asset-lifecycle.provider-BDaCpGsv.mjs').then((m) => m.AXMAssetManagementAssetLifecycleStatusProvider)),
291
+ {
292
+ provide: AXP_ENTITY_STORAGE_MIDDLEWARE,
293
+ useValue: assetSystemTypeHasChildMiddleware,
294
+ multi: true,
295
+ },
296
+ {
297
+ provide: AXP_ENTITY_STORAGE_MIDDLEWARE,
298
+ useValue: assetRentalHistoryPartyMiddleware,
299
+ multi: true,
300
+ },
301
+ ],
302
+ }]
303
+ }] });
304
+
305
+ //#region ---- Imports ----
306
+ //#endregion
307
+ //#region ---- Feature Definition Provider ----
308
+ /**
309
+ * Asset Management Feature Definition Provider.
310
+ * Provides feature definitions for the Asset Management module.
311
+ */
312
+ class AXMAssetManagementFeatureDefinitionProvider {
313
+ async provide(context) {
314
+ // Asset Management feature
315
+ context.addFeature(AXMAssetManagementFeatureKeys.AssetManagement, {
316
+ title: '@asset-management:features.asset-management.title',
317
+ description: '@asset-management:features.asset-management.description',
318
+ defaultValue: true,
319
+ type: 'boolean',
320
+ });
321
+ // // Advanced Maintenance feature
322
+ // context.addFeature(AXMAssetManagementFeatureKeys.AdvancedMaintenance, {
323
+ // title: '@asset-management:features.advanced-maintenance.title',
324
+ // description: '@asset-management:features.advanced-maintenance.description',
325
+ // defaultValue: false,
326
+ // type: 'boolean',
327
+ // });
328
+ // // Maintenance Scheduling feature
329
+ // context.addFeature(AXMAssetManagementFeatureKeys.MaintenanceScheduling, {
330
+ // title: '@asset-management:features.maintenance-scheduling.title',
331
+ // description: '@asset-management:features.maintenance-scheduling.description',
332
+ // defaultValue: true,
333
+ // type: 'boolean',
334
+ // });
335
+ // // Inspection Workflow feature
336
+ // context.addFeature(AXMAssetManagementFeatureKeys.InspectionWorkflow, {
337
+ // title: '@asset-management:features.inspection-workflow.title',
338
+ // description: '@asset-management:features.inspection-workflow.description',
339
+ // defaultValue: true,
340
+ // type: 'boolean',
341
+ // });
342
+ }
343
+ }
344
+ //#endregion
345
+
346
+ var featureDefinition_provider = /*#__PURE__*/Object.freeze({
347
+ __proto__: null,
348
+ AXMAssetManagementFeatureDefinitionProvider: AXMAssetManagementFeatureDefinitionProvider
349
+ });
350
+
351
+ //#region ---- Imports ----
352
+ //#endregion
353
+ //#region ---- Menu Provider ----
354
+ /**
355
+ * Asset Management Menu Provider.
356
+ * Provides menu items for the Asset Management module with permission and feature checks.
357
+ */
358
+ class AXMAssetManagementMenuProvider {
359
+ constructor() {
360
+ this.entityService = inject(AXPEntityService);
361
+ }
362
+ async provide(context) {
363
+ const module = RootConfig.module;
364
+ const keys = AXPAssetsMaintenancePermissionKeys;
365
+ const PLAN_EQUIPMENT_MENU_KEY = 'Configuration:Menu:PlantEquipment';
366
+ context.find(AXPCommonMenuKeys.PlantEquipment).addItems([
367
+ {
368
+ name: AXMAssetManagementMenuKeys.Assets,
369
+ text: '@asset-management:assets.menus.root.title',
370
+ path: this.entityService.createPath(module.name, RootConfig.entities.asset.name),
371
+ priority: 2,
372
+ icon: RootConfig.entities.asset.icon,
373
+ policy: {
374
+ features: [module.name],
375
+ permissions: [keys.MaintainAssets],
376
+ },
377
+ },
378
+ {
379
+ name: AXMAssetManagementMenuKeys.AssetSystems,
380
+ text: '@asset-management:assetSystems.menus.root.title',
381
+ path: this.entityService.createPath(module.name, RootConfig.entities.assetSystem.name),
382
+ priority: 3,
383
+ icon: RootConfig.entities.assetSystem.icon,
384
+ policy: {
385
+ features: [module.name],
386
+ permissions: [keys.MaintainAssets],
387
+ },
388
+ },
389
+ ]);
390
+ const mainAssetsMaintenanceDivider = context.find(AXMAssetManagementMenuKeys.MainPlanEquipmentAssetsMaintenanceDivider);
391
+ if (!mainAssetsMaintenanceDivider.exists) {
392
+ context.find(AXPCommonMenuKeys.PlantEquipment).addItems([
393
+ {
394
+ name: AXMAssetManagementMenuKeys.MainPlanEquipmentAssetsMaintenanceDivider,
395
+ text: '',
396
+ type: 'break',
397
+ priority: 10,
398
+ },
399
+ ]);
400
+ }
401
+ const planEquipmentMenu = context.find(PLAN_EQUIPMENT_MENU_KEY);
402
+ if (!planEquipmentMenu.exists) {
403
+ context.find(AXPCommonMenuKeys.Configuration).addItems([
404
+ {
405
+ name: PLAN_EQUIPMENT_MENU_KEY,
406
+ priority: 9000,
407
+ text: '@general:menus.plan-equipment.title',
408
+ icon: module.icon,
409
+ },
410
+ ]);
411
+ }
412
+ context.find(PLAN_EQUIPMENT_MENU_KEY).addItems([
413
+ {
414
+ name: AXMAssetManagementMenuKeys.AssetTypes,
415
+ text: '@asset-management:assetTypes.menus.root.title',
416
+ path: this.entityService.createPath(module.name, RootConfig.entities.assetType.name),
417
+ priority: 2,
418
+ icon: RootConfig.entities.assetType.icon,
419
+ policy: {
420
+ features: [module.name],
421
+ permissions: [keys.Setup],
422
+ },
423
+ },
424
+ {
425
+ name: AXMAssetManagementMenuKeys.AssetSystemTypes,
426
+ text: '@asset-management:assetSystemTypes.menus.root.title',
427
+ path: this.entityService.createPath(module.name, RootConfig.entities.assetSystemType.name),
428
+ priority: 3,
429
+ icon: RootConfig.entities.assetSystemType.icon,
430
+ policy: {
431
+ features: [module.name],
432
+ permissions: [keys.Setup],
433
+ },
434
+ },
435
+ ]);
436
+ const planEquipmentAssetsMaintenanceDivider = context.find(AXMAssetManagementMenuKeys.PlanEquipmentAssetsMaintenanceDivider);
437
+ if (!planEquipmentAssetsMaintenanceDivider.exists) {
438
+ context.find(PLAN_EQUIPMENT_MENU_KEY).addItems([
439
+ {
440
+ name: AXMAssetManagementMenuKeys.PlanEquipmentAssetsMaintenanceDivider,
441
+ text: '',
442
+ type: 'break',
443
+ priority: 10,
444
+ },
445
+ ]);
446
+ }
447
+ }
448
+ }
449
+ //#endregion
450
+
451
+ var menu_provider = /*#__PURE__*/Object.freeze({
452
+ __proto__: null,
453
+ AXMAssetManagementMenuProvider: AXMAssetManagementMenuProvider
454
+ });
455
+
456
+ //#endregion
457
+ //#region ----- Permission Definition Provider -----
458
+ /**
459
+ * Registers Assets & Maintenance capabilities owned by asset-management.
460
+ */
461
+ class AXMAssetManagementPermissionDefinitionProvider {
462
+ async define(context) {
463
+ const keys = AXPAssetsMaintenancePermissionKeys;
464
+ const groups = AXPCommonPermissionGroupKeys;
465
+ const P = '@common:assets-maintenance.permissions';
466
+ const G = '@common:permission-groups';
467
+ context.addGroup(groups.AssetsMaintenance.Root, `${G}.assets-maintenance.title`, `${G}.assets-maintenance.description`, { order: 70 });
468
+ context
469
+ .addGroup(groups.AssetsMaintenance.Assets, `${G}.assets-maintenance-assets.title`, `${G}.assets-maintenance-assets.description`, { parent: groups.AssetsMaintenance.Root, order: 10 })
470
+ .addPermission(keys.Access, `${P}.access.title`, `${P}.access.description`)
471
+ .endPermission()
472
+ .addPermission(keys.MaintainAssets, `${P}.maintain-assets.title`, `${P}.maintain-assets.description`)
473
+ .endPermission()
474
+ .addPermission(keys.Setup, `${P}.setup.title`, `${P}.setup.description`)
475
+ .endPermission()
476
+ .endGroup();
477
+ }
478
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementPermissionDefinitionProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
479
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementPermissionDefinitionProvider }); }
480
+ }
481
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetManagementPermissionDefinitionProvider, decorators: [{
482
+ type: Injectable
483
+ }] });
484
+
485
+ var permissionDefinition_provider = /*#__PURE__*/Object.freeze({
486
+ __proto__: null,
487
+ AXMAssetManagementPermissionDefinitionProvider: AXMAssetManagementPermissionDefinitionProvider
488
+ });
489
+
490
+ //#region ---- Imports ----
491
+ //#endregion
492
+ //#region ---- Search Command Provider ----
493
+ class AXMAssetManagementSearchCommandProvider extends AXPSearchCommandProvider {
494
+ constructor() {
495
+ super(...arguments);
496
+ this.commands = [
497
+ {
498
+ id: 'create-asset',
499
+ group: 'command',
500
+ title: '@asset-management:assets.search.create-asset.title',
501
+ icon: RootConfig.entities.asset.icon,
502
+ description: '@asset-management:assets.search.create-asset.description',
503
+ command: {
504
+ name: 'Entity:Create',
505
+ options: createEntityCommandOptions(`${RootConfig.module.name}.${RootConfig.entities.asset.name}`, {
506
+ name: RootConfig.entities.asset.name,
507
+ module: RootConfig.module.name,
508
+ title: RootConfig.entities.asset.title,
509
+ }),
510
+ },
511
+ },
512
+ ];
513
+ }
514
+ }
515
+ //#endregion
516
+
517
+ var searchCommand_provider = /*#__PURE__*/Object.freeze({
518
+ __proto__: null,
519
+ AXMAssetManagementSearchCommandProvider: AXMAssetManagementSearchCommandProvider
520
+ });
521
+
522
+ class AXMAssetManagementAssetStatusProvider extends AXPStatusProvider {
523
+ constructor() {
524
+ super(...arguments);
525
+ this.key = AXM_ASSET_STATUS_DEFINITION_KEY;
526
+ this.statuses = [
527
+ {
528
+ name: AXMAssetStatus.Available,
529
+ title: '@asset-management:assets.states.available',
530
+ icon: 'fa-light fa-circle-check',
531
+ color: 'success',
532
+ order: 1,
533
+ isInitial: true,
534
+ isFinal: false,
535
+ generateView: true,
536
+ },
537
+ {
538
+ name: AXMAssetStatus.Assigned,
539
+ title: '@asset-management:assets.states.assigned',
540
+ icon: 'fa-light fa-user-check',
541
+ color: 'info',
542
+ order: 2,
543
+ isInitial: false,
544
+ isFinal: false,
545
+ generateView: true,
546
+ },
547
+ {
548
+ name: AXMAssetStatus.UnderMaintenanceInspection,
549
+ title: '@asset-management:assets.states.under-maintenance',
550
+ icon: 'fa-light fa-toolbox',
551
+ color: 'warning',
552
+ order: 3,
553
+ isInitial: false,
554
+ isFinal: false,
555
+ generateView: true,
556
+ },
557
+ {
558
+ name: AXMAssetStatus.OutOfService,
559
+ title: '@asset-management:assets.states.out-of-service',
560
+ icon: 'fa-light fa-triangle-exclamation',
561
+ color: 'danger',
562
+ order: 4,
563
+ isInitial: false,
564
+ isFinal: false,
565
+ generateView: true,
566
+ },
567
+ {
568
+ name: AXMAssetStatus.Lost,
569
+ title: '@asset-management:assets.states.lost',
570
+ icon: 'fa-light fa-magnifying-glass-minus',
571
+ color: 'danger',
572
+ order: 5,
573
+ isInitial: false,
574
+ isFinal: false,
575
+ generateView: true,
576
+ },
577
+ ];
578
+ this.transitions = [
579
+ {
580
+ from: AXMAssetStatus.Available,
581
+ to: AXMAssetStatus.Assigned,
582
+ title: '@asset-management:assets.transitions.assign',
583
+ },
584
+ {
585
+ from: AXMAssetStatus.Assigned,
586
+ to: AXMAssetStatus.Available,
587
+ title: '@asset-management:assets.transitions.unassign',
588
+ },
589
+ {
590
+ from: AXMAssetStatus.Available,
591
+ to: AXMAssetStatus.UnderMaintenanceInspection,
592
+ title: '@asset-management:assets.transitions.start-maintenance',
593
+ },
594
+ {
595
+ from: AXMAssetStatus.Assigned,
596
+ to: AXMAssetStatus.UnderMaintenanceInspection,
597
+ title: '@asset-management:assets.transitions.start-maintenance',
598
+ },
599
+ {
600
+ from: AXMAssetStatus.OutOfService,
601
+ to: AXMAssetStatus.UnderMaintenanceInspection,
602
+ title: '@asset-management:assets.transitions.start-maintenance',
603
+ },
604
+ {
605
+ from: AXMAssetStatus.UnderMaintenanceInspection,
606
+ to: AXMAssetStatus.Available,
607
+ title: '@asset-management:assets.transitions.release-as-available',
608
+ },
609
+ {
610
+ from: AXMAssetStatus.UnderMaintenanceInspection,
611
+ to: AXMAssetStatus.Assigned,
612
+ title: '@asset-management:assets.transitions.restore-assignment',
613
+ },
614
+ {
615
+ from: AXMAssetStatus.UnderMaintenanceInspection,
616
+ to: AXMAssetStatus.OutOfService,
617
+ title: '@asset-management:assets.transitions.keep-out-of-service',
618
+ },
619
+ {
620
+ from: AXMAssetStatus.Available,
621
+ to: AXMAssetStatus.OutOfService,
622
+ title: '@asset-management:assets.transitions.mark-out-of-service',
623
+ },
624
+ {
625
+ from: AXMAssetStatus.Assigned,
626
+ to: AXMAssetStatus.OutOfService,
627
+ title: '@asset-management:assets.transitions.mark-out-of-service',
628
+ },
629
+ {
630
+ from: AXMAssetStatus.OutOfService,
631
+ to: AXMAssetStatus.Available,
632
+ title: '@asset-management:assets.transitions.restore-service',
633
+ },
634
+ {
635
+ from: AXMAssetStatus.OutOfService,
636
+ to: AXMAssetStatus.Assigned,
637
+ title: '@asset-management:assets.transitions.restore-service',
638
+ },
639
+ {
640
+ from: AXMAssetStatus.Available,
641
+ to: AXMAssetStatus.Lost,
642
+ title: '@asset-management:assets.transitions.mark-lost',
643
+ },
644
+ {
645
+ from: AXMAssetStatus.Assigned,
646
+ to: AXMAssetStatus.Lost,
647
+ title: '@asset-management:assets.transitions.mark-lost',
648
+ },
649
+ {
650
+ from: AXMAssetStatus.OutOfService,
651
+ to: AXMAssetStatus.Lost,
652
+ title: '@asset-management:assets.transitions.mark-lost',
653
+ },
654
+ {
655
+ from: AXMAssetStatus.UnderMaintenanceInspection,
656
+ to: AXMAssetStatus.Lost,
657
+ title: '@asset-management:assets.transitions.mark-lost',
658
+ },
659
+ {
660
+ from: AXMAssetStatus.Lost,
661
+ to: AXMAssetStatus.Available,
662
+ title: '@asset-management:assets.transitions.found',
663
+ },
664
+ {
665
+ from: AXMAssetStatus.Lost,
666
+ to: AXMAssetStatus.Assigned,
667
+ title: '@asset-management:assets.transitions.found',
668
+ },
669
+ {
670
+ from: AXMAssetStatus.Lost,
671
+ to: AXMAssetStatus.OutOfService,
672
+ title: '@asset-management:assets.transitions.found-out-of-service',
673
+ },
674
+ ];
675
+ // readonly transitions: AXPStatusTransition[] = [
676
+ // {
677
+ // from: AXMAssetStatus.Available,
678
+ // to: AXMAssetStatus.Assigned,
679
+ // title: '@asset-management:assets.transitions.assign',
680
+ // },
681
+ // {
682
+ // from: AXMAssetStatus.Assigned,
683
+ // to: AXMAssetStatus.Available,
684
+ // title: '@asset-management:assets.transitions.unassign',
685
+ // },
686
+ // {
687
+ // from: AXMAssetStatus.Assigned,
688
+ // to: AXMAssetStatus.UnderMaintenanceInspection,
689
+ // title: '@asset-management:assets.transitions.start-maintenance',
690
+ // },
691
+ // {
692
+ // from: AXMAssetStatus.Assigned,
693
+ // to: AXMAssetStatus.OutOfService,
694
+ // title: '@asset-management:assets.transitions.mark-out-of-service',
695
+ // },
696
+ // {
697
+ // from: AXMAssetStatus.Available,
698
+ // to: AXMAssetStatus.UnderMaintenanceInspection,
699
+ // title: '@asset-management:assets.transitions.start-maintenance',
700
+ // },
701
+ // {
702
+ // from: AXMAssetStatus.OutOfService,
703
+ // to: AXMAssetStatus.UnderMaintenanceInspection,
704
+ // title: '@asset-management:assets.transitions.start-maintenance',
705
+ // },
706
+ // {
707
+ // from: AXMAssetStatus.UnderMaintenanceInspection,
708
+ // to: AXMAssetStatus.Available,
709
+ // title: '@asset-management:assets.transitions.complete-maintenance',
710
+ // },
711
+ // {
712
+ // from: AXMAssetStatus.Available,
713
+ // to: AXMAssetStatus.OutOfService,
714
+ // title: '@asset-management:assets.transitions.mark-out-of-service',
715
+ // },
716
+ // {
717
+ // from: AXMAssetStatus.OutOfService,
718
+ // to: AXMAssetStatus.Available,
719
+ // title: '@asset-management:assets.transitions.restore-service',
720
+ // },
721
+ // {
722
+ // from: AXMAssetStatus.Available,
723
+ // to: AXMAssetStatus.Lost,
724
+ // title: '@asset-management:assets.transitions.mark-lost',
725
+ // },
726
+ // {
727
+ // from: AXMAssetStatus.Assigned,
728
+ // to: AXMAssetStatus.Lost,
729
+ // title: '@asset-management:assets.transitions.mark-lost',
730
+ // },
731
+ // {
732
+ // from: AXMAssetStatus.Lost,
733
+ // to: AXMAssetStatus.Available,
734
+ // title: '@asset-management:assets.transitions.found',
735
+ // },
736
+ // ];
737
+ this.defaultStatus = AXMAssetStatus.Available;
738
+ }
739
+ }
740
+
741
+ var assetStatus_provider = /*#__PURE__*/Object.freeze({
742
+ __proto__: null,
743
+ AXMAssetManagementAssetStatusProvider: AXMAssetManagementAssetStatusProvider
744
+ });
745
+
746
+ const rentalHistorySharedCreateFields = [
747
+ 'rentalDirection',
748
+ 'createdDate',
749
+ 'startDate',
750
+ 'monthlyRent',
751
+ 'expectedReturnDate',
752
+ 'actualReturnDate',
753
+ 'status',
754
+ 'description',
755
+ ];
756
+ //#endregion
757
+ //#region ---- Helpers ----
758
+ function buildRentCreateOptions(direction) {
759
+ const isRentFrom = direction === 'rentFrom';
760
+ return {
761
+ includeProperties: [
762
+ ...rentalHistorySharedCreateFields,
763
+ isRentFrom ? 'rentedFromId' : 'rentedToId',
764
+ ],
765
+ excludeProperties: [isRentFrom ? 'rentedToId' : 'rentedFromId'],
766
+ decoration: {
767
+ header: {
768
+ title: isRentFrom
769
+ ? '@asset-management:assetRentalHistories.entities.assetRentalHistory.actions.rent-from.dialog-title'
770
+ : '@asset-management:assetRentalHistories.entities.assetRentalHistory.actions.rent-to.dialog-title',
771
+ },
772
+ },
773
+ process: {
774
+ redirect: false,
775
+ canCreateNewOne: true,
776
+ },
777
+ };
778
+ }
779
+ //#endregion
780
+ //#region ---- Commands ----
781
+ class AXMCreateAssetRentalHistoryRentCommandBase {
782
+ constructor() {
783
+ this.commandService = inject(AXPCommandService);
784
+ }
785
+ async execute(input) {
786
+ const seedData = this.resolveSeedData(input);
787
+ const createOptions = buildRentCreateOptions(this.rentalDirection);
788
+ const result = await this.commandService.execute('Entity:Create', {
789
+ entity: RootConfig.entities.assetRentalHistory.source,
790
+ entityInfo: {
791
+ name: RootConfig.entities.assetRentalHistory.name,
792
+ module: RootConfig.module.name,
793
+ title: RootConfig.entities.assetRentalHistory.title,
794
+ },
795
+ data: seedData,
796
+ options: createOptions,
797
+ });
798
+ return result ?? { success: false };
799
+ }
800
+ resolveSeedData(input) {
801
+ const ctx = input;
802
+ const options = (input.options ?? ctx.options ?? {});
803
+ const process = (options['process'] ?? {});
804
+ const processData = process['data'];
805
+ const contextData = ctx.data ?? input.data ?? {};
806
+ return merge({}, typeof contextData === 'object' &&
807
+ contextData !== null &&
808
+ !Array.isArray(contextData)
809
+ ? contextData
810
+ : {}, typeof processData === 'object' &&
811
+ processData !== null &&
812
+ !Array.isArray(processData)
813
+ ? processData
814
+ : {}, { rentalDirection: this.rentalDirection });
815
+ }
816
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateAssetRentalHistoryRentCommandBase, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
817
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateAssetRentalHistoryRentCommandBase, providedIn: 'root' }); }
818
+ }
819
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateAssetRentalHistoryRentCommandBase, decorators: [{
820
+ type: Injectable,
821
+ args: [{ providedIn: 'root' }]
822
+ }] });
823
+ class AXMCreateAssetRentalHistoryRentFromCommand extends AXMCreateAssetRentalHistoryRentCommandBase {
824
+ constructor() {
825
+ super(...arguments);
826
+ this.rentalDirection = 'rentFrom';
827
+ }
828
+ static { this.commandKey = AXMAssetManagementCommandKeys.AssetRentalHistory.RentFrom; }
829
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateAssetRentalHistoryRentFromCommand, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
830
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateAssetRentalHistoryRentFromCommand, providedIn: 'root' }); }
831
+ }
832
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateAssetRentalHistoryRentFromCommand, decorators: [{
833
+ type: Injectable,
834
+ args: [{ providedIn: 'root' }]
835
+ }] });
836
+ class AXMCreateAssetRentalHistoryRentToCommand extends AXMCreateAssetRentalHistoryRentCommandBase {
837
+ constructor() {
838
+ super(...arguments);
839
+ this.rentalDirection = 'rentTo';
840
+ }
841
+ static { this.commandKey = AXMAssetManagementCommandKeys.AssetRentalHistory.RentTo; }
842
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateAssetRentalHistoryRentToCommand, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
843
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateAssetRentalHistoryRentToCommand, providedIn: 'root' }); }
844
+ }
845
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateAssetRentalHistoryRentToCommand, decorators: [{
846
+ type: Injectable,
847
+ args: [{ providedIn: 'root' }]
848
+ }] });
849
+
850
+ var createAssetRentalHistoryRent_commands = /*#__PURE__*/Object.freeze({
851
+ __proto__: null,
852
+ AXMCreateAssetRentalHistoryRentFromCommand: AXMCreateAssetRentalHistoryRentFromCommand,
853
+ AXMCreateAssetRentalHistoryRentToCommand: AXMCreateAssetRentalHistoryRentToCommand
854
+ });
855
+
856
+ function normalizeAssetTypeBuilderLocalizedField(value, fallback) {
857
+ if (value == null || value === '') {
858
+ return fallback;
859
+ }
860
+ if (typeof value === 'string') {
861
+ const trimmed = value.trim();
862
+ return trimmed || fallback;
863
+ }
864
+ if (typeof value === 'object' && !Array.isArray(value)) {
865
+ const record = value;
866
+ const anyLocaleText = Object.values(record).some((v) => typeof v === 'string' && String(v).trim() !== '');
867
+ if (anyLocaleText) {
868
+ return value;
869
+ }
870
+ }
871
+ return fallback;
872
+ }
873
+ function createAssetTypeSectionBuilderDefaultValue(assetTypeId) {
874
+ return {
875
+ sections: [
876
+ {
877
+ id: AXPDataGenerator.uuid(),
878
+ code: 1,
879
+ title: 'New Section',
880
+ assetTypeId: assetTypeId ?? '',
881
+ description: '',
882
+ items: [],
883
+ },
884
+ ],
885
+ };
886
+ }
887
+ function normalizeAssetTypeSectionBuilderValue(value, assetTypeId) {
888
+ if (!value || typeof value !== 'object') {
889
+ return createAssetTypeSectionBuilderDefaultValue(assetTypeId);
890
+ }
891
+ const raw = value;
892
+ if (!Array.isArray(raw.sections) || raw.sections.length === 0) {
893
+ return createAssetTypeSectionBuilderDefaultValue(assetTypeId);
894
+ }
895
+ return {
896
+ sections: raw.sections.map((section, sectionIndex) => {
897
+ const sectionData = (section ?? {});
898
+ const id = typeof sectionData.id === 'string' && sectionData.id ? sectionData.id : AXPDataGenerator.uuid();
899
+ const sectionItems = Array.isArray(sectionData.items) ? sectionData.items : [];
900
+ return {
901
+ id,
902
+ code: typeof sectionData.code === 'number' ? sectionData.code : sectionIndex + 1,
903
+ title: normalizeAssetTypeBuilderLocalizedField(sectionData.title, `Section ${sectionIndex + 1}`),
904
+ assetTypeId: typeof sectionData.assetTypeId === 'string' && sectionData.assetTypeId
905
+ ? sectionData.assetTypeId
906
+ : assetTypeId ?? '',
907
+ description: normalizeAssetTypeBuilderLocalizedField(sectionData.description, ''),
908
+ items: sectionItems.map((item, itemIndex) => {
909
+ const itemData = (item ?? {});
910
+ const rawAt = itemData.assetType;
911
+ const assetTypeIdFromRef = rawAt &&
912
+ typeof rawAt === 'object' &&
913
+ typeof rawAt.id === 'string' &&
914
+ String(rawAt.id).trim()
915
+ ? String(rawAt.id).trim()
916
+ : '';
917
+ const normalizedAssetTypeId = typeof itemData.assetTypeId === 'string' && itemData.assetTypeId.trim()
918
+ ? itemData.assetTypeId.trim()
919
+ : assetTypeIdFromRef || (assetTypeId ?? '');
920
+ const assetTypeRef = rawAt && typeof rawAt === 'object'
921
+ ? {
922
+ id: typeof rawAt.id === 'string' && String(rawAt.id).trim()
923
+ ? String(rawAt.id).trim()
924
+ : normalizedAssetTypeId,
925
+ name: normalizeAssetTypeBuilderLocalizedField(rawAt.name, ''),
926
+ }
927
+ : {
928
+ id: normalizedAssetTypeId,
929
+ name: '',
930
+ };
931
+ return {
932
+ id: typeof itemData.id === 'string' && itemData.id ? itemData.id : AXPDataGenerator.uuid(),
933
+ assetTypeSectionId: typeof itemData.assetTypeSectionId === 'string' && itemData.assetTypeSectionId
934
+ ? itemData.assetTypeSectionId
935
+ : id,
936
+ assetTypeSectionComponentId: typeof itemData.assetTypeSectionComponentId === 'string' && itemData.assetTypeSectionComponentId
937
+ ? itemData.assetTypeSectionComponentId
938
+ : undefined,
939
+ assetTypeId: normalizedAssetTypeId,
940
+ assetType: assetTypeRef,
941
+ assetTypeSection: itemData.assetTypeSection,
942
+ roleName: normalizeAssetTypeBuilderLocalizedField(itemData.roleName, ''),
943
+ count: typeof itemData.count === 'number' ? itemData.count : 1,
944
+ hasMaintenance: itemData.hasMaintenance === true,
945
+ assetId: typeof itemData.assetId === 'string' && itemData.assetId ? itemData.assetId : undefined,
946
+ asset: itemData.asset && typeof itemData.asset === 'object'
947
+ ? {
948
+ id: typeof itemData.asset.id === 'string'
949
+ ? (itemData.asset.id ?? '')
950
+ : '',
951
+ code: typeof itemData.asset.code === 'string'
952
+ ? itemData.asset.code
953
+ : undefined,
954
+ title: normalizeAssetTypeBuilderLocalizedField(itemData.asset.title, ''),
955
+ }
956
+ : undefined,
957
+ };
958
+ }),
959
+ };
960
+ }),
961
+ };
962
+ }
963
+
964
+ class AXMAssetTypeSectionBuilderComponent {
965
+ constructor() {
966
+ this.value = input(null, /* @ts-ignore */
967
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
968
+ this.assetTypeId = input(undefined, /* @ts-ignore */
969
+ ...(ngDevMode ? [{ debugName: "assetTypeId" }] : /* istanbul ignore next */ []));
970
+ this.options = input({}, /* @ts-ignore */
971
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
972
+ this.valueChange = output();
973
+ this.standardRef = viewChild(AXPStandardSectionItemsBuilderComponent, /* @ts-ignore */
974
+ ...(ngDevMode ? [{ debugName: "standardRef" }] : /* istanbul ignore next */ []));
975
+ this.layoutBuilder = inject(AXPLayoutBuilderService);
976
+ this.translationService = inject(AXTranslationService);
977
+ this.disabledSectionPrompt = false;
978
+ this.disabledAddSectionPrompt = async () => null;
979
+ this.builderValue = signal({ sections: [] }, /* @ts-ignore */
980
+ ...(ngDevMode ? [{ debugName: "builderValue" }] : /* istanbul ignore next */ []));
981
+ this.standardConfig = computed(() => ({
982
+ mapItemToView: (item, _section) => this.mapItemToView(item),
983
+ promptAddSection: this.allowAddSection() ? (value) => this.promptAddSection(value) : this.disabledAddSectionPrompt,
984
+ promptEditSection: this.allowEditSection()
985
+ ? (section, value) => this.promptEditSection(section, value)
986
+ : this.disabledSectionPrompt,
987
+ promptAddItems: this.allowAddItems() ? (sectionId, value) => this.promptAddItems(sectionId, value) : undefined,
988
+ promptEditItem: (item, sectionId, value) => this.linkAssetMode()
989
+ ? this.promptEditLinkedAssetItem(item, sectionId, value)
990
+ : this.promptEditItem(item, sectionId, value),
991
+ confirmRemoveSection: this.linkAssetMode() ? async () => false : undefined,
992
+ confirmRemoveItem: this.linkAssetMode()
993
+ ? (item, sectionId) => this.confirmRemoveLinkedAssetItem(item, sectionId)
994
+ : undefined,
995
+ showSectionTechnicalName: false,
996
+ allowReorderSections: this.allowReorderSections(),
997
+ minSectionCount: this.allowAddSection() ? 1 : Number.MAX_SAFE_INTEGER,
998
+ texts: {
999
+ addSection: '@asset-management:assetTypeSections.entities.assetTypeSection.actions.add-section.title',
1000
+ addItem: '@asset-management:assetTypeSectionComponents.entities.assetTypeSectionComponent.actions.add-item.title',
1001
+ },
1002
+ }), /* @ts-ignore */
1003
+ ...(ngDevMode ? [{ debugName: "standardConfig" }] : /* istanbul ignore next */ []));
1004
+ this.hideHeaderActions = computed(() => this.options()['hideHeaderActions'] === true, /* @ts-ignore */
1005
+ ...(ngDevMode ? [{ debugName: "hideHeaderActions" }] : /* istanbul ignore next */ []));
1006
+ this.allowAddSection = computed(() => this.options()['allowAddSection'] !== false, /* @ts-ignore */
1007
+ ...(ngDevMode ? [{ debugName: "allowAddSection" }] : /* istanbul ignore next */ []));
1008
+ this.allowEditSection = computed(() => {
1009
+ if (this.linkAssetMode()) {
1010
+ return false;
1011
+ }
1012
+ return this.options()['allowEditSection'] !== false;
1013
+ }, /* @ts-ignore */
1014
+ ...(ngDevMode ? [{ debugName: "allowEditSection" }] : /* istanbul ignore next */ []));
1015
+ this.allowAddItems = computed(() => this.options()['allowAddItems'] !== false, /* @ts-ignore */
1016
+ ...(ngDevMode ? [{ debugName: "allowAddItems" }] : /* istanbul ignore next */ []));
1017
+ this.allowReorderSections = computed(() => {
1018
+ const option = this.options()['allowReorderSections'];
1019
+ return typeof option === 'boolean' ? option : true;
1020
+ }, /* @ts-ignore */
1021
+ ...(ngDevMode ? [{ debugName: "allowReorderSections" }] : /* istanbul ignore next */ []));
1022
+ this.readonly = computed(() => this.options()['readonly'] === true, /* @ts-ignore */
1023
+ ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
1024
+ this.linkAssetMode = computed(() => this.options()['linkAssetMode'] === true, /* @ts-ignore */
1025
+ ...(ngDevMode ? [{ debugName: "linkAssetMode" }] : /* istanbul ignore next */ []));
1026
+ this.#syncValueToBuilder = effect(() => {
1027
+ const normalized = normalizeAssetTypeSectionBuilderValue(this.value(), this.assetTypeId());
1028
+ this.builderValue.set(this.toBuilderValue(normalized));
1029
+ }, /* @ts-ignore */
1030
+ ...(ngDevMode ? [{ debugName: "#syncValueToBuilder" }] : /* istanbul ignore next */ []));
1031
+ }
1032
+ #syncValueToBuilder;
1033
+ async addSection() {
1034
+ await this.standardRef()?.addSection();
1035
+ }
1036
+ /** Aligns section-items baseline to persisted value after save. */
1037
+ syncAfterSave(value) {
1038
+ const normalized = normalizeAssetTypeSectionBuilderValue(value, this.assetTypeId());
1039
+ this.builderValue.set(this.toBuilderValue(normalized));
1040
+ queueMicrotask(() => this.standardRef()?.markClean());
1041
+ }
1042
+ /** Re-applies the current `[value]` input and clears builder dirty state after discard. */
1043
+ resetAfterDiscard() {
1044
+ const normalized = normalizeAssetTypeSectionBuilderValue(this.value(), this.assetTypeId());
1045
+ this.builderValue.set(this.toBuilderValue(normalized));
1046
+ queueMicrotask(() => this.standardRef()?.markClean());
1047
+ }
1048
+ onBuilderValueChange(next) {
1049
+ this.builderValue.set(next);
1050
+ const value = this.fromBuilderValue(next);
1051
+ this.valueChange.emit(value);
1052
+ }
1053
+ async promptAddSection(value) {
1054
+ const seed = {
1055
+ code: this.getNextSectionCode(value),
1056
+ title: '',
1057
+ description: '',
1058
+ };
1059
+ const dialogValue = await this.promptSectionForm('create', seed, value);
1060
+ if (!dialogValue)
1061
+ return null;
1062
+ const sectionId = AXPDataGenerator.uuid();
1063
+ return {
1064
+ id: sectionId,
1065
+ name: sectionId,
1066
+ code: dialogValue.code,
1067
+ title: dialogValue.title,
1068
+ description: dialogValue.description,
1069
+ };
1070
+ }
1071
+ async promptEditSection(section, value) {
1072
+ const seed = {
1073
+ code: Number(section['code'] ?? 0) || this.getNextSectionCode(value),
1074
+ title: this.resolveLocalizedTextForDialog(section['title']),
1075
+ description: this.resolveLocalizedTextForDialog(section['description']),
1076
+ };
1077
+ const dialogValue = await this.promptSectionForm('edit', seed, value, section.id);
1078
+ if (!dialogValue)
1079
+ return null;
1080
+ return {
1081
+ ...section,
1082
+ code: dialogValue.code,
1083
+ title: dialogValue.title,
1084
+ description: dialogValue.description,
1085
+ };
1086
+ }
1087
+ async promptSectionForm(mode, seed, value, currentSectionId) {
1088
+ const title = mode === 'create'
1089
+ ? (await this.translationService.translateAsync('@asset-management:assetTypeSections.entities.assetTypeSection.actions.add-section.title')) || 'Add Section'
1090
+ : (await this.translationService.translateAsync('@general:actions.edit.title')) || 'Edit';
1091
+ const result = await this.layoutBuilder
1092
+ .create()
1093
+ .dialog((dialog) => {
1094
+ dialog
1095
+ .setTitle(title)
1096
+ .setSize('sm')
1097
+ .setCloseButton(true)
1098
+ .setContext({ ...seed })
1099
+ .content((layout) => {
1100
+ layout.mode('edit');
1101
+ layout.dynamicForm(this.buildSectionFormDefinition());
1102
+ })
1103
+ .setActions((actions) => {
1104
+ actions.cancel('@general:actions.cancel.title').submit('@general:actions.save.title');
1105
+ })
1106
+ .onAction((dialogRef) => {
1107
+ if (dialogRef.action() === 'cancel') {
1108
+ return { success: false };
1109
+ }
1110
+ const context = dialogRef.context();
1111
+ const code = Number(context.code ?? 0);
1112
+ const titleValue = String(context.title ?? '').trim();
1113
+ const description = String(context.description ?? '');
1114
+ if (!Number.isFinite(code) || code <= 0 || !titleValue) {
1115
+ return { success: false };
1116
+ }
1117
+ if (this.isDuplicateSectionCode(value, code, currentSectionId)) {
1118
+ return { success: false };
1119
+ }
1120
+ return { success: true, kind: 'object', value: {
1121
+ code,
1122
+ title: titleValue,
1123
+ description,
1124
+ },
1125
+ };
1126
+ });
1127
+ })
1128
+ .show();
1129
+ return result?.success && result.value ? result.value : null;
1130
+ }
1131
+ isDuplicateSectionCode(value, code, currentSectionId) {
1132
+ return value.sections.some((section) => {
1133
+ if (currentSectionId && section.id === currentSectionId)
1134
+ return false;
1135
+ const sectionCode = Number(section['code'] ?? 0);
1136
+ return Number.isFinite(sectionCode) && sectionCode === code;
1137
+ });
1138
+ }
1139
+ getNextSectionCode(value) {
1140
+ const maxCode = value.sections.reduce((max, section) => {
1141
+ const sectionCode = Number(section['code'] ?? 0);
1142
+ if (!Number.isFinite(sectionCode))
1143
+ return max;
1144
+ return Math.max(max, sectionCode);
1145
+ }, 0);
1146
+ return maxCode + 1;
1147
+ }
1148
+ mapItemToView(item) {
1149
+ const roleName = this.resolveLocalizedTextForDialog(item['roleName'] ?? '');
1150
+ const assetTypeName = this.resolveLocalizedTextForDialog(item['assetType']?.name ?? '');
1151
+ const count = Number(item['count'] ?? 0);
1152
+ const hasMaintenance = item['hasMaintenance'] === true;
1153
+ const linkedAsset = item['asset'];
1154
+ const linkedAssetCode = typeof linkedAsset?.code === 'string' ? linkedAsset.code : '';
1155
+ const linkedAssetTitle = this.resolveLocalizedTextForDialog(linkedAsset?.title ?? '');
1156
+ if (this.linkAssetMode()) {
1157
+ const hasLinkedAsset = !!item['assetId'];
1158
+ const linkedAssetName = linkedAssetTitle || '@asset-management:assets.entities.asset.components.asset-component.no-link';
1159
+ const linkedAssetDisplay = linkedAssetCode ? `${linkedAssetName} (${linkedAssetCode})` : linkedAssetName;
1160
+ return {
1161
+ icon: hasLinkedAsset ? 'fa-light fa-link' : 'fa-light fa-link-slash',
1162
+ iconTone: hasLinkedAsset ? 'easy' : 'hard',
1163
+ title: roleName,
1164
+ name: linkedAssetDisplay,
1165
+ badges: [
1166
+ {
1167
+ text: hasLinkedAsset
1168
+ ? '@asset-management:assets.entities.asset.components.asset-component.assigned'
1169
+ : '@asset-management:assets.entities.asset.components.asset-component.unassigned',
1170
+ variant: hasLinkedAsset ? 'success' : 'warning',
1171
+ },
1172
+ ...(hasLinkedAsset && linkedAssetTitle
1173
+ ? [
1174
+ {
1175
+ text: linkedAssetTitle,
1176
+ variant: 'neutral',
1177
+ },
1178
+ ]
1179
+ : []),
1180
+ {
1181
+ text: assetTypeName,
1182
+ variant: 'neutral',
1183
+ },
1184
+ ],
1185
+ };
1186
+ }
1187
+ return {
1188
+ icon: 'fa-light fa-cube',
1189
+ title: roleName,
1190
+ name: assetTypeName,
1191
+ badges: [
1192
+ {
1193
+ text: `x${count}`,
1194
+ variant: 'neutral',
1195
+ },
1196
+ ...(hasMaintenance
1197
+ ? [
1198
+ {
1199
+ text: '@asset-management:assetTypeSectionComponents.entities.assetTypeSectionComponent.fields.hasMaintenance.title',
1200
+ variant: 'success',
1201
+ },
1202
+ ]
1203
+ : []),
1204
+ ],
1205
+ };
1206
+ }
1207
+ async promptAddItems(sectionId, value) {
1208
+ const section = value.sections.find((item) => item.id === sectionId);
1209
+ if (!section)
1210
+ return null;
1211
+ const itemValue = await this.promptItemForm('create');
1212
+ if (!itemValue)
1213
+ return null;
1214
+ const nextOrder = section.items.length;
1215
+ const id = AXPDataGenerator.uuid();
1216
+ return [
1217
+ {
1218
+ id,
1219
+ order: nextOrder,
1220
+ name: itemValue.assetType.name,
1221
+ roleName: itemValue.roleName,
1222
+ count: itemValue.count,
1223
+ hasMaintenance: itemValue.hasMaintenance,
1224
+ assetType: itemValue.assetType,
1225
+ assetTypeSectionId: section.id,
1226
+ assetTypeId: itemValue.assetTypeId || this.assetTypeId() || '',
1227
+ },
1228
+ ];
1229
+ }
1230
+ async promptEditItem(item, _sectionId, _value) {
1231
+ const dialogValue = await this.promptItemForm('edit', {
1232
+ assetTypeId: String(item['assetTypeId'] ?? this.assetTypeId() ?? ''),
1233
+ assetType: item['assetType'] ?? {
1234
+ id: String(item['assetTypeId'] ?? this.assetTypeId() ?? ''),
1235
+ name: '',
1236
+ },
1237
+ roleName: this.resolveLocalizedTextForDialog(item['roleName'] ?? item['name'] ?? ''),
1238
+ count: Number(item['count'] ?? 1),
1239
+ hasMaintenance: item['hasMaintenance'] === true,
1240
+ });
1241
+ if (!dialogValue)
1242
+ return null;
1243
+ return {
1244
+ ...item,
1245
+ name: dialogValue.assetType.name,
1246
+ roleName: dialogValue.roleName,
1247
+ count: dialogValue.count,
1248
+ hasMaintenance: dialogValue.hasMaintenance,
1249
+ assetTypeId: dialogValue.assetTypeId,
1250
+ assetType: dialogValue.assetType,
1251
+ };
1252
+ }
1253
+ async promptEditLinkedAssetItem(item, _sectionId, _value) {
1254
+ const seed = {
1255
+ assetId: String(item['assetId'] ?? ''),
1256
+ assetTypeId: String(item['assetTypeId'] ?? '').trim(),
1257
+ asset: item['asset'] ?? {
1258
+ id: '',
1259
+ code: '',
1260
+ title: '',
1261
+ },
1262
+ };
1263
+ const result = await this.promptLinkAssetForm(seed);
1264
+ if (!result) {
1265
+ return null;
1266
+ }
1267
+ return {
1268
+ ...item,
1269
+ assetId: result.assetId || undefined,
1270
+ asset: result.assetId ? result.asset : undefined,
1271
+ };
1272
+ }
1273
+ async promptLinkAssetForm(seed) {
1274
+ const title = (await this.translationService.translateAsync('@asset-management:assets.entities.asset.components.asset-component.actions.edit.title')) || 'Edit Component Asset';
1275
+ const result = await this.layoutBuilder
1276
+ .create()
1277
+ .dialog((dialog) => {
1278
+ dialog
1279
+ .setTitle(title)
1280
+ .setSize('sm')
1281
+ .setCloseButton(true)
1282
+ .setContext({ ...seed })
1283
+ .content((layout) => {
1284
+ layout.mode('edit');
1285
+ layout.dynamicForm(this.buildLinkAssetFormDefinition());
1286
+ })
1287
+ .setActions((actions) => {
1288
+ actions.cancel('@general:actions.cancel.title').submit('@general:actions.save.title');
1289
+ })
1290
+ .onAction((dialogRef) => {
1291
+ if (dialogRef.action() === 'cancel') {
1292
+ return { success: false };
1293
+ }
1294
+ const context = dialogRef.context();
1295
+ const assetId = String(context.assetId ?? '').trim();
1296
+ const asset = context.asset ?? { id: assetId, code: '', title: '' };
1297
+ return { success: true, kind: 'object', value: {
1298
+ assetId,
1299
+ assetTypeId: String(context.assetTypeId ?? ''),
1300
+ asset: {
1301
+ id: String(asset.id ?? assetId),
1302
+ code: String(asset.code ?? ''),
1303
+ title: String(asset.title ?? ''),
1304
+ },
1305
+ },
1306
+ };
1307
+ });
1308
+ })
1309
+ .show();
1310
+ return result?.success && result.value ? result.value : null;
1311
+ }
1312
+ async confirmRemoveLinkedAssetItem(item, sectionId) {
1313
+ const hasLinkedAsset = typeof item['assetId'] === 'string' && item['assetId'].length > 0;
1314
+ if (!hasLinkedAsset) {
1315
+ return false;
1316
+ }
1317
+ const deleteTitle = (await this.translationService.translateAsync('@asset-management:assets.entities.asset.components.asset-component.actions.delete.title')) || 'Remove Component Asset';
1318
+ const linkedAsset = item['asset'];
1319
+ const linkedAssetTitle = this.resolveLocalizedTextForDialog(linkedAsset?.title ?? '');
1320
+ const linkedAssetCode = typeof linkedAsset?.code === 'string' ? linkedAsset.code : '';
1321
+ const assetDisplay = linkedAssetTitle || linkedAssetCode || String(item['assetId'] ?? '');
1322
+ const assetLabel = `${assetDisplay}${linkedAssetCode && linkedAssetTitle ? ` (${linkedAssetCode})` : ''}`;
1323
+ const deleteResult = await this.layoutBuilder
1324
+ .create()
1325
+ .dialog((dialog) => {
1326
+ dialog
1327
+ .setTitle(deleteTitle)
1328
+ .setMessage('@general:workflow.confirm-delete')
1329
+ .setSize('sm')
1330
+ .setCloseButton(true)
1331
+ .setContext({ assetLabel })
1332
+ .content((layout) => {
1333
+ layout.mode('edit');
1334
+ layout.dynamicForm(this.buildRemoveLinkedAssetFormDefinition());
1335
+ })
1336
+ .setActions((actions) => {
1337
+ actions.cancel('@general:actions.cancel.title').custom({
1338
+ title: '@general:actions.delete.title',
1339
+ color: 'danger',
1340
+ command: {
1341
+ name: 'delete',
1342
+ options: {
1343
+ validate: false,
1344
+ },
1345
+ },
1346
+ });
1347
+ })
1348
+ .onAction((dialogRef) => {
1349
+ if (dialogRef.action() !== 'delete') {
1350
+ return { success: false };
1351
+ }
1352
+ return { success: true, kind: 'object', data: undefined };
1353
+ });
1354
+ })
1355
+ .show();
1356
+ if (deleteResult?.success === true) {
1357
+ this.unlinkLinkedAssetItem(item.id, sectionId);
1358
+ }
1359
+ // Prevent structural row removal; only unlink asset assignment.
1360
+ return false;
1361
+ }
1362
+ buildRemoveLinkedAssetFormDefinition() {
1363
+ const group = {
1364
+ name: 'unlink-asset',
1365
+ title: '@asset-management:assets.entities.asset.components.asset-component.title',
1366
+ parameters: [
1367
+ {
1368
+ path: 'assetLabel',
1369
+ title: '@asset-management:assets.entities.asset.title',
1370
+ widget: {
1371
+ path: 'assetLabel',
1372
+ type: AXPWidgetsCatalog.text,
1373
+ options: {
1374
+ disabled: true,
1375
+ },
1376
+ },
1377
+ },
1378
+ ],
1379
+ };
1380
+ return { groups: [group] };
1381
+ }
1382
+ unlinkLinkedAssetItem(itemId, sectionId) {
1383
+ const nextValue = {
1384
+ sections: this.builderValue().sections.map((section) => {
1385
+ if (section.id !== sectionId) {
1386
+ return section;
1387
+ }
1388
+ return {
1389
+ ...section,
1390
+ items: section.items.map((item) => {
1391
+ if (item.id !== itemId) {
1392
+ return item;
1393
+ }
1394
+ return {
1395
+ ...item,
1396
+ assetId: undefined,
1397
+ asset: undefined,
1398
+ };
1399
+ }),
1400
+ };
1401
+ }),
1402
+ };
1403
+ this.onBuilderValueChange(nextValue);
1404
+ }
1405
+ async promptItemForm(mode, seed) {
1406
+ const defaultSeed = seed ?? {
1407
+ assetTypeId: '',
1408
+ assetType: {
1409
+ id: '',
1410
+ name: '',
1411
+ },
1412
+ roleName: '',
1413
+ count: 1,
1414
+ hasMaintenance: false,
1415
+ };
1416
+ const title = mode === 'create'
1417
+ ? (await this.translationService.translateAsync('@asset-management:assetTypeSectionComponents.entities.assetTypeSectionComponent.actions.add-item.title')) || 'Add Component'
1418
+ : (await this.translationService.translateAsync('@general:actions.edit.title')) || 'Edit';
1419
+ const formDefinition = this.buildItemFormDefinition();
1420
+ const result = await this.layoutBuilder
1421
+ .create()
1422
+ .dialog((dialog) => {
1423
+ dialog
1424
+ .setTitle(title)
1425
+ .setSize('sm')
1426
+ .setCloseButton(true)
1427
+ .setContext({ ...defaultSeed })
1428
+ .content((layout) => {
1429
+ layout.mode('edit');
1430
+ layout.dynamicForm(formDefinition);
1431
+ })
1432
+ .setActions((actions) => {
1433
+ actions.cancel('@general:actions.cancel.title').submit('@general:actions.save.title');
1434
+ })
1435
+ .onAction((dialogRef) => {
1436
+ if (dialogRef.action() === 'cancel') {
1437
+ return { success: false };
1438
+ }
1439
+ const context = dialogRef.context();
1440
+ const assetTypeId = String(context.assetTypeId ?? '').trim();
1441
+ const assetType = context.assetType;
1442
+ const roleName = String(context.roleName ?? '').trim();
1443
+ const count = Number(context.count ?? 0);
1444
+ const hasMaintenance = context.hasMaintenance === true;
1445
+ if (!assetTypeId) {
1446
+ return { success: false };
1447
+ }
1448
+ return { success: true, kind: 'object', value: {
1449
+ assetTypeId,
1450
+ assetType: {
1451
+ id: String(assetType?.id ?? assetTypeId),
1452
+ name: String(assetType?.name ?? ''),
1453
+ },
1454
+ roleName,
1455
+ count: Number.isFinite(count) && count > 0 ? count : 1,
1456
+ hasMaintenance,
1457
+ },
1458
+ };
1459
+ });
1460
+ })
1461
+ .show();
1462
+ return result?.success && result.value ? result.value : null;
1463
+ }
1464
+ buildItemFormDefinition() {
1465
+ const excludedAssetTypeId = this.assetTypeId();
1466
+ const group = {
1467
+ name: 'component',
1468
+ title: '@asset-management:assetTypeSectionComponents.entities.assetTypeSectionComponent.title',
1469
+ parameters: [
1470
+ {
1471
+ path: 'assetTypeId',
1472
+ title: '@asset-management:assetTypes.entities.assetType.title',
1473
+ widget: {
1474
+ path: 'assetTypeId',
1475
+ type: AXPWidgetsCatalog.lookup,
1476
+ options: {
1477
+ entity: `${RootConfig.module.name}.${RootConfig.entities.assetType.name}`,
1478
+ textField: 'title',
1479
+ valueField: 'id',
1480
+ look: 'select',
1481
+ multiple: false,
1482
+ expose: [
1483
+ { source: 'id', target: 'assetType.id' },
1484
+ { source: 'title', target: 'assetType.name' },
1485
+ { source: 'hasMaintenance', target: 'hasMaintenance' },
1486
+ ],
1487
+ ...(excludedAssetTypeId
1488
+ ? {
1489
+ filter: { field: 'id', operator: { type: 'notEqual' }, value: excludedAssetTypeId },
1490
+ }
1491
+ : {}),
1492
+ validations: [{ rule: 'required' }],
1493
+ },
1494
+ },
1495
+ },
1496
+ this.toFieldDefinition('hasMaintenance', '@asset-management:assetTypeSectionComponents.entities.assetTypeSectionComponent.fields.hasMaintenance.title', AXPWidgetsCatalog.toggle),
1497
+ this.toFieldDefinition('count', '@asset-management:assetTypeSectionComponents.entities.assetTypeSectionComponent.fields.count.title', AXPWidgetsCatalog.number, [{ rule: 'required' }]),
1498
+ this.toFieldDefinition('roleName', '@asset-management:assetTypeSectionComponents.entities.assetTypeSectionComponent.fields.roleName.title', AXPWidgetsCatalog.largeText),
1499
+ ],
1500
+ };
1501
+ return { groups: [group] };
1502
+ }
1503
+ buildSectionFormDefinition() {
1504
+ const group = {
1505
+ name: 'section',
1506
+ title: '@asset-management:assetTypeSections.entities.assetTypeSection.title',
1507
+ parameters: [
1508
+ this.toFieldDefinition('code', '@asset-management:assetTypeSections.entities.assetTypeSection.fields.code.title', AXPWidgetsCatalog.number, [{ rule: 'required' }]),
1509
+ this.toFieldDefinition('title', '@asset-management:assetTypeSections.entities.assetTypeSection.fields.title.title', AXPWidgetsCatalog.text, [{ rule: 'required' }]),
1510
+ this.toFieldDefinition('description', '@asset-management:assetTypeSections.entities.assetTypeSection.fields.description.title', AXPWidgetsCatalog.largeText),
1511
+ ],
1512
+ };
1513
+ return { groups: [group] };
1514
+ }
1515
+ buildLinkAssetFormDefinition() {
1516
+ const currentAssetId = this.options()['mainAssetId'];
1517
+ const filters = [
1518
+ ...(typeof currentAssetId === 'string' && currentAssetId
1519
+ ? [{ field: 'id', operator: { type: 'notEqual' }, value: currentAssetId }]
1520
+ : []),
1521
+ {
1522
+ field: 'assetTypeId',
1523
+ operator: { type: 'equal' },
1524
+ value: "{{ context.eval('assetTypeId') }}",
1525
+ },
1526
+ ];
1527
+ const group = {
1528
+ name: 'asset-link',
1529
+ title: '@asset-management:assets.entities.asset.title',
1530
+ parameters: [
1531
+ {
1532
+ path: 'assetId',
1533
+ title: '@asset-management:assets.entities.asset.title',
1534
+ widget: {
1535
+ path: 'assetId',
1536
+ type: AXPWidgetsCatalog.lookup,
1537
+ options: {
1538
+ entity: `${RootConfig.module.name}.${RootConfig.entities.asset.name}`,
1539
+ textField: 'title',
1540
+ valueField: 'id',
1541
+ look: 'lookup',
1542
+ allowSearch: true,
1543
+ hasClearButton: true,
1544
+ columns: ['code', 'title', 'assetTypeId', 'statusId', 'locationId'],
1545
+ expose: [
1546
+ { source: 'id', target: 'asset.id' },
1547
+ { source: 'code', target: 'asset.code' },
1548
+ { source: 'title', target: 'asset.title' },
1549
+ ],
1550
+ filter: filters.length === 1 ? filters[0] : { logic: 'and', filters },
1551
+ validations: [{ rule: 'required' }],
1552
+ },
1553
+ },
1554
+ },
1555
+ ],
1556
+ };
1557
+ return { groups: [group] };
1558
+ }
1559
+ toFieldDefinition(path, title, widgetType, validations = []) {
1560
+ return {
1561
+ path,
1562
+ title,
1563
+ widget: {
1564
+ path,
1565
+ type: widgetType,
1566
+ ...(validations.length > 0 ? { options: { validations } } : {}),
1567
+ },
1568
+ };
1569
+ }
1570
+ toBuilderValue(value) {
1571
+ return {
1572
+ sections: value.sections.map((section) => ({
1573
+ id: section.id,
1574
+ order: section.code - 1,
1575
+ name: section.id,
1576
+ code: section.code,
1577
+ title: section.title,
1578
+ description: section.description,
1579
+ items: section.items.map((item, itemIndex) => ({
1580
+ id: item.id,
1581
+ order: itemIndex,
1582
+ name: this.resolveLocalizedTextForDialog(item.assetType?.name ?? ''),
1583
+ assetType: item.assetType,
1584
+ roleName: item.roleName,
1585
+ count: item.count,
1586
+ hasMaintenance: item.hasMaintenance,
1587
+ assetTypeId: item.assetTypeId,
1588
+ assetTypeSectionId: item.assetTypeSectionId || section.id,
1589
+ assetTypeSectionComponentId: item.assetTypeSectionComponentId || item.id,
1590
+ assetId: item.assetId,
1591
+ asset: item.asset,
1592
+ })),
1593
+ })),
1594
+ };
1595
+ }
1596
+ fromBuilderValue(value) {
1597
+ const assetTypeId = this.assetTypeId() ?? '';
1598
+ return {
1599
+ sections: value.sections.map((section, sectionIndex) => {
1600
+ const sectionId = section.id || AXPDataGenerator.uuid();
1601
+ const sectionCodeRaw = Number(section['code'] ?? sectionIndex + 1);
1602
+ const sectionCode = Number.isFinite(sectionCodeRaw) && sectionCodeRaw > 0 ? sectionCodeRaw : sectionIndex + 1;
1603
+ return {
1604
+ id: sectionId,
1605
+ code: sectionCode,
1606
+ title: normalizeAssetTypeBuilderLocalizedField(section['title'], `Section ${sectionCode}`),
1607
+ assetTypeId,
1608
+ description: normalizeAssetTypeBuilderLocalizedField(section['description'], ''),
1609
+ items: section.items.map((item) => this.mapBuilderItemToDomain(item, sectionId, assetTypeId)),
1610
+ };
1611
+ }),
1612
+ };
1613
+ }
1614
+ mapBuilderItemToDomain(item, sectionId, assetTypeId) {
1615
+ const rawAt = item['assetType'];
1616
+ const normalizedAssetTypeId = (() => {
1617
+ const fromField = typeof item['assetTypeId'] === 'string' ? String(item['assetTypeId']).trim() : '';
1618
+ if (fromField)
1619
+ return fromField;
1620
+ const fromRef = typeof rawAt?.id === 'string' ? String(rawAt.id).trim() : '';
1621
+ if (fromRef)
1622
+ return fromRef;
1623
+ return String(assetTypeId ?? '');
1624
+ })();
1625
+ return {
1626
+ id: item.id || AXPDataGenerator.uuid(),
1627
+ assetTypeSectionId: String(item['assetTypeSectionId'] ?? sectionId),
1628
+ assetTypeId: normalizedAssetTypeId,
1629
+ assetType: {
1630
+ id: String(rawAt?.id ?? normalizedAssetTypeId),
1631
+ name: normalizeAssetTypeBuilderLocalizedField(rawAt?.name, ''),
1632
+ },
1633
+ roleName: normalizeAssetTypeBuilderLocalizedField(item['roleName'], ''),
1634
+ count: Number(item['count'] ?? 1),
1635
+ hasMaintenance: item['hasMaintenance'] === true,
1636
+ assetTypeSectionComponentId: String(item['assetTypeSectionComponentId'] ?? ''),
1637
+ assetId: typeof item['assetId'] === 'string' ? item['assetId'] : undefined,
1638
+ asset: item['asset'] && typeof item['asset'] === 'object'
1639
+ ? {
1640
+ id: String(item['asset'].id ?? ''),
1641
+ code: typeof item['asset'].code === 'string'
1642
+ ? (item['asset'].code ?? '')
1643
+ : undefined,
1644
+ title: normalizeAssetTypeBuilderLocalizedField(item['asset'].title, ''),
1645
+ }
1646
+ : undefined,
1647
+ };
1648
+ }
1649
+ resolveLocalizedTextForDialog(value) {
1650
+ if (value == null || value === '') {
1651
+ return '';
1652
+ }
1653
+ if (typeof value === 'string') {
1654
+ return value;
1655
+ }
1656
+ return resolveMultiLanguageString(value, this.translationService.getActiveLang());
1657
+ }
1658
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetTypeSectionBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1659
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXMAssetTypeSectionBuilderComponent, isStandalone: true, selector: "axm-asset-type-section-builder", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, assetTypeId: { classPropertyName: "assetTypeId", publicName: "assetTypeId", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, viewQueries: [{ propertyName: "standardRef", first: true, predicate: AXPStandardSectionItemsBuilderComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"axm-asset-type-section-builder\">\n @if (!readonly() && !hideHeaderActions() && allowAddSection()) {\n <div class=\"__header\">\n <div class=\"__header-left\"></div>\n <div class=\"__header-right\">\n <ax-button\n class=\"ax-sm\"\n [text]=\"'@asset-management:assetTypeSections.entities.assetTypeSection.actions.add-section.title' | translate | async\"\n [color]=\"'primary'\"\n (onClick)=\"addSection()\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-layer-group\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n }\n\n <axp-standard-section-items-builder\n class=\"block min-h-[200px]\"\n [value]=\"builderValue()\"\n [config]=\"standardConfig()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"onBuilderValueChange($event)\" />\n</div>\n", styles: [".axm-asset-type-section-builder .__header{margin-bottom:calc(var(--spacing, .25rem) * 3);display:flex;align-items:center;justify-content:space-between}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStandardSectionItemsBuilderComponent, selector: "axp-standard-section-items-builder", inputs: ["value", "config", "readonly"], outputs: ["valueChange"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1660
+ }
1661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetTypeSectionBuilderComponent, decorators: [{
1662
+ type: Component,
1663
+ args: [{ selector: 'axm-asset-type-section-builder', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
1664
+ CommonModule,
1665
+ AXButtonModule,
1666
+ AXDecoratorModule,
1667
+ AXTranslationModule,
1668
+ AXPStandardSectionItemsBuilderComponent,
1669
+ ], template: "<div class=\"axm-asset-type-section-builder\">\n @if (!readonly() && !hideHeaderActions() && allowAddSection()) {\n <div class=\"__header\">\n <div class=\"__header-left\"></div>\n <div class=\"__header-right\">\n <ax-button\n class=\"ax-sm\"\n [text]=\"'@asset-management:assetTypeSections.entities.assetTypeSection.actions.add-section.title' | translate | async\"\n [color]=\"'primary'\"\n (onClick)=\"addSection()\">\n <ax-prefix>\n <ax-icon icon=\"fa-light fa-layer-group\"></ax-icon>\n </ax-prefix>\n </ax-button>\n </div>\n </div>\n }\n\n <axp-standard-section-items-builder\n class=\"block min-h-[200px]\"\n [value]=\"builderValue()\"\n [config]=\"standardConfig()\"\n [readonly]=\"readonly()\"\n (valueChange)=\"onBuilderValueChange($event)\" />\n</div>\n", styles: [".axm-asset-type-section-builder .__header{margin-bottom:calc(var(--spacing, .25rem) * 3);display:flex;align-items:center;justify-content:space-between}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
1670
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], assetTypeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "assetTypeId", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], standardRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPStandardSectionItemsBuilderComponent), { isSignal: true }] }] } });
1671
+
1672
+ const ASSET_TYPE_COMPONENTS_SAVE_COMMAND = 'AssetType:Components:Save';
1673
+ class AXMSaveAssetTypeComponentsCommand {
1674
+ constructor() {
1675
+ this.entityService = inject(AXPEntityService);
1676
+ this.translationService = inject(AXTranslationService);
1677
+ }
1678
+ async execute(input) {
1679
+ const id = input?.id;
1680
+ const entityRef = input?.entity || `${RootConfig.module.name}.${RootConfig.entities.assetType.name}`;
1681
+ const field = input?.field || 'components';
1682
+ const value = input?.value ?? { sections: [] };
1683
+ if (!id) {
1684
+ return {
1685
+ success: false,
1686
+ message: {
1687
+ text: await this.translationService.translateAsync('@general:messages.entity.invalid-data'),
1688
+ },
1689
+ };
1690
+ }
1691
+ try {
1692
+ const payload = { [field]: value };
1693
+ const isAssetTypeComponentsUpdate = entityRef === `${RootConfig.module.name}.${RootConfig.entities.assetType.name}` && field === 'components';
1694
+ if (isAssetTypeComponentsUpdate) {
1695
+ payload['name'] = this.ensureLatinName(input.name, {
1696
+ fallback: input.title,
1697
+ id,
1698
+ });
1699
+ }
1700
+ await this.entityService.withEntity(entityRef).data().update(id, payload);
1701
+ return { success: true, kind: 'object',
1702
+ data: { id, field, value },
1703
+ message: {
1704
+ text: await this.translationService.translateAsync('@general:messages.generic.success.description'),
1705
+ },
1706
+ };
1707
+ }
1708
+ catch (error) {
1709
+ const message = error instanceof Error
1710
+ ? error.message
1711
+ : await this.translationService.translateAsync('@general:messages.generic.error.description');
1712
+ return {
1713
+ success: false,
1714
+ message: {
1715
+ text: message,
1716
+ },
1717
+ };
1718
+ }
1719
+ }
1720
+ ensureLatinName(raw, context) {
1721
+ const fromRaw = this.toLatinIdentifier(raw);
1722
+ if (fromRaw) {
1723
+ return fromRaw;
1724
+ }
1725
+ const fromTitle = this.toLatinIdentifier(context?.fallback);
1726
+ if (fromTitle) {
1727
+ return fromTitle;
1728
+ }
1729
+ const fromId = this.toLatinIdentifier(context?.id);
1730
+ if (fromId) {
1731
+ return fromId;
1732
+ }
1733
+ return 'asset_type';
1734
+ }
1735
+ toLatinIdentifier(value) {
1736
+ if (typeof value !== 'string') {
1737
+ return '';
1738
+ }
1739
+ return value
1740
+ .replace(/[^A-Za-z0-9_-]+/g, '_')
1741
+ .replace(/^_+|_+$/g, '')
1742
+ .replace(/_{2,}/g, '_');
1743
+ }
1744
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMSaveAssetTypeComponentsCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1745
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMSaveAssetTypeComponentsCommand, providedIn: 'root' }); }
1746
+ }
1747
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMSaveAssetTypeComponentsCommand, decorators: [{
1748
+ type: Injectable,
1749
+ args: [{
1750
+ providedIn: 'root',
1751
+ }]
1752
+ }] });
1753
+
1754
+ var saveAssetTypeComponents_command = /*#__PURE__*/Object.freeze({
1755
+ __proto__: null,
1756
+ ASSET_TYPE_COMPONENTS_SAVE_COMMAND: ASSET_TYPE_COMPONENTS_SAVE_COMMAND,
1757
+ AXMSaveAssetTypeComponentsCommand: AXMSaveAssetTypeComponentsCommand
1758
+ });
1759
+
1760
+ const ASSET_TYPE_SECTIONS_ADD_SECTION_COMMAND = 'AssetType:Sections:AddSection';
1761
+ const ASSET_TYPE_SECTIONS_DISCARD_COMMAND = 'AssetType:Sections:Discard';
1762
+ const ASSET_TYPE_SECTIONS_SAVE_COMMAND = 'AssetType:Sections:Save';
1763
+ class AXMAssetTypeSectionBuilderPageComponent extends AXPPageLayoutBaseComponent {
1764
+ constructor() {
1765
+ super(...arguments);
1766
+ /** Default `{}`: required inputs throw (NG0950) if read before `AXPPageComponentRendererDirective` calls `setInput` after `createComponent`. */
1767
+ this.rootContext = input({}, /* @ts-ignore */
1768
+ ...(ngDevMode ? [{ debugName: "rootContext" }] : /* istanbul ignore next */ []));
1769
+ this.pageConfig = input(/* @ts-ignore */
1770
+ ...(ngDevMode ? [undefined, { debugName: "pageConfig" }] : /* istanbul ignore next */ []));
1771
+ this.options = input({}, /* @ts-ignore */
1772
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1773
+ this.builderRef = viewChild('builderRef', /* @ts-ignore */
1774
+ ...(ngDevMode ? [{ debugName: "builderRef" }] : /* istanbul ignore next */ []));
1775
+ this.commandService = inject(AXPCommandService);
1776
+ this.entityService = inject(AXPEntityService);
1777
+ this.hostPageLayout = inject(AXPPageLayoutBase, { optional: true, skipSelf: true });
1778
+ this.baselineValue = signal(null, /* @ts-ignore */
1779
+ ...(ngDevMode ? [{ debugName: "baselineValue" }] : /* istanbul ignore next */ []));
1780
+ this.committedValue = signal(null, /* @ts-ignore */
1781
+ ...(ngDevMode ? [{ debugName: "committedValue" }] : /* istanbul ignore next */ []));
1782
+ this.config = computed(() => ({
1783
+ ...(this.pageConfig() ?? {}),
1784
+ ...(this.options() ?? {}),
1785
+ }), /* @ts-ignore */
1786
+ ...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
1787
+ this.fieldName = computed(() => {
1788
+ const field = this.readStringConfig('field');
1789
+ return field || 'components';
1790
+ }, /* @ts-ignore */
1791
+ ...(ngDevMode ? [{ debugName: "fieldName" }] : /* istanbul ignore next */ []));
1792
+ this.sourceFieldName = computed(() => {
1793
+ const field = this.readStringConfig('sourceField');
1794
+ return field || this.fieldName();
1795
+ }, /* @ts-ignore */
1796
+ ...(ngDevMode ? [{ debugName: "sourceFieldName" }] : /* istanbul ignore next */ []));
1797
+ this.sourceIdPath = computed(() => {
1798
+ const path = this.readStringConfig('sourceIdPath');
1799
+ return path || 'id';
1800
+ }, /* @ts-ignore */
1801
+ ...(ngDevMode ? [{ debugName: "sourceIdPath" }] : /* istanbul ignore next */ []));
1802
+ this.targetIdPath = computed(() => {
1803
+ const path = this.readStringConfig('targetIdPath');
1804
+ return path || this.sourceIdPath();
1805
+ }, /* @ts-ignore */
1806
+ ...(ngDevMode ? [{ debugName: "targetIdPath" }] : /* istanbul ignore next */ []));
1807
+ this.sourceEntityRef = computed(() => {
1808
+ const entity = this.readStringConfig('sourceEntity');
1809
+ return entity || `${RootConfig.module.name}.${RootConfig.entities.assetType.name}`;
1810
+ }, /* @ts-ignore */
1811
+ ...(ngDevMode ? [{ debugName: "sourceEntityRef" }] : /* istanbul ignore next */ []));
1812
+ this.targetEntityRef = computed(() => {
1813
+ const entity = this.readStringConfig('targetEntity');
1814
+ return entity || this.sourceEntityRef();
1815
+ }, /* @ts-ignore */
1816
+ ...(ngDevMode ? [{ debugName: "targetEntityRef" }] : /* istanbul ignore next */ []));
1817
+ this.context = computed(() => this.rootContext(), /* @ts-ignore */
1818
+ ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
1819
+ this.sourceRecordId = computed(() => {
1820
+ const id = get(this.context(), this.sourceIdPath());
1821
+ return typeof id === 'string' ? id : undefined;
1822
+ }, /* @ts-ignore */
1823
+ ...(ngDevMode ? [{ debugName: "sourceRecordId" }] : /* istanbul ignore next */ []));
1824
+ this.targetRecordId = computed(() => {
1825
+ const id = get(this.context(), this.targetIdPath());
1826
+ return typeof id === 'string' ? id : undefined;
1827
+ }, /* @ts-ignore */
1828
+ ...(ngDevMode ? [{ debugName: "targetRecordId" }] : /* istanbul ignore next */ []));
1829
+ this.readonly = computed(() => this.readBooleanConfig('readonly'), /* @ts-ignore */
1830
+ ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
1831
+ this.linkAssetMode = computed(() => this.readBooleanConfig('linkAssetMode'), /* @ts-ignore */
1832
+ ...(ngDevMode ? [{ debugName: "linkAssetMode" }] : /* istanbul ignore next */ []));
1833
+ this.allowAddSection = computed(() => this.readBooleanConfig('allowAddSection', true), /* @ts-ignore */
1834
+ ...(ngDevMode ? [{ debugName: "allowAddSection" }] : /* istanbul ignore next */ []));
1835
+ this.allowAddItems = computed(() => this.readBooleanConfig('allowAddItems', true), /* @ts-ignore */
1836
+ ...(ngDevMode ? [{ debugName: "allowAddItems" }] : /* istanbul ignore next */ []));
1837
+ this.allowEditSection = computed(() => this.readBooleanConfig('allowEditSection', true), /* @ts-ignore */
1838
+ ...(ngDevMode ? [{ debugName: "allowEditSection" }] : /* istanbul ignore next */ []));
1839
+ this.allowReorderSections = computed(() => this.readBooleanConfig('allowReorderSections', true), /* @ts-ignore */
1840
+ ...(ngDevMode ? [{ debugName: "allowReorderSections" }] : /* istanbul ignore next */ []));
1841
+ this.builderOptions = computed(() => ({
1842
+ hideHeaderActions: true,
1843
+ readonly: this.readonly(),
1844
+ linkAssetMode: this.linkAssetMode(),
1845
+ mainAssetId: this.targetRecordId(),
1846
+ allowAddSection: this.allowAddSection(),
1847
+ allowEditSection: this.allowEditSection(),
1848
+ allowAddItems: this.allowAddItems(),
1849
+ allowReorderSections: this.allowReorderSections(),
1850
+ }), /* @ts-ignore */
1851
+ ...(ngDevMode ? [{ debugName: "builderOptions" }] : /* istanbul ignore next */ []));
1852
+ this.isDirty = computed(() => {
1853
+ const baseline = this.baselineValue();
1854
+ const committed = this.committedValue();
1855
+ return !isEqual(baseline ?? null, committed ?? null);
1856
+ }, /* @ts-ignore */
1857
+ ...(ngDevMode ? [{ debugName: "isDirty" }] : /* istanbul ignore next */ []));
1858
+ this.#syncFromContext = effect((onCleanup) => {
1859
+ const ctx = this.context();
1860
+ const id = this.sourceRecordId();
1861
+ const value = get(ctx, this.fieldName());
1862
+ const linkedFromContext = get(ctx, this.fieldName());
1863
+ const sourceValue = get(ctx, this.sourceFieldName());
1864
+ const sourceField = this.sourceFieldName();
1865
+ const immediateValue = this.linkAssetMode()
1866
+ ? this.buildLinkedAssetValue(sourceValue ?? null, this.pickPersistedComponentLinks(linkedFromContext, undefined))
1867
+ : (value ?? null);
1868
+ this.baselineValue.set(immediateValue);
1869
+ this.committedValue.set(immediateValue);
1870
+ untracked(() => this.recompute());
1871
+ if (!id) {
1872
+ return;
1873
+ }
1874
+ let cancelled = false;
1875
+ const sourceEntityRef = this.sourceEntityRef();
1876
+ const targetEntityRef = this.targetEntityRef();
1877
+ const targetRecordId = this.targetRecordId();
1878
+ const linkMode = this.linkAssetMode();
1879
+ const fieldName = this.fieldName();
1880
+ const shouldFetchPersistedLinksFromTarget = linkMode &&
1881
+ typeof targetRecordId === 'string' &&
1882
+ targetRecordId.length > 0 &&
1883
+ targetEntityRef !== sourceEntityRef;
1884
+ const load = async () => {
1885
+ try {
1886
+ const [sourceRecord, targetRecord] = await Promise.all([
1887
+ this.entityService.withEntity(sourceEntityRef).data().byKey(id),
1888
+ shouldFetchPersistedLinksFromTarget
1889
+ ? this.entityService.withEntity(targetEntityRef).data().byKey(targetRecordId)
1890
+ : Promise.resolve(null),
1891
+ ]);
1892
+ if (cancelled)
1893
+ return;
1894
+ const fallbackValue = get(sourceRecord, sourceField);
1895
+ const recordLinksUnknown = linkMode && shouldFetchPersistedLinksFromTarget
1896
+ ? get(targetRecord, fieldName)
1897
+ : linkMode
1898
+ ? get(sourceRecord, fieldName)
1899
+ : undefined;
1900
+ const resolved = linkMode
1901
+ ? this.buildLinkedAssetValue(sourceValue ?? fallbackValue ?? null, this.pickPersistedComponentLinks(linkedFromContext, recordLinksUnknown))
1902
+ : this.hasSections(value)
1903
+ ? value
1904
+ : (fallbackValue ?? null);
1905
+ this.baselineValue.set(resolved);
1906
+ this.committedValue.set(resolved);
1907
+ untracked(() => this.recompute());
1908
+ }
1909
+ catch {
1910
+ if (cancelled)
1911
+ return;
1912
+ this.baselineValue.set(immediateValue);
1913
+ this.committedValue.set(immediateValue);
1914
+ untracked(() => this.recompute());
1915
+ }
1916
+ };
1917
+ void load();
1918
+ onCleanup(() => {
1919
+ cancelled = true;
1920
+ });
1921
+ }, /* @ts-ignore */
1922
+ ...(ngDevMode ? [{ debugName: "#syncFromContext" }] : /* istanbul ignore next */ []));
1923
+ }
1924
+ #syncFromContext;
1925
+ onValueChange(value) {
1926
+ this.committedValue.set(value);
1927
+ this.updateRootContextField(this.fieldName(), this.linkAssetMode() ? this.toAssetComponentValue(value) : value);
1928
+ this.recompute();
1929
+ }
1930
+ getWizardStepContext() {
1931
+ const field = this.fieldName();
1932
+ const value = this.committedValue();
1933
+ if (!field || value == null) {
1934
+ return null;
1935
+ }
1936
+ const ctxValue = this.linkAssetMode() ? this.toAssetComponentValue(value) : value;
1937
+ return { [field]: cloneDeep(ctxValue) };
1938
+ }
1939
+ hasUnsavedChanges() {
1940
+ return this.isDirty();
1941
+ }
1942
+ async getPrimaryMenuItems() {
1943
+ if (this.readonly()) {
1944
+ return [];
1945
+ }
1946
+ const addSectionTitle = (await this.translateService.translateAsync('@asset-management:assetTypeSections.entities.assetTypeSection.actions.add-section.title')) || 'Add Section';
1947
+ const discardTitle = (await this.translateService.translateAsync('@general:actions.discard.title')) || 'Discard';
1948
+ const saveTitle = (await this.translateService.translateAsync('@general:actions.save.title')) || 'Save';
1949
+ return [
1950
+ ...(this.allowAddSection()
1951
+ ? [
1952
+ {
1953
+ title: addSectionTitle,
1954
+ icon: 'fa-light fa-layer-group',
1955
+ zone: 'header',
1956
+ priority: 'primary',
1957
+ color: 'primary',
1958
+ command: { name: ASSET_TYPE_SECTIONS_ADD_SECTION_COMMAND, options: {} },
1959
+ },
1960
+ ]
1961
+ : []),
1962
+ {
1963
+ title: discardTitle,
1964
+ icon: 'fa-light fa-rotate-left',
1965
+ zone: 'footer',
1966
+ priority: 'secondary',
1967
+ color: 'default',
1968
+ visible: this.isDirty(),
1969
+ command: { name: ASSET_TYPE_SECTIONS_DISCARD_COMMAND, options: {} },
1970
+ },
1971
+ {
1972
+ title: saveTitle,
1973
+ icon: 'fa-light fa-floppy-disk',
1974
+ zone: 'footer',
1975
+ priority: 'primary',
1976
+ color: 'primary',
1977
+ visible: this.isDirty(),
1978
+ command: { name: ASSET_TYPE_SECTIONS_SAVE_COMMAND, options: {} },
1979
+ },
1980
+ ];
1981
+ }
1982
+ async execute(command) {
1983
+ if (command.name === ASSET_TYPE_SECTIONS_ADD_SECTION_COMMAND) {
1984
+ await this.builderRef()?.addSection();
1985
+ return;
1986
+ }
1987
+ if (command.name === ASSET_TYPE_SECTIONS_DISCARD_COMMAND) {
1988
+ this.handleDiscard();
1989
+ return;
1990
+ }
1991
+ if (command.name !== ASSET_TYPE_SECTIONS_SAVE_COMMAND)
1992
+ return;
1993
+ await this.handleSave({ silent: false });
1994
+ }
1995
+ async handleSave(options) {
1996
+ const silent = options?.silent === true;
1997
+ const id = this.targetRecordId();
1998
+ const entity = this.targetEntityRef();
1999
+ const field = this.fieldName();
2000
+ const value = this.committedValue();
2001
+ if (!id || !entity || !field) {
2002
+ if (!silent) {
2003
+ this.toastService.show({
2004
+ color: 'danger',
2005
+ title: await this.translateService.translateAsync('@general:messages.error.title'),
2006
+ content: await this.translateService.translateAsync('@general:messages.entity.invalid-data'),
2007
+ });
2008
+ }
2009
+ return;
2010
+ }
2011
+ const root = (this.rootContext() ?? {});
2012
+ const result = await this.commandService.execute(ASSET_TYPE_COMPONENTS_SAVE_COMMAND, {
2013
+ id,
2014
+ entity,
2015
+ field,
2016
+ value: this.linkAssetMode() ? this.toAssetComponentValue(value ?? { sections: [] }) : (value ?? { sections: [] }),
2017
+ name: root['name'],
2018
+ title: root['title'],
2019
+ });
2020
+ if (result?.success) {
2021
+ const saved = value ?? { sections: [] };
2022
+ this.baselineValue.set(saved);
2023
+ this.committedValue.set(saved);
2024
+ this.updateRootContextField(field, this.linkAssetMode() ? this.toAssetComponentValue(saved) : saved);
2025
+ this.builderRef()?.syncAfterSave(saved);
2026
+ this.recompute();
2027
+ if (!silent) {
2028
+ this.toastService.show({
2029
+ color: 'success',
2030
+ title: await this.translateService.translateAsync('@general:messages.generic.success.title'),
2031
+ content: await this.translateService.translateAsync('@general:messages.generic.success.description'),
2032
+ });
2033
+ }
2034
+ return;
2035
+ }
2036
+ this.toastService.show({
2037
+ color: 'danger',
2038
+ title: await this.translateService.translateAsync('@general:messages.error.title'),
2039
+ content: await this.translateService.translateAsync('@general:messages.entity.update-failed'),
2040
+ });
2041
+ }
2042
+ recompute() {
2043
+ super.recompute();
2044
+ const layout = this.hostPageLayout;
2045
+ layout?.recompute?.();
2046
+ }
2047
+ handleDiscard() {
2048
+ const baseline = this.baselineValue() ?? { sections: [] };
2049
+ const field = this.fieldName();
2050
+ this.committedValue.set(baseline);
2051
+ this.updateRootContextField(field, this.linkAssetMode() ? this.toAssetComponentValue(baseline) : baseline);
2052
+ this.builderRef()?.resetAfterDiscard();
2053
+ this.recompute();
2054
+ }
2055
+ /** Keeps details-view `rootContext` in sync so sidebar navigation remounts with latest data. */
2056
+ updateRootContextField(field, value) {
2057
+ const ctx = this.rootContext();
2058
+ if (!ctx || typeof ctx !== 'object') {
2059
+ return;
2060
+ }
2061
+ ctx[field] = cloneDeep(value);
2062
+ }
2063
+ buildLinkedAssetValue(source, links) {
2064
+ if (!source || !Array.isArray(source.sections)) {
2065
+ return { sections: [] };
2066
+ }
2067
+ const groupedLinks = new Map();
2068
+ for (const link of links ?? []) {
2069
+ const key = String(link.assetTypeSectionComponentId ?? '');
2070
+ if (!key)
2071
+ continue;
2072
+ if (!groupedLinks.has(key))
2073
+ groupedLinks.set(key, []);
2074
+ groupedLinks.get(key).push(link);
2075
+ }
2076
+ return {
2077
+ sections: source.sections.map((section) => ({
2078
+ ...section,
2079
+ items: (section.items ?? []).flatMap((item) => {
2080
+ const componentId = String(item.id ?? '');
2081
+ const count = Math.max(1, Number(item.count ?? 1));
2082
+ const existing = groupedLinks.get(componentId) ?? [];
2083
+ return Array.from({ length: count }).map((_, index) => {
2084
+ const linked = existing[index];
2085
+ return {
2086
+ ...item,
2087
+ id: `${componentId}__slot_${index + 1}`,
2088
+ count: 1,
2089
+ assetTypeSectionComponentId: componentId,
2090
+ roleName: this.withSlotSuffix(item.roleName, index + 1),
2091
+ assetId: linked?.assetId,
2092
+ asset: linked?.asset
2093
+ ? {
2094
+ id: linked.asset.id,
2095
+ code: linked.asset.code,
2096
+ title: linked.asset.title,
2097
+ }
2098
+ : undefined,
2099
+ };
2100
+ });
2101
+ }),
2102
+ })),
2103
+ };
2104
+ }
2105
+ withSlotSuffix(value, slotIndex) {
2106
+ const normalized = normalizeAssetTypeBuilderLocalizedField(value, '');
2107
+ const suffix = `#${slotIndex}`;
2108
+ if (typeof normalized === 'string') {
2109
+ const base = normalized.trim();
2110
+ return base ? `${base} ${suffix}` : suffix;
2111
+ }
2112
+ return Object.fromEntries(Object.entries(normalized).map(([locale, text]) => {
2113
+ const trimmed = typeof text === 'string' ? text.trim() : '';
2114
+ return [locale, trimmed ? `${trimmed} ${suffix}` : suffix];
2115
+ }));
2116
+ }
2117
+ toAssetComponentValue(value) {
2118
+ const mainAssetId = this.targetRecordId() ?? '';
2119
+ return value.sections.flatMap((section) => (section.items ?? [])
2120
+ .filter((item) => typeof item.assetId === 'string' && item.assetId)
2121
+ .map((item) => ({
2122
+ id: item.id,
2123
+ assetId: String(item.assetId),
2124
+ asset: {
2125
+ id: String(item.asset?.id ?? item.assetId),
2126
+ code: String(item.asset?.code ?? ''),
2127
+ title: String(item.asset?.title ?? ''),
2128
+ },
2129
+ mainAssetId,
2130
+ mainAsset: {
2131
+ id: mainAssetId,
2132
+ code: '',
2133
+ title: '',
2134
+ },
2135
+ assetTypeSectionComponentId: String(item.assetTypeSectionComponentId ?? ''),
2136
+ assetTypeSectionComponent: {
2137
+ id: String(item.assetTypeSectionComponentId ?? ''),
2138
+ code: String(item.assetTypeSectionId ?? ''),
2139
+ title: String(item.roleName ?? ''),
2140
+ assetTypeId: String(item.assetTypeId ?? ''),
2141
+ assetType: {
2142
+ id: String(item.assetType?.id ?? String(item.assetTypeId ?? '')),
2143
+ name: item.assetType?.name ?? '',
2144
+ },
2145
+ },
2146
+ })));
2147
+ }
2148
+ readStringConfig(key) {
2149
+ const rootValue = this.config()[key];
2150
+ if (typeof rootValue === 'string') {
2151
+ return rootValue;
2152
+ }
2153
+ return '';
2154
+ }
2155
+ readBooleanConfig(key, defaultValue = false) {
2156
+ const rootValue = this.config()[key];
2157
+ if (typeof rootValue === 'boolean') {
2158
+ return rootValue;
2159
+ }
2160
+ return defaultValue;
2161
+ }
2162
+ hasSections(value) {
2163
+ return !!value && Array.isArray(value.sections) && value.sections.length > 0;
2164
+ }
2165
+ /**
2166
+ * Prefer links loaded with the record (`byKey`). Context often holds `assetComponents: []` from a
2167
+ * partial row merge; `[] ?? serverLinks` would keep the empty array and hide persisted assignments.
2168
+ */
2169
+ pickPersistedComponentLinks(contextLinks, recordLinks) {
2170
+ if (recordLinks !== undefined) {
2171
+ return Array.isArray(recordLinks) ? recordLinks : [];
2172
+ }
2173
+ return Array.isArray(contextLinks) ? contextLinks : null;
2174
+ }
2175
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetTypeSectionBuilderPageComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2176
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.8", type: AXMAssetTypeSectionBuilderPageComponent, isStandalone: true, selector: "axm-asset-type-section-builder-page", inputs: { rootContext: { classPropertyName: "rootContext", publicName: "rootContext", isSignal: true, isRequired: false, transformFunction: null }, pageConfig: { classPropertyName: "pageConfig", publicName: "pageConfig", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.axp-page-layout-base": "true" } }, providers: [
2177
+ {
2178
+ provide: AXPPageLayoutBase,
2179
+ useExisting: AXMAssetTypeSectionBuilderPageComponent,
2180
+ },
2181
+ ], viewQueries: [{ propertyName: "builderRef", first: true, predicate: ["builderRef"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
2182
+ <axm-asset-type-section-builder
2183
+ #builderRef
2184
+ [value]="committedValue()"
2185
+ [assetTypeId]="sourceRecordId()"
2186
+ [options]="builderOptions()"
2187
+ (valueChange)="onValueChange($event)"
2188
+ />
2189
+ `, isInline: true, dependencies: [{ kind: "component", type: AXMAssetTypeSectionBuilderComponent, selector: "axm-asset-type-section-builder", inputs: ["value", "assetTypeId", "options"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2190
+ }
2191
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetTypeSectionBuilderPageComponent, decorators: [{
2192
+ type: Component,
2193
+ args: [{
2194
+ selector: 'axm-asset-type-section-builder-page',
2195
+ standalone: true,
2196
+ imports: [AXMAssetTypeSectionBuilderComponent],
2197
+ providers: [
2198
+ {
2199
+ provide: AXPPageLayoutBase,
2200
+ useExisting: AXMAssetTypeSectionBuilderPageComponent,
2201
+ },
2202
+ ],
2203
+ template: `
2204
+ <axm-asset-type-section-builder
2205
+ #builderRef
2206
+ [value]="committedValue()"
2207
+ [assetTypeId]="sourceRecordId()"
2208
+ [options]="builderOptions()"
2209
+ (valueChange)="onValueChange($event)"
2210
+ />
2211
+ `,
2212
+ host: {
2213
+ '[class.axp-page-layout-base]': 'true',
2214
+ },
2215
+ changeDetection: ChangeDetectionStrategy.OnPush,
2216
+ }]
2217
+ }], propDecorators: { rootContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootContext", required: false }] }], pageConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageConfig", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], builderRef: [{ type: i0.ViewChild, args: ['builderRef', { isSignal: true }] }] } });
2218
+
2219
+ var assetTypeSectionBuilderPage_component = /*#__PURE__*/Object.freeze({
2220
+ __proto__: null,
2221
+ AXMAssetTypeSectionBuilderPageComponent: AXMAssetTypeSectionBuilderPageComponent
2222
+ });
2223
+
2224
+ class AXMAssetTypeComponentsPageComponentProvider {
2225
+ async components() {
2226
+ return [
2227
+ {
2228
+ key: ASSET_TYPE_COMPONENTS_PAGE_COMPONENT_KEY,
2229
+ loader: () => Promise.resolve().then(function () { return assetTypeSectionBuilderPage_component; }).then((m) => m.AXMAssetTypeSectionBuilderPageComponent),
2230
+ },
2231
+ ];
2232
+ }
2233
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetTypeComponentsPageComponentProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2234
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetTypeComponentsPageComponentProvider }); }
2235
+ }
2236
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetTypeComponentsPageComponentProvider, decorators: [{
2237
+ type: Injectable
2238
+ }] });
2239
+
2240
+ var index = /*#__PURE__*/_mergeNamespaces({
2241
+ __proto__: null,
2242
+ ASSET_TYPE_COMPONENTS_SAVE_COMMAND: ASSET_TYPE_COMPONENTS_SAVE_COMMAND,
2243
+ AXMAssetTypeComponentsPageComponentProvider: AXMAssetTypeComponentsPageComponentProvider,
2244
+ AXMAssetTypeSectionBuilderComponent: AXMAssetTypeSectionBuilderComponent,
2245
+ AXMAssetTypeSectionBuilderPageComponent: AXMAssetTypeSectionBuilderPageComponent,
2246
+ AXMSaveAssetTypeComponentsCommand: AXMSaveAssetTypeComponentsCommand,
2247
+ createAssetTypeSectionBuilderDefaultValue: createAssetTypeSectionBuilderDefaultValue,
2248
+ normalizeAssetTypeBuilderLocalizedField: normalizeAssetTypeBuilderLocalizedField,
2249
+ normalizeAssetTypeSectionBuilderValue: normalizeAssetTypeSectionBuilderValue
2250
+ }, [contracts]);
2251
+
2252
+ //#region ---- Imports ----
2253
+ //#endregion
2254
+ //#region ---- Constants ----
2255
+ const BLOCKING_WORK_ORDER_STATUSES = new Set(['in-progress', 'completed']);
2256
+ const PROTECTED_LIFECYCLE_STATUSES = new Set([AXMAssetLifecycleStatus.Retired, AXMAssetLifecycleStatus.Disposed]);
2257
+ const PROTECTED_OPERATIONAL_STATUSES = new Set([AXMAssetStatus.Lost]);
2258
+ //#endregion
2259
+ /**
2260
+ * Central policy for asset operational status changes driven by maintenance work orders.
2261
+ * Callers must not set `Asset.statusId` directly from workflows — use {@link recalculate} only.
2262
+ */
2263
+ class AXMAssetOperationalStatusService {
2264
+ constructor() {
2265
+ //#region ---- Services & Dependencies ----
2266
+ this.entityService = inject(AXPEntityService);
2267
+ }
2268
+ //#endregion
2269
+ //#region ---- Public API ----
2270
+ async recalculate(assetIdRaw, trigger, context = {}) {
2271
+ const assetId = assetIdRaw?.trim();
2272
+ if (!assetId) {
2273
+ return {
2274
+ assetId: '',
2275
+ previousStatusId: null,
2276
+ statusId: null,
2277
+ changed: false,
2278
+ reason: 'assetId is required',
2279
+ };
2280
+ }
2281
+ const asset = (await this.entityService.withEntity('AssetManagement', 'Asset').data().byKey(assetId));
2282
+ if (!asset?.id) {
2283
+ return {
2284
+ assetId,
2285
+ previousStatusId: null,
2286
+ statusId: null,
2287
+ changed: false,
2288
+ reason: 'Asset not found',
2289
+ };
2290
+ }
2291
+ const previousStatusId = isString(asset.statusId) ? asset.statusId.trim() : null;
2292
+ const lifecycleStatusId = isString(asset.lifecycleStatusId) ? asset.lifecycleStatusId.trim() : null;
2293
+ if (lifecycleStatusId && PROTECTED_LIFECYCLE_STATUSES.has(lifecycleStatusId)) {
2294
+ return {
2295
+ assetId,
2296
+ previousStatusId,
2297
+ statusId: previousStatusId,
2298
+ changed: false,
2299
+ reason: `${trigger}: asset lifecycle is protected`,
2300
+ };
2301
+ }
2302
+ if (previousStatusId && PROTECTED_OPERATIONAL_STATUSES.has(previousStatusId)) {
2303
+ return {
2304
+ assetId,
2305
+ previousStatusId,
2306
+ statusId: previousStatusId,
2307
+ changed: false,
2308
+ reason: `${trigger}: asset status is protected`,
2309
+ };
2310
+ }
2311
+ const maintenanceTypeId = await this.resolveMaintenanceTypeId(assetId, context);
2312
+ const shouldBlock = await this.shouldBlockAssetForMaintenance(assetId, maintenanceTypeId);
2313
+ let nextStatusId = previousStatusId;
2314
+ const update = {};
2315
+ if (trigger === 'work-started') {
2316
+ if (shouldBlock) {
2317
+ nextStatusId = AXMAssetStatus.UnderMaintenanceInspection;
2318
+ if (previousStatusId === AXMAssetStatus.Available || previousStatusId === AXMAssetStatus.Assigned) {
2319
+ update.statusBeforeInterruptionId = previousStatusId;
2320
+ }
2321
+ }
2322
+ }
2323
+ if (trigger === 'work-completed') {
2324
+ const hasBlockingWork = await this.hasBlockingWorkOrders(assetId);
2325
+ if (hasBlockingWork) {
2326
+ nextStatusId = AXMAssetStatus.UnderMaintenanceInspection;
2327
+ }
2328
+ // Do not release the asset here — completed work still awaits sign-off.
2329
+ }
2330
+ if (trigger === 'work-signed-off') {
2331
+ const hasBlockingWork = await this.hasBlockingWorkOrders(assetId);
2332
+ if (hasBlockingWork) {
2333
+ nextStatusId = AXMAssetStatus.UnderMaintenanceInspection;
2334
+ }
2335
+ else {
2336
+ nextStatusId = this.resolveRestoredStatus(asset);
2337
+ update.statusBeforeInterruptionId = null;
2338
+ }
2339
+ }
2340
+ const changed = !!nextStatusId && nextStatusId !== previousStatusId;
2341
+ const hasFieldUpdates = update.statusBeforeInterruptionId !== undefined;
2342
+ if ((changed && nextStatusId) || hasFieldUpdates) {
2343
+ await this.entityService
2344
+ .withEntity('AssetManagement', 'Asset')
2345
+ .data()
2346
+ .update(assetId, {
2347
+ ...update,
2348
+ ...(changed && nextStatusId ? { statusId: nextStatusId } : {}),
2349
+ });
2350
+ }
2351
+ return {
2352
+ assetId,
2353
+ previousStatusId,
2354
+ statusId: nextStatusId,
2355
+ changed,
2356
+ reason: changed ? `${trigger}: status recalculated` : `${trigger}: status unchanged`,
2357
+ };
2358
+ }
2359
+ //#endregion
2360
+ //#region ---- Policy helpers ----
2361
+ async resolveMaintenanceTypeId(assetId, context) {
2362
+ const fromContext = context.maintenanceTypeId?.trim();
2363
+ if (fromContext) {
2364
+ return fromContext;
2365
+ }
2366
+ const workOrderId = context.workOrderId?.trim();
2367
+ if (workOrderId) {
2368
+ const workOrder = (await this.entityService
2369
+ .withEntity('MaintenanceManagement', 'WorkOrder')
2370
+ .data()
2371
+ .byKey(workOrderId));
2372
+ const fromWorkOrder = workOrder?.maintenanceTypeId?.trim();
2373
+ if (fromWorkOrder) {
2374
+ return fromWorkOrder;
2375
+ }
2376
+ }
2377
+ const page = await this.entityService
2378
+ .withEntity('MaintenanceManagement', 'WorkOrder')
2379
+ .data()
2380
+ .query({
2381
+ skip: 0,
2382
+ take: 1,
2383
+ filter: {
2384
+ logic: 'and',
2385
+ filters: [
2386
+ { field: 'assetId', operator: { type: 'equal' }, value: assetId },
2387
+ { field: 'statusId', operator: { type: 'equal' }, value: 'in-progress' },
2388
+ ],
2389
+ },
2390
+ });
2391
+ const row = defaultTo(get(page, 'items[0]'), null);
2392
+ return row?.maintenanceTypeId?.trim() ?? '';
2393
+ }
2394
+ /**
2395
+ * Inspection-like maintenance types keep the asset blocked under the combined operational status.
2396
+ * TODO: replace title/code heuristic with an explicit MaintenanceType classification field when available.
2397
+ */
2398
+ async shouldBlockAssetForMaintenance(_assetId, maintenanceTypeId) {
2399
+ if (!maintenanceTypeId) {
2400
+ return true;
2401
+ }
2402
+ const maintenanceType = (await this.entityService
2403
+ .withEntity('MaintenanceManagement', 'MaintenanceType')
2404
+ .data()
2405
+ .byKey(maintenanceTypeId));
2406
+ if (!maintenanceType) {
2407
+ return true;
2408
+ }
2409
+ const code = maintenanceType.code?.toLowerCase() ?? '';
2410
+ const titleText = this.resolveDisplayText(maintenanceType.title).toLowerCase();
2411
+ const isInspection = code.includes('inspect') || titleText.includes('inspect');
2412
+ if (isInspection) {
2413
+ return true;
2414
+ }
2415
+ // Non-inspection maintenance that does not block the asset is not modeled yet.
2416
+ // TODO: honor MaintenanceType.blocksAsset (or equivalent) when introduced.
2417
+ return true;
2418
+ }
2419
+ async hasBlockingWorkOrders(assetId) {
2420
+ const page = await this.entityService
2421
+ .withEntity('MaintenanceManagement', 'WorkOrder')
2422
+ .data()
2423
+ .query({
2424
+ skip: 0,
2425
+ take: 100,
2426
+ filter: {
2427
+ logic: 'and',
2428
+ filters: [{ field: 'assetId', operator: { type: 'equal' }, value: assetId }],
2429
+ },
2430
+ });
2431
+ const items = defaultTo(get(page, 'items'), []);
2432
+ return items.some((row) => {
2433
+ const statusId = row.statusId?.trim();
2434
+ return !!statusId && BLOCKING_WORK_ORDER_STATUSES.has(statusId);
2435
+ });
2436
+ }
2437
+ resolveRestoredStatus(asset) {
2438
+ const saved = isString(asset.statusBeforeInterruptionId) ? asset.statusBeforeInterruptionId.trim() : null;
2439
+ if (saved) {
2440
+ return saved;
2441
+ }
2442
+ return AXMAssetStatus.Available;
2443
+ }
2444
+ resolveDisplayText(value) {
2445
+ if (isString(value)) {
2446
+ return value;
2447
+ }
2448
+ if (value && typeof value === 'object') {
2449
+ const record = value;
2450
+ return record['en-US'] ?? record['fa-IR'] ?? Object.values(record)[0] ?? '';
2451
+ }
2452
+ return '';
2453
+ }
2454
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetOperationalStatusService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2455
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetOperationalStatusService, providedIn: 'root' }); }
2456
+ }
2457
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMAssetOperationalStatusService, decorators: [{
2458
+ type: Injectable,
2459
+ args: [{ providedIn: 'root' }]
2460
+ }] });
2461
+
2462
+ //#region ---- Types ----
2463
+ //#endregion
2464
+
2465
+ // export * from './asset-status.provider';
2466
+
2467
+ /**
2468
+ * Generated bundle index. Do not edit.
2469
+ */
2470
+
2471
+ export { ASSET_TYPE_COMPONENTS_SAVE_COMMAND, AXMAssetManagementAssetStatusProvider, AXMAssetManagementFeatureDefinitionProvider, AXMAssetManagementMenuProvider, AXMAssetManagementModule, AXMAssetManagementPermissionDefinitionProvider, AXMAssetManagementSearchCommandProvider, AXMAssetOperationalStatusService, AXMAssetTypeComponentsPageComponentProvider, AXMAssetTypeSectionBuilderComponent, AXMAssetTypeSectionBuilderPageComponent, AXMCreateAssetRentalHistoryRentFromCommand, AXMCreateAssetRentalHistoryRentToCommand, AXMSaveAssetTypeComponentsCommand, assertRentalPartyForDirection, createAssetTypeSectionBuilderDefaultValue, normalizeAssetTypeBuilderLocalizedField, normalizeAssetTypeSectionBuilderValue };
2472
+ //# sourceMappingURL=acorex-platform-module-asset-management-client.mjs.map