@masterteam/governance 0.0.1
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,1482 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { inject, Injectable, computed, input, effect, Component, viewChild, linkedSignal, signal } from '@angular/core';
|
|
4
|
+
import { Table } from '@masterteam/components/table';
|
|
5
|
+
import * as i1 from 'primeng/skeleton';
|
|
6
|
+
import { SkeletonModule } from 'primeng/skeleton';
|
|
7
|
+
import { Action, Selector, State, Store, select } from '@ngxs/store';
|
|
8
|
+
import { HttpClient } from '@angular/common/http';
|
|
9
|
+
import { CrudStateBase, handleApiRequest, TextFieldConfig, NumberFieldConfig, TextareaFieldConfig, SelectFieldConfig, ToggleFieldConfig, ValidatorConfig, DateFieldConfig, MultiSelectFieldConfig } from '@masterteam/components';
|
|
10
|
+
import { TranslocoService, TranslocoDirective, TranslocoPipe } from '@jsverse/transloco';
|
|
11
|
+
import { ModalService } from '@masterteam/components/modal';
|
|
12
|
+
import * as i2 from '@angular/forms';
|
|
13
|
+
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
14
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
15
|
+
import { DynamicForm } from '@masterteam/forms/dynamic-form';
|
|
16
|
+
import { Button } from '@masterteam/components/button';
|
|
17
|
+
import { ModalRef, DialogService } from '@masterteam/components/dialog';
|
|
18
|
+
import { finalize } from 'rxjs';
|
|
19
|
+
import { Breadcrumb } from '@masterteam/components/breadcrumb';
|
|
20
|
+
import { Icon } from '@masterteam/icons';
|
|
21
|
+
import { Chip } from '@masterteam/components/chip';
|
|
22
|
+
|
|
23
|
+
class SetLevelId {
|
|
24
|
+
levelId;
|
|
25
|
+
static type = '[Governance] Set Level ID';
|
|
26
|
+
constructor(levelId) {
|
|
27
|
+
this.levelId = levelId;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
class GetGovernanceRules {
|
|
31
|
+
static type = '[Governance] Get Rules';
|
|
32
|
+
}
|
|
33
|
+
class GetGovernanceRule {
|
|
34
|
+
id;
|
|
35
|
+
static type = '[Governance] Get Rule';
|
|
36
|
+
constructor(id) {
|
|
37
|
+
this.id = id;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
class GetGovernanceRuleTypes {
|
|
41
|
+
static type = '[Governance] Get Rule Types';
|
|
42
|
+
}
|
|
43
|
+
class AddGovernanceRule {
|
|
44
|
+
rule;
|
|
45
|
+
static type = '[Governance] Add Rule';
|
|
46
|
+
constructor(rule) {
|
|
47
|
+
this.rule = rule;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
class UpdateGovernanceRule {
|
|
51
|
+
id;
|
|
52
|
+
rule;
|
|
53
|
+
static type = '[Governance] Update Rule';
|
|
54
|
+
constructor(id, rule) {
|
|
55
|
+
this.id = id;
|
|
56
|
+
this.rule = rule;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
class DeleteGovernanceRule {
|
|
60
|
+
id;
|
|
61
|
+
static type = '[Governance] Delete Rule';
|
|
62
|
+
constructor(id) {
|
|
63
|
+
this.id = id;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
class ToggleGovernanceRuleActive {
|
|
67
|
+
id;
|
|
68
|
+
static type = '[Governance] Toggle Rule Active';
|
|
69
|
+
constructor(id) {
|
|
70
|
+
this.id = id;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
class ClearSelectedRule {
|
|
74
|
+
static type = '[Governance] Clear Selected Rule';
|
|
75
|
+
}
|
|
76
|
+
class ResetGovernanceState {
|
|
77
|
+
static type = '[Governance] Reset State';
|
|
78
|
+
}
|
|
79
|
+
class GetModules {
|
|
80
|
+
static type = '[Governance] Get Modules';
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get request schemas for a specific scope
|
|
84
|
+
* @param targetScope - 'Level' or 'Module'
|
|
85
|
+
* @param targetModuleKey - Required if scope is 'Module'
|
|
86
|
+
*/
|
|
87
|
+
class GetRequestSchemas {
|
|
88
|
+
targetScope;
|
|
89
|
+
targetModuleKey;
|
|
90
|
+
static type = '[Governance] Get Request Schemas';
|
|
91
|
+
constructor(targetScope, targetModuleKey) {
|
|
92
|
+
this.targetScope = targetScope;
|
|
93
|
+
this.targetModuleKey = targetModuleKey;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get properties for a specific scope
|
|
98
|
+
* @param targetScope - 'Level' or 'Module'
|
|
99
|
+
* @param targetModuleKey - Required if scope is 'Module'
|
|
100
|
+
*/
|
|
101
|
+
class GetProperties {
|
|
102
|
+
targetScope;
|
|
103
|
+
targetModuleKey;
|
|
104
|
+
static type = '[Governance] Get Properties';
|
|
105
|
+
constructor(targetScope, targetModuleKey) {
|
|
106
|
+
this.targetScope = targetScope;
|
|
107
|
+
this.targetModuleKey = targetModuleKey;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Clear request schemas (when scope/module changes)
|
|
112
|
+
*/
|
|
113
|
+
class ClearRequestSchemas {
|
|
114
|
+
static type = '[Governance] Clear Request Schemas';
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Clear properties (when scope/module changes)
|
|
118
|
+
*/
|
|
119
|
+
class ClearProperties {
|
|
120
|
+
static type = '[Governance] Clear Properties';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// Rule Type Enum
|
|
125
|
+
// ============================================================================
|
|
126
|
+
var GovernanceRuleTypeEnum;
|
|
127
|
+
(function (GovernanceRuleTypeEnum) {
|
|
128
|
+
GovernanceRuleTypeEnum[GovernanceRuleTypeEnum["ModuleExists"] = 1] = "ModuleExists";
|
|
129
|
+
GovernanceRuleTypeEnum[GovernanceRuleTypeEnum["ModuleDoesNotExist"] = 2] = "ModuleDoesNotExist";
|
|
130
|
+
GovernanceRuleTypeEnum[GovernanceRuleTypeEnum["ModuleMaxCount"] = 3] = "ModuleMaxCount";
|
|
131
|
+
GovernanceRuleTypeEnum[GovernanceRuleTypeEnum["ModuleMinCount"] = 4] = "ModuleMinCount";
|
|
132
|
+
GovernanceRuleTypeEnum[GovernanceRuleTypeEnum["FieldRequired"] = 5] = "FieldRequired";
|
|
133
|
+
GovernanceRuleTypeEnum[GovernanceRuleTypeEnum["FieldValue"] = 6] = "FieldValue";
|
|
134
|
+
GovernanceRuleTypeEnum[GovernanceRuleTypeEnum["PhaseGateModuleRequired"] = 7] = "PhaseGateModuleRequired";
|
|
135
|
+
GovernanceRuleTypeEnum[GovernanceRuleTypeEnum["NoActiveRequest"] = 8] = "NoActiveRequest";
|
|
136
|
+
})(GovernanceRuleTypeEnum || (GovernanceRuleTypeEnum = {}));
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// State Model
|
|
139
|
+
// ============================================================================
|
|
140
|
+
var GovernanceActionKey;
|
|
141
|
+
(function (GovernanceActionKey) {
|
|
142
|
+
GovernanceActionKey["GetRules"] = "getRules";
|
|
143
|
+
GovernanceActionKey["GetRule"] = "getRule";
|
|
144
|
+
GovernanceActionKey["GetRuleTypes"] = "getRuleTypes";
|
|
145
|
+
GovernanceActionKey["AddRule"] = "addRule";
|
|
146
|
+
GovernanceActionKey["UpdateRule"] = "updateRule";
|
|
147
|
+
GovernanceActionKey["DeleteRule"] = "deleteRule";
|
|
148
|
+
GovernanceActionKey["ToggleRuleActive"] = "toggleRuleActive";
|
|
149
|
+
GovernanceActionKey["GetModules"] = "getModules";
|
|
150
|
+
GovernanceActionKey["GetRequestSchemas"] = "getRequestSchemas";
|
|
151
|
+
GovernanceActionKey["GetProperties"] = "getProperties";
|
|
152
|
+
})(GovernanceActionKey || (GovernanceActionKey = {}));
|
|
153
|
+
|
|
154
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
155
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
156
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
157
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
158
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
159
|
+
};
|
|
160
|
+
const DEFAULTS = {
|
|
161
|
+
levelId: null,
|
|
162
|
+
rules: [],
|
|
163
|
+
ruleTypes: [],
|
|
164
|
+
modules: [],
|
|
165
|
+
requestSchemas: [],
|
|
166
|
+
properties: [],
|
|
167
|
+
selectedRule: null,
|
|
168
|
+
loadingActive: [],
|
|
169
|
+
errors: {},
|
|
170
|
+
};
|
|
171
|
+
let GovernanceState = class GovernanceState extends CrudStateBase {
|
|
172
|
+
http = inject(HttpClient);
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// Selectors - Individual data selectors for fine-grained reactivity
|
|
175
|
+
// ============================================================================
|
|
176
|
+
static getLevelId(state) {
|
|
177
|
+
return state.levelId;
|
|
178
|
+
}
|
|
179
|
+
static getRules(state) {
|
|
180
|
+
return state.rules;
|
|
181
|
+
}
|
|
182
|
+
static getRuleTypes(state) {
|
|
183
|
+
return state.ruleTypes;
|
|
184
|
+
}
|
|
185
|
+
static getSelectedRule(state) {
|
|
186
|
+
return state.selectedRule;
|
|
187
|
+
}
|
|
188
|
+
static getModules(state) {
|
|
189
|
+
return state.modules;
|
|
190
|
+
}
|
|
191
|
+
static getRequestSchemas(state) {
|
|
192
|
+
return state.requestSchemas;
|
|
193
|
+
}
|
|
194
|
+
static getProperties(state) {
|
|
195
|
+
return state.properties;
|
|
196
|
+
}
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// Loading/Error Slice Selectors - REQUIRED for optimal performance
|
|
199
|
+
// ============================================================================
|
|
200
|
+
static getLoadingActive(state) {
|
|
201
|
+
return state.loadingActive;
|
|
202
|
+
}
|
|
203
|
+
static getErrors(state) {
|
|
204
|
+
return state.errors;
|
|
205
|
+
}
|
|
206
|
+
// ============================================================================
|
|
207
|
+
// Level Context
|
|
208
|
+
// ============================================================================
|
|
209
|
+
setLevelId(ctx, { levelId }) {
|
|
210
|
+
ctx.patchState({ levelId });
|
|
211
|
+
}
|
|
212
|
+
resetState(ctx) {
|
|
213
|
+
ctx.setState(DEFAULTS);
|
|
214
|
+
}
|
|
215
|
+
// ============================================================================
|
|
216
|
+
// CRUD Actions
|
|
217
|
+
// ============================================================================
|
|
218
|
+
getRules(ctx) {
|
|
219
|
+
const { levelId } = ctx.getState();
|
|
220
|
+
if (!levelId) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const req$ = this.http.get(`control-panel/levels/${levelId}/governance-rules`);
|
|
224
|
+
return handleApiRequest({
|
|
225
|
+
ctx,
|
|
226
|
+
key: GovernanceActionKey.GetRules,
|
|
227
|
+
request$: req$,
|
|
228
|
+
onSuccess: (response) => ({
|
|
229
|
+
rules: response.data ?? [],
|
|
230
|
+
}),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
getRule(ctx, { id }) {
|
|
234
|
+
const { levelId } = ctx.getState();
|
|
235
|
+
if (!levelId) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const req$ = this.http.get(`control-panel/levels/${levelId}/governance-rules/${id}`);
|
|
239
|
+
return handleApiRequest({
|
|
240
|
+
ctx,
|
|
241
|
+
key: GovernanceActionKey.GetRule,
|
|
242
|
+
request$: req$,
|
|
243
|
+
onSuccess: (response) => ({
|
|
244
|
+
selectedRule: response.data ?? null,
|
|
245
|
+
}),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
getRuleTypes(ctx) {
|
|
249
|
+
const req$ = this.http.get('control-panel/governance/rule-types');
|
|
250
|
+
return handleApiRequest({
|
|
251
|
+
ctx,
|
|
252
|
+
key: GovernanceActionKey.GetRuleTypes,
|
|
253
|
+
request$: req$,
|
|
254
|
+
onSuccess: (response) => ({
|
|
255
|
+
ruleTypes: response.data ?? [],
|
|
256
|
+
}),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
addRule(ctx, { rule }) {
|
|
260
|
+
const { levelId } = ctx.getState();
|
|
261
|
+
if (!levelId) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const req$ = this.http.post(`control-panel/levels/${levelId}/governance-rules`, rule);
|
|
265
|
+
return this.create(ctx, {
|
|
266
|
+
key: GovernanceActionKey.AddRule,
|
|
267
|
+
request$: req$,
|
|
268
|
+
stateProperty: (state) => state.rules,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
updateRule(ctx, { id, rule }) {
|
|
272
|
+
const { levelId } = ctx.getState();
|
|
273
|
+
if (!levelId) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const req$ = this.http.put(`control-panel/levels/${levelId}/governance-rules/${id}`, rule);
|
|
277
|
+
return this.update(ctx, {
|
|
278
|
+
key: GovernanceActionKey.UpdateRule,
|
|
279
|
+
request$: req$,
|
|
280
|
+
uniqueKey: 'id',
|
|
281
|
+
id,
|
|
282
|
+
stateProperty: (state) => state.rules,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
deleteRule(ctx, { id }) {
|
|
286
|
+
const { levelId } = ctx.getState();
|
|
287
|
+
if (!levelId) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const req$ = this.http.delete(`control-panel/levels/${levelId}/governance-rules/${id}`);
|
|
291
|
+
return this.delete(ctx, {
|
|
292
|
+
key: GovernanceActionKey.DeleteRule,
|
|
293
|
+
request$: req$,
|
|
294
|
+
uniqueKey: 'id',
|
|
295
|
+
id,
|
|
296
|
+
stateProperty: (state) => state.rules,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
toggleRuleActive(ctx, { id }) {
|
|
300
|
+
const { levelId } = ctx.getState();
|
|
301
|
+
if (!levelId) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const req$ = this.http.patch(`control-panel/levels/${levelId}/governance-rules/${id}/toggle`, {});
|
|
305
|
+
return handleApiRequest({
|
|
306
|
+
ctx,
|
|
307
|
+
key: GovernanceActionKey.ToggleRuleActive,
|
|
308
|
+
request$: req$,
|
|
309
|
+
onSuccess: (response, state) => {
|
|
310
|
+
const updatedRule = response.data;
|
|
311
|
+
if (!updatedRule)
|
|
312
|
+
return {};
|
|
313
|
+
return {
|
|
314
|
+
rules: this.adapter.upsertOne(state.rules, updatedRule, 'id'),
|
|
315
|
+
};
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
// ============================================================================
|
|
320
|
+
// Reference Data Actions
|
|
321
|
+
// ============================================================================
|
|
322
|
+
getModules(ctx) {
|
|
323
|
+
const { levelId } = ctx.getState();
|
|
324
|
+
if (!levelId) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const req$ = this.http.get(`Modules?LevelId=${levelId}`);
|
|
328
|
+
return handleApiRequest({
|
|
329
|
+
ctx,
|
|
330
|
+
key: GovernanceActionKey.GetModules,
|
|
331
|
+
request$: req$,
|
|
332
|
+
onSuccess: (response) => ({
|
|
333
|
+
// Only keep active modules for Target Module selection
|
|
334
|
+
modules: (response.data ?? []).filter((m) => m.isActive),
|
|
335
|
+
}),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
clearSelectedRule(ctx) {
|
|
339
|
+
ctx.patchState({ selectedRule: null });
|
|
340
|
+
}
|
|
341
|
+
// ============================================================================
|
|
342
|
+
// Request Schema & Properties Actions (for optionsSource)
|
|
343
|
+
// ============================================================================
|
|
344
|
+
getRequestSchemas(ctx, { targetScope, targetModuleKey }) {
|
|
345
|
+
const { levelId, modules } = ctx.getState();
|
|
346
|
+
if (!levelId) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
// Build endpoint based on scope
|
|
350
|
+
// Level scope: ProcessBuilder/level/{levelId}/{moduleType}/{moduleId}/requestSchema
|
|
351
|
+
// Module scope: ProcessBuilder/level/{levelId}/module/{targetModuleId}/{moduleType}/{moduleId}/requestSchema
|
|
352
|
+
let basePath = `ProcessBuilder/level/${levelId}`;
|
|
353
|
+
// For Module scope, we need to find the module by key to get its ID
|
|
354
|
+
if (targetScope === 'Module' && targetModuleKey) {
|
|
355
|
+
const targetModule = modules.find((m) => m.key === targetModuleKey);
|
|
356
|
+
if (targetModule) {
|
|
357
|
+
basePath += `/module/${targetModule.id}`;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// Get request schemas (assuming a common endpoint pattern)
|
|
361
|
+
const req$ = this.http.get(`${basePath}/requestSchemas`);
|
|
362
|
+
return handleApiRequest({
|
|
363
|
+
ctx,
|
|
364
|
+
key: GovernanceActionKey.GetRequestSchemas,
|
|
365
|
+
request$: req$,
|
|
366
|
+
onSuccess: (response) => ({
|
|
367
|
+
requestSchemas: (response.data ?? []).filter((s) => s.isPublished),
|
|
368
|
+
}),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
getProperties(ctx, { targetScope, targetModuleKey }) {
|
|
372
|
+
const { levelId, modules } = ctx.getState();
|
|
373
|
+
if (!levelId) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
// Build endpoint based on scope
|
|
377
|
+
let basePath = `Properties?LevelId=${levelId}`;
|
|
378
|
+
// For Module scope, add module filter
|
|
379
|
+
if (targetScope === 'Module' && targetModuleKey) {
|
|
380
|
+
const targetModule = modules.find((m) => m.key === targetModuleKey);
|
|
381
|
+
if (targetModule) {
|
|
382
|
+
basePath = `Properties?LevelId=${levelId}&ModuleId=${targetModule.id}`;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const req$ = this.http.get(basePath);
|
|
386
|
+
return handleApiRequest({
|
|
387
|
+
ctx,
|
|
388
|
+
key: GovernanceActionKey.GetProperties,
|
|
389
|
+
request$: req$,
|
|
390
|
+
onSuccess: (response) => ({
|
|
391
|
+
properties: response.data ?? [],
|
|
392
|
+
}),
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
clearRequestSchemas(ctx) {
|
|
396
|
+
ctx.patchState({ requestSchemas: [] });
|
|
397
|
+
}
|
|
398
|
+
clearProperties(ctx) {
|
|
399
|
+
ctx.patchState({ properties: [] });
|
|
400
|
+
}
|
|
401
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceState, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
402
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceState });
|
|
403
|
+
};
|
|
404
|
+
__decorate([
|
|
405
|
+
Action(SetLevelId)
|
|
406
|
+
], GovernanceState.prototype, "setLevelId", null);
|
|
407
|
+
__decorate([
|
|
408
|
+
Action(ResetGovernanceState)
|
|
409
|
+
], GovernanceState.prototype, "resetState", null);
|
|
410
|
+
__decorate([
|
|
411
|
+
Action(GetGovernanceRules)
|
|
412
|
+
], GovernanceState.prototype, "getRules", null);
|
|
413
|
+
__decorate([
|
|
414
|
+
Action(GetGovernanceRule)
|
|
415
|
+
], GovernanceState.prototype, "getRule", null);
|
|
416
|
+
__decorate([
|
|
417
|
+
Action(GetGovernanceRuleTypes)
|
|
418
|
+
], GovernanceState.prototype, "getRuleTypes", null);
|
|
419
|
+
__decorate([
|
|
420
|
+
Action(AddGovernanceRule)
|
|
421
|
+
], GovernanceState.prototype, "addRule", null);
|
|
422
|
+
__decorate([
|
|
423
|
+
Action(UpdateGovernanceRule)
|
|
424
|
+
], GovernanceState.prototype, "updateRule", null);
|
|
425
|
+
__decorate([
|
|
426
|
+
Action(DeleteGovernanceRule)
|
|
427
|
+
], GovernanceState.prototype, "deleteRule", null);
|
|
428
|
+
__decorate([
|
|
429
|
+
Action(ToggleGovernanceRuleActive)
|
|
430
|
+
], GovernanceState.prototype, "toggleRuleActive", null);
|
|
431
|
+
__decorate([
|
|
432
|
+
Action(GetModules)
|
|
433
|
+
], GovernanceState.prototype, "getModules", null);
|
|
434
|
+
__decorate([
|
|
435
|
+
Action(ClearSelectedRule)
|
|
436
|
+
], GovernanceState.prototype, "clearSelectedRule", null);
|
|
437
|
+
__decorate([
|
|
438
|
+
Action(GetRequestSchemas)
|
|
439
|
+
], GovernanceState.prototype, "getRequestSchemas", null);
|
|
440
|
+
__decorate([
|
|
441
|
+
Action(GetProperties)
|
|
442
|
+
], GovernanceState.prototype, "getProperties", null);
|
|
443
|
+
__decorate([
|
|
444
|
+
Action(ClearRequestSchemas)
|
|
445
|
+
], GovernanceState.prototype, "clearRequestSchemas", null);
|
|
446
|
+
__decorate([
|
|
447
|
+
Action(ClearProperties)
|
|
448
|
+
], GovernanceState.prototype, "clearProperties", null);
|
|
449
|
+
__decorate([
|
|
450
|
+
Selector()
|
|
451
|
+
], GovernanceState, "getLevelId", null);
|
|
452
|
+
__decorate([
|
|
453
|
+
Selector()
|
|
454
|
+
], GovernanceState, "getRules", null);
|
|
455
|
+
__decorate([
|
|
456
|
+
Selector()
|
|
457
|
+
], GovernanceState, "getRuleTypes", null);
|
|
458
|
+
__decorate([
|
|
459
|
+
Selector()
|
|
460
|
+
], GovernanceState, "getSelectedRule", null);
|
|
461
|
+
__decorate([
|
|
462
|
+
Selector()
|
|
463
|
+
], GovernanceState, "getModules", null);
|
|
464
|
+
__decorate([
|
|
465
|
+
Selector()
|
|
466
|
+
], GovernanceState, "getRequestSchemas", null);
|
|
467
|
+
__decorate([
|
|
468
|
+
Selector()
|
|
469
|
+
], GovernanceState, "getProperties", null);
|
|
470
|
+
__decorate([
|
|
471
|
+
Selector()
|
|
472
|
+
], GovernanceState, "getLoadingActive", null);
|
|
473
|
+
__decorate([
|
|
474
|
+
Selector()
|
|
475
|
+
], GovernanceState, "getErrors", null);
|
|
476
|
+
GovernanceState = __decorate([
|
|
477
|
+
State({
|
|
478
|
+
name: 'governance',
|
|
479
|
+
defaults: DEFAULTS,
|
|
480
|
+
})
|
|
481
|
+
], GovernanceState);
|
|
482
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceState, decorators: [{
|
|
483
|
+
type: Injectable
|
|
484
|
+
}], propDecorators: { setLevelId: [], resetState: [], getRules: [], getRule: [], getRuleTypes: [], addRule: [], updateRule: [], deleteRule: [], toggleRuleActive: [], getModules: [], clearSelectedRule: [], getRequestSchemas: [], getProperties: [], clearRequestSchemas: [], clearProperties: [] } });
|
|
485
|
+
|
|
486
|
+
class GovernanceFacade {
|
|
487
|
+
store = inject(Store);
|
|
488
|
+
// ============================================================================
|
|
489
|
+
// Data Selectors - Memoized by NGXS (fine-grained reactivity)
|
|
490
|
+
// ============================================================================
|
|
491
|
+
levelId = select(GovernanceState.getLevelId);
|
|
492
|
+
rules = select(GovernanceState.getRules);
|
|
493
|
+
ruleTypes = select(GovernanceState.getRuleTypes);
|
|
494
|
+
selectedRule = select(GovernanceState.getSelectedRule);
|
|
495
|
+
modules = select(GovernanceState.getModules);
|
|
496
|
+
requestSchemas = select(GovernanceState.getRequestSchemas);
|
|
497
|
+
properties = select(GovernanceState.getProperties);
|
|
498
|
+
// ============================================================================
|
|
499
|
+
// Loading/Error Slices - Memoized by NGXS
|
|
500
|
+
// ============================================================================
|
|
501
|
+
loadingActive = select(GovernanceState.getLoadingActive);
|
|
502
|
+
errors = select(GovernanceState.getErrors);
|
|
503
|
+
// ============================================================================
|
|
504
|
+
// Loading Signals - Computed from slice (minimal reactivity)
|
|
505
|
+
// ============================================================================
|
|
506
|
+
isLoadingRules = computed(() => this.loadingActive().includes(GovernanceActionKey.GetRules), ...(ngDevMode ? [{ debugName: "isLoadingRules" }] : []));
|
|
507
|
+
isLoadingRule = computed(() => this.loadingActive().includes(GovernanceActionKey.GetRule), ...(ngDevMode ? [{ debugName: "isLoadingRule" }] : []));
|
|
508
|
+
isLoadingRuleTypes = computed(() => this.loadingActive().includes(GovernanceActionKey.GetRuleTypes), ...(ngDevMode ? [{ debugName: "isLoadingRuleTypes" }] : []));
|
|
509
|
+
isAddingRule = computed(() => this.loadingActive().includes(GovernanceActionKey.AddRule), ...(ngDevMode ? [{ debugName: "isAddingRule" }] : []));
|
|
510
|
+
isUpdatingRule = computed(() => this.loadingActive().includes(GovernanceActionKey.UpdateRule), ...(ngDevMode ? [{ debugName: "isUpdatingRule" }] : []));
|
|
511
|
+
isDeletingRule = computed(() => this.loadingActive().includes(GovernanceActionKey.DeleteRule), ...(ngDevMode ? [{ debugName: "isDeletingRule" }] : []));
|
|
512
|
+
isTogglingRule = computed(() => this.loadingActive().includes(GovernanceActionKey.ToggleRuleActive), ...(ngDevMode ? [{ debugName: "isTogglingRule" }] : []));
|
|
513
|
+
isLoadingModules = computed(() => this.loadingActive().includes(GovernanceActionKey.GetModules), ...(ngDevMode ? [{ debugName: "isLoadingModules" }] : []));
|
|
514
|
+
isLoadingRequestSchemas = computed(() => this.loadingActive().includes(GovernanceActionKey.GetRequestSchemas), ...(ngDevMode ? [{ debugName: "isLoadingRequestSchemas" }] : []));
|
|
515
|
+
isLoadingProperties = computed(() => this.loadingActive().includes(GovernanceActionKey.GetProperties), ...(ngDevMode ? [{ debugName: "isLoadingProperties" }] : []));
|
|
516
|
+
// ============================================================================
|
|
517
|
+
// Error Signals - Computed from slice (minimal reactivity)
|
|
518
|
+
// ============================================================================
|
|
519
|
+
rulesError = computed(() => this.errors()[GovernanceActionKey.GetRules] ?? null, ...(ngDevMode ? [{ debugName: "rulesError" }] : []));
|
|
520
|
+
ruleError = computed(() => this.errors()[GovernanceActionKey.GetRule] ?? null, ...(ngDevMode ? [{ debugName: "ruleError" }] : []));
|
|
521
|
+
ruleTypesError = computed(() => this.errors()[GovernanceActionKey.GetRuleTypes] ?? null, ...(ngDevMode ? [{ debugName: "ruleTypesError" }] : []));
|
|
522
|
+
addRuleError = computed(() => this.errors()[GovernanceActionKey.AddRule] ?? null, ...(ngDevMode ? [{ debugName: "addRuleError" }] : []));
|
|
523
|
+
updateRuleError = computed(() => this.errors()[GovernanceActionKey.UpdateRule] ?? null, ...(ngDevMode ? [{ debugName: "updateRuleError" }] : []));
|
|
524
|
+
deleteRuleError = computed(() => this.errors()[GovernanceActionKey.DeleteRule] ?? null, ...(ngDevMode ? [{ debugName: "deleteRuleError" }] : []));
|
|
525
|
+
modulesError = computed(() => this.errors()[GovernanceActionKey.GetModules] ?? null, ...(ngDevMode ? [{ debugName: "modulesError" }] : []));
|
|
526
|
+
requestSchemasError = computed(() => this.errors()[GovernanceActionKey.GetRequestSchemas] ?? null, ...(ngDevMode ? [{ debugName: "requestSchemasError" }] : []));
|
|
527
|
+
propertiesError = computed(() => this.errors()[GovernanceActionKey.GetProperties] ?? null, ...(ngDevMode ? [{ debugName: "propertiesError" }] : []));
|
|
528
|
+
// ============================================================================
|
|
529
|
+
// Derived Data - Computed from data selectors
|
|
530
|
+
// ============================================================================
|
|
531
|
+
activeRules = computed(() => this.rules().filter((r) => r.isActive === true), ...(ngDevMode ? [{ debugName: "activeRules" }] : []));
|
|
532
|
+
inactiveRules = computed(() => this.rules().filter((r) => r.isActive === false), ...(ngDevMode ? [{ debugName: "inactiveRules" }] : []));
|
|
533
|
+
rulesCount = computed(() => this.rules().length, ...(ngDevMode ? [{ debugName: "rulesCount" }] : []));
|
|
534
|
+
// ============================================================================
|
|
535
|
+
// Action Dispatchers
|
|
536
|
+
// ============================================================================
|
|
537
|
+
setLevelId(levelId) {
|
|
538
|
+
return this.store.dispatch(new SetLevelId(levelId));
|
|
539
|
+
}
|
|
540
|
+
getRules() {
|
|
541
|
+
return this.store.dispatch(new GetGovernanceRules());
|
|
542
|
+
}
|
|
543
|
+
getRule(id) {
|
|
544
|
+
return this.store.dispatch(new GetGovernanceRule(id));
|
|
545
|
+
}
|
|
546
|
+
getRuleTypes() {
|
|
547
|
+
return this.store.dispatch(new GetGovernanceRuleTypes());
|
|
548
|
+
}
|
|
549
|
+
getModules() {
|
|
550
|
+
return this.store.dispatch(new GetModules());
|
|
551
|
+
}
|
|
552
|
+
addRule(rule) {
|
|
553
|
+
return this.store.dispatch(new AddGovernanceRule(rule));
|
|
554
|
+
}
|
|
555
|
+
updateRule(id, rule) {
|
|
556
|
+
return this.store.dispatch(new UpdateGovernanceRule(id, rule));
|
|
557
|
+
}
|
|
558
|
+
deleteRule(id) {
|
|
559
|
+
return this.store.dispatch(new DeleteGovernanceRule(id));
|
|
560
|
+
}
|
|
561
|
+
toggleRuleActive(id) {
|
|
562
|
+
return this.store.dispatch(new ToggleGovernanceRuleActive(id));
|
|
563
|
+
}
|
|
564
|
+
clearSelectedRule() {
|
|
565
|
+
return this.store.dispatch(new ClearSelectedRule());
|
|
566
|
+
}
|
|
567
|
+
resetState() {
|
|
568
|
+
return this.store.dispatch(new ResetGovernanceState());
|
|
569
|
+
}
|
|
570
|
+
// ============================================================================
|
|
571
|
+
// Request Schema & Properties Actions (for optionsSource)
|
|
572
|
+
// ============================================================================
|
|
573
|
+
getRequestSchemas(targetScope, targetModuleKey) {
|
|
574
|
+
return this.store.dispatch(new GetRequestSchemas(targetScope, targetModuleKey));
|
|
575
|
+
}
|
|
576
|
+
getProperties(targetScope, targetModuleKey) {
|
|
577
|
+
return this.store.dispatch(new GetProperties(targetScope, targetModuleKey));
|
|
578
|
+
}
|
|
579
|
+
clearRequestSchemas() {
|
|
580
|
+
return this.store.dispatch(new ClearRequestSchemas());
|
|
581
|
+
}
|
|
582
|
+
clearProperties() {
|
|
583
|
+
return this.store.dispatch(new ClearProperties());
|
|
584
|
+
}
|
|
585
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
586
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceFacade, providedIn: 'root' });
|
|
587
|
+
}
|
|
588
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceFacade, decorators: [{
|
|
589
|
+
type: Injectable,
|
|
590
|
+
args: [{ providedIn: 'root' }]
|
|
591
|
+
}] });
|
|
592
|
+
|
|
593
|
+
class GovernanceRuleForm {
|
|
594
|
+
// ============================================================================
|
|
595
|
+
// Injected Services
|
|
596
|
+
// ============================================================================
|
|
597
|
+
modal = inject(ModalService);
|
|
598
|
+
ref = inject(ModalRef);
|
|
599
|
+
translocoService = inject(TranslocoService);
|
|
600
|
+
facade = inject(GovernanceFacade);
|
|
601
|
+
// ============================================================================
|
|
602
|
+
// Input - rule to edit (null for create)
|
|
603
|
+
// ============================================================================
|
|
604
|
+
ruleForEdit = input(null, ...(ngDevMode ? [{ debugName: "ruleForEdit" }] : []));
|
|
605
|
+
// ============================================================================
|
|
606
|
+
// Facade Data
|
|
607
|
+
// ============================================================================
|
|
608
|
+
selectedRule = this.facade.selectedRule;
|
|
609
|
+
ruleTypes = this.facade.ruleTypes;
|
|
610
|
+
isLoadingRule = this.facade.isLoadingRule;
|
|
611
|
+
isAddingRule = this.facade.isAddingRule;
|
|
612
|
+
isUpdatingRule = this.facade.isUpdatingRule;
|
|
613
|
+
// For optionsSource: 'auto-populated' (modules from API)
|
|
614
|
+
modules = this.facade.modules;
|
|
615
|
+
// For optionsSource: 'requestSchema' and 'properties'
|
|
616
|
+
requestSchemas = this.facade.requestSchemas;
|
|
617
|
+
properties = this.facade.properties;
|
|
618
|
+
isLoadingRequestSchemas = this.facade.isLoadingRequestSchemas;
|
|
619
|
+
isLoadingProperties = this.facade.isLoadingProperties;
|
|
620
|
+
// ============================================================================
|
|
621
|
+
// Form Control
|
|
622
|
+
// ============================================================================
|
|
623
|
+
ruleFormControl = new FormControl();
|
|
624
|
+
formValue = toSignal(this.ruleFormControl.valueChanges);
|
|
625
|
+
// ============================================================================
|
|
626
|
+
// Selected Rule Type (from form value)
|
|
627
|
+
// ============================================================================
|
|
628
|
+
selectedRuleType = computed(() => {
|
|
629
|
+
const formValue = this.formValue();
|
|
630
|
+
const ruleTypeKey = formValue?.ruleType;
|
|
631
|
+
if (!ruleTypeKey)
|
|
632
|
+
return null;
|
|
633
|
+
return this.ruleTypes().find((rt) => rt.type === ruleTypeKey) ?? null;
|
|
634
|
+
}, ...(ngDevMode ? [{ debugName: "selectedRuleType" }] : []));
|
|
635
|
+
// ============================================================================
|
|
636
|
+
// Target Scope and Module Key (from form value)
|
|
637
|
+
// ============================================================================
|
|
638
|
+
targetScope = computed(() => this.formValue()?.targetScope ?? 'Level', ...(ngDevMode ? [{ debugName: "targetScope" }] : []));
|
|
639
|
+
targetModuleKey = computed(() => this.formValue()?.targetModuleKey ?? null, ...(ngDevMode ? [{ debugName: "targetModuleKey" }] : []));
|
|
640
|
+
// ============================================================================
|
|
641
|
+
// Options from state for optionsSource: 'requestSchema'
|
|
642
|
+
// ============================================================================
|
|
643
|
+
requestSchemaOptions = computed(() => {
|
|
644
|
+
const lang = this.translocoService.getActiveLang();
|
|
645
|
+
return this.requestSchemas().map((schema) => ({
|
|
646
|
+
label: schema.name[lang] || schema.name.en,
|
|
647
|
+
value: schema.id.toString(),
|
|
648
|
+
}));
|
|
649
|
+
}, ...(ngDevMode ? [{ debugName: "requestSchemaOptions" }] : []));
|
|
650
|
+
// ============================================================================
|
|
651
|
+
// Options from state for optionsSource: 'properties'
|
|
652
|
+
// ============================================================================
|
|
653
|
+
propertyOptions = computed(() => {
|
|
654
|
+
const lang = this.translocoService.getActiveLang();
|
|
655
|
+
return this.properties().map((prop) => ({
|
|
656
|
+
label: prop.name[lang] || prop.name.en,
|
|
657
|
+
value: prop.key,
|
|
658
|
+
}));
|
|
659
|
+
}, ...(ngDevMode ? [{ debugName: "propertyOptions" }] : []));
|
|
660
|
+
// ============================================================================
|
|
661
|
+
// Module Options for optionsSource: 'auto-populated' (moduleKey, requiredModuleKey)
|
|
662
|
+
// ============================================================================
|
|
663
|
+
moduleOptions = computed(() => {
|
|
664
|
+
const lang = this.translocoService.getActiveLang();
|
|
665
|
+
return this.modules().map((mod) => ({
|
|
666
|
+
label: mod.name[lang] || mod.name.en,
|
|
667
|
+
value: mod.key,
|
|
668
|
+
}));
|
|
669
|
+
}, ...(ngDevMode ? [{ debugName: "moduleOptions" }] : []));
|
|
670
|
+
// ============================================================================
|
|
671
|
+
// Scope Options based on selected rule type
|
|
672
|
+
// ============================================================================
|
|
673
|
+
scopeOptions = computed(() => {
|
|
674
|
+
const t = (key) => this.translocoService.translate(`governance.scopes.${key.toLowerCase()}`);
|
|
675
|
+
const ruleType = this.selectedRuleType();
|
|
676
|
+
if (!ruleType) {
|
|
677
|
+
return [
|
|
678
|
+
{ label: t('level'), value: 'Level' },
|
|
679
|
+
{ label: t('module'), value: 'Module' },
|
|
680
|
+
{ label: t('phasegate'), value: 'PhaseGate' },
|
|
681
|
+
];
|
|
682
|
+
}
|
|
683
|
+
return ruleType.supportedScopes.map((scope) => ({
|
|
684
|
+
label: t(scope.toLowerCase()),
|
|
685
|
+
value: scope,
|
|
686
|
+
}));
|
|
687
|
+
}, ...(ngDevMode ? [{ debugName: "scopeOptions" }] : []));
|
|
688
|
+
// ============================================================================
|
|
689
|
+
// Operation Options based on selected rule type
|
|
690
|
+
// ============================================================================
|
|
691
|
+
operationOptions = computed(() => {
|
|
692
|
+
const t = (key) => this.translocoService.translate(`governance.operations.${key.toLowerCase()}`);
|
|
693
|
+
const ruleType = this.selectedRuleType();
|
|
694
|
+
if (!ruleType) {
|
|
695
|
+
return [
|
|
696
|
+
{ label: t('all'), value: '' },
|
|
697
|
+
{ label: t('create'), value: 'Create' },
|
|
698
|
+
{ label: t('update'), value: 'Update' },
|
|
699
|
+
{ label: t('delete'), value: 'Delete' },
|
|
700
|
+
];
|
|
701
|
+
}
|
|
702
|
+
return [
|
|
703
|
+
{ label: t('all'), value: '' },
|
|
704
|
+
...ruleType.supportedOperations.map((op) => ({
|
|
705
|
+
label: t(op.toLowerCase()),
|
|
706
|
+
value: op,
|
|
707
|
+
})),
|
|
708
|
+
];
|
|
709
|
+
}, ...(ngDevMode ? [{ debugName: "operationOptions" }] : []));
|
|
710
|
+
// ============================================================================
|
|
711
|
+
// Rule Type Options
|
|
712
|
+
// ============================================================================
|
|
713
|
+
ruleTypeOptions = computed(() => {
|
|
714
|
+
const lang = this.translocoService.getActiveLang();
|
|
715
|
+
return this.ruleTypes().map((rt) => ({
|
|
716
|
+
label: rt.name[lang] || rt.name.en,
|
|
717
|
+
value: rt.type,
|
|
718
|
+
}));
|
|
719
|
+
}, ...(ngDevMode ? [{ debugName: "ruleTypeOptions" }] : []));
|
|
720
|
+
// ============================================================================
|
|
721
|
+
// Dynamic Form Configuration
|
|
722
|
+
// ============================================================================
|
|
723
|
+
formConfig = computed(() => {
|
|
724
|
+
const t = (key) => this.translocoService.translate(`governance.${key}`);
|
|
725
|
+
const ruleType = this.selectedRuleType();
|
|
726
|
+
const isEdit = !!this.ruleForEdit()?.id;
|
|
727
|
+
return {
|
|
728
|
+
sections: [
|
|
729
|
+
// Section 1: Basic Information
|
|
730
|
+
{
|
|
731
|
+
key: 'basicInfo',
|
|
732
|
+
type: 'header',
|
|
733
|
+
label: t('basic-information'),
|
|
734
|
+
order: 1,
|
|
735
|
+
fields: [
|
|
736
|
+
new TextFieldConfig({
|
|
737
|
+
key: 'key',
|
|
738
|
+
label: t('rule-key'),
|
|
739
|
+
placeholder: 'e.g., charter-required',
|
|
740
|
+
validators: [
|
|
741
|
+
ValidatorConfig.required(),
|
|
742
|
+
ValidatorConfig.pattern('^[a-z0-9-]+$', 'Only lowercase letters, numbers and hyphens allowed'),
|
|
743
|
+
],
|
|
744
|
+
order: 1,
|
|
745
|
+
colSpan: 6,
|
|
746
|
+
disabled: isEdit,
|
|
747
|
+
}),
|
|
748
|
+
new NumberFieldConfig({
|
|
749
|
+
key: 'priority',
|
|
750
|
+
label: t('priority'),
|
|
751
|
+
placeholder: '10',
|
|
752
|
+
validators: [ValidatorConfig.required()],
|
|
753
|
+
min: 0,
|
|
754
|
+
max: 1000,
|
|
755
|
+
order: 2,
|
|
756
|
+
colSpan: 6,
|
|
757
|
+
}),
|
|
758
|
+
new TextFieldConfig({
|
|
759
|
+
key: 'name_en',
|
|
760
|
+
label: t('name-en'),
|
|
761
|
+
placeholder: t('name-en-placeholder'),
|
|
762
|
+
validators: [ValidatorConfig.required()],
|
|
763
|
+
order: 3,
|
|
764
|
+
colSpan: 6,
|
|
765
|
+
}),
|
|
766
|
+
new TextFieldConfig({
|
|
767
|
+
key: 'name_ar',
|
|
768
|
+
label: t('name-ar'),
|
|
769
|
+
placeholder: t('name-ar-placeholder'),
|
|
770
|
+
validators: [ValidatorConfig.required()],
|
|
771
|
+
order: 4,
|
|
772
|
+
colSpan: 6,
|
|
773
|
+
}),
|
|
774
|
+
new TextareaFieldConfig({
|
|
775
|
+
key: 'description_en',
|
|
776
|
+
label: t('description-en'),
|
|
777
|
+
placeholder: t('description-en-placeholder'),
|
|
778
|
+
order: 5,
|
|
779
|
+
colSpan: 6,
|
|
780
|
+
}),
|
|
781
|
+
new TextareaFieldConfig({
|
|
782
|
+
key: 'description_ar',
|
|
783
|
+
label: t('description-ar'),
|
|
784
|
+
placeholder: t('description-ar-placeholder'),
|
|
785
|
+
order: 6,
|
|
786
|
+
colSpan: 6,
|
|
787
|
+
}),
|
|
788
|
+
new TextareaFieldConfig({
|
|
789
|
+
key: 'errorMessage_en',
|
|
790
|
+
label: t('error-message-en'),
|
|
791
|
+
placeholder: t('error-message-en-placeholder'),
|
|
792
|
+
validators: [ValidatorConfig.required()],
|
|
793
|
+
order: 7,
|
|
794
|
+
colSpan: 6,
|
|
795
|
+
}),
|
|
796
|
+
new TextareaFieldConfig({
|
|
797
|
+
key: 'errorMessage_ar',
|
|
798
|
+
label: t('error-message-ar'),
|
|
799
|
+
placeholder: t('error-message-ar-placeholder'),
|
|
800
|
+
validators: [ValidatorConfig.required()],
|
|
801
|
+
order: 8,
|
|
802
|
+
colSpan: 6,
|
|
803
|
+
}),
|
|
804
|
+
],
|
|
805
|
+
},
|
|
806
|
+
// Section 2: Rule Target
|
|
807
|
+
{
|
|
808
|
+
key: 'ruleTarget',
|
|
809
|
+
type: 'header',
|
|
810
|
+
label: t('rule-target'),
|
|
811
|
+
order: 2,
|
|
812
|
+
fields: [
|
|
813
|
+
new SelectFieldConfig({
|
|
814
|
+
key: 'ruleType',
|
|
815
|
+
label: t('rule-type'),
|
|
816
|
+
options: this.ruleTypeOptions(),
|
|
817
|
+
optionLabel: 'label',
|
|
818
|
+
optionValue: 'value',
|
|
819
|
+
validators: [ValidatorConfig.required()],
|
|
820
|
+
order: 1,
|
|
821
|
+
colSpan: 6,
|
|
822
|
+
disabled: isEdit,
|
|
823
|
+
}),
|
|
824
|
+
new SelectFieldConfig({
|
|
825
|
+
key: 'targetScope',
|
|
826
|
+
label: t('target-scope'),
|
|
827
|
+
options: this.scopeOptions(),
|
|
828
|
+
optionLabel: 'label',
|
|
829
|
+
optionValue: 'value',
|
|
830
|
+
validators: [ValidatorConfig.required()],
|
|
831
|
+
order: 2,
|
|
832
|
+
colSpan: 6,
|
|
833
|
+
}),
|
|
834
|
+
new SelectFieldConfig({
|
|
835
|
+
key: 'targetModuleKey',
|
|
836
|
+
label: t('target-module'),
|
|
837
|
+
placeholder: t('select-target-module'),
|
|
838
|
+
options: this.moduleOptions(),
|
|
839
|
+
optionLabel: 'label',
|
|
840
|
+
optionValue: 'value',
|
|
841
|
+
order: 3,
|
|
842
|
+
colSpan: 6,
|
|
843
|
+
relations: [
|
|
844
|
+
{
|
|
845
|
+
key: 'targetScope',
|
|
846
|
+
value: 'Module',
|
|
847
|
+
action: 'show',
|
|
848
|
+
},
|
|
849
|
+
],
|
|
850
|
+
}),
|
|
851
|
+
new SelectFieldConfig({
|
|
852
|
+
key: 'targetOperationKey',
|
|
853
|
+
label: t('target-operation'),
|
|
854
|
+
options: this.operationOptions(),
|
|
855
|
+
optionLabel: 'label',
|
|
856
|
+
optionValue: 'value',
|
|
857
|
+
order: 4,
|
|
858
|
+
colSpan: 6,
|
|
859
|
+
}),
|
|
860
|
+
],
|
|
861
|
+
},
|
|
862
|
+
// Section 3: Rule-Specific Configuration (dynamic)
|
|
863
|
+
...(ruleType ? [this.buildConfigurationSection(ruleType)] : []),
|
|
864
|
+
// Section 4: Execution Settings
|
|
865
|
+
{
|
|
866
|
+
key: 'executionSettings',
|
|
867
|
+
type: 'header',
|
|
868
|
+
label: t('execution-settings'),
|
|
869
|
+
order: 4,
|
|
870
|
+
fields: [
|
|
871
|
+
new ToggleFieldConfig({
|
|
872
|
+
key: 'stopOnFailure',
|
|
873
|
+
label: t('stop-on-failure'),
|
|
874
|
+
order: 1,
|
|
875
|
+
colSpan: 6,
|
|
876
|
+
}),
|
|
877
|
+
new ToggleFieldConfig({
|
|
878
|
+
key: 'isActive',
|
|
879
|
+
label: t('active-rule'),
|
|
880
|
+
order: 2,
|
|
881
|
+
colSpan: 6,
|
|
882
|
+
}),
|
|
883
|
+
],
|
|
884
|
+
},
|
|
885
|
+
],
|
|
886
|
+
};
|
|
887
|
+
}, ...(ngDevMode ? [{ debugName: "formConfig" }] : []));
|
|
888
|
+
// ============================================================================
|
|
889
|
+
// Constructor with Effect for Form Population
|
|
890
|
+
// ============================================================================
|
|
891
|
+
constructor() {
|
|
892
|
+
// Effect to populate form when editing existing rule
|
|
893
|
+
effect(() => {
|
|
894
|
+
const ruleId = this.ruleForEdit()?.id;
|
|
895
|
+
if (this.selectedRule() && ruleId) {
|
|
896
|
+
const rule = this.selectedRule();
|
|
897
|
+
if (!rule)
|
|
898
|
+
return;
|
|
899
|
+
// Unpack rule data to form format
|
|
900
|
+
const formData = this.unpackRuleToForm(rule);
|
|
901
|
+
this.ruleFormControl.patchValue(formData);
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
// Default values for new rule
|
|
905
|
+
this.ruleFormControl.reset({
|
|
906
|
+
priority: 10,
|
|
907
|
+
stopOnFailure: true,
|
|
908
|
+
isActive: true,
|
|
909
|
+
targetScope: 'Module',
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
// Effect to apply default values when rule type changes (for new rules only)
|
|
914
|
+
effect(() => {
|
|
915
|
+
const ruleType = this.selectedRuleType();
|
|
916
|
+
const ruleId = this.ruleForEdit()?.id;
|
|
917
|
+
// Only apply defaults for new rules (not editing)
|
|
918
|
+
if (ruleType && !ruleId) {
|
|
919
|
+
const defaults = this.getDefaultsFromSchema(ruleType);
|
|
920
|
+
if (Object.keys(defaults).length > 0) {
|
|
921
|
+
const currentValue = this.ruleFormControl.value ?? {};
|
|
922
|
+
this.ruleFormControl.patchValue({ ...currentValue, ...defaults });
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
// Effect to fetch requestSchemas and properties when scope/module changes
|
|
927
|
+
// This runs when configurationSchema has fields with optionsSource = 'requestSchema' or 'properties'
|
|
928
|
+
effect(() => {
|
|
929
|
+
const ruleType = this.selectedRuleType();
|
|
930
|
+
const scope = this.targetScope();
|
|
931
|
+
const moduleKey = this.targetModuleKey();
|
|
932
|
+
if (!ruleType)
|
|
933
|
+
return;
|
|
934
|
+
// Check if any field needs requestSchema options
|
|
935
|
+
const needsRequestSchemas = ruleType.configurationSchema.some((f) => f.optionsSource === 'requestSchema');
|
|
936
|
+
// Check if any field needs properties options
|
|
937
|
+
const needsProperties = ruleType.configurationSchema.some((f) => f.optionsSource === 'properties');
|
|
938
|
+
// For Module scope, we need a target module key
|
|
939
|
+
if (scope === 'Module' && !moduleKey) {
|
|
940
|
+
// Clear options when module is not selected
|
|
941
|
+
if (needsRequestSchemas) {
|
|
942
|
+
this.facade.clearRequestSchemas();
|
|
943
|
+
}
|
|
944
|
+
if (needsProperties) {
|
|
945
|
+
this.facade.clearProperties();
|
|
946
|
+
}
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
// Fetch options based on scope
|
|
950
|
+
if (needsRequestSchemas) {
|
|
951
|
+
this.facade.getRequestSchemas(scope, moduleKey ?? undefined);
|
|
952
|
+
}
|
|
953
|
+
if (needsProperties) {
|
|
954
|
+
this.facade.getProperties(scope, moduleKey ?? undefined);
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Extract default values from rule type's configuration schema
|
|
960
|
+
*/
|
|
961
|
+
getDefaultsFromSchema(ruleType) {
|
|
962
|
+
const defaults = {};
|
|
963
|
+
for (const field of ruleType.configurationSchema) {
|
|
964
|
+
if (field.defaultValue !== undefined) {
|
|
965
|
+
defaults[`config_${field.key}`] = field.defaultValue;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return defaults;
|
|
969
|
+
}
|
|
970
|
+
ngOnInit() {
|
|
971
|
+
const ruleId = this.ruleForEdit()?.id;
|
|
972
|
+
if (ruleId) {
|
|
973
|
+
this.facade.getRule(ruleId);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
// ============================================================================
|
|
977
|
+
// Submit Handler
|
|
978
|
+
// ============================================================================
|
|
979
|
+
onSubmit() {
|
|
980
|
+
if (!this.ruleFormControl.valid) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const formValue = this.ruleFormControl.value;
|
|
984
|
+
const ruleId = this.ruleForEdit()?.id;
|
|
985
|
+
if (ruleId) {
|
|
986
|
+
// Update
|
|
987
|
+
const payload = this.packFormToUpdateDto(formValue);
|
|
988
|
+
this.facade.updateRule(ruleId, payload).subscribe({
|
|
989
|
+
next: () => {
|
|
990
|
+
this.ref.close(true);
|
|
991
|
+
},
|
|
992
|
+
error: () => { },
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
// Create
|
|
997
|
+
const payload = this.packFormToCreateDto(formValue);
|
|
998
|
+
this.facade.addRule(payload).subscribe({
|
|
999
|
+
next: () => {
|
|
1000
|
+
this.ref.close(true);
|
|
1001
|
+
},
|
|
1002
|
+
error: () => { },
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
// ============================================================================
|
|
1007
|
+
// Build Configuration Section Based on Rule Type
|
|
1008
|
+
// ============================================================================
|
|
1009
|
+
buildConfigurationSection(ruleType) {
|
|
1010
|
+
const t = (key) => this.translocoService.translate(`governance.${key}`);
|
|
1011
|
+
return {
|
|
1012
|
+
key: 'configuration',
|
|
1013
|
+
type: 'header',
|
|
1014
|
+
label: t('rule-configuration'),
|
|
1015
|
+
order: 3,
|
|
1016
|
+
fields: this.buildConfigFieldsForType(ruleType),
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
buildConfigFieldsForType(ruleType) {
|
|
1020
|
+
const lang = this.translocoService.getActiveLang();
|
|
1021
|
+
// Build fields dynamically from configurationSchema array
|
|
1022
|
+
return ruleType.configurationSchema.map((field, index) => this.schemaFieldToFormField(field, index + 1, lang));
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Convert a ConfigurationFieldSchema to DynamicForm BaseFieldConfig
|
|
1026
|
+
*/
|
|
1027
|
+
schemaFieldToFormField(field, order, lang) {
|
|
1028
|
+
const label = field.label[lang] || field.label.en;
|
|
1029
|
+
const colSpan = field.colSpan ?? 6;
|
|
1030
|
+
const validators = field.required ? [ValidatorConfig.required()] : [];
|
|
1031
|
+
switch (field.type) {
|
|
1032
|
+
case 'text':
|
|
1033
|
+
return new TextFieldConfig({
|
|
1034
|
+
key: `config_${field.key}`,
|
|
1035
|
+
label,
|
|
1036
|
+
validators,
|
|
1037
|
+
order,
|
|
1038
|
+
colSpan,
|
|
1039
|
+
});
|
|
1040
|
+
case 'textarea':
|
|
1041
|
+
return new TextareaFieldConfig({
|
|
1042
|
+
key: `config_${field.key}`,
|
|
1043
|
+
label,
|
|
1044
|
+
validators,
|
|
1045
|
+
order,
|
|
1046
|
+
colSpan,
|
|
1047
|
+
});
|
|
1048
|
+
case 'number':
|
|
1049
|
+
return new NumberFieldConfig({
|
|
1050
|
+
key: `config_${field.key}`,
|
|
1051
|
+
label,
|
|
1052
|
+
validators,
|
|
1053
|
+
min: field.min,
|
|
1054
|
+
max: field.max,
|
|
1055
|
+
order,
|
|
1056
|
+
colSpan,
|
|
1057
|
+
});
|
|
1058
|
+
case 'select':
|
|
1059
|
+
return new SelectFieldConfig({
|
|
1060
|
+
key: `config_${field.key}`,
|
|
1061
|
+
label,
|
|
1062
|
+
options: this.getOptionsForField(field),
|
|
1063
|
+
optionLabel: 'label',
|
|
1064
|
+
optionValue: 'value',
|
|
1065
|
+
validators,
|
|
1066
|
+
order,
|
|
1067
|
+
colSpan,
|
|
1068
|
+
});
|
|
1069
|
+
case 'multi-select':
|
|
1070
|
+
return new MultiSelectFieldConfig({
|
|
1071
|
+
key: `config_${field.key}`,
|
|
1072
|
+
label,
|
|
1073
|
+
options: this.getOptionsForField(field),
|
|
1074
|
+
optionLabel: 'label',
|
|
1075
|
+
optionValue: 'value',
|
|
1076
|
+
validators,
|
|
1077
|
+
order,
|
|
1078
|
+
colSpan,
|
|
1079
|
+
});
|
|
1080
|
+
case 'checkbox':
|
|
1081
|
+
return new ToggleFieldConfig({
|
|
1082
|
+
key: `config_${field.key}`,
|
|
1083
|
+
label,
|
|
1084
|
+
order,
|
|
1085
|
+
colSpan,
|
|
1086
|
+
});
|
|
1087
|
+
case 'date':
|
|
1088
|
+
return new DateFieldConfig({
|
|
1089
|
+
key: `config_${field.key}`,
|
|
1090
|
+
label,
|
|
1091
|
+
validators,
|
|
1092
|
+
order,
|
|
1093
|
+
colSpan,
|
|
1094
|
+
});
|
|
1095
|
+
default:
|
|
1096
|
+
// Fallback to text field for unknown types
|
|
1097
|
+
return new TextFieldConfig({
|
|
1098
|
+
key: `config_${field.key}`,
|
|
1099
|
+
label,
|
|
1100
|
+
validators,
|
|
1101
|
+
order,
|
|
1102
|
+
colSpan,
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Get options for a field based on its optionsSource
|
|
1108
|
+
* - 'static' or 'auto-populated': Use options from schema (already in API response)
|
|
1109
|
+
* - 'requestSchema': Use fetched request schemas from state
|
|
1110
|
+
* - 'properties': Use fetched properties from state
|
|
1111
|
+
*/
|
|
1112
|
+
getOptionsForField(field) {
|
|
1113
|
+
switch (field.optionsSource) {
|
|
1114
|
+
case 'requestSchema':
|
|
1115
|
+
return this.requestSchemaOptions();
|
|
1116
|
+
case 'properties':
|
|
1117
|
+
return this.propertyOptions();
|
|
1118
|
+
case 'auto-populated':
|
|
1119
|
+
case 'static':
|
|
1120
|
+
default:
|
|
1121
|
+
// Use options directly from schema
|
|
1122
|
+
return field.options ?? [];
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
// ============================================================================
|
|
1126
|
+
// Data Transformation Helpers
|
|
1127
|
+
// ============================================================================
|
|
1128
|
+
unpackRuleToForm(rule) {
|
|
1129
|
+
const formData = {
|
|
1130
|
+
key: rule.key,
|
|
1131
|
+
priority: rule.priority,
|
|
1132
|
+
name_en: rule.name?.en ?? '',
|
|
1133
|
+
name_ar: rule.name?.ar ?? '',
|
|
1134
|
+
description_en: rule.description?.en ?? '',
|
|
1135
|
+
description_ar: rule.description?.ar ?? '',
|
|
1136
|
+
errorMessage_en: rule.errorMessage?.en ?? '',
|
|
1137
|
+
errorMessage_ar: rule.errorMessage?.ar ?? '',
|
|
1138
|
+
ruleType: rule.ruleTypeKey,
|
|
1139
|
+
targetScope: rule.targetScope,
|
|
1140
|
+
targetModuleKey: rule.targetModuleKey ?? '',
|
|
1141
|
+
targetOperationKey: rule.targetOperationKey ?? '',
|
|
1142
|
+
stopOnFailure: rule.stopOnFailure,
|
|
1143
|
+
isActive: rule.isActive,
|
|
1144
|
+
};
|
|
1145
|
+
// Unpack configuration fields with config_ prefix
|
|
1146
|
+
if (rule.configuration) {
|
|
1147
|
+
Object.entries(rule.configuration).forEach(([key, value]) => {
|
|
1148
|
+
formData[`config_${key}`] = value;
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
return formData;
|
|
1152
|
+
}
|
|
1153
|
+
packFormToCreateDto(formValue) {
|
|
1154
|
+
return {
|
|
1155
|
+
key: formValue['key'],
|
|
1156
|
+
name: {
|
|
1157
|
+
en: formValue['name_en'],
|
|
1158
|
+
ar: formValue['name_ar'],
|
|
1159
|
+
},
|
|
1160
|
+
description: formValue['description_en'] || formValue['description_ar']
|
|
1161
|
+
? {
|
|
1162
|
+
en: formValue['description_en'] ?? '',
|
|
1163
|
+
ar: formValue['description_ar'] ?? '',
|
|
1164
|
+
}
|
|
1165
|
+
: undefined,
|
|
1166
|
+
errorMessage: {
|
|
1167
|
+
en: formValue['errorMessage_en'],
|
|
1168
|
+
ar: formValue['errorMessage_ar'],
|
|
1169
|
+
},
|
|
1170
|
+
ruleType: formValue['ruleType'],
|
|
1171
|
+
targetScope: formValue['targetScope'],
|
|
1172
|
+
targetModuleKey: formValue['targetModuleKey'] || undefined,
|
|
1173
|
+
targetOperationKey: formValue['targetOperationKey'] || undefined,
|
|
1174
|
+
configuration: this.extractConfiguration(formValue),
|
|
1175
|
+
priority: formValue['priority'],
|
|
1176
|
+
stopOnFailure: formValue['stopOnFailure'] ?? true,
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
packFormToUpdateDto(formValue) {
|
|
1180
|
+
return {
|
|
1181
|
+
...this.packFormToCreateDto(formValue),
|
|
1182
|
+
isActive: formValue['isActive'] ?? true,
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
extractConfiguration(formValue) {
|
|
1186
|
+
const config = {};
|
|
1187
|
+
Object.entries(formValue).forEach(([key, value]) => {
|
|
1188
|
+
if (key.startsWith('config_') &&
|
|
1189
|
+
value !== null &&
|
|
1190
|
+
value !== undefined &&
|
|
1191
|
+
value !== '') {
|
|
1192
|
+
const configKey = key.replace('config_', '');
|
|
1193
|
+
config[configKey] = value;
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
return Object.keys(config).length > 0 ? config : undefined;
|
|
1197
|
+
}
|
|
1198
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceRuleForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1199
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: GovernanceRuleForm, isStandalone: true, selector: "mt-governance-rule-form", inputs: { ruleForEdit: { classPropertyName: "ruleForEdit", publicName: "ruleForEdit", isSignal: true, isRequired: false, transformFunction: null } }, providers: [DialogService], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'governance'\">\r\n <div [class]=\"'flex flex-col gap-3 p-4 overflow-y-auto ' + modal.contentClass\">\r\n @if (isLoadingRule()) {\r\n <p-skeleton class=\"my-4 mt-7\" height=\"3rem\" />\r\n <p-skeleton class=\"my-4\" height=\"3rem\" />\r\n <p-skeleton class=\"my-4\" height=\"6rem\" />\r\n <p-skeleton class=\"my-4\" height=\"3rem\" />\r\n <p-skeleton class=\"my-4\" height=\"3rem\" />\r\n } @else {\r\n <mt-dynamic-form\r\n [formConfig]=\"formConfig()\"\r\n [formControl]=\"ruleFormControl\"\r\n />\r\n }\r\n </div>\r\n\r\n <div [class]=\"modal.footerClass\">\r\n <mt-button\r\n [label]=\"'cancel' | transloco\"\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n (click)=\"ref.close()\"\r\n />\r\n <mt-button\r\n [label]=\"ruleForEdit() ? t('update') : t('create')\"\r\n [loading]=\"isAddingRule() || isUpdatingRule()\"\r\n [disabled]=\"!ruleFormControl.valid\"\r\n (click)=\"onSubmit()\"\r\n />\r\n </div>\r\n</ng-container>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: DynamicForm, selector: "mt-dynamic-form", inputs: ["formConfig"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
1200
|
+
}
|
|
1201
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceRuleForm, decorators: [{
|
|
1202
|
+
type: Component,
|
|
1203
|
+
args: [{ selector: 'mt-governance-rule-form', standalone: true, imports: [
|
|
1204
|
+
CommonModule,
|
|
1205
|
+
Button,
|
|
1206
|
+
TranslocoDirective,
|
|
1207
|
+
SkeletonModule,
|
|
1208
|
+
DynamicForm,
|
|
1209
|
+
ReactiveFormsModule,
|
|
1210
|
+
TranslocoPipe,
|
|
1211
|
+
], providers: [DialogService], template: "<ng-container *transloco=\"let t; prefix: 'governance'\">\r\n <div [class]=\"'flex flex-col gap-3 p-4 overflow-y-auto ' + modal.contentClass\">\r\n @if (isLoadingRule()) {\r\n <p-skeleton class=\"my-4 mt-7\" height=\"3rem\" />\r\n <p-skeleton class=\"my-4\" height=\"3rem\" />\r\n <p-skeleton class=\"my-4\" height=\"6rem\" />\r\n <p-skeleton class=\"my-4\" height=\"3rem\" />\r\n <p-skeleton class=\"my-4\" height=\"3rem\" />\r\n } @else {\r\n <mt-dynamic-form\r\n [formConfig]=\"formConfig()\"\r\n [formControl]=\"ruleFormControl\"\r\n />\r\n }\r\n </div>\r\n\r\n <div [class]=\"modal.footerClass\">\r\n <mt-button\r\n [label]=\"'cancel' | transloco\"\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n (click)=\"ref.close()\"\r\n />\r\n <mt-button\r\n [label]=\"ruleForEdit() ? t('update') : t('create')\"\r\n [loading]=\"isAddingRule() || isUpdatingRule()\"\r\n [disabled]=\"!ruleFormControl.valid\"\r\n (click)=\"onSubmit()\"\r\n />\r\n </div>\r\n</ng-container>\r\n" }]
|
|
1212
|
+
}], ctorParameters: () => [], propDecorators: { ruleForEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "ruleForEdit", required: false }] }] } });
|
|
1213
|
+
|
|
1214
|
+
class GovernanceRulesList {
|
|
1215
|
+
// ============================================================================
|
|
1216
|
+
// Input - levelId from parent page
|
|
1217
|
+
// ============================================================================
|
|
1218
|
+
levelId = input.required(...(ngDevMode ? [{ debugName: "levelId" }] : []));
|
|
1219
|
+
// ============================================================================
|
|
1220
|
+
// Template References for Custom Columns
|
|
1221
|
+
// ============================================================================
|
|
1222
|
+
typeCol = viewChild.required('typeCol');
|
|
1223
|
+
scopeCol = viewChild.required('scopeCol');
|
|
1224
|
+
priorityCol = viewChild.required('priorityCol');
|
|
1225
|
+
// ============================================================================
|
|
1226
|
+
// Services
|
|
1227
|
+
// ============================================================================
|
|
1228
|
+
facade = inject(GovernanceFacade);
|
|
1229
|
+
modal = inject(ModalService);
|
|
1230
|
+
translocoService = inject(TranslocoService);
|
|
1231
|
+
// ============================================================================
|
|
1232
|
+
// Breadcrumb
|
|
1233
|
+
// ============================================================================
|
|
1234
|
+
breadcrumbItems = linkedSignal(() => [
|
|
1235
|
+
{
|
|
1236
|
+
label: '',
|
|
1237
|
+
icon: 'general.home-line',
|
|
1238
|
+
routerLink: '/control-panel/workspaces',
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
label: this.translocoService.translate('governance.control-panel'),
|
|
1242
|
+
routerLink: '/control-panel',
|
|
1243
|
+
},
|
|
1244
|
+
{
|
|
1245
|
+
label: this.translocoService.translate('governance.governance-rules'),
|
|
1246
|
+
},
|
|
1247
|
+
], ...(ngDevMode ? [{ debugName: "breadcrumbItems" }] : []));
|
|
1248
|
+
// ============================================================================
|
|
1249
|
+
// Data Computed from Facade
|
|
1250
|
+
// ============================================================================
|
|
1251
|
+
rules = computed(() => {
|
|
1252
|
+
const tab = this.activeTab();
|
|
1253
|
+
const all = this.allRules();
|
|
1254
|
+
const activeRules = this.activeRules();
|
|
1255
|
+
const inactiveRules = this.inactiveRules();
|
|
1256
|
+
switch (tab) {
|
|
1257
|
+
case 'active':
|
|
1258
|
+
return activeRules;
|
|
1259
|
+
case 'inactive':
|
|
1260
|
+
return inactiveRules;
|
|
1261
|
+
case 'all':
|
|
1262
|
+
default:
|
|
1263
|
+
return all;
|
|
1264
|
+
}
|
|
1265
|
+
}, ...(ngDevMode ? [{ debugName: "rules" }] : []));
|
|
1266
|
+
// ============================================================================
|
|
1267
|
+
// Tabs
|
|
1268
|
+
// ============================================================================
|
|
1269
|
+
tabs = signal([
|
|
1270
|
+
{
|
|
1271
|
+
label: this.translocoService.translate('governance.all'),
|
|
1272
|
+
value: 'all',
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
label: this.translocoService.translate('governance.active'),
|
|
1276
|
+
value: 'active',
|
|
1277
|
+
},
|
|
1278
|
+
{
|
|
1279
|
+
label: this.translocoService.translate('governance.inactive'),
|
|
1280
|
+
value: 'inactive',
|
|
1281
|
+
},
|
|
1282
|
+
], ...(ngDevMode ? [{ debugName: "tabs" }] : []));
|
|
1283
|
+
activeTab = signal('all', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
|
|
1284
|
+
// ============================================================================
|
|
1285
|
+
// Table Actions
|
|
1286
|
+
// ============================================================================
|
|
1287
|
+
tableActions = signal([
|
|
1288
|
+
{
|
|
1289
|
+
icon: 'general.plus',
|
|
1290
|
+
label: this.translocoService.translate('governance.add-rule'),
|
|
1291
|
+
color: 'primary',
|
|
1292
|
+
action: () => {
|
|
1293
|
+
this.openRuleDialog();
|
|
1294
|
+
},
|
|
1295
|
+
},
|
|
1296
|
+
], ...(ngDevMode ? [{ debugName: "tableActions" }] : []));
|
|
1297
|
+
deletingRowIds = signal([], ...(ngDevMode ? [{ debugName: "deletingRowIds" }] : []));
|
|
1298
|
+
rowActions = signal([
|
|
1299
|
+
{
|
|
1300
|
+
icon: 'custom.pencil',
|
|
1301
|
+
tooltip: this.translocoService.translate('edit'),
|
|
1302
|
+
color: 'primary',
|
|
1303
|
+
action: (row) => {
|
|
1304
|
+
this.facade.clearSelectedRule();
|
|
1305
|
+
this.openRuleDialog(row);
|
|
1306
|
+
},
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
icon: 'general.trash-01',
|
|
1310
|
+
tooltip: this.translocoService.translate('delete'),
|
|
1311
|
+
color: 'danger',
|
|
1312
|
+
variant: 'outlined',
|
|
1313
|
+
action: (row) => {
|
|
1314
|
+
this.deletingRowIds.update((ids) => [...ids, row.id]);
|
|
1315
|
+
this.facade
|
|
1316
|
+
.deleteRule(row.id)
|
|
1317
|
+
.pipe(finalize(() => {
|
|
1318
|
+
this.deletingRowIds.update((ids) => ids.filter((id) => id !== row.id));
|
|
1319
|
+
}))
|
|
1320
|
+
.subscribe();
|
|
1321
|
+
},
|
|
1322
|
+
confirmation: {
|
|
1323
|
+
type: 'popup',
|
|
1324
|
+
confirmationType: 'delete',
|
|
1325
|
+
},
|
|
1326
|
+
loading: (row) => this.deletingRowIds().includes(row.id),
|
|
1327
|
+
},
|
|
1328
|
+
], ...(ngDevMode ? [{ debugName: "rowActions" }] : []));
|
|
1329
|
+
// ============================================================================
|
|
1330
|
+
// Table Columns
|
|
1331
|
+
// ============================================================================
|
|
1332
|
+
tableColumns = linkedSignal(() => [
|
|
1333
|
+
{
|
|
1334
|
+
key: 'priority',
|
|
1335
|
+
label: this.translocoService.translate('governance.priority'),
|
|
1336
|
+
type: 'custom',
|
|
1337
|
+
customCellTpl: this.priorityCol(),
|
|
1338
|
+
sortable: true,
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
key: 'name.en',
|
|
1342
|
+
label: this.translocoService.translate('governance.name'),
|
|
1343
|
+
type: 'text',
|
|
1344
|
+
sortable: true,
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
key: 'ruleTypeKey',
|
|
1348
|
+
label: this.translocoService.translate('governance.rule-type'),
|
|
1349
|
+
type: 'custom',
|
|
1350
|
+
customCellTpl: this.typeCol(),
|
|
1351
|
+
filterConfig: {
|
|
1352
|
+
type: 'select',
|
|
1353
|
+
label: this.translocoService.translate('governance.rule-type'),
|
|
1354
|
+
options: this.getRuleTypeOptions(),
|
|
1355
|
+
},
|
|
1356
|
+
},
|
|
1357
|
+
{
|
|
1358
|
+
key: 'targetScope',
|
|
1359
|
+
label: this.translocoService.translate('governance.scope'),
|
|
1360
|
+
type: 'custom',
|
|
1361
|
+
customCellTpl: this.scopeCol(),
|
|
1362
|
+
filterConfig: {
|
|
1363
|
+
type: 'select',
|
|
1364
|
+
label: this.translocoService.translate('governance.scope'),
|
|
1365
|
+
options: [
|
|
1366
|
+
{ label: 'Level', value: 'Level' },
|
|
1367
|
+
{ label: 'Module', value: 'Module' },
|
|
1368
|
+
{ label: 'PhaseGate', value: 'PhaseGate' },
|
|
1369
|
+
],
|
|
1370
|
+
},
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
key: 'targetModuleKey',
|
|
1374
|
+
label: this.translocoService.translate('governance.target-module'),
|
|
1375
|
+
type: 'text',
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
key: 'isActive',
|
|
1379
|
+
label: this.translocoService.translate('governance.status'),
|
|
1380
|
+
type: 'boolean',
|
|
1381
|
+
filterConfig: {
|
|
1382
|
+
type: 'select',
|
|
1383
|
+
label: this.translocoService.translate('governance.status'),
|
|
1384
|
+
options: [
|
|
1385
|
+
{
|
|
1386
|
+
label: this.translocoService.translate('governance.active'),
|
|
1387
|
+
value: true,
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
label: this.translocoService.translate('governance.inactive'),
|
|
1391
|
+
value: false,
|
|
1392
|
+
},
|
|
1393
|
+
],
|
|
1394
|
+
},
|
|
1395
|
+
},
|
|
1396
|
+
], ...(ngDevMode ? [{ debugName: "tableColumns" }] : []));
|
|
1397
|
+
// ============================================================================
|
|
1398
|
+
// Private
|
|
1399
|
+
// ============================================================================
|
|
1400
|
+
loading = this.facade.isLoadingRules;
|
|
1401
|
+
allRules = this.facade.rules;
|
|
1402
|
+
activeRules = this.facade.activeRules;
|
|
1403
|
+
inactiveRules = this.facade.inactiveRules;
|
|
1404
|
+
ngOnDestroy() {
|
|
1405
|
+
this.facade.resetState();
|
|
1406
|
+
}
|
|
1407
|
+
openRuleDialog(rule = null) {
|
|
1408
|
+
const modalType = rule ? 'drawer' : 'drawer';
|
|
1409
|
+
this.modal.openModal(GovernanceRuleForm, modalType, {
|
|
1410
|
+
header: rule
|
|
1411
|
+
? this.translocoService.translate('governance.edit-rule')
|
|
1412
|
+
: this.translocoService.translate('governance.add-rule'),
|
|
1413
|
+
styleClass: '!w-[50rem] !absolute ',
|
|
1414
|
+
position: modalType === 'drawer' ? 'end' : '',
|
|
1415
|
+
appendTo: modalType === 'drawer' ? 'page-content' : 'body',
|
|
1416
|
+
dismissableMask: true,
|
|
1417
|
+
dismissible: true,
|
|
1418
|
+
inputValues: {
|
|
1419
|
+
ruleForEdit: rule,
|
|
1420
|
+
},
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
onCellChange(event) {
|
|
1424
|
+
if (event.column === 'isActive' && event.type === 'boolean') {
|
|
1425
|
+
this.facade.toggleRuleActive(event.row.id);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
getRuleTypeOptions() {
|
|
1429
|
+
return [
|
|
1430
|
+
{ label: 'Module Exists', value: 'ModuleExists' },
|
|
1431
|
+
{ label: 'Module Does Not Exist', value: 'ModuleDoesNotExist' },
|
|
1432
|
+
{ label: 'Module Max Count', value: 'ModuleMaxCount' },
|
|
1433
|
+
{ label: 'Module Min Count', value: 'ModuleMinCount' },
|
|
1434
|
+
{ label: 'Field Required', value: 'FieldRequired' },
|
|
1435
|
+
{ label: 'Field Value', value: 'FieldValue' },
|
|
1436
|
+
{ label: 'Phase Gate Module Required', value: 'PhaseGateModuleRequired' },
|
|
1437
|
+
{ label: 'No Active Request', value: 'NoActiveRequest' },
|
|
1438
|
+
];
|
|
1439
|
+
}
|
|
1440
|
+
getRuleTypeChipClass(ruleTypeKey) {
|
|
1441
|
+
const classes = {
|
|
1442
|
+
ModuleExists: 'bg-blue-100 text-blue-700',
|
|
1443
|
+
ModuleDoesNotExist: 'bg-red-100 text-red-700',
|
|
1444
|
+
ModuleMaxCount: 'bg-amber-100 text-amber-700',
|
|
1445
|
+
ModuleMinCount: 'bg-orange-100 text-orange-700',
|
|
1446
|
+
FieldRequired: 'bg-purple-100 text-purple-700',
|
|
1447
|
+
FieldValue: 'bg-indigo-100 text-indigo-700',
|
|
1448
|
+
PhaseGateModuleRequired: 'bg-green-100 text-green-700',
|
|
1449
|
+
NoActiveRequest: 'bg-rose-100 text-rose-700',
|
|
1450
|
+
};
|
|
1451
|
+
return classes[ruleTypeKey] ?? 'bg-slate-100 text-slate-700';
|
|
1452
|
+
}
|
|
1453
|
+
getScopeIcon(scope) {
|
|
1454
|
+
const icons = {
|
|
1455
|
+
Level: 'general.layers-three-01',
|
|
1456
|
+
Module: 'general.cube-01',
|
|
1457
|
+
PhaseGate: 'general.flag-01',
|
|
1458
|
+
};
|
|
1459
|
+
return icons[scope] ?? 'general.file-02';
|
|
1460
|
+
}
|
|
1461
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceRulesList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1462
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: GovernanceRulesList, isStandalone: true, selector: "mt-governance-rules-list", inputs: { levelId: { classPropertyName: "levelId", publicName: "levelId", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "typeCol", first: true, predicate: ["typeCol"], descendants: true, isSignal: true }, { propertyName: "scopeCol", first: true, predicate: ["scopeCol"], descendants: true, isSignal: true }, { propertyName: "priorityCol", first: true, predicate: ["priorityCol"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'governance'\">\r\n <div class=\"space-y-4\">\r\n <div class=\"flex items-center justify-between\">\r\n <div class=\"space-y-1\">\r\n <h1 class=\"text-xl font-semibold text-slate-900 tracking-tight\">\r\n {{ t(\"governance-rules\") }}\r\n </h1>\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n [styleClass]=\"'flex justify-start mx-1'\"\r\n ></mt-breadcrumb>\r\n </div>\r\n </div>\r\n\r\n <!-- Rule Type Column Template -->\r\n <ng-template #typeCol let-row>\r\n <mt-chip\r\n [label]=\"row.ruleTypeKey\"\r\n [styleClass]=\"getRuleTypeChipClass(row.ruleTypeKey)\"\r\n />\r\n </ng-template>\r\n\r\n <!-- Scope Column Template -->\r\n <ng-template #scopeCol let-row>\r\n <div class=\"flex items-center gap-2\">\r\n <mt-icon\r\n class=\"text-lg\"\r\n [icon]=\"getScopeIcon(row.targetScope)\"\r\n ></mt-icon>\r\n <span>{{ row.targetScope }}</span>\r\n @if (row.targetModuleKey) {\r\n <span class=\"text-slate-500\">\u2192 {{ row.targetModuleKey }}</span>\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Priority Column Template -->\r\n <ng-template #priorityCol let-row>\r\n <div\r\n class=\"flex items-center justify-center w-8 h-8 rounded-full bg-slate-100 text-slate-700 font-semibold text-sm\"\r\n >\r\n {{ row.priority }}\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Table -->\r\n <mt-table\r\n [tabs]=\"tabs()\"\r\n [(activeTab)]=\"activeTab\"\r\n [data]=\"rules()\"\r\n [columns]=\"tableColumns()\"\r\n [actions]=\"tableActions()\"\r\n [rowActions]=\"rowActions()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [loading]=\"loading()\"\r\n (cellChange)=\"onCellChange($event)\"\r\n >\r\n </mt-table>\r\n </div>\r\n</ng-container>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "generalSearch", "showFilters", "loading", "updating", "lazy", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "exportable", "exportFilename", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "pageSize", "currentPage", "first", "filterTerm"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: Chip, selector: "mt-chip", inputs: ["label", "icon", "image", "removable", "removeIcon", "styleClass"], outputs: ["onRemove", "onImageError"] }] });
|
|
1463
|
+
}
|
|
1464
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: GovernanceRulesList, decorators: [{
|
|
1465
|
+
type: Component,
|
|
1466
|
+
args: [{ selector: 'mt-governance-rules-list', standalone: true, imports: [
|
|
1467
|
+
CommonModule,
|
|
1468
|
+
SkeletonModule,
|
|
1469
|
+
Table,
|
|
1470
|
+
TranslocoDirective,
|
|
1471
|
+
Breadcrumb,
|
|
1472
|
+
Icon,
|
|
1473
|
+
Chip,
|
|
1474
|
+
], template: "<ng-container *transloco=\"let t; prefix: 'governance'\">\r\n <div class=\"space-y-4\">\r\n <div class=\"flex items-center justify-between\">\r\n <div class=\"space-y-1\">\r\n <h1 class=\"text-xl font-semibold text-slate-900 tracking-tight\">\r\n {{ t(\"governance-rules\") }}\r\n </h1>\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n [styleClass]=\"'flex justify-start mx-1'\"\r\n ></mt-breadcrumb>\r\n </div>\r\n </div>\r\n\r\n <!-- Rule Type Column Template -->\r\n <ng-template #typeCol let-row>\r\n <mt-chip\r\n [label]=\"row.ruleTypeKey\"\r\n [styleClass]=\"getRuleTypeChipClass(row.ruleTypeKey)\"\r\n />\r\n </ng-template>\r\n\r\n <!-- Scope Column Template -->\r\n <ng-template #scopeCol let-row>\r\n <div class=\"flex items-center gap-2\">\r\n <mt-icon\r\n class=\"text-lg\"\r\n [icon]=\"getScopeIcon(row.targetScope)\"\r\n ></mt-icon>\r\n <span>{{ row.targetScope }}</span>\r\n @if (row.targetModuleKey) {\r\n <span class=\"text-slate-500\">\u2192 {{ row.targetModuleKey }}</span>\r\n }\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Priority Column Template -->\r\n <ng-template #priorityCol let-row>\r\n <div\r\n class=\"flex items-center justify-center w-8 h-8 rounded-full bg-slate-100 text-slate-700 font-semibold text-sm\"\r\n >\r\n {{ row.priority }}\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Table -->\r\n <mt-table\r\n [tabs]=\"tabs()\"\r\n [(activeTab)]=\"activeTab\"\r\n [data]=\"rules()\"\r\n [columns]=\"tableColumns()\"\r\n [actions]=\"tableActions()\"\r\n [rowActions]=\"rowActions()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [loading]=\"loading()\"\r\n (cellChange)=\"onCellChange($event)\"\r\n >\r\n </mt-table>\r\n </div>\r\n</ng-container>\r\n" }]
|
|
1475
|
+
}], propDecorators: { levelId: [{ type: i0.Input, args: [{ isSignal: true, alias: "levelId", required: true }] }], typeCol: [{ type: i0.ViewChild, args: ['typeCol', { isSignal: true }] }], scopeCol: [{ type: i0.ViewChild, args: ['scopeCol', { isSignal: true }] }], priorityCol: [{ type: i0.ViewChild, args: ['priorityCol', { isSignal: true }] }] } });
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* Generated bundle index. Do not edit.
|
|
1479
|
+
*/
|
|
1480
|
+
|
|
1481
|
+
export { AddGovernanceRule, ClearProperties, ClearRequestSchemas, ClearSelectedRule, DeleteGovernanceRule, GetGovernanceRule, GetGovernanceRuleTypes, GetGovernanceRules, GetModules, GetProperties, GetRequestSchemas, GovernanceActionKey, GovernanceFacade, GovernanceRuleForm, GovernanceRuleTypeEnum, GovernanceRulesList, GovernanceState, ResetGovernanceState, SetLevelId, ToggleGovernanceRuleActive, UpdateGovernanceRule };
|
|
1482
|
+
//# sourceMappingURL=masterteam-governance.mjs.map
|