@masterteam/workflow 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/i18n/ar.json +27 -4
- package/assets/i18n/en.json +24 -1
- package/assets/workflow.css +2 -2
- package/fesm2022/masterteam-workflow.mjs +1371 -450
- package/fesm2022/masterteam-workflow.mjs.map +1 -1
- package/package.json +6 -6
- package/types/masterteam-workflow.d.ts +237 -75
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable, computed, Component, signal,
|
|
3
|
-
import {
|
|
4
|
-
import { StructureBuilder } from '@masterteam/structure-builder';
|
|
5
|
-
import { Action, Selector, State, Store, select } from '@ngxs/store';
|
|
6
|
-
import { HttpClient, HttpContextToken } from '@angular/common/http';
|
|
7
|
-
import { of } from 'rxjs';
|
|
8
|
-
import { tap, catchError, finalize } from 'rxjs/operators';
|
|
9
|
-
import { Tabs } from '@masterteam/components/tabs';
|
|
2
|
+
import { inject, Injectable, computed, Component, signal, effect } from '@angular/core';
|
|
3
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
10
4
|
import * as i1 from '@angular/forms';
|
|
11
|
-
import { FormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
12
|
-
import { DynamicForm } from '@masterteam/forms/dynamic-form';
|
|
13
|
-
import { Skeleton } from 'primeng/skeleton';
|
|
14
|
-
import { ToggleField } from '@masterteam/components/toggle-field';
|
|
15
|
-
import { Button } from '@masterteam/components/button';
|
|
5
|
+
import { FormGroup, FormControl, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
16
6
|
import * as i2 from '@jsverse/transloco';
|
|
17
7
|
import { TranslocoService, TranslocoModule } from '@jsverse/transloco';
|
|
8
|
+
import { Button } from '@masterteam/components/button';
|
|
9
|
+
import { Card } from '@masterteam/components/card';
|
|
18
10
|
import { ConfirmationService } from '@masterteam/components/confirmation';
|
|
19
|
-
import { NotificationTemplate, NotificationFacade } from '@masterteam/notification';
|
|
20
11
|
import { ModalService } from '@masterteam/components/modal';
|
|
12
|
+
import { NumberField } from '@masterteam/components/number-field';
|
|
21
13
|
import { SelectField } from '@masterteam/components/select-field';
|
|
14
|
+
import { TextField } from '@masterteam/components/text-field';
|
|
15
|
+
import { TextareaField } from '@masterteam/components/textarea-field';
|
|
16
|
+
import { ToggleField } from '@masterteam/components/toggle-field';
|
|
22
17
|
import { FormBuilder, FormBuilderFacade } from '@masterteam/form-builder';
|
|
18
|
+
import { NotificationTemplate, NotificationFacade } from '@masterteam/notification';
|
|
19
|
+
import { StructureBuilder } from '@masterteam/structure-builder';
|
|
20
|
+
import { Tabs } from '@masterteam/components/tabs';
|
|
21
|
+
import { Skeleton } from 'primeng/skeleton';
|
|
22
|
+
import { Action, Selector, State, Store, select } from '@ngxs/store';
|
|
23
|
+
import { HttpClient, HttpContextToken } from '@angular/common/http';
|
|
24
|
+
import { of } from 'rxjs';
|
|
25
|
+
import { tap, catchError, finalize } from 'rxjs/operators';
|
|
23
26
|
|
|
24
27
|
class SetModuleInfo {
|
|
25
28
|
moduleType;
|
|
@@ -55,6 +58,16 @@ class GetGroups {
|
|
|
55
58
|
class GetRolesForModule {
|
|
56
59
|
static type = '[Workflow] Get Roles For Module';
|
|
57
60
|
}
|
|
61
|
+
class GetAppActions {
|
|
62
|
+
static type = '[Workflow] Get App Actions';
|
|
63
|
+
}
|
|
64
|
+
class GetAppActionDetail {
|
|
65
|
+
actionKey;
|
|
66
|
+
static type = '[Workflow] Get App Action Detail';
|
|
67
|
+
constructor(actionKey) {
|
|
68
|
+
this.actionKey = actionKey;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
58
71
|
class GetStep {
|
|
59
72
|
stepId;
|
|
60
73
|
static type = '[Workflow] Get Step';
|
|
@@ -161,6 +174,8 @@ const DEFAULT_STATE = {
|
|
|
161
174
|
formulaProperties: [],
|
|
162
175
|
groups: [],
|
|
163
176
|
roles: [],
|
|
177
|
+
appActionDescriptors: [],
|
|
178
|
+
selectedAppActionDescriptor: null,
|
|
164
179
|
selectedStep: null,
|
|
165
180
|
isFlowValid: null,
|
|
166
181
|
loadingActive: [],
|
|
@@ -171,6 +186,7 @@ let WorkflowState = class WorkflowState {
|
|
|
171
186
|
baseUrl = 'ProcessBuilder';
|
|
172
187
|
groupsUrl = 'identity/Groups';
|
|
173
188
|
rolesUrl = 'identity/roles/scopes';
|
|
189
|
+
workflowAppActionsUrl = 'workflow-app-actions';
|
|
174
190
|
static workflowId(state) {
|
|
175
191
|
return state.workflowId;
|
|
176
192
|
}
|
|
@@ -195,6 +211,12 @@ let WorkflowState = class WorkflowState {
|
|
|
195
211
|
static roles(state) {
|
|
196
212
|
return state.roles;
|
|
197
213
|
}
|
|
214
|
+
static appActionDescriptors(state) {
|
|
215
|
+
return state.appActionDescriptors;
|
|
216
|
+
}
|
|
217
|
+
static selectedAppActionDescriptor(state) {
|
|
218
|
+
return state.selectedAppActionDescriptor;
|
|
219
|
+
}
|
|
198
220
|
static selectedStep(state) {
|
|
199
221
|
return state.selectedStep;
|
|
200
222
|
}
|
|
@@ -276,7 +298,9 @@ let WorkflowState = class WorkflowState {
|
|
|
276
298
|
params: { Mode: 'edit' },
|
|
277
299
|
})
|
|
278
300
|
.pipe(tap((response) => {
|
|
279
|
-
const workflow = response?.data
|
|
301
|
+
const workflow = response?.data
|
|
302
|
+
? normalizeWorkflowSchema(response.data)
|
|
303
|
+
: null;
|
|
280
304
|
ctx.patchState({
|
|
281
305
|
workflow,
|
|
282
306
|
isFlowValid: workflow?.isValid ?? null,
|
|
@@ -353,6 +377,56 @@ let WorkflowState = class WorkflowState {
|
|
|
353
377
|
return of([]);
|
|
354
378
|
}), finalize(() => endLoading(ctx, 'getRolesForModule')));
|
|
355
379
|
}
|
|
380
|
+
getAppActions(ctx, _action) {
|
|
381
|
+
const state = ctx.getState();
|
|
382
|
+
if (state.appActionDescriptors.length) {
|
|
383
|
+
return of(state.appActionDescriptors);
|
|
384
|
+
}
|
|
385
|
+
startLoading(ctx, 'getAppActions');
|
|
386
|
+
return this.http
|
|
387
|
+
.get(`${this.workflowAppActionsUrl}/actions`)
|
|
388
|
+
.pipe(tap((response) => {
|
|
389
|
+
const descriptors = normalizeWorkflowAppActionDescriptors(response?.data);
|
|
390
|
+
ctx.patchState({
|
|
391
|
+
appActionDescriptors: descriptors,
|
|
392
|
+
});
|
|
393
|
+
}), catchError((error) => {
|
|
394
|
+
const message = error?.error?.message ??
|
|
395
|
+
error?.message ??
|
|
396
|
+
'Failed to load app actions';
|
|
397
|
+
setLoadingError(ctx, 'getAppActions', message);
|
|
398
|
+
return of([]);
|
|
399
|
+
}), finalize(() => endLoading(ctx, 'getAppActions')));
|
|
400
|
+
}
|
|
401
|
+
getAppActionDetail(ctx, action) {
|
|
402
|
+
const state = ctx.getState();
|
|
403
|
+
const cachedDescriptor = state.appActionDescriptors.find((descriptor) => descriptor.actionKey === action.actionKey &&
|
|
404
|
+
!!descriptor.configSchemaJson);
|
|
405
|
+
if (cachedDescriptor) {
|
|
406
|
+
ctx.patchState({
|
|
407
|
+
selectedAppActionDescriptor: cachedDescriptor,
|
|
408
|
+
});
|
|
409
|
+
return of(cachedDescriptor);
|
|
410
|
+
}
|
|
411
|
+
startLoading(ctx, 'getAppActionDetail');
|
|
412
|
+
return this.http
|
|
413
|
+
.get(`${this.workflowAppActionsUrl}/actions/${encodeURIComponent(action.actionKey)}`)
|
|
414
|
+
.pipe(tap((response) => {
|
|
415
|
+
const descriptor = normalizeWorkflowAppActionDescriptor(response?.data);
|
|
416
|
+
const currentState = ctx.getState();
|
|
417
|
+
const descriptors = upsertWorkflowAppActionDescriptor(currentState.appActionDescriptors, descriptor);
|
|
418
|
+
ctx.patchState({
|
|
419
|
+
appActionDescriptors: descriptors,
|
|
420
|
+
selectedAppActionDescriptor: descriptor,
|
|
421
|
+
});
|
|
422
|
+
}), catchError((error) => {
|
|
423
|
+
const message = error?.error?.message ??
|
|
424
|
+
error?.message ??
|
|
425
|
+
'Failed to load app action details';
|
|
426
|
+
setLoadingError(ctx, 'getAppActionDetail', message);
|
|
427
|
+
return of(null);
|
|
428
|
+
}), finalize(() => endLoading(ctx, 'getAppActionDetail')));
|
|
429
|
+
}
|
|
356
430
|
getStep(ctx, action) {
|
|
357
431
|
startLoading(ctx, 'getStep');
|
|
358
432
|
return this.http
|
|
@@ -360,7 +434,9 @@ let WorkflowState = class WorkflowState {
|
|
|
360
434
|
params: { Mode: 'edit' },
|
|
361
435
|
})
|
|
362
436
|
.pipe(tap((response) => {
|
|
363
|
-
const selectedStep = response?.data
|
|
437
|
+
const selectedStep = response?.data
|
|
438
|
+
? normalizeWorkflowStepDetail(response.data)
|
|
439
|
+
: null;
|
|
364
440
|
ctx.patchState({ selectedStep });
|
|
365
441
|
}), catchError((error) => {
|
|
366
442
|
const message = error?.error?.message ?? error?.message ?? 'Failed to load step';
|
|
@@ -400,11 +476,11 @@ let WorkflowState = class WorkflowState {
|
|
|
400
476
|
return of(null);
|
|
401
477
|
}
|
|
402
478
|
const tempId = -Date.now();
|
|
403
|
-
const tempStep = {
|
|
479
|
+
const tempStep = normalizeWorkflowStepSchema({
|
|
404
480
|
...action.payload,
|
|
405
481
|
id: tempId,
|
|
406
482
|
loading: true,
|
|
407
|
-
};
|
|
483
|
+
});
|
|
408
484
|
if (state.workflow) {
|
|
409
485
|
ctx.patchState({
|
|
410
486
|
workflow: {
|
|
@@ -417,10 +493,16 @@ let WorkflowState = class WorkflowState {
|
|
|
417
493
|
return this.http
|
|
418
494
|
.post(`${this.baseUrl}/requestSchema/${workflowId}/stepSchema`, action.payload)
|
|
419
495
|
.pipe(tap((response) => {
|
|
420
|
-
const createdStep = response?.data
|
|
496
|
+
const createdStep = response?.data
|
|
497
|
+
? normalizeWorkflowStepSchema({
|
|
498
|
+
...tempStep,
|
|
499
|
+
...response.data,
|
|
500
|
+
loading: false,
|
|
501
|
+
})
|
|
502
|
+
: null;
|
|
421
503
|
const currentState = ctx.getState();
|
|
422
504
|
if (createdStep && currentState.workflow) {
|
|
423
|
-
const updatedSteps = currentState.workflow.stepsSchema.map((step) => step.id === tempId ?
|
|
505
|
+
const updatedSteps = currentState.workflow.stepsSchema.map((step) => (step.id === tempId ? createdStep : step));
|
|
424
506
|
ctx.patchState({
|
|
425
507
|
workflow: {
|
|
426
508
|
...currentState.workflow,
|
|
@@ -468,15 +550,20 @@ let WorkflowState = class WorkflowState {
|
|
|
468
550
|
return this.http
|
|
469
551
|
.put(`${this.baseUrl}/${endpoint}/${action.stepId}`, action.payload)
|
|
470
552
|
.pipe(tap((response) => {
|
|
471
|
-
const updatedStep = response?.data;
|
|
472
553
|
const currentState = ctx.getState();
|
|
554
|
+
const existingStep = currentState.workflow?.stepsSchema.find((step) => step.id === action.stepId) ?? null;
|
|
555
|
+
const updatedStep = response?.data
|
|
556
|
+
? normalizeWorkflowStepSchema({
|
|
557
|
+
...existingStep,
|
|
558
|
+
...response.data,
|
|
559
|
+
loading: false,
|
|
560
|
+
})
|
|
561
|
+
: null;
|
|
473
562
|
if (updatedStep && currentState.workflow) {
|
|
474
563
|
ctx.patchState({
|
|
475
564
|
workflow: {
|
|
476
565
|
...currentState.workflow,
|
|
477
|
-
stepsSchema: currentState.workflow.stepsSchema.map((step) => step.id === updatedStep.id
|
|
478
|
-
? { ...updatedStep, loading: false }
|
|
479
|
-
: step),
|
|
566
|
+
stepsSchema: currentState.workflow.stepsSchema.map((step) => step.id === updatedStep.id ? updatedStep : step),
|
|
480
567
|
},
|
|
481
568
|
});
|
|
482
569
|
}
|
|
@@ -765,6 +852,12 @@ __decorate([
|
|
|
765
852
|
__decorate([
|
|
766
853
|
Action(GetRolesForModule)
|
|
767
854
|
], WorkflowState.prototype, "getRolesForModule", null);
|
|
855
|
+
__decorate([
|
|
856
|
+
Action(GetAppActions)
|
|
857
|
+
], WorkflowState.prototype, "getAppActions", null);
|
|
858
|
+
__decorate([
|
|
859
|
+
Action(GetAppActionDetail)
|
|
860
|
+
], WorkflowState.prototype, "getAppActionDetail", null);
|
|
768
861
|
__decorate([
|
|
769
862
|
Action(GetStep)
|
|
770
863
|
], WorkflowState.prototype, "getStep", null);
|
|
@@ -816,6 +909,12 @@ __decorate([
|
|
|
816
909
|
__decorate([
|
|
817
910
|
Selector()
|
|
818
911
|
], WorkflowState, "roles", null);
|
|
912
|
+
__decorate([
|
|
913
|
+
Selector()
|
|
914
|
+
], WorkflowState, "appActionDescriptors", null);
|
|
915
|
+
__decorate([
|
|
916
|
+
Selector()
|
|
917
|
+
], WorkflowState, "selectedAppActionDescriptor", null);
|
|
819
918
|
__decorate([
|
|
820
919
|
Selector()
|
|
821
920
|
], WorkflowState, "selectedStep", null);
|
|
@@ -842,7 +941,7 @@ WorkflowState = __decorate([
|
|
|
842
941
|
], WorkflowState);
|
|
843
942
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowState, decorators: [{
|
|
844
943
|
type: Injectable
|
|
845
|
-
}], propDecorators: { setModuleInfo: [], getWorkflows: [], getWorkflow: [], getFormulaProperties: [], getGroups: [], getRolesForModule: [], getStep: [], validateFlow: [], createStep: [], updateStep: [], createConnection: [], updateConnection: [], publishWorkflow: [], deleteStep: [], deleteConnection: [] } });
|
|
944
|
+
}], propDecorators: { setModuleInfo: [], getWorkflows: [], getWorkflow: [], getFormulaProperties: [], getGroups: [], getRolesForModule: [], getAppActions: [], getAppActionDetail: [], getStep: [], validateFlow: [], createStep: [], updateStep: [], createConnection: [], updateConnection: [], publishWorkflow: [], deleteStep: [], deleteConnection: [] } });
|
|
846
945
|
function normalizeWorkflowRoles(data) {
|
|
847
946
|
if (!Array.isArray(data)) {
|
|
848
947
|
return [];
|
|
@@ -859,6 +958,175 @@ function normalizeWorkflowRoles(data) {
|
|
|
859
958
|
}
|
|
860
959
|
return data.map(normalizeWorkflowRole).filter(Boolean);
|
|
861
960
|
}
|
|
961
|
+
function normalizeWorkflowSchema(input) {
|
|
962
|
+
return {
|
|
963
|
+
...input,
|
|
964
|
+
stepsSchema: Array.isArray(input?.stepsSchema)
|
|
965
|
+
? input.stepsSchema.map(normalizeWorkflowStepSchema)
|
|
966
|
+
: [],
|
|
967
|
+
connections: Array.isArray(input?.connections) ? input.connections : [],
|
|
968
|
+
properties: Array.isArray(input?.properties) ? input.properties : [],
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
function normalizeWorkflowStepSchema(input) {
|
|
972
|
+
const type = normalizeWorkflowStepType$1(input?.type);
|
|
973
|
+
const appAction = normalizeWorkflowAppAction(input, type);
|
|
974
|
+
return {
|
|
975
|
+
...input,
|
|
976
|
+
id: Number(input?.id ?? 0),
|
|
977
|
+
name: normalizeWorkflowStepName(input?.name),
|
|
978
|
+
sla: Number(input?.sla ?? 0),
|
|
979
|
+
isInitial: !!input?.isInitial,
|
|
980
|
+
type,
|
|
981
|
+
targetType: input?.targetType ?? undefined,
|
|
982
|
+
targetValue: input?.targetValue === null || input?.targetValue === undefined
|
|
983
|
+
? undefined
|
|
984
|
+
: String(input.targetValue),
|
|
985
|
+
appAction,
|
|
986
|
+
loading: !!input?.loading,
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
function normalizeWorkflowStepDetail(input) {
|
|
990
|
+
const type = normalizeWorkflowStepType$1(input?.type);
|
|
991
|
+
const appAction = normalizeWorkflowAppAction(input, type);
|
|
992
|
+
return {
|
|
993
|
+
...input,
|
|
994
|
+
id: Number(input?.id ?? 0),
|
|
995
|
+
name: normalizeWorkflowRecordName(input?.name),
|
|
996
|
+
sla: Number(input?.sla ?? 0),
|
|
997
|
+
isInitial: !!input?.isInitial,
|
|
998
|
+
type,
|
|
999
|
+
targetType: input?.targetType ?? undefined,
|
|
1000
|
+
targetValue: input?.targetValue === null || input?.targetValue === undefined
|
|
1001
|
+
? undefined
|
|
1002
|
+
: String(input.targetValue),
|
|
1003
|
+
properties: Array.isArray(input?.properties) ? input.properties : [],
|
|
1004
|
+
hasNotification: !!input?.hasNotification,
|
|
1005
|
+
hasForm: input?.hasForm === undefined || input?.hasForm === null
|
|
1006
|
+
? undefined
|
|
1007
|
+
: !!input.hasForm,
|
|
1008
|
+
appAction,
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
function normalizeWorkflowStepType$1(type) {
|
|
1012
|
+
return type === 'AppAction' ? 'AppAction' : 'UserInput';
|
|
1013
|
+
}
|
|
1014
|
+
function normalizeWorkflowStepName(name) {
|
|
1015
|
+
if (typeof name === 'string') {
|
|
1016
|
+
return name;
|
|
1017
|
+
}
|
|
1018
|
+
if (name && typeof name === 'object') {
|
|
1019
|
+
return name;
|
|
1020
|
+
}
|
|
1021
|
+
return '';
|
|
1022
|
+
}
|
|
1023
|
+
function normalizeWorkflowRecordName(name) {
|
|
1024
|
+
if (name && typeof name === 'object') {
|
|
1025
|
+
return name;
|
|
1026
|
+
}
|
|
1027
|
+
const label = typeof name === 'string' ? name : '';
|
|
1028
|
+
return {
|
|
1029
|
+
display: label,
|
|
1030
|
+
en: label,
|
|
1031
|
+
ar: label,
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
function normalizeWorkflowAppAction(input, type) {
|
|
1035
|
+
const candidate = isRecord$1(input?.appAction) ? input.appAction : input;
|
|
1036
|
+
const hasAppActionData = !!candidate?.appCode ||
|
|
1037
|
+
!!candidate?.actionKey ||
|
|
1038
|
+
candidate?.configJson !== undefined ||
|
|
1039
|
+
candidate?.failureBehavior !== undefined ||
|
|
1040
|
+
candidate?.timeoutSeconds !== undefined;
|
|
1041
|
+
if (!hasAppActionData && type !== 'AppAction') {
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
return {
|
|
1045
|
+
appCode: typeof candidate?.appCode === 'string' && candidate.appCode.trim().length
|
|
1046
|
+
? candidate.appCode
|
|
1047
|
+
: 'pplus',
|
|
1048
|
+
actionKey: typeof candidate?.actionKey === 'string' ? candidate.actionKey : '',
|
|
1049
|
+
configJson: typeof candidate?.configJson === 'string'
|
|
1050
|
+
? candidate.configJson
|
|
1051
|
+
: safeJsonStringify(candidate?.configJson ?? {}),
|
|
1052
|
+
failureBehavior: typeof candidate?.failureBehavior === 'string'
|
|
1053
|
+
? candidate.failureBehavior
|
|
1054
|
+
: 'Stop',
|
|
1055
|
+
timeoutSeconds: Number(candidate?.timeoutSeconds ?? 300),
|
|
1056
|
+
displayName: typeof candidate?.displayName === 'string' ? candidate.displayName : null,
|
|
1057
|
+
description: typeof candidate?.description === 'string' ? candidate.description : null,
|
|
1058
|
+
configSchemaJson: typeof candidate?.configSchemaJson === 'string'
|
|
1059
|
+
? candidate.configSchemaJson
|
|
1060
|
+
: null,
|
|
1061
|
+
requiredContextKeys: Array.isArray(candidate?.requiredContextKeys)
|
|
1062
|
+
? candidate.requiredContextKeys.map(String)
|
|
1063
|
+
: [],
|
|
1064
|
+
supportedScopes: Array.isArray(candidate?.supportedScopes)
|
|
1065
|
+
? candidate.supportedScopes.map(String)
|
|
1066
|
+
: [],
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
function normalizeWorkflowAppActionDescriptors(data) {
|
|
1070
|
+
return extractCollection(data, ['actions', 'items'])
|
|
1071
|
+
.map(normalizeWorkflowAppActionDescriptor)
|
|
1072
|
+
.filter(Boolean);
|
|
1073
|
+
}
|
|
1074
|
+
function normalizeWorkflowAppActionDescriptor(data) {
|
|
1075
|
+
const record = isRecord$1(data) ? data : {};
|
|
1076
|
+
return {
|
|
1077
|
+
appCode: readString(record, 'appCode') ?? 'pplus',
|
|
1078
|
+
actionKey: readString(record, 'actionKey') ?? '',
|
|
1079
|
+
displayName: readString(record, 'displayName') ??
|
|
1080
|
+
readString(record, 'name') ??
|
|
1081
|
+
readString(record, 'actionKey') ??
|
|
1082
|
+
'',
|
|
1083
|
+
description: readString(record, 'description') ?? null,
|
|
1084
|
+
configSchemaJson: readString(record, 'configSchemaJson') ?? null,
|
|
1085
|
+
requiredContextKeys: Array.isArray(record['requiredContextKeys'])
|
|
1086
|
+
? record['requiredContextKeys'].map(String)
|
|
1087
|
+
: [],
|
|
1088
|
+
supportedScopes: Array.isArray(record['supportedScopes'])
|
|
1089
|
+
? record['supportedScopes'].map(String)
|
|
1090
|
+
: [],
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
function upsertWorkflowAppActionDescriptor(descriptors, descriptor) {
|
|
1094
|
+
const index = descriptors.findIndex((item) => item.actionKey === descriptor.actionKey);
|
|
1095
|
+
if (index < 0) {
|
|
1096
|
+
return [...descriptors, descriptor];
|
|
1097
|
+
}
|
|
1098
|
+
return descriptors.map((item, currentIndex) => currentIndex === index ? { ...item, ...descriptor } : item);
|
|
1099
|
+
}
|
|
1100
|
+
function extractCollection(data, nestedKeys) {
|
|
1101
|
+
if (Array.isArray(data)) {
|
|
1102
|
+
return data.filter(isRecord$1);
|
|
1103
|
+
}
|
|
1104
|
+
if (!isRecord$1(data)) {
|
|
1105
|
+
return [];
|
|
1106
|
+
}
|
|
1107
|
+
for (const key of nestedKeys) {
|
|
1108
|
+
const nestedValue = data[key];
|
|
1109
|
+
if (Array.isArray(nestedValue)) {
|
|
1110
|
+
return nestedValue.filter(isRecord$1);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
return [];
|
|
1114
|
+
}
|
|
1115
|
+
function readString(record, key) {
|
|
1116
|
+
const value = record[key];
|
|
1117
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : null;
|
|
1118
|
+
}
|
|
1119
|
+
function safeJsonStringify(value) {
|
|
1120
|
+
try {
|
|
1121
|
+
return JSON.stringify(value ?? {});
|
|
1122
|
+
}
|
|
1123
|
+
catch {
|
|
1124
|
+
return '{}';
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
function isRecord$1(value) {
|
|
1128
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
1129
|
+
}
|
|
862
1130
|
function normalizeWorkflowRole(role) {
|
|
863
1131
|
const value = role?.value ?? role?.id;
|
|
864
1132
|
return {
|
|
@@ -906,6 +1174,8 @@ class WorkflowFacade {
|
|
|
906
1174
|
formulaProperties = select(WorkflowState.formulaProperties);
|
|
907
1175
|
groups = select(WorkflowState.groups);
|
|
908
1176
|
roles = select(WorkflowState.roles);
|
|
1177
|
+
appActionDescriptors = select(WorkflowState.appActionDescriptors);
|
|
1178
|
+
selectedAppActionDescriptor = select(WorkflowState.selectedAppActionDescriptor);
|
|
909
1179
|
selectedStep = select(WorkflowState.selectedStep);
|
|
910
1180
|
isFlowValid = select(WorkflowState.isFlowValid);
|
|
911
1181
|
steps = select(WorkflowState.stepsSchema);
|
|
@@ -939,6 +1209,12 @@ class WorkflowFacade {
|
|
|
939
1209
|
loadRolesForModule() {
|
|
940
1210
|
return this.store.dispatch(new GetRolesForModule());
|
|
941
1211
|
}
|
|
1212
|
+
loadAppActions() {
|
|
1213
|
+
return this.store.dispatch(new GetAppActions());
|
|
1214
|
+
}
|
|
1215
|
+
loadAppActionDetail(actionKey) {
|
|
1216
|
+
return this.store.dispatch(new GetAppActionDetail(actionKey));
|
|
1217
|
+
}
|
|
942
1218
|
loadStep(stepId) {
|
|
943
1219
|
return this.store.dispatch(new GetStep(stepId));
|
|
944
1220
|
}
|
|
@@ -978,23 +1254,405 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
978
1254
|
|
|
979
1255
|
// shared/models/api.model.ts
|
|
980
1256
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1257
|
+
const SUPPORTED_PROPERTY_TYPES = [
|
|
1258
|
+
'string',
|
|
1259
|
+
'number',
|
|
1260
|
+
'boolean',
|
|
1261
|
+
'array',
|
|
1262
|
+
'object',
|
|
1263
|
+
];
|
|
1264
|
+
function buildWorkflowAppActionConfigEditorDefinition(configSchemaJson) {
|
|
1265
|
+
if (!configSchemaJson?.trim()) {
|
|
1266
|
+
return {
|
|
1267
|
+
mode: 'schema',
|
|
1268
|
+
schema: {
|
|
1269
|
+
type: 'object',
|
|
1270
|
+
properties: [],
|
|
1271
|
+
required: [],
|
|
1272
|
+
},
|
|
1273
|
+
fields: [],
|
|
1274
|
+
errors: [],
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
let parsedSchema;
|
|
1278
|
+
try {
|
|
1279
|
+
parsedSchema = JSON.parse(configSchemaJson);
|
|
1280
|
+
}
|
|
1281
|
+
catch {
|
|
1282
|
+
return buildRawDefinition('Invalid config schema JSON.');
|
|
1283
|
+
}
|
|
1284
|
+
if (!isRecord(parsedSchema) || parsedSchema['type'] !== 'object') {
|
|
1285
|
+
return buildRawDefinition('Config schema must use a root object.');
|
|
1286
|
+
}
|
|
1287
|
+
const propertyEntries = Object.entries(parsedSchema['properties'] ?? {});
|
|
1288
|
+
const requiredKeys = Array.isArray(parsedSchema['required'])
|
|
1289
|
+
? parsedSchema['required'].map(String)
|
|
1290
|
+
: [];
|
|
1291
|
+
const properties = [];
|
|
1292
|
+
for (const [key, rawProperty] of propertyEntries) {
|
|
1293
|
+
if (!isRecord(rawProperty)) {
|
|
1294
|
+
return buildRawDefinition(`Unsupported schema for "${key}".`);
|
|
1295
|
+
}
|
|
1296
|
+
const propertyType = resolvePropertyType(rawProperty);
|
|
1297
|
+
if (!propertyType) {
|
|
1298
|
+
return buildRawDefinition(`Unsupported schema type for "${key}".`);
|
|
1299
|
+
}
|
|
1300
|
+
properties.push({
|
|
1301
|
+
key,
|
|
1302
|
+
title: resolvePropertyTitle(key, rawProperty),
|
|
1303
|
+
description: typeof rawProperty['description'] === 'string'
|
|
1304
|
+
? rawProperty['description']
|
|
1305
|
+
: null,
|
|
1306
|
+
type: propertyType,
|
|
1307
|
+
required: requiredKeys.includes(key),
|
|
1308
|
+
enumValues: Array.isArray(rawProperty['enum'])
|
|
1309
|
+
? rawProperty['enum'].filter(isPrimitiveEnumValue)
|
|
1310
|
+
: undefined,
|
|
1311
|
+
rawSchema: rawProperty,
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
const schema = {
|
|
1315
|
+
type: 'object',
|
|
1316
|
+
properties,
|
|
1317
|
+
required: requiredKeys,
|
|
1318
|
+
};
|
|
1319
|
+
return {
|
|
1320
|
+
mode: 'schema',
|
|
1321
|
+
schema,
|
|
1322
|
+
fields: properties.map(mapSchemaPropertyToField),
|
|
1323
|
+
errors: [],
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
function mapWorkflowAppActionConfigJsonToEditorValue(configJson, definition) {
|
|
1327
|
+
const rawConfigJson = typeof configJson === 'string' && configJson.trim().length
|
|
1328
|
+
? configJson
|
|
1329
|
+
: '{}';
|
|
1330
|
+
if (definition.mode === 'raw' || !definition.schema) {
|
|
1331
|
+
const errors = isValidJson(rawConfigJson) ? [] : ['Invalid config JSON.'];
|
|
1332
|
+
return {
|
|
1333
|
+
fields: {},
|
|
1334
|
+
rawConfigJson,
|
|
1335
|
+
errors,
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
let parsedValue = {};
|
|
1339
|
+
try {
|
|
1340
|
+
const candidate = JSON.parse(rawConfigJson);
|
|
1341
|
+
if (isRecord(candidate)) {
|
|
1342
|
+
parsedValue = candidate;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
catch {
|
|
1346
|
+
return {
|
|
1347
|
+
fields: {},
|
|
1348
|
+
rawConfigJson,
|
|
1349
|
+
errors: ['Invalid config JSON.'],
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
const fields = definition.fields.reduce((result, field) => {
|
|
1353
|
+
const fieldValue = parsedValue[field.key];
|
|
1354
|
+
if (field.kind === 'json') {
|
|
1355
|
+
result[field.key] =
|
|
1356
|
+
fieldValue === undefined ? '' : JSON.stringify(fieldValue, null, 2);
|
|
1357
|
+
return result;
|
|
1358
|
+
}
|
|
1359
|
+
result[field.key] = fieldValue ?? null;
|
|
1360
|
+
return result;
|
|
1361
|
+
}, {});
|
|
1362
|
+
return {
|
|
1363
|
+
fields,
|
|
1364
|
+
rawConfigJson,
|
|
1365
|
+
errors: [],
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
function serializeWorkflowAppActionConfigValue(value, definition, rawConfigJson) {
|
|
1369
|
+
if (definition.mode === 'raw' || !definition.schema) {
|
|
1370
|
+
const normalizedRawConfigJson = typeof rawConfigJson === 'string' && rawConfigJson.trim().length
|
|
1371
|
+
? rawConfigJson
|
|
1372
|
+
: '{}';
|
|
1373
|
+
if (!isValidJson(normalizedRawConfigJson)) {
|
|
1374
|
+
return {
|
|
1375
|
+
configJson: normalizedRawConfigJson,
|
|
1376
|
+
errors: ['Invalid config JSON.'],
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
return {
|
|
1380
|
+
configJson: normalizedRawConfigJson,
|
|
1381
|
+
errors: [],
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
const errors = [];
|
|
1385
|
+
const serializedValue = {};
|
|
1386
|
+
definition.fields.forEach((field) => {
|
|
1387
|
+
const rawFieldValue = value[field.key];
|
|
1388
|
+
if (field.kind === 'json') {
|
|
1389
|
+
const stringValue = typeof rawFieldValue === 'string' ? rawFieldValue.trim() : '';
|
|
1390
|
+
if (!stringValue) {
|
|
1391
|
+
if (field.required) {
|
|
1392
|
+
errors.push(`${field.title} is required.`);
|
|
1393
|
+
}
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
try {
|
|
1397
|
+
const parsedFieldValue = JSON.parse(stringValue);
|
|
1398
|
+
if (field.jsonValueType === 'array' &&
|
|
1399
|
+
!Array.isArray(parsedFieldValue)) {
|
|
1400
|
+
errors.push(`${field.title} must be a JSON array.`);
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
if (field.jsonValueType === 'object' &&
|
|
1404
|
+
(!isRecord(parsedFieldValue) || Array.isArray(parsedFieldValue))) {
|
|
1405
|
+
errors.push(`${field.title} must be a JSON object.`);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
serializedValue[field.key] = parsedFieldValue;
|
|
1409
|
+
}
|
|
1410
|
+
catch {
|
|
1411
|
+
errors.push(`${field.title} must be valid JSON.`);
|
|
1412
|
+
}
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
if (rawFieldValue === null ||
|
|
1416
|
+
rawFieldValue === undefined ||
|
|
1417
|
+
rawFieldValue === '') {
|
|
1418
|
+
if (field.required) {
|
|
1419
|
+
errors.push(`${field.title} is required.`);
|
|
1420
|
+
}
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
serializedValue[field.key] = rawFieldValue;
|
|
1424
|
+
});
|
|
1425
|
+
return {
|
|
1426
|
+
configJson: JSON.stringify(serializedValue),
|
|
1427
|
+
errors,
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
function buildRawDefinition(error) {
|
|
1431
|
+
return {
|
|
1432
|
+
mode: 'raw',
|
|
1433
|
+
schema: null,
|
|
1434
|
+
fields: [],
|
|
1435
|
+
errors: [error],
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
function mapSchemaPropertyToField(property) {
|
|
1439
|
+
if (property.enumValues?.length) {
|
|
1440
|
+
return {
|
|
1441
|
+
key: property.key,
|
|
1442
|
+
title: property.title,
|
|
1443
|
+
description: property.description,
|
|
1444
|
+
type: property.type,
|
|
1445
|
+
kind: 'select',
|
|
1446
|
+
required: property.required,
|
|
1447
|
+
enumValues: property.enumValues,
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
if (property.type === 'array' || property.type === 'object') {
|
|
1451
|
+
return {
|
|
1452
|
+
key: property.key,
|
|
1453
|
+
title: property.title,
|
|
1454
|
+
description: property.description,
|
|
1455
|
+
type: property.type,
|
|
1456
|
+
kind: 'json',
|
|
1457
|
+
required: property.required,
|
|
1458
|
+
jsonValueType: property.type,
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
return {
|
|
1462
|
+
key: property.key,
|
|
1463
|
+
title: property.title,
|
|
1464
|
+
description: property.description,
|
|
1465
|
+
type: property.type,
|
|
1466
|
+
kind: property.type === 'boolean'
|
|
1467
|
+
? 'toggle'
|
|
1468
|
+
: property.type === 'number'
|
|
1469
|
+
? 'number'
|
|
1470
|
+
: 'text',
|
|
1471
|
+
required: property.required,
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
function resolvePropertyType(property) {
|
|
1475
|
+
const typeValue = property['type'];
|
|
1476
|
+
if (typeof typeValue === 'string') {
|
|
1477
|
+
return normalizePropertyType(typeValue);
|
|
1478
|
+
}
|
|
1479
|
+
if (Array.isArray(typeValue)) {
|
|
1480
|
+
for (const item of typeValue) {
|
|
1481
|
+
if (typeof item !== 'string' || item === 'null') {
|
|
1482
|
+
continue;
|
|
1483
|
+
}
|
|
1484
|
+
const normalizedType = normalizePropertyType(item);
|
|
1485
|
+
if (normalizedType) {
|
|
1486
|
+
return normalizedType;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
if (Array.isArray(property['enum']) && property['enum'].length > 0) {
|
|
1491
|
+
const firstEnumValue = property['enum'].find((item) => item !== null);
|
|
1492
|
+
if (typeof firstEnumValue === 'boolean') {
|
|
1493
|
+
return 'boolean';
|
|
1494
|
+
}
|
|
1495
|
+
if (typeof firstEnumValue === 'number') {
|
|
1496
|
+
return 'number';
|
|
1497
|
+
}
|
|
1498
|
+
if (typeof firstEnumValue === 'string') {
|
|
1499
|
+
return 'string';
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
return null;
|
|
1503
|
+
}
|
|
1504
|
+
function normalizePropertyType(type) {
|
|
1505
|
+
if (type === 'integer' || type === 'number') {
|
|
1506
|
+
return 'number';
|
|
1507
|
+
}
|
|
1508
|
+
return SUPPORTED_PROPERTY_TYPES.includes(type)
|
|
1509
|
+
? type
|
|
1510
|
+
: null;
|
|
1511
|
+
}
|
|
1512
|
+
function resolvePropertyTitle(key, property) {
|
|
1513
|
+
if (typeof property['title'] === 'string' &&
|
|
1514
|
+
property['title'].trim().length) {
|
|
1515
|
+
return property['title'];
|
|
1516
|
+
}
|
|
1517
|
+
return key
|
|
1518
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
1519
|
+
.replace(/[_-]+/g, ' ')
|
|
1520
|
+
.replace(/\s+/g, ' ')
|
|
1521
|
+
.trim()
|
|
1522
|
+
.replace(/\b\w/g, (character) => character.toUpperCase());
|
|
1523
|
+
}
|
|
1524
|
+
function isPrimitiveEnumValue(value) {
|
|
1525
|
+
return (typeof value === 'string' ||
|
|
1526
|
+
typeof value === 'number' ||
|
|
1527
|
+
typeof value === 'boolean');
|
|
1528
|
+
}
|
|
1529
|
+
function isValidJson(value) {
|
|
1530
|
+
try {
|
|
1531
|
+
JSON.parse(value);
|
|
1532
|
+
return true;
|
|
1533
|
+
}
|
|
1534
|
+
catch {
|
|
1535
|
+
return false;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
function isRecord(value) {
|
|
1539
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
const DEFAULT_WORKFLOW_STEP_NAME = {
|
|
1543
|
+
en: '',
|
|
1544
|
+
ar: '',
|
|
1545
|
+
};
|
|
1546
|
+
const DEFAULT_APP_ACTION_EDITOR_VALUE = {
|
|
1547
|
+
actionKey: null,
|
|
1548
|
+
failureBehavior: 'Stop',
|
|
1549
|
+
timeoutSeconds: 300,
|
|
1550
|
+
configJson: '{}',
|
|
1551
|
+
};
|
|
1552
|
+
function createDefaultWorkflowStepEditorValue() {
|
|
1553
|
+
return {
|
|
1554
|
+
name: { ...DEFAULT_WORKFLOW_STEP_NAME },
|
|
1555
|
+
type: 'UserInput',
|
|
1556
|
+
targetType: '1',
|
|
1557
|
+
group: null,
|
|
1558
|
+
role: null,
|
|
1559
|
+
sla: 0,
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
function mapWorkflowStepToEditorState(step) {
|
|
1563
|
+
if (!step) {
|
|
1564
|
+
return {
|
|
1565
|
+
stepFormValue: createDefaultWorkflowStepEditorValue(),
|
|
1566
|
+
appActionValue: { ...DEFAULT_APP_ACTION_EDITOR_VALUE },
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
const type = normalizeWorkflowStepType(step.type);
|
|
1570
|
+
return {
|
|
1571
|
+
stepFormValue: {
|
|
1572
|
+
name: normalizeStepName(step.name),
|
|
1573
|
+
type,
|
|
1574
|
+
targetType: step.targetType || '1',
|
|
1575
|
+
group: type === 'UserInput' && step.targetType === '1' && step.targetValue
|
|
1576
|
+
? Number(step.targetValue)
|
|
1577
|
+
: null,
|
|
1578
|
+
role: type === 'UserInput' && step.targetType === '2'
|
|
1579
|
+
? (step.targetValue ?? null)
|
|
1580
|
+
: null,
|
|
1581
|
+
sla: Number(step.sla ?? 0),
|
|
1582
|
+
},
|
|
1583
|
+
appActionValue: {
|
|
1584
|
+
actionKey: step.appAction?.actionKey ?? null,
|
|
1585
|
+
failureBehavior: step.appAction?.failureBehavior ?? 'Stop',
|
|
1586
|
+
timeoutSeconds: Number(step.appAction?.timeoutSeconds ?? 300),
|
|
1587
|
+
configJson: step.appAction?.configJson ?? '{}',
|
|
1588
|
+
},
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
function buildWorkflowStepPayload({ formValue, properties, hasNotification, appActionValue, }) {
|
|
1592
|
+
const type = normalizeWorkflowStepType(formValue.type);
|
|
1593
|
+
if (type === 'AppAction') {
|
|
1594
|
+
return {
|
|
1595
|
+
type,
|
|
1596
|
+
name: normalizeStepName(formValue.name),
|
|
1597
|
+
sla: Number(formValue.sla ?? 0),
|
|
1598
|
+
properties: [],
|
|
1599
|
+
hasNotification,
|
|
1600
|
+
appCode: 'pplus',
|
|
1601
|
+
actionKey: appActionValue?.actionKey ?? '',
|
|
1602
|
+
configJson: appActionValue?.configJson ?? '{}',
|
|
1603
|
+
failureBehavior: appActionValue?.failureBehavior ?? 'Stop',
|
|
1604
|
+
timeoutSeconds: Number(appActionValue?.timeoutSeconds ?? 300),
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
return {
|
|
1608
|
+
type,
|
|
1609
|
+
name: normalizeStepName(formValue.name),
|
|
1610
|
+
targetType: formValue.targetType || '1',
|
|
1611
|
+
targetValue: formValue.targetType === '1'
|
|
1612
|
+
? (formValue.group ?? undefined)
|
|
1613
|
+
: (formValue.role ?? undefined),
|
|
1614
|
+
sla: Number(formValue.sla ?? 0),
|
|
1615
|
+
properties,
|
|
1616
|
+
hasNotification,
|
|
1617
|
+
hasForm: true,
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
function normalizeWorkflowStepType(type) {
|
|
1621
|
+
return type === 'AppAction' ? 'AppAction' : 'UserInput';
|
|
1622
|
+
}
|
|
1623
|
+
function normalizeStepName(name) {
|
|
1624
|
+
if (name && typeof name === 'object') {
|
|
1625
|
+
return {
|
|
1626
|
+
en: name['en'] ?? '',
|
|
1627
|
+
ar: name['ar'] ?? '',
|
|
1628
|
+
...(name['display'] ? { display: name['display'] } : {}),
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
const label = typeof name === 'string' ? name : '';
|
|
1632
|
+
return {
|
|
1633
|
+
display: label,
|
|
1634
|
+
en: label,
|
|
1635
|
+
ar: label,
|
|
1636
|
+
};
|
|
984
1637
|
}
|
|
985
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowNotificationsDrawer, decorators: [{
|
|
986
|
-
type: Component,
|
|
987
|
-
args: [{ selector: 'mt-workflow-notifications-drawer', imports: [Card, NotificationTemplate, Card], template: "<div class=\"overflow-hidden h-full w-full\">\r\n <div\r\n class=\"flex-1 flex justify-center items-start p-4 h-full overflow-y-auto\"\r\n >\r\n <div class=\"w-2/3\">\r\n <mt-card headless>\r\n <mt-notification-template />\r\n </mt-card>\r\n </div>\r\n </div>\r\n</div>\r\n" }]
|
|
988
|
-
}] });
|
|
989
1638
|
|
|
990
1639
|
class WorkflowFormDrawer {
|
|
991
1640
|
modal = inject(ModalService);
|
|
992
1641
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowFormDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
993
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: WorkflowFormDrawer, isStandalone: true, selector: "mt-workflow-form-drawer", ngImport: i0, template: "<div [class]=\"modal.contentClass + ' p-4'\">\
|
|
1642
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: WorkflowFormDrawer, isStandalone: true, selector: "mt-workflow-form-drawer", ngImport: i0, template: "<div [class]=\"modal.contentClass + ' p-4'\">\n <mt-form-builder\n class=\"flex-1\"\n canvasStyleClass=\"!w-full\"\n mode=\"manageProperties\"\n />\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: FormBuilder, selector: "mt-form-builder", inputs: ["canvasStyleClass", "mode"] }] });
|
|
994
1643
|
}
|
|
995
1644
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowFormDrawer, decorators: [{
|
|
996
1645
|
type: Component,
|
|
997
|
-
args: [{ selector: 'mt-workflow-form-drawer', imports: [FormBuilder], template: "<div [class]=\"modal.contentClass + ' p-4'\">\
|
|
1646
|
+
args: [{ selector: 'mt-workflow-form-drawer', imports: [FormBuilder], template: "<div [class]=\"modal.contentClass + ' p-4'\">\n <mt-form-builder\n class=\"flex-1\"\n canvasStyleClass=\"!w-full\"\n mode=\"manageProperties\"\n />\n</div>\n" }]
|
|
1647
|
+
}] });
|
|
1648
|
+
|
|
1649
|
+
class WorkflowNotificationsDrawer {
|
|
1650
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowNotificationsDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1651
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: WorkflowNotificationsDrawer, isStandalone: true, selector: "mt-workflow-notifications-drawer", ngImport: i0, template: "<div class=\"overflow-hidden h-full w-full\">\n <div\n class=\"flex-1 flex justify-center items-start p-4 h-full overflow-y-auto\"\n >\n <div class=\"w-2/3\">\n <mt-card headless>\n <mt-notification-template />\n </mt-card>\n </div>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: NotificationTemplate, selector: "mt-notification-template" }] });
|
|
1652
|
+
}
|
|
1653
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowNotificationsDrawer, decorators: [{
|
|
1654
|
+
type: Component,
|
|
1655
|
+
args: [{ selector: 'mt-workflow-notifications-drawer', imports: [Card, NotificationTemplate, Card], template: "<div class=\"overflow-hidden h-full w-full\">\n <div\n class=\"flex-1 flex justify-center items-start p-4 h-full overflow-y-auto\"\n >\n <div class=\"w-2/3\">\n <mt-card headless>\n <mt-notification-template />\n </mt-card>\n </div>\n </div>\n</div>\n" }]
|
|
998
1656
|
}] });
|
|
999
1657
|
|
|
1000
1658
|
class WorkflowBuilder {
|
|
@@ -1002,236 +1660,182 @@ class WorkflowBuilder {
|
|
|
1002
1660
|
notificationFacade = inject(NotificationFacade);
|
|
1003
1661
|
formBuilderFacade = inject(FormBuilderFacade);
|
|
1004
1662
|
transloco = inject(TranslocoService);
|
|
1663
|
+
confirmationService = inject(ConfirmationService);
|
|
1005
1664
|
modal = inject(ModalService);
|
|
1006
|
-
|
|
1665
|
+
isPatchingStepForm = false;
|
|
1666
|
+
lastAppActionDescriptorSignature = '';
|
|
1007
1667
|
mainTab = signal('workflow', ...(ngDevMode ? [{ debugName: "mainTab" }] : /* istanbul ignore next */ []));
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1668
|
+
selectedTab = signal('tab1', ...(ngDevMode ? [{ debugName: "selectedTab" }] : /* istanbul ignore next */ []));
|
|
1669
|
+
isPublished = signal(false, ...(ngDevMode ? [{ debugName: "isPublished" }] : /* istanbul ignore next */ []));
|
|
1670
|
+
isEditingInitialNode = signal(false, ...(ngDevMode ? [{ debugName: "isEditingInitialNode" }] : /* istanbul ignore next */ []));
|
|
1671
|
+
isCreatingStep = signal(false, ...(ngDevMode ? [{ debugName: "isCreatingStep" }] : /* istanbul ignore next */ []));
|
|
1672
|
+
currentStepType = signal('UserInput', ...(ngDevMode ? [{ debugName: "currentStepType" }] : /* istanbul ignore next */ []));
|
|
1673
|
+
draftAppActionDescriptor = signal(null, ...(ngDevMode ? [{ debugName: "draftAppActionDescriptor" }] : /* istanbul ignore next */ []));
|
|
1674
|
+
nodeFields = signal({
|
|
1675
|
+
name: 'name.display',
|
|
1676
|
+
}, ...(ngDevMode ? [{ debugName: "nodeFields" }] : /* istanbul ignore next */ []));
|
|
1677
|
+
workflow = this.workflowFacade.workflow;
|
|
1678
|
+
workflows = this.workflowFacade.workflows;
|
|
1679
|
+
selectedStep = this.workflowFacade.selectedStep;
|
|
1018
1680
|
groups = this.workflowFacade.groups;
|
|
1019
1681
|
roles = this.workflowFacade.roles;
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1682
|
+
appActionDescriptors = this.workflowFacade.appActionDescriptors;
|
|
1683
|
+
selectedAppActionDescriptor = this.workflowFacade.selectedAppActionDescriptor;
|
|
1684
|
+
isFlowValid = this.workflowFacade.isFlowValid;
|
|
1023
1685
|
loading = this.workflowFacade.isLoading('getWorkflow');
|
|
1024
1686
|
loadingStep = this.workflowFacade.isLoading('getStep');
|
|
1025
|
-
|
|
1026
|
-
|
|
1687
|
+
loadingAppActions = this.workflowFacade.isLoading('getAppActions');
|
|
1688
|
+
loadingAppActionDetail = this.workflowFacade.isLoading('getAppActionDetail');
|
|
1689
|
+
appActionListError = this.workflowFacade.error('getAppActions');
|
|
1690
|
+
appActionDetailError = this.workflowFacade.error('getAppActionDetail');
|
|
1027
1691
|
selectedWorkflowId = computed(() => this.workflow()?.id, ...(ngDevMode ? [{ debugName: "selectedWorkflowId" }] : /* istanbul ignore next */ []));
|
|
1028
1692
|
connectionFormulaSchemaId = computed(() => this.workflow()?.id, ...(ngDevMode ? [{ debugName: "connectionFormulaSchemaId" }] : /* istanbul ignore next */ []));
|
|
1029
1693
|
hasMultipleWorkflows = computed(() => this.workflows().length > 1, ...(ngDevMode ? [{ debugName: "hasMultipleWorkflows" }] : /* istanbul ignore next */ []));
|
|
1030
|
-
confirmationService = inject(ConfirmationService);
|
|
1031
|
-
selectedTab = signal('tab1', ...(ngDevMode ? [{ debugName: "selectedTab" }] : /* istanbul ignore next */ []));
|
|
1032
|
-
nodeFields = signal({
|
|
1033
|
-
name: 'name.display',
|
|
1034
|
-
}, ...(ngDevMode ? [{ debugName: "nodeFields" }] : /* istanbul ignore next */ []));
|
|
1035
|
-
nodeFormControl = new FormControl();
|
|
1036
|
-
isPublished = signal(false, ...(ngDevMode ? [{ debugName: "isPublished" }] : /* istanbul ignore next */ []));
|
|
1037
1694
|
isPublishDisabled = computed(() => !this.isFlowValid(), ...(ngDevMode ? [{ debugName: "isPublishDisabled" }] : /* istanbul ignore next */ []));
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
})
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1695
|
+
hasGroupedRoles = computed(() => this.roles().some((role) => Array.isArray(role?.items)), ...(ngDevMode ? [{ debugName: "hasGroupedRoles" }] : /* istanbul ignore next */ []));
|
|
1696
|
+
stepForm = new FormGroup({
|
|
1697
|
+
nameEn: new FormControl('', {
|
|
1698
|
+
nonNullable: true,
|
|
1699
|
+
validators: [Validators.required],
|
|
1700
|
+
}),
|
|
1701
|
+
nameAr: new FormControl('', {
|
|
1702
|
+
nonNullable: true,
|
|
1703
|
+
validators: [Validators.required],
|
|
1704
|
+
}),
|
|
1705
|
+
type: new FormControl('UserInput', {
|
|
1706
|
+
nonNullable: true,
|
|
1707
|
+
validators: [Validators.required],
|
|
1708
|
+
}),
|
|
1709
|
+
targetType: new FormControl('1', {
|
|
1710
|
+
nonNullable: true,
|
|
1711
|
+
validators: [Validators.required],
|
|
1712
|
+
}),
|
|
1713
|
+
group: new FormControl(null),
|
|
1714
|
+
role: new FormControl(null),
|
|
1715
|
+
sla: new FormControl(0, {
|
|
1716
|
+
nonNullable: true,
|
|
1717
|
+
validators: [Validators.required, Validators.min(0)],
|
|
1718
|
+
}),
|
|
1719
|
+
});
|
|
1720
|
+
appActionSettingsForm = new FormGroup({
|
|
1721
|
+
actionKey: new FormControl(null, Validators.required),
|
|
1722
|
+
failureBehavior: new FormControl('Stop', {
|
|
1723
|
+
nonNullable: true,
|
|
1724
|
+
validators: [Validators.required],
|
|
1725
|
+
}),
|
|
1726
|
+
timeoutSeconds: new FormControl(300, {
|
|
1727
|
+
nonNullable: true,
|
|
1728
|
+
validators: [Validators.required, Validators.min(1)],
|
|
1729
|
+
}),
|
|
1730
|
+
});
|
|
1731
|
+
appActionRawConfigControl = new FormControl('{}', {
|
|
1732
|
+
nonNullable: true,
|
|
1733
|
+
});
|
|
1734
|
+
appActionConfigForm = new FormGroup({});
|
|
1735
|
+
showHideControl = new FormControl(false, {
|
|
1736
|
+
nonNullable: true,
|
|
1737
|
+
});
|
|
1738
|
+
appActionConfigDefinition = signal(buildWorkflowAppActionConfigEditorDefinition(null), ...(ngDevMode ? [{ debugName: "appActionConfigDefinition" }] : /* istanbul ignore next */ []));
|
|
1739
|
+
propertiesTableDataState = signal([], ...(ngDevMode ? [{ debugName: "propertiesTableDataState" }] : /* istanbul ignore next */ []));
|
|
1740
|
+
propertiesTableData = computed(() => this.propertiesTableDataState(), ...(ngDevMode ? [{ debugName: "propertiesTableData" }] : /* istanbul ignore next */ []));
|
|
1741
|
+
mainTabsList = computed(() => [
|
|
1059
1742
|
{
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
type: 'text',
|
|
1743
|
+
label: this.transloco.translate('workflow.builder.workflow'),
|
|
1744
|
+
value: 'workflow',
|
|
1063
1745
|
},
|
|
1064
1746
|
{
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
type: 'boolean',
|
|
1747
|
+
label: this.transloco.translate('workflow.builder.notification'),
|
|
1748
|
+
value: 'notification',
|
|
1068
1749
|
},
|
|
1750
|
+
], ...(ngDevMode ? [{ debugName: "mainTabsList" }] : /* istanbul ignore next */ []));
|
|
1751
|
+
failureBehaviorOptions = computed(() => [
|
|
1069
1752
|
{
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
type: 'boolean',
|
|
1753
|
+
label: this.transloco.translate('workflow.builder.failureBehaviorStop'),
|
|
1754
|
+
value: 'Stop',
|
|
1073
1755
|
},
|
|
1074
|
-
|
|
1075
|
-
|
|
1756
|
+
{
|
|
1757
|
+
label: this.transloco.translate('workflow.builder.failureBehaviorContinue'),
|
|
1758
|
+
value: 'Continue',
|
|
1759
|
+
},
|
|
1760
|
+
], ...(ngDevMode ? [{ debugName: "failureBehaviorOptions" }] : /* istanbul ignore next */ []));
|
|
1076
1761
|
tabsList = computed(() => {
|
|
1077
|
-
const
|
|
1762
|
+
const tabs = [
|
|
1078
1763
|
{
|
|
1079
1764
|
label: this.transloco.translate('workflow.builder.tabSettings'),
|
|
1080
1765
|
value: 'tab1',
|
|
1081
1766
|
},
|
|
1082
1767
|
];
|
|
1083
|
-
if (this.isCreatingStep()) {
|
|
1084
|
-
|
|
1768
|
+
if (!this.isCreatingStep() && this.currentStepType() === 'UserInput') {
|
|
1769
|
+
tabs.push({
|
|
1770
|
+
label: this.transloco.translate('workflow.builder.form'),
|
|
1771
|
+
value: 'tab2',
|
|
1772
|
+
});
|
|
1085
1773
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
value: 'tab2',
|
|
1089
|
-
});
|
|
1090
|
-
// Add notification tab only for non-initial nodes
|
|
1091
|
-
if (!this.isEditingInitialNode()) {
|
|
1092
|
-
baseTabs.push({
|
|
1774
|
+
if (!this.isCreatingStep() && !this.isEditingInitialNode()) {
|
|
1775
|
+
tabs.push({
|
|
1093
1776
|
label: this.transloco.translate('workflow.builder.notification'),
|
|
1094
1777
|
value: 'tab3',
|
|
1095
1778
|
});
|
|
1096
1779
|
}
|
|
1097
|
-
return
|
|
1780
|
+
return tabs;
|
|
1098
1781
|
}, ...(ngDevMode ? [{ debugName: "tabsList" }] : /* istanbul ignore next */ []));
|
|
1099
|
-
|
|
1100
|
-
showSaveButton: () => this.selectedTab() === 'tab1',
|
|
1101
|
-
disableSaveButton: () => this.selectedTab() !== 'tab1' || this.nodeFormControl.invalid,
|
|
1102
|
-
};
|
|
1103
|
-
availableNodes = [
|
|
1782
|
+
availableNodes = computed(() => [
|
|
1104
1783
|
{
|
|
1105
1784
|
id: 'FormStep',
|
|
1785
|
+
type: 'UserInput',
|
|
1106
1786
|
label: this.transloco.translate('workflow.builder.newStep'),
|
|
1787
|
+
tab: [this.transloco.translate('workflow.builder.userInputTab')],
|
|
1107
1788
|
name: {
|
|
1108
1789
|
en: 'Form Step',
|
|
1109
1790
|
ar: 'خطوة النموذج',
|
|
1110
1791
|
},
|
|
1111
1792
|
targetType: '1',
|
|
1112
1793
|
icon: 'general.plus-circle',
|
|
1113
|
-
color: '#
|
|
1794
|
+
color: '#0369A1',
|
|
1114
1795
|
},
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
label: this.transloco.translate('workflow.builder.nameEnglish'),
|
|
1125
|
-
placeholder: this.transloco.translate('workflow.builder.enterNameEnglish'),
|
|
1126
|
-
cssClass: 'w-full',
|
|
1127
|
-
required: true,
|
|
1128
|
-
},
|
|
1129
|
-
{
|
|
1130
|
-
key: 'name.ar',
|
|
1131
|
-
label: this.transloco.translate('workflow.builder.nameArabic'),
|
|
1132
|
-
placeholder: this.transloco.translate('workflow.builder.enterNameArabic'),
|
|
1133
|
-
cssClass: 'w-full',
|
|
1134
|
-
required: true,
|
|
1135
|
-
},
|
|
1136
|
-
],
|
|
1137
|
-
};
|
|
1138
|
-
// Approver section with dynamic options
|
|
1139
|
-
approverSection = computed(() => ({
|
|
1140
|
-
key: 'approver',
|
|
1141
|
-
type: 'header',
|
|
1142
|
-
label: this.transloco.translate('workflow.builder.approver'),
|
|
1143
|
-
headerClass: 'rounded-t-xl bg-slate-50 px-4 py-3 text-sm font-semibold text-slate-900 border border-slate-200 border-b-0',
|
|
1144
|
-
bodyClass: 'rounded-b-xl border border-slate-200 p-4 space-y-3',
|
|
1145
|
-
cssClass: ' ',
|
|
1146
|
-
fields: [
|
|
1147
|
-
{
|
|
1148
|
-
key: 'targetType',
|
|
1149
|
-
type: 'radio-button',
|
|
1150
|
-
orientation: 'horizontal',
|
|
1151
|
-
defaultValue: '1',
|
|
1152
|
-
options: [
|
|
1153
|
-
{
|
|
1154
|
-
label: this.transloco.translate('workflow.builder.group'),
|
|
1155
|
-
value: '1',
|
|
1156
|
-
},
|
|
1157
|
-
{
|
|
1158
|
-
label: this.transloco.translate('workflow.builder.role'),
|
|
1159
|
-
value: '2',
|
|
1160
|
-
},
|
|
1161
|
-
],
|
|
1162
|
-
optionLabel: 'label',
|
|
1163
|
-
optionValue: 'value',
|
|
1164
|
-
placeholder: this.transloco.translate('workflow.builder.enter-role'),
|
|
1165
|
-
cssClass: 'flex items-center gap-6 text-sm text-slate-700',
|
|
1166
|
-
required: true,
|
|
1167
|
-
},
|
|
1168
|
-
{
|
|
1169
|
-
key: 'group',
|
|
1170
|
-
label: this.transloco.translate('workflow.builder.select'),
|
|
1171
|
-
placeholder: this.transloco.translate('workflow.builder.selectGroup'),
|
|
1172
|
-
cssClass: 'mt-2 pt-3 border-t border-slate-200 w-full',
|
|
1173
|
-
required: true,
|
|
1174
|
-
type: 'select',
|
|
1175
|
-
options: this.groups(),
|
|
1176
|
-
optionLabel: 'name.display',
|
|
1177
|
-
optionValue: 'id',
|
|
1178
|
-
relations: [{ key: 'targetType', value: '1', action: 'show' }],
|
|
1179
|
-
},
|
|
1180
|
-
{
|
|
1181
|
-
key: 'role',
|
|
1182
|
-
label: this.transloco.translate('workflow.builder.select'),
|
|
1183
|
-
placeholder: this.transloco.translate('workflow.builder.enter-target-value'),
|
|
1184
|
-
cssClass: 'mt-2 pt-3 border-t border-slate-200 w-full',
|
|
1185
|
-
required: true,
|
|
1186
|
-
type: 'select',
|
|
1187
|
-
options: this.roles(),
|
|
1188
|
-
group: this.hasGroupedRoles(),
|
|
1189
|
-
optionGroupLabel: 'label',
|
|
1190
|
-
optionGroupChildren: 'items',
|
|
1191
|
-
optionLabel: 'name.display',
|
|
1192
|
-
optionValue: 'value',
|
|
1193
|
-
relations: [{ key: 'targetType', value: '2', action: 'show' }],
|
|
1194
|
-
},
|
|
1195
|
-
],
|
|
1196
|
-
}), ...(ngDevMode ? [{ debugName: "approverSection" }] : /* istanbul ignore next */ []));
|
|
1197
|
-
slaSection = {
|
|
1198
|
-
key: 'slaSection',
|
|
1199
|
-
type: 'none',
|
|
1200
|
-
bodyClass: 'space-y-1',
|
|
1201
|
-
fields: [
|
|
1202
|
-
{
|
|
1203
|
-
key: 'sla',
|
|
1204
|
-
label: this.transloco.translate('workflow.builder.sla'),
|
|
1205
|
-
placeholder: this.transloco.translate('workflow.builder.enterSla'),
|
|
1206
|
-
cssClass: 'w-full',
|
|
1207
|
-
required: true,
|
|
1796
|
+
...this.appActionDescriptors().map((descriptor) => ({
|
|
1797
|
+
id: `AppActionStep:${descriptor.actionKey}`,
|
|
1798
|
+
type: 'AppAction',
|
|
1799
|
+
tab: [this.transloco.translate('workflow.builder.appActionsTab')],
|
|
1800
|
+
label: descriptor.displayName,
|
|
1801
|
+
name: {
|
|
1802
|
+
en: descriptor.displayName,
|
|
1803
|
+
ar: descriptor.displayName,
|
|
1804
|
+
display: descriptor.displayName,
|
|
1208
1805
|
},
|
|
1209
|
-
|
|
1806
|
+
appCode: descriptor.appCode,
|
|
1807
|
+
actionKey: descriptor.actionKey,
|
|
1808
|
+
displayName: descriptor.displayName,
|
|
1809
|
+
description: descriptor.description ?? null,
|
|
1810
|
+
configSchemaJson: descriptor.configSchemaJson ?? null,
|
|
1811
|
+
requiredContextKeys: descriptor.requiredContextKeys ?? [],
|
|
1812
|
+
supportedScopes: descriptor.supportedScopes ?? [],
|
|
1813
|
+
icon: 'dev.dataflow-01',
|
|
1814
|
+
color: '#B45309',
|
|
1815
|
+
})),
|
|
1816
|
+
], ...(ngDevMode ? [{ debugName: "availableNodes" }] : /* istanbul ignore next */ []));
|
|
1817
|
+
nodeActions = signal([
|
|
1818
|
+
{
|
|
1819
|
+
key: 'edit',
|
|
1820
|
+
icon: 'general.edit-05',
|
|
1821
|
+
variant: 'outlined',
|
|
1822
|
+
size: 'small',
|
|
1823
|
+
tooltip: 'Edit',
|
|
1824
|
+
},
|
|
1825
|
+
{
|
|
1826
|
+
key: 'delete',
|
|
1827
|
+
icon: 'general.trash-01',
|
|
1828
|
+
variant: 'outlined',
|
|
1829
|
+
size: 'small',
|
|
1830
|
+
severity: 'danger',
|
|
1831
|
+
tooltip: 'Delete',
|
|
1832
|
+
condition: (node) => !node.isInitial,
|
|
1833
|
+
},
|
|
1834
|
+
], ...(ngDevMode ? [{ debugName: "nodeActions" }] : /* istanbul ignore next */ []));
|
|
1835
|
+
nodeDialogFooterConfig = {
|
|
1836
|
+
showSaveButton: () => this.selectedTab() === 'tab1',
|
|
1837
|
+
disableSaveButton: () => this.isNodeDialogSaveDisabled(),
|
|
1210
1838
|
};
|
|
1211
|
-
// Dynamic form configuration using computed signal
|
|
1212
|
-
// Automatically rebuilds when isEditingInitialNode changes
|
|
1213
|
-
nodeForm = linkedSignal(() => {
|
|
1214
|
-
const sections = [this.baseNameFields];
|
|
1215
|
-
// Conditionally include approver and SLA sections based on isEditingInitialNode
|
|
1216
|
-
// Initial nodes only show name fields
|
|
1217
|
-
if (!this.isEditingInitialNode()) {
|
|
1218
|
-
sections.push(this.approverSection());
|
|
1219
|
-
sections.push(this.slaSection);
|
|
1220
|
-
}
|
|
1221
|
-
return {
|
|
1222
|
-
sections,
|
|
1223
|
-
layout: {},
|
|
1224
|
-
};
|
|
1225
|
-
}, ...(ngDevMode ? [{ debugName: "nodeForm" }] : /* istanbul ignore next */ []));
|
|
1226
|
-
// Computed signal for form state info - useful for debugging or UI indicators
|
|
1227
|
-
formStateInfo = computed(() => ({
|
|
1228
|
-
isInitialNode: this.isEditingInitialNode(),
|
|
1229
|
-
sectionsCount: this.nodeForm().sections.length,
|
|
1230
|
-
hasApproverSection: !this.isEditingInitialNode(),
|
|
1231
|
-
hasSlaSection: !this.isEditingInitialNode(),
|
|
1232
|
-
formMode: this.isEditingInitialNode() ? 'initial-node' : 'standard-node',
|
|
1233
|
-
}), ...(ngDevMode ? [{ debugName: "formStateInfo" }] : /* istanbul ignore next */ []));
|
|
1234
|
-
// Sample connection form configuration
|
|
1235
1839
|
connectionForm = signal({
|
|
1236
1840
|
sections: [
|
|
1237
1841
|
{
|
|
@@ -1255,79 +1859,95 @@ class WorkflowBuilder {
|
|
|
1255
1859
|
toolbarTabs: ['functions', 'operators'],
|
|
1256
1860
|
isProcessBuilder: true,
|
|
1257
1861
|
}, ...(ngDevMode ? [{ debugName: "connectionFormulaConfig" }] : /* istanbul ignore next */ []));
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
connections
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1862
|
+
steps = computed(() => this.workflowFacade.steps().map((step) => ({
|
|
1863
|
+
...step,
|
|
1864
|
+
color: step.type === 'AppAction' ? '#B45309' : '#0369A1',
|
|
1865
|
+
})), ...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
|
|
1866
|
+
connections = computed(() => this.workflowFacade.connections().map((connection) => ({
|
|
1867
|
+
...connection,
|
|
1868
|
+
from: connection.source,
|
|
1869
|
+
to: connection.target,
|
|
1870
|
+
})), ...(ngDevMode ? [{ debugName: "connections" }] : /* istanbul ignore next */ []));
|
|
1871
|
+
currentAppActionDescriptor = computed(() => {
|
|
1872
|
+
const actionKey = this.appActionSettingsForm.controls.actionKey.value;
|
|
1873
|
+
if (!actionKey) {
|
|
1874
|
+
return null;
|
|
1875
|
+
}
|
|
1876
|
+
const selectedDescriptor = this.selectedAppActionDescriptor();
|
|
1877
|
+
if (selectedDescriptor?.actionKey === actionKey) {
|
|
1878
|
+
return selectedDescriptor;
|
|
1879
|
+
}
|
|
1880
|
+
const cachedDescriptor = this.appActionDescriptors().find((descriptor) => descriptor.actionKey === actionKey);
|
|
1881
|
+
if (cachedDescriptor) {
|
|
1882
|
+
return cachedDescriptor;
|
|
1883
|
+
}
|
|
1884
|
+
const draftDescriptor = this.draftAppActionDescriptor();
|
|
1885
|
+
if (draftDescriptor?.actionKey === actionKey) {
|
|
1886
|
+
return draftDescriptor;
|
|
1887
|
+
}
|
|
1888
|
+
const step = this.selectedStep();
|
|
1889
|
+
if (step?.type === 'AppAction' &&
|
|
1890
|
+
step.appAction?.actionKey === actionKey) {
|
|
1891
|
+
return {
|
|
1892
|
+
appCode: step.appAction.appCode,
|
|
1893
|
+
actionKey,
|
|
1894
|
+
displayName: step.appAction.displayName ?? actionKey,
|
|
1895
|
+
description: step.appAction.description ?? null,
|
|
1896
|
+
configSchemaJson: step.appAction.configSchemaJson ?? null,
|
|
1897
|
+
requiredContextKeys: step.appAction.requiredContextKeys ?? [],
|
|
1898
|
+
supportedScopes: step.appAction.supportedScopes ?? [],
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
return null;
|
|
1902
|
+
}, ...(ngDevMode ? [{ debugName: "currentAppActionDescriptor" }] : /* istanbul ignore next */ []));
|
|
1293
1903
|
constructor() {
|
|
1294
|
-
// Effect to initialize isPublished from workflow.isPublished
|
|
1295
1904
|
effect(() => {
|
|
1296
1905
|
const workflow = this.workflow();
|
|
1297
|
-
if (workflow
|
|
1906
|
+
if (workflow?.isPublished !== undefined) {
|
|
1298
1907
|
this.isPublished.set(workflow.isPublished);
|
|
1299
1908
|
}
|
|
1300
1909
|
});
|
|
1301
|
-
|
|
1910
|
+
effect(() => {
|
|
1911
|
+
if (this.mainTab() === 'workflow') {
|
|
1912
|
+
this.workflowFacade.loadAppActions();
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1302
1915
|
effect(() => {
|
|
1303
1916
|
const step = this.selectedStep();
|
|
1304
1917
|
if (step) {
|
|
1305
|
-
|
|
1306
|
-
this.nodeFormControl.patchValue({
|
|
1307
|
-
name: step.name,
|
|
1308
|
-
targetType: step.targetType || '1',
|
|
1309
|
-
group: step.targetType === '1' ? parseInt(step.targetValue) : null,
|
|
1310
|
-
role: step.targetType === '2' ? step.targetValue : null,
|
|
1311
|
-
sla: step.sla,
|
|
1312
|
-
});
|
|
1313
|
-
// Update properties table with step's property permissions
|
|
1314
|
-
const properties = this.workflow()?.properties || [];
|
|
1315
|
-
const updatedTableData = properties.map((prop) => {
|
|
1316
|
-
const stepProperty = step.properties?.find((p) => p.propertyId === prop.id);
|
|
1317
|
-
return {
|
|
1318
|
-
id: prop.id,
|
|
1319
|
-
name: prop?.property?.name,
|
|
1320
|
-
isVisible: stepProperty?.isRead || false,
|
|
1321
|
-
isEditable: stepProperty?.isWrite || false,
|
|
1322
|
-
_original: prop,
|
|
1323
|
-
};
|
|
1324
|
-
});
|
|
1325
|
-
this._propertiesTableData.set(updatedTableData);
|
|
1326
|
-
this.showHideControl.patchValue(step.hasNotification ?? false);
|
|
1918
|
+
this.applyLoadedStep(step);
|
|
1327
1919
|
}
|
|
1328
1920
|
});
|
|
1921
|
+
effect(() => {
|
|
1922
|
+
if (this.currentStepType() !== 'AppAction') {
|
|
1923
|
+
this.lastAppActionDescriptorSignature = '';
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
const actionKey = this.appActionSettingsForm.controls.actionKey.value;
|
|
1927
|
+
if (!actionKey) {
|
|
1928
|
+
this.lastAppActionDescriptorSignature = '';
|
|
1929
|
+
return;
|
|
1930
|
+
}
|
|
1931
|
+
const descriptor = this.currentAppActionDescriptor();
|
|
1932
|
+
const signature = this.createAppActionDescriptorSignature(actionKey, descriptor?.configSchemaJson ?? null);
|
|
1933
|
+
if (signature === this.lastAppActionDescriptorSignature) {
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
this.rebuildAppActionConfigEditor(this.getCurrentAppActionConfigJson(), descriptor?.configSchemaJson ?? null);
|
|
1937
|
+
this.lastAppActionDescriptorSignature = signature;
|
|
1938
|
+
});
|
|
1939
|
+
this.stepForm.controls.targetType.valueChanges
|
|
1940
|
+
.pipe(takeUntilDestroyed())
|
|
1941
|
+
.subscribe(() => this.syncUserInputValidators());
|
|
1942
|
+
this.appActionSettingsForm.controls.actionKey.valueChanges
|
|
1943
|
+
.pipe(takeUntilDestroyed())
|
|
1944
|
+
.subscribe((actionKey) => {
|
|
1945
|
+
if (this.isPatchingStepForm) {
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
this.onAppActionActionChanged(actionKey);
|
|
1949
|
+
});
|
|
1329
1950
|
}
|
|
1330
|
-
// Handle main tab change (Workflow/Notification)
|
|
1331
1951
|
onMainTabChange(tab) {
|
|
1332
1952
|
this.mainTab.set(tab);
|
|
1333
1953
|
if (tab === 'notification') {
|
|
@@ -1338,19 +1958,16 @@ class WorkflowBuilder {
|
|
|
1338
1958
|
}
|
|
1339
1959
|
}
|
|
1340
1960
|
}
|
|
1341
|
-
// Handle node dialog tab change (Settings/Properties/Notification)
|
|
1342
1961
|
onNodeDialogTabChange(tab) {
|
|
1343
1962
|
this.selectedTab.set(tab);
|
|
1344
1963
|
}
|
|
1345
1964
|
onPublishToggle(isPublished) {
|
|
1346
|
-
// Prevent publishing if workflow is not valid
|
|
1347
1965
|
if (isPublished && !this.isFlowValid()) {
|
|
1348
1966
|
this.isPublished.set(false);
|
|
1349
1967
|
return;
|
|
1350
1968
|
}
|
|
1351
1969
|
this.workflowFacade.publishWorkflow(isPublished).subscribe({
|
|
1352
1970
|
error: () => {
|
|
1353
|
-
// Revert the toggle on error
|
|
1354
1971
|
this.isPublished.set(!isPublished);
|
|
1355
1972
|
},
|
|
1356
1973
|
});
|
|
@@ -1358,175 +1975,102 @@ class WorkflowBuilder {
|
|
|
1358
1975
|
onWorkflowChange(workflowId) {
|
|
1359
1976
|
this.workflowFacade.selectWorkflow(workflowId);
|
|
1360
1977
|
}
|
|
1361
|
-
clearForm() {
|
|
1362
|
-
// Reset isEditingInitialNode flag
|
|
1363
|
-
this.isEditingInitialNode.set(false);
|
|
1364
|
-
// Reset form to default values
|
|
1365
|
-
this.nodeFormControl.reset({
|
|
1366
|
-
name: { en: '', ar: '' },
|
|
1367
|
-
targetType: '1',
|
|
1368
|
-
group: null,
|
|
1369
|
-
role: null,
|
|
1370
|
-
sla: 0,
|
|
1371
|
-
});
|
|
1372
|
-
// Reset properties table to default state (all unchecked)
|
|
1373
|
-
const properties = this.workflow()?.properties || [];
|
|
1374
|
-
const resetTableData = properties.map((prop) => ({
|
|
1375
|
-
id: prop.id,
|
|
1376
|
-
name: prop?.property?.name,
|
|
1377
|
-
isVisible: false,
|
|
1378
|
-
isEditable: false,
|
|
1379
|
-
_original: prop,
|
|
1380
|
-
}));
|
|
1381
|
-
this._propertiesTableData.set(resetTableData);
|
|
1382
|
-
// Reset toggle controls
|
|
1383
|
-
this.showHideControl.patchValue(false);
|
|
1384
|
-
// Reset to first tab
|
|
1385
|
-
this.selectedTab.set('tab1');
|
|
1386
|
-
}
|
|
1387
|
-
onPropertyCellChange(event) {
|
|
1388
|
-
if (event.type !== 'boolean')
|
|
1389
|
-
return;
|
|
1390
|
-
switch (event.column) {
|
|
1391
|
-
case 'isEditable':
|
|
1392
|
-
if (event.value)
|
|
1393
|
-
event.row.isVisible = true;
|
|
1394
|
-
break;
|
|
1395
|
-
case 'isVisible':
|
|
1396
|
-
if (!event.value)
|
|
1397
|
-
event.row.isEditable = false;
|
|
1398
|
-
break;
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
1978
|
onStructureAction(event) {
|
|
1402
1979
|
switch (event.action) {
|
|
1403
|
-
case '
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
isRead: prop.isVisible,
|
|
1414
|
-
isWrite: prop.isEditable,
|
|
1415
|
-
}));
|
|
1416
|
-
// Prepare step payload
|
|
1417
|
-
const stepPayload = {
|
|
1418
|
-
name: formValue.name,
|
|
1419
|
-
targetType: formValue.targetType,
|
|
1420
|
-
targetValue: formValue.targetType === '1' ? formValue.group : formValue.role,
|
|
1421
|
-
sla: formValue.sla || 0,
|
|
1422
|
-
properties: propertiesPayload,
|
|
1423
|
-
hasNotification: this.showHideControl.value,
|
|
1424
|
-
hasForm: true,
|
|
1425
|
-
};
|
|
1426
|
-
// Create step via facade
|
|
1427
|
-
this.workflowFacade.createStep(stepPayload);
|
|
1980
|
+
case 'startCreating': {
|
|
1981
|
+
const stepType = normalizeWorkflowStepType(event.data?.type);
|
|
1982
|
+
this.clearForm(stepType);
|
|
1983
|
+
this.isCreatingStep.set(true);
|
|
1984
|
+
this.applyCreateNodeDefaults(event.data);
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
case 'startUpdating': {
|
|
1988
|
+
if (!event.data) {
|
|
1989
|
+
return;
|
|
1428
1990
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
// Load step data
|
|
1439
|
-
this.workflowFacade.loadStep(stepId);
|
|
1440
|
-
if (stepId) {
|
|
1441
|
-
this.notificationFacade.setModuleInfo('step', stepId);
|
|
1442
|
-
this.notificationFacade.loadBaseData();
|
|
1991
|
+
this.clearForm(normalizeWorkflowStepType(event.data.type));
|
|
1992
|
+
this.isCreatingStep.set(false);
|
|
1993
|
+
this.isEditingInitialNode.set(!!event.data.isInitial);
|
|
1994
|
+
this.configureStepTypeControl();
|
|
1995
|
+
this.workflowFacade.loadStep(event.data.id);
|
|
1996
|
+
if (event.data.id) {
|
|
1997
|
+
this.notificationFacade.setModuleInfo('step', event.data.id);
|
|
1998
|
+
this.notificationFacade.loadBaseData();
|
|
1999
|
+
if (event.data.type !== 'AppAction') {
|
|
1443
2000
|
const workflowId = this.workflow()?.id;
|
|
1444
2001
|
if (workflowId) {
|
|
1445
|
-
this.formBuilderFacade.setModuleInfo('processstep',
|
|
2002
|
+
this.formBuilderFacade.setModuleInfo('processstep', event.data.id, 'processschema', workflowId);
|
|
1446
2003
|
this.formBuilderFacade.getFormConfiguration();
|
|
1447
2004
|
}
|
|
1448
2005
|
}
|
|
1449
2006
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
this.
|
|
1454
|
-
this.
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
.filter((prop) => prop.isVisible || prop.isEditable)
|
|
1465
|
-
.map((prop) => ({
|
|
1466
|
-
PropertyId: prop.id,
|
|
1467
|
-
isRead: prop.isVisible,
|
|
1468
|
-
isWrite: prop.isEditable,
|
|
1469
|
-
}));
|
|
1470
|
-
// Prepare step payload
|
|
1471
|
-
const stepPayload = {
|
|
1472
|
-
name: formValue.name,
|
|
1473
|
-
targetType: formValue.targetType,
|
|
1474
|
-
targetValue: formValue.targetType === '1' ? formValue.group : formValue.role,
|
|
1475
|
-
sla: formValue.sla || event.data.sla,
|
|
1476
|
-
properties: propertiesPayload,
|
|
1477
|
-
hasNotification: this.showHideControl.value,
|
|
1478
|
-
hasForm: true,
|
|
1479
|
-
};
|
|
1480
|
-
// Update step via facade, passing isInitial flag
|
|
1481
|
-
this.workflowFacade.updateStep(stepId, stepPayload, isInitial);
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
2009
|
+
case 'createNode': {
|
|
2010
|
+
this.isCreatingStep.set(false);
|
|
2011
|
+
const stepPayload = this.buildStepPayload();
|
|
2012
|
+
if (!stepPayload) {
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
this.workflowFacade.createStep(stepPayload);
|
|
2016
|
+
return;
|
|
2017
|
+
}
|
|
2018
|
+
case 'updateNode': {
|
|
2019
|
+
if (!event.data?.id) {
|
|
2020
|
+
return;
|
|
1482
2021
|
}
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
const nodeIdToDelete = event.data.id;
|
|
1487
|
-
this.confirmationService.confirmDelete({
|
|
1488
|
-
type: 'dialog',
|
|
1489
|
-
accept: () => {
|
|
1490
|
-
this.workflowFacade.deleteStep(nodeIdToDelete);
|
|
1491
|
-
},
|
|
1492
|
-
reject: () => { },
|
|
1493
|
-
});
|
|
2022
|
+
const stepPayload = this.buildStepPayload();
|
|
2023
|
+
if (!stepPayload) {
|
|
2024
|
+
return;
|
|
1494
2025
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
targetStepId: event.data.to,
|
|
1502
|
-
priority: event.data.priority ?? 1,
|
|
1503
|
-
formula: event.data.formula ?? null,
|
|
1504
|
-
};
|
|
1505
|
-
this.workflowFacade.createConnection(connectionPayload);
|
|
2026
|
+
this.workflowFacade.updateStep(event.data.id, stepPayload, !!event.data.isInitial);
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
case 'deleteNode': {
|
|
2030
|
+
if (!event.data) {
|
|
2031
|
+
return;
|
|
1506
2032
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
2033
|
+
this.confirmationService.confirmDelete({
|
|
2034
|
+
type: 'dialog',
|
|
2035
|
+
accept: () => {
|
|
2036
|
+
this.workflowFacade.deleteStep(event.data.id);
|
|
2037
|
+
},
|
|
2038
|
+
reject: () => { },
|
|
2039
|
+
});
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
case 'createConnection': {
|
|
2043
|
+
if (!event.data) {
|
|
2044
|
+
return;
|
|
1519
2045
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
2046
|
+
this.workflowFacade.createConnection({
|
|
2047
|
+
sourceStepId: event.data.from,
|
|
2048
|
+
targetStepId: event.data.to,
|
|
2049
|
+
priority: event.data.priority ?? 1,
|
|
2050
|
+
formula: event.data.formula ?? null,
|
|
2051
|
+
});
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
case 'updateConnection': {
|
|
2055
|
+
if (!event.data) {
|
|
2056
|
+
return;
|
|
1526
2057
|
}
|
|
1527
|
-
|
|
2058
|
+
this.workflowFacade.updateConnection(event.data.id, {
|
|
2059
|
+
sourceStepId: event.data.from,
|
|
2060
|
+
targetStepId: event.data.to,
|
|
2061
|
+
priority: event.data.priority ?? 1,
|
|
2062
|
+
formula: event.data.formula ?? null,
|
|
2063
|
+
});
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
case 'deleteConnection': {
|
|
2067
|
+
if (event.data?.id) {
|
|
2068
|
+
this.workflowFacade.deleteConnection(event.data.id);
|
|
2069
|
+
}
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
1528
2072
|
default:
|
|
1529
|
-
|
|
2073
|
+
return;
|
|
1530
2074
|
}
|
|
1531
2075
|
}
|
|
1532
2076
|
openNotificationsModal() {
|
|
@@ -1539,10 +2083,7 @@ class WorkflowBuilder {
|
|
|
1539
2083
|
appendTo: '#page-content',
|
|
1540
2084
|
inputValues: {},
|
|
1541
2085
|
});
|
|
1542
|
-
notificationsDialogRef.onClose.subscribe((
|
|
1543
|
-
if (result?.saved) {
|
|
1544
|
-
}
|
|
1545
|
-
});
|
|
2086
|
+
notificationsDialogRef.onClose.subscribe(() => { });
|
|
1546
2087
|
}
|
|
1547
2088
|
openFormModal() {
|
|
1548
2089
|
const formDialogRef = this.modal.openModal(WorkflowFormDrawer, 'drawer', {
|
|
@@ -1556,8 +2097,386 @@ class WorkflowBuilder {
|
|
|
1556
2097
|
});
|
|
1557
2098
|
formDialogRef.onClose.subscribe(() => { });
|
|
1558
2099
|
}
|
|
2100
|
+
getStepBadgeClass(type) {
|
|
2101
|
+
return type === 'AppAction'
|
|
2102
|
+
? 'inline-flex rounded-full bg-amber-100 px-2 py-1 text-xs font-semibold text-amber-800'
|
|
2103
|
+
: 'inline-flex rounded-full bg-sky-100 px-2 py-1 text-xs font-semibold text-sky-800';
|
|
2104
|
+
}
|
|
2105
|
+
getStepCardTitle(step) {
|
|
2106
|
+
return this.resolveDisplayName(step.name);
|
|
2107
|
+
}
|
|
2108
|
+
getStepTypeLabel(type) {
|
|
2109
|
+
return this.transloco.translate(type === 'AppAction'
|
|
2110
|
+
? 'workflow.builder.stepTypeAppAction'
|
|
2111
|
+
: 'workflow.builder.stepTypeUserInput');
|
|
2112
|
+
}
|
|
2113
|
+
getAppActionConfigControl(key) {
|
|
2114
|
+
return this.appActionConfigForm.get(key);
|
|
2115
|
+
}
|
|
2116
|
+
getAppActionEnumOptions(field) {
|
|
2117
|
+
return (field.enumValues ?? []).map((value) => ({
|
|
2118
|
+
label: String(value),
|
|
2119
|
+
value,
|
|
2120
|
+
}));
|
|
2121
|
+
}
|
|
2122
|
+
getAppActionDescription() {
|
|
2123
|
+
return this.currentAppActionDescriptor()?.description ?? null;
|
|
2124
|
+
}
|
|
2125
|
+
getAppActionDisplayName() {
|
|
2126
|
+
return (this.currentAppActionDescriptor()?.displayName ||
|
|
2127
|
+
this.appActionSettingsForm.controls.actionKey.value ||
|
|
2128
|
+
this.transloco.translate('workflow.builder.appActionUnavailable'));
|
|
2129
|
+
}
|
|
2130
|
+
getAppActionKey() {
|
|
2131
|
+
return this.appActionSettingsForm.controls.actionKey.value ?? '';
|
|
2132
|
+
}
|
|
2133
|
+
getAppActionConfigMessages() {
|
|
2134
|
+
const definition = this.appActionConfigDefinition();
|
|
2135
|
+
const serialization = serializeWorkflowAppActionConfigValue(this.appActionConfigForm.getRawValue(), definition, this.appActionRawConfigControl.value);
|
|
2136
|
+
return Array.from(new Set([
|
|
2137
|
+
...definition.errors,
|
|
2138
|
+
...(this.appActionDetailError() ? [this.appActionDetailError()] : []),
|
|
2139
|
+
...serialization.errors,
|
|
2140
|
+
]));
|
|
2141
|
+
}
|
|
2142
|
+
getAppActionDiscoveryMessage() {
|
|
2143
|
+
if (this.currentStepType() !== 'AppAction') {
|
|
2144
|
+
return null;
|
|
2145
|
+
}
|
|
2146
|
+
if (this.appActionListError()) {
|
|
2147
|
+
return this.appActionListError();
|
|
2148
|
+
}
|
|
2149
|
+
return null;
|
|
2150
|
+
}
|
|
2151
|
+
clearForm(stepType = 'UserInput') {
|
|
2152
|
+
const defaults = createDefaultWorkflowStepEditorValue();
|
|
2153
|
+
this.isPatchingStepForm = true;
|
|
2154
|
+
this.isEditingInitialNode.set(false);
|
|
2155
|
+
this.currentStepType.set(stepType);
|
|
2156
|
+
this.draftAppActionDescriptor.set(null);
|
|
2157
|
+
this.selectedTab.set('tab1');
|
|
2158
|
+
this.stepForm.reset({
|
|
2159
|
+
nameEn: defaults.name['en'],
|
|
2160
|
+
nameAr: defaults.name['ar'],
|
|
2161
|
+
type: stepType,
|
|
2162
|
+
targetType: defaults.targetType,
|
|
2163
|
+
group: defaults.group,
|
|
2164
|
+
role: defaults.role,
|
|
2165
|
+
sla: defaults.sla,
|
|
2166
|
+
}, { emitEvent: false });
|
|
2167
|
+
this.configureStepTypeControl();
|
|
2168
|
+
this.resetPropertiesTable();
|
|
2169
|
+
this.showHideControl.setValue(false, { emitEvent: false });
|
|
2170
|
+
this.resetAppActionState();
|
|
2171
|
+
this.isPatchingStepForm = false;
|
|
2172
|
+
this.syncUserInputValidators();
|
|
2173
|
+
if (stepType === 'AppAction') {
|
|
2174
|
+
this.ensureAppActionDiscoveryLoaded();
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
applyLoadedStep(step) {
|
|
2178
|
+
const editorState = mapWorkflowStepToEditorState(step);
|
|
2179
|
+
const stepType = step.isInitial
|
|
2180
|
+
? 'UserInput'
|
|
2181
|
+
: editorState.stepFormValue.type;
|
|
2182
|
+
const descriptor = this.resolveDescriptorForActionKey(editorState.appActionValue.actionKey, step);
|
|
2183
|
+
this.isPatchingStepForm = true;
|
|
2184
|
+
this.isEditingInitialNode.set(!!step.isInitial);
|
|
2185
|
+
this.currentStepType.set(stepType);
|
|
2186
|
+
this.selectedTab.set('tab1');
|
|
2187
|
+
this.stepForm.reset({
|
|
2188
|
+
nameEn: editorState.stepFormValue.name['en'] ?? '',
|
|
2189
|
+
nameAr: editorState.stepFormValue.name['ar'] ?? '',
|
|
2190
|
+
type: stepType,
|
|
2191
|
+
targetType: editorState.stepFormValue.targetType ?? '1',
|
|
2192
|
+
group: editorState.stepFormValue.group,
|
|
2193
|
+
role: editorState.stepFormValue.role,
|
|
2194
|
+
sla: editorState.stepFormValue.sla,
|
|
2195
|
+
}, { emitEvent: false });
|
|
2196
|
+
this.configureStepTypeControl();
|
|
2197
|
+
this.populatePropertiesTable(step);
|
|
2198
|
+
this.showHideControl.setValue(step.hasNotification ?? false, {
|
|
2199
|
+
emitEvent: false,
|
|
2200
|
+
});
|
|
2201
|
+
this.appActionSettingsForm.reset({
|
|
2202
|
+
actionKey: editorState.appActionValue.actionKey,
|
|
2203
|
+
failureBehavior: editorState.appActionValue.failureBehavior,
|
|
2204
|
+
timeoutSeconds: editorState.appActionValue.timeoutSeconds,
|
|
2205
|
+
}, { emitEvent: false });
|
|
2206
|
+
this.rebuildAppActionConfigEditor(editorState.appActionValue.configJson, descriptor?.configSchemaJson ?? step.appAction?.configSchemaJson ?? null);
|
|
2207
|
+
this.lastAppActionDescriptorSignature =
|
|
2208
|
+
this.createAppActionDescriptorSignature(editorState.appActionValue.actionKey, descriptor?.configSchemaJson ??
|
|
2209
|
+
step.appAction?.configSchemaJson ??
|
|
2210
|
+
null);
|
|
2211
|
+
this.isPatchingStepForm = false;
|
|
2212
|
+
this.syncUserInputValidators();
|
|
2213
|
+
if (stepType === 'AppAction') {
|
|
2214
|
+
this.ensureAppActionDiscoveryLoaded();
|
|
2215
|
+
this.ensureAppActionDetailLoaded(editorState.appActionValue.actionKey);
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
onAppActionActionChanged(actionKey) {
|
|
2219
|
+
this.selectedTab.set('tab1');
|
|
2220
|
+
this.lastAppActionDescriptorSignature = '';
|
|
2221
|
+
this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, this.resolveDescriptorForActionKey(actionKey)?.configSchemaJson ?? null);
|
|
2222
|
+
if (actionKey) {
|
|
2223
|
+
this.ensureAppActionDetailLoaded(actionKey);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
applyCreateNodeDefaults(nodeData) {
|
|
2227
|
+
if (!nodeData) {
|
|
2228
|
+
return;
|
|
2229
|
+
}
|
|
2230
|
+
const stepType = normalizeWorkflowStepType(nodeData.type);
|
|
2231
|
+
if (stepType !== 'AppAction') {
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
const actionKey = typeof nodeData.actionKey === 'string' ? nodeData.actionKey : null;
|
|
2235
|
+
const displayName = typeof nodeData.displayName === 'string'
|
|
2236
|
+
? nodeData.displayName
|
|
2237
|
+
: typeof nodeData.label === 'string'
|
|
2238
|
+
? nodeData.label
|
|
2239
|
+
: '';
|
|
2240
|
+
this.draftAppActionDescriptor.set({
|
|
2241
|
+
appCode: typeof nodeData.appCode === 'string' ? nodeData.appCode : 'pplus',
|
|
2242
|
+
actionKey: actionKey ?? '',
|
|
2243
|
+
displayName,
|
|
2244
|
+
description: typeof nodeData.description === 'string' ? nodeData.description : null,
|
|
2245
|
+
configSchemaJson: typeof nodeData.configSchemaJson === 'string'
|
|
2246
|
+
? nodeData.configSchemaJson
|
|
2247
|
+
: null,
|
|
2248
|
+
requiredContextKeys: Array.isArray(nodeData.requiredContextKeys)
|
|
2249
|
+
? nodeData.requiredContextKeys.map(String)
|
|
2250
|
+
: [],
|
|
2251
|
+
supportedScopes: Array.isArray(nodeData.supportedScopes)
|
|
2252
|
+
? nodeData.supportedScopes.map(String)
|
|
2253
|
+
: [],
|
|
2254
|
+
});
|
|
2255
|
+
this.isPatchingStepForm = true;
|
|
2256
|
+
this.stepForm.patchValue({
|
|
2257
|
+
nameEn: displayName,
|
|
2258
|
+
nameAr: displayName,
|
|
2259
|
+
type: 'AppAction',
|
|
2260
|
+
}, { emitEvent: false });
|
|
2261
|
+
this.appActionSettingsForm.patchValue({
|
|
2262
|
+
actionKey,
|
|
2263
|
+
failureBehavior: DEFAULT_APP_ACTION_EDITOR_VALUE.failureBehavior,
|
|
2264
|
+
timeoutSeconds: DEFAULT_APP_ACTION_EDITOR_VALUE.timeoutSeconds,
|
|
2265
|
+
}, { emitEvent: false });
|
|
2266
|
+
this.isPatchingStepForm = false;
|
|
2267
|
+
this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, this.draftAppActionDescriptor()?.configSchemaJson ?? null);
|
|
2268
|
+
this.ensureAppActionDetailLoaded(actionKey);
|
|
2269
|
+
}
|
|
2270
|
+
resetPropertiesTable() {
|
|
2271
|
+
const properties = this.workflow()?.properties ?? [];
|
|
2272
|
+
this.propertiesTableDataState.set(properties.map((property) => ({
|
|
2273
|
+
id: property.id,
|
|
2274
|
+
name: property.property.name,
|
|
2275
|
+
isVisible: false,
|
|
2276
|
+
isEditable: false,
|
|
2277
|
+
_original: property,
|
|
2278
|
+
})));
|
|
2279
|
+
}
|
|
2280
|
+
populatePropertiesTable(step) {
|
|
2281
|
+
const properties = this.workflow()?.properties ?? [];
|
|
2282
|
+
this.propertiesTableDataState.set(properties.map((property) => {
|
|
2283
|
+
const stepProperty = step.properties?.find((item) => item.propertyId === property.id);
|
|
2284
|
+
return {
|
|
2285
|
+
id: property.id,
|
|
2286
|
+
name: property.property.name,
|
|
2287
|
+
isVisible: stepProperty?.isRead || false,
|
|
2288
|
+
isEditable: stepProperty?.isWrite || false,
|
|
2289
|
+
_original: property,
|
|
2290
|
+
};
|
|
2291
|
+
}));
|
|
2292
|
+
}
|
|
2293
|
+
configureStepTypeControl() {
|
|
2294
|
+
if (this.isEditingInitialNode()) {
|
|
2295
|
+
this.stepForm.controls.type.disable({ emitEvent: false });
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
this.stepForm.controls.type.enable({ emitEvent: false });
|
|
2299
|
+
}
|
|
2300
|
+
syncUserInputValidators() {
|
|
2301
|
+
const groupControl = this.stepForm.controls.group;
|
|
2302
|
+
const roleControl = this.stepForm.controls.role;
|
|
2303
|
+
const targetType = this.stepForm.controls.targetType.value;
|
|
2304
|
+
groupControl.clearValidators();
|
|
2305
|
+
roleControl.clearValidators();
|
|
2306
|
+
if (this.currentStepType() === 'UserInput' &&
|
|
2307
|
+
!this.isEditingInitialNode() &&
|
|
2308
|
+
targetType === '1') {
|
|
2309
|
+
groupControl.addValidators(Validators.required);
|
|
2310
|
+
}
|
|
2311
|
+
if (this.currentStepType() === 'UserInput' &&
|
|
2312
|
+
!this.isEditingInitialNode() &&
|
|
2313
|
+
targetType === '2') {
|
|
2314
|
+
roleControl.addValidators(Validators.required);
|
|
2315
|
+
}
|
|
2316
|
+
groupControl.updateValueAndValidity({ emitEvent: false });
|
|
2317
|
+
roleControl.updateValueAndValidity({ emitEvent: false });
|
|
2318
|
+
}
|
|
2319
|
+
resetAppActionState() {
|
|
2320
|
+
this.appActionSettingsForm.reset({
|
|
2321
|
+
actionKey: DEFAULT_APP_ACTION_EDITOR_VALUE.actionKey,
|
|
2322
|
+
failureBehavior: DEFAULT_APP_ACTION_EDITOR_VALUE.failureBehavior,
|
|
2323
|
+
timeoutSeconds: DEFAULT_APP_ACTION_EDITOR_VALUE.timeoutSeconds,
|
|
2324
|
+
}, { emitEvent: false });
|
|
2325
|
+
this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, null);
|
|
2326
|
+
this.lastAppActionDescriptorSignature = '';
|
|
2327
|
+
}
|
|
2328
|
+
rebuildAppActionConfigEditor(configJson, configSchemaJson) {
|
|
2329
|
+
const definition = buildWorkflowAppActionConfigEditorDefinition(configSchemaJson);
|
|
2330
|
+
const editorValue = mapWorkflowAppActionConfigJsonToEditorValue(configJson, definition);
|
|
2331
|
+
Object.keys(this.appActionConfigForm.controls).forEach((key) => {
|
|
2332
|
+
this.appActionConfigForm.removeControl(key);
|
|
2333
|
+
});
|
|
2334
|
+
definition.fields.forEach((field) => {
|
|
2335
|
+
this.appActionConfigForm.addControl(field.key, new FormControl(editorValue.fields[field.key] ?? null, {
|
|
2336
|
+
validators: field.required ? [Validators.required] : [],
|
|
2337
|
+
}));
|
|
2338
|
+
});
|
|
2339
|
+
this.appActionRawConfigControl.setValue(editorValue.rawConfigJson, {
|
|
2340
|
+
emitEvent: false,
|
|
2341
|
+
});
|
|
2342
|
+
this.appActionConfigDefinition.set(definition);
|
|
2343
|
+
}
|
|
2344
|
+
ensureAppActionDiscoveryLoaded() {
|
|
2345
|
+
this.workflowFacade.loadAppActions();
|
|
2346
|
+
}
|
|
2347
|
+
ensureAppActionDetailLoaded(actionKey) {
|
|
2348
|
+
if (!actionKey) {
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
const cachedDescriptor = this.resolveDescriptorForActionKey(actionKey);
|
|
2352
|
+
if (cachedDescriptor?.configSchemaJson) {
|
|
2353
|
+
return;
|
|
2354
|
+
}
|
|
2355
|
+
this.workflowFacade.loadAppActionDetail(actionKey);
|
|
2356
|
+
}
|
|
2357
|
+
resolveDescriptorForActionKey(actionKey, step) {
|
|
2358
|
+
if (!actionKey) {
|
|
2359
|
+
return null;
|
|
2360
|
+
}
|
|
2361
|
+
const selectedDescriptor = this.selectedAppActionDescriptor();
|
|
2362
|
+
if (selectedDescriptor?.actionKey === actionKey) {
|
|
2363
|
+
return selectedDescriptor;
|
|
2364
|
+
}
|
|
2365
|
+
const cachedDescriptor = this.appActionDescriptors().find((descriptor) => descriptor.actionKey === actionKey);
|
|
2366
|
+
if (cachedDescriptor) {
|
|
2367
|
+
return cachedDescriptor;
|
|
2368
|
+
}
|
|
2369
|
+
const currentStep = step ?? this.selectedStep();
|
|
2370
|
+
if (currentStep?.type === 'AppAction' &&
|
|
2371
|
+
currentStep.appAction?.actionKey === actionKey) {
|
|
2372
|
+
return {
|
|
2373
|
+
appCode: currentStep.appAction.appCode,
|
|
2374
|
+
actionKey,
|
|
2375
|
+
displayName: currentStep.appAction.displayName ?? actionKey,
|
|
2376
|
+
description: currentStep.appAction.description ?? null,
|
|
2377
|
+
configSchemaJson: currentStep.appAction.configSchemaJson ?? null,
|
|
2378
|
+
requiredContextKeys: currentStep.appAction.requiredContextKeys ?? [],
|
|
2379
|
+
supportedScopes: currentStep.appAction.supportedScopes ?? [],
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2382
|
+
return null;
|
|
2383
|
+
}
|
|
2384
|
+
buildStepPayload() {
|
|
2385
|
+
if (this.isNodeDialogSaveDisabled()) {
|
|
2386
|
+
return null;
|
|
2387
|
+
}
|
|
2388
|
+
const formValue = this.stepForm.getRawValue();
|
|
2389
|
+
const appActionConfig = serializeWorkflowAppActionConfigValue(this.appActionConfigForm.getRawValue(), this.appActionConfigDefinition(), this.appActionRawConfigControl.value);
|
|
2390
|
+
return buildWorkflowStepPayload({
|
|
2391
|
+
formValue: {
|
|
2392
|
+
name: {
|
|
2393
|
+
en: formValue.nameEn,
|
|
2394
|
+
ar: formValue.nameAr,
|
|
2395
|
+
},
|
|
2396
|
+
type: normalizeWorkflowStepType(formValue.type),
|
|
2397
|
+
targetType: formValue.targetType || '1',
|
|
2398
|
+
group: formValue.group,
|
|
2399
|
+
role: this.normalizeRoleValue(formValue.role),
|
|
2400
|
+
sla: Number(formValue.sla ?? 0),
|
|
2401
|
+
},
|
|
2402
|
+
properties: this.buildPropertiesPayload(),
|
|
2403
|
+
hasNotification: this.showHideControl.value,
|
|
2404
|
+
appActionValue: this.currentStepType() === 'AppAction'
|
|
2405
|
+
? {
|
|
2406
|
+
actionKey: this.appActionSettingsForm.controls.actionKey.value,
|
|
2407
|
+
failureBehavior: this.appActionSettingsForm.controls.failureBehavior.value,
|
|
2408
|
+
timeoutSeconds: Number(this.appActionSettingsForm.controls.timeoutSeconds.value ?? 300),
|
|
2409
|
+
configJson: appActionConfig.configJson,
|
|
2410
|
+
}
|
|
2411
|
+
: null,
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
buildPropertiesPayload() {
|
|
2415
|
+
return this.propertiesTableData()
|
|
2416
|
+
.filter((property) => property.isVisible || property.isEditable)
|
|
2417
|
+
.map((property) => ({
|
|
2418
|
+
PropertyId: property.id,
|
|
2419
|
+
isRead: property.isVisible,
|
|
2420
|
+
isWrite: property.isEditable,
|
|
2421
|
+
}));
|
|
2422
|
+
}
|
|
2423
|
+
getCurrentAppActionConfigJson() {
|
|
2424
|
+
const definition = this.appActionConfigDefinition();
|
|
2425
|
+
if (definition.mode === 'raw') {
|
|
2426
|
+
return this.appActionRawConfigControl.value;
|
|
2427
|
+
}
|
|
2428
|
+
return serializeWorkflowAppActionConfigValue(this.appActionConfigForm.getRawValue(), definition, this.appActionRawConfigControl.value).configJson;
|
|
2429
|
+
}
|
|
2430
|
+
createAppActionDescriptorSignature(actionKey, configSchemaJson) {
|
|
2431
|
+
return `${actionKey ?? ''}::${configSchemaJson ?? ''}`;
|
|
2432
|
+
}
|
|
2433
|
+
isNodeDialogSaveDisabled() {
|
|
2434
|
+
if (this.selectedTab() !== 'tab1' || this.loadingStep()) {
|
|
2435
|
+
return true;
|
|
2436
|
+
}
|
|
2437
|
+
if (this.stepForm.controls.nameEn.invalid ||
|
|
2438
|
+
this.stepForm.controls.nameAr.invalid) {
|
|
2439
|
+
return true;
|
|
2440
|
+
}
|
|
2441
|
+
if (this.currentStepType() === 'UserInput') {
|
|
2442
|
+
if (!this.isEditingInitialNode() && this.stepForm.controls.sla.invalid) {
|
|
2443
|
+
return true;
|
|
2444
|
+
}
|
|
2445
|
+
if (this.isEditingInitialNode()) {
|
|
2446
|
+
return false;
|
|
2447
|
+
}
|
|
2448
|
+
return this.stepForm.controls.targetType.value === '1'
|
|
2449
|
+
? this.stepForm.controls.group.invalid
|
|
2450
|
+
: this.stepForm.controls.role.invalid;
|
|
2451
|
+
}
|
|
2452
|
+
if (this.stepForm.controls.sla.invalid) {
|
|
2453
|
+
return true;
|
|
2454
|
+
}
|
|
2455
|
+
if (!!this.getAppActionDiscoveryMessage()) {
|
|
2456
|
+
return true;
|
|
2457
|
+
}
|
|
2458
|
+
if (this.appActionSettingsForm.invalid) {
|
|
2459
|
+
return true;
|
|
2460
|
+
}
|
|
2461
|
+
return this.getAppActionConfigMessages().length > 0;
|
|
2462
|
+
}
|
|
2463
|
+
normalizeRoleValue(value) {
|
|
2464
|
+
if (value === null || value === undefined || value === '') {
|
|
2465
|
+
return null;
|
|
2466
|
+
}
|
|
2467
|
+
return String(value);
|
|
2468
|
+
}
|
|
2469
|
+
resolveDisplayName(name) {
|
|
2470
|
+
if (typeof name === 'string') {
|
|
2471
|
+
return name;
|
|
2472
|
+
}
|
|
2473
|
+
if (name && typeof name === 'object') {
|
|
2474
|
+
return name['display'] || name['en'] || name['ar'] || '';
|
|
2475
|
+
}
|
|
2476
|
+
return '';
|
|
2477
|
+
}
|
|
1559
2478
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1560
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowBuilder, isStandalone: true, selector: "mt-workflow-builder", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'workflow.builder'\">\r\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\r\n <!-- Main Tabs at top center -->\r\n <div class=\"flex justify-center w-full py-2\">\r\n <mt-tabs\r\n [active]=\"mainTab()\"\r\n (activeChange)=\"onMainTabChange($event)\"\r\n [options]=\"mainTabsList\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n </div>\r\n @switch (mainTab()) {\r\n @case (\"workflow\") {\r\n @if (!loading()) {\r\n <mt-structure-builder\r\n class=\"flex-1\"\r\n [availableNodes]=\"availableNodes\"\r\n [connectionForm]=\"connectionForm()\"\r\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\r\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\r\n [nodeActions]=\"nodeActions()\"\r\n [nodes]=\"steps()\"\r\n [connections]=\"connections()\"\r\n (action)=\"onStructureAction($event)\"\r\n [addModalType]=\"'drawer'\"\r\n [addModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [updateModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [addModalHeader]=\"'workflow.builder.addStep' | transloco\"\r\n [updateModalHeader]=\"'workflow.builder.editStep' | transloco\"\r\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\r\n [appendTo]=\"'page-content'\"\r\n [nodeFields]=\"nodeFields()\"\r\n [availableTabsClass]=\"\r\n hasMultipleWorkflows() ? 'absolute top-30 start-4 z-1 w-64' : ''\r\n \"\r\n [layoutDirection]=\"'LR'\"\r\n >\r\n <div flowContent>\r\n @if (hasMultipleWorkflows()) {\r\n <div class=\"absolute top-4 start-4 z-10\">\r\n <mt-card class=\"w-64\">\r\n <!-- <div class=\"flex items-center gap-3\">\r\n <label class=\"text-lg font-medium\">{{\r\n 'workflow.builder.selectWorkflow' | transloco\r\n }}</label>\r\n </div> -->\r\n <mt-select-field\r\n [options]=\"workflows()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [ngModel]=\"selectedWorkflowId()\"\r\n (ngModelChange)=\"onWorkflowChange($event)\"\r\n class=\"mt-2\"\r\n />\r\n </mt-card>\r\n </div>\r\n }\r\n <div class=\"absolute top-4 end-4 z-10\">\r\n <div class=\"flex flex-col gap-2 w-80\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-10\">\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"dev.dataflow-01\"\r\n />\r\n <label>{{\r\n \"workflow.builder.enableWorkflow\" | transloco\r\n }}</label>\r\n </div>\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onPublishToggle($event)\"\r\n [disabled]=\"isPublishDisabled()\"\r\n ></mt-toggle-field>\r\n </div>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n <ng-template\r\n #nodeDialog\r\n let-close=\"close\"\r\n let-save=\"save\"\r\n let-node=\"node\"\r\n let-drawerController=\"drawerController\"\r\n >\r\n <div class=\"p-4\">\r\n @if (loadingStep()) {\r\n <!-- Loading skeleton -->\r\n <div class=\"space-y-4\">\r\n <!-- Tabs skeleton -->\r\n <div class=\"flex gap-2 mb-4\">\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <!-- Form fields skeleton -->\r\n <div class=\"space-y-4\">\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"3rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"4rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Actual content -->\r\n <mt-tabs\r\n [active]=\"selectedTab()\"\r\n (activeChange)=\"onNodeDialogTabChange($event)\"\r\n [options]=\"tabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n @switch (selectedTab()) {\r\n @case (\"tab1\") {\r\n <div class=\"mt-4\">\r\n <mt-dynamic-form\r\n [formConfig]=\"nodeForm()\"\r\n [formControl]=\"nodeFormControl\"\r\n ></mt-dynamic-form>\r\n </div>\r\n }\r\n @case (\"tab2\") {\r\n <div class=\"mt-4\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-4\">\r\n <div class=\"flex flex-1 items-center gap-3\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"file.file-check-02\"\r\n />\r\n <div class=\"flex flex-col\">\r\n <label class=\"font-medium\">{{\r\n t(\"configureForm\")\r\n }}</label>\r\n <span class=\"text-sm text-muted-color\">{{\r\n t(\"configureForm-description\")\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureForm')\"\r\n size=\"small\"\r\n (onClick)=\"openFormModal()\"\r\n ></mt-button>\r\n </div>\r\n </mt-card>\r\n </div>\r\n\r\n <!-- Properties table (hidden in manageProperties mode) -->\r\n <!-- <div class=\"mt-2\">\r\n <mt-table\r\n [data]=\"propertiesTableData()\"\r\n [columns]=\"propertiesColumns()\"\r\n [selectableRows]=\"false\"\r\n [showGridlines]=\"true\"\r\n [stripedRows]=\"true\"\r\n (cellChange)=\"onPropertyCellChange($event)\"\r\n ></mt-table>\r\n </div> -->\r\n }\r\n @case (\"tab3\") {\r\n <div class=\"mt-4\">\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-notifications')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureNotifications')\"\r\n size=\"small\"\r\n (onClick)=\"openNotificationsModal()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-structure-builder>\r\n } @else {\r\n <p-skeleton width=\"100%\" height=\"50rem\" />\r\n }\r\n }\r\n @case (\"notification\") {\r\n <div class=\"flex-1 flex justify-center items-start p-4\">\r\n <div class=\"w-1/2 max-[1025px]:w-full\">\r\n <mt-card [title]=\"t('notifications')\">\r\n <mt-notification-template />\r\n </mt-card>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "component", type: StructureBuilder, selector: "mt-structure-builder", inputs: ["availableNodes", "nodeForm", "nodeDialogFooterConfig", "connectionForm", "connectionFormulaSchemaId", "connectionFormulaConfig", "nodeActions", "nodeFields", "isAutoLayout", "readonly", "addModalType", "updateModalType", "addModalStyleClass", "updateModalStyleClass", "addModalHeader", "updateModalHeader", "appendTo", "availableTabsClass", "layoutDirection", "nodes", "connections", "nodeTemplate"], outputs: ["nodeActionsEvent", "action", "nodesChange", "connectionsChange"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "size", "fluid", "disabled"], outputs: ["activeChange", "onChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DynamicForm, selector: "mt-dynamic-form", inputs: ["formConfig", "forcedHiddenFieldKeys", "preserveForcedHiddenValues", "visibleSectionKeys"], outputs: ["runtimeMessagesChange"] }, { kind: "component", type: Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { 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: "ngmodule", type: TranslocoModule }, { kind: "directive", type: i2.TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: NotificationTemplate, selector: "mt-notification-template" }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape"], outputs: ["onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
2479
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowBuilder, isStandalone: true, selector: "mt-workflow-builder", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'workflow.builder'\">\r\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\r\n <div class=\"flex justify-center w-full py-2\">\r\n <mt-tabs\r\n [active]=\"mainTab()\"\r\n (activeChange)=\"onMainTabChange($event)\"\r\n [options]=\"mainTabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n </div>\r\n @switch (mainTab()) {\r\n @case (\"workflow\") {\r\n @if (!loading()) {\r\n <mt-structure-builder\r\n class=\"flex-1\"\r\n [availableNodes]=\"availableNodes()\"\r\n [availableNodesLabel]=\"t('stepTemplates')\"\r\n [connectionForm]=\"connectionForm()\"\r\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\r\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\r\n [nodeActions]=\"nodeActions()\"\r\n [nodes]=\"steps()\"\r\n [connections]=\"connections()\"\r\n (action)=\"onStructureAction($event)\"\r\n [addModalType]=\"'drawer'\"\r\n [addModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [updateModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [addModalHeader]=\"'workflow.builder.addStep' | transloco\"\r\n [updateModalHeader]=\"'workflow.builder.editStep' | transloco\"\r\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\r\n [appendTo]=\"'page-content'\"\r\n [nodeFields]=\"nodeFields()\"\r\n [availableTabsClass]=\"hasMultipleWorkflows() ? '!mt-28' : ''\"\r\n [layoutDirection]=\"'LR'\"\r\n >\r\n <div flowContent class=\"pointer-events-none\">\r\n @if (hasMultipleWorkflows()) {\r\n <div class=\"pointer-events-auto absolute top-4 start-4 z-10\">\r\n <mt-card class=\"w-64\">\r\n <mt-select-field\r\n [options]=\"workflows()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [ngModel]=\"selectedWorkflowId()\"\r\n (ngModelChange)=\"onWorkflowChange($event)\"\r\n class=\"mt-2\"\r\n />\r\n </mt-card>\r\n </div>\r\n }\r\n <div class=\"pointer-events-auto absolute top-4 end-4 z-10\">\r\n <div class=\"flex flex-col gap-2 w-80\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-10\">\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"dev.dataflow-01\"\r\n />\r\n <label>{{\r\n \"workflow.builder.enableWorkflow\" | transloco\r\n }}</label>\r\n </div>\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onPublishToggle($event)\"\r\n [disabled]=\"isPublishDisabled()\"\r\n ></mt-toggle-field>\r\n </div>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n <ng-template\r\n #nodeTemplate\r\n let-node\r\n let-actions=\"actions\"\r\n let-onAction=\"onAction\"\r\n >\r\n @if (node.type === \"AppAction\") {\r\n <!-- Compact App Action card: narrower, tighter padding, smaller text -->\r\n <div\r\n class=\"flex min-h-[6rem] min-w-[13rem] flex-col overflow-hidden rounded-2xl bg-content\"\r\n >\r\n <div class=\"flex flex-1 flex-col gap-1.5 px-3 py-2.5\">\r\n <span [class]=\"getStepBadgeClass(node.type)\">\r\n {{ getStepTypeLabel(node.type) }}\r\n </span>\r\n <div\r\n class=\"text-sm leading-tight font-semibold text-surface-900\"\r\n >\r\n {{ getStepCardTitle(node) }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"flex min-h-[2.25rem] items-center justify-end gap-1.5 border-t border-surface-200 bg-surface-50 px-3 py-1\"\r\n >\r\n @for (action of actions; track action.key) {\r\n <mt-button\r\n size=\"small\"\r\n [variant]=\"action?.variant\"\r\n [icon]=\"action?.icon\"\r\n [tooltip]=\"action?.tooltip\"\r\n [severity]=\"action?.severity\"\r\n (onClick)=\"onAction(action, node)\"\r\n >\r\n </mt-button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Form Step card: roomier default layout -->\r\n <div\r\n class=\"min-w-[17rem] overflow-hidden rounded-2xl bg-content\"\r\n >\r\n <div class=\"space-y-3 px-4 py-4\">\r\n <span [class]=\"getStepBadgeClass(node.type)\">\r\n {{ getStepTypeLabel(node.type) }}\r\n </span>\r\n <div class=\"text-base font-semibold text-surface-900\">\r\n {{ getStepCardTitle(node) }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"flex min-h-[3rem] items-center justify-end gap-2 border-t border-surface-200 bg-surface-50 px-4 py-3\"\r\n >\r\n @for (action of actions; track action.key) {\r\n <mt-button\r\n [size]=\"action?.size || 'small'\"\r\n [variant]=\"action?.variant\"\r\n [icon]=\"action?.icon\"\r\n [tooltip]=\"action?.tooltip\"\r\n [severity]=\"action?.severity\"\r\n (onClick)=\"onAction(action, node)\"\r\n >\r\n </mt-button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </ng-template>\r\n <ng-template\r\n #nodeDialog\r\n let-close=\"close\"\r\n let-save=\"save\"\r\n let-node=\"node\"\r\n let-drawerController=\"drawerController\"\r\n >\r\n <div class=\"p-4\">\r\n @if (loadingStep()) {\r\n <div class=\"space-y-4\">\r\n <div class=\"flex gap-2 mb-4\">\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <!-- Form fields skeleton -->\r\n <div class=\"space-y-4\">\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"3rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"4rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-tabs\r\n [active]=\"selectedTab()\"\r\n (activeChange)=\"onNodeDialogTabChange($event)\"\r\n [options]=\"tabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n @switch (selectedTab()) {\r\n @case (\"tab1\") {\r\n <div class=\"mt-4 space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <mt-text-field\r\n [label]=\"t('nameEnglish')\"\r\n [placeholder]=\"t('enterNameEnglish')\"\r\n [formControl]=\"stepForm.controls.nameEn\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n <mt-text-field\r\n [label]=\"t('nameArabic')\"\r\n [placeholder]=\"t('enterNameArabic')\"\r\n [formControl]=\"stepForm.controls.nameAr\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n </div>\r\n\r\n @if (currentStepType() === \"UserInput\") {\r\n @if (!isEditingInitialNode()) {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"approver\") }}\r\n </div>\r\n <mt-select-field\r\n [label]=\"t('approver')\"\r\n [options]=\"[\r\n { label: t('group'), value: '1' },\r\n { label: t('role'), value: '2' },\r\n ]\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.targetType\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n @if (\r\n stepForm.controls.targetType.value === \"1\"\r\n ) {\r\n <mt-select-field\r\n [label]=\"t('group')\"\r\n [placeholder]=\"t('selectGroup')\"\r\n [options]=\"groups()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [formControl]=\"stepForm.controls.group\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n } @else {\r\n <mt-select-field\r\n [label]=\"t('role')\"\r\n [placeholder]=\"t('selectRole')\"\r\n [options]=\"roles()\"\r\n [group]=\"hasGroupedRoles()\"\r\n optionGroupLabel=\"label\"\r\n optionGroupChildren=\"items\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.role\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n }\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n } @else {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"app\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-700\"\r\n >\r\n {{ t(\"appLabelPplus\") }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"action\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-900\"\r\n >\r\n {{ getAppActionDisplayName() }}\r\n </div>\r\n @if (getAppActionKey()) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ getAppActionKey() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (\r\n getAppActionDescription();\r\n as description\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-muted-color\"\r\n >\r\n {{ description }}\r\n </div>\r\n }\r\n\r\n @if (\r\n getAppActionDiscoveryMessage();\r\n as discoveryMessage\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\r\n >\r\n {{ discoveryMessage }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div>\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionConfiguration\") }}\r\n </div>\r\n <div class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"appActionConfigurationDescription\") }}\r\n </div>\r\n </div>\r\n\r\n @if (\r\n loadingAppActionDetail() &&\r\n appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div class=\"space-y-3\">\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"6rem\"\r\n ></p-skeleton>\r\n </div>\r\n } @else if (\r\n !appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-dashed border-surface-300 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionUnavailable\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"schema\" &&\r\n appActionConfigDefinition().fields.length === 0\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionNoConfigRequired\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"raw\"\r\n ) {\r\n <div class=\"space-y-3\">\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\r\n >\r\n {{ t(\"appActionRawConfigFallback\") }}\r\n </div>\r\n <mt-textarea-field\r\n [label]=\"t('appActionConfigJson')\"\r\n [placeholder]=\"\r\n t('enterAppActionConfigJson')\r\n \"\r\n [formControl]=\"appActionRawConfigControl\"\r\n [rows]=\"'10'\"\r\n ></mt-textarea-field>\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (\r\n field of appActionConfigDefinition().fields;\r\n track field.key\r\n ) {\r\n <div class=\"space-y-2\">\r\n @switch (field.kind) {\r\n @case (\"text\") {\r\n <mt-text-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-text-field>\r\n }\r\n @case (\"number\") {\r\n <mt-number-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-number-field>\r\n }\r\n @case (\"toggle\") {\r\n <mt-toggle-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n ></mt-toggle-field>\r\n }\r\n @case (\"select\") {\r\n <mt-select-field\r\n [label]=\"field.title\"\r\n [options]=\"\r\n getAppActionEnumOptions(field)\r\n \"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-select-field>\r\n }\r\n @case (\"json\") {\r\n <mt-textarea-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n [rows]=\"'8'\"\r\n ></mt-textarea-field>\r\n }\r\n }\r\n\r\n @if (field.description) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ field.description }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (getAppActionConfigMessages().length) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-3 text-sm text-red-700\"\r\n >\r\n @for (\r\n message of getAppActionConfigMessages();\r\n track message\r\n ) {\r\n <div>{{ message }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"advancedExecution\") }}\r\n </div>\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n\r\n <mt-select-field\r\n [label]=\"t('failureBehavior')\"\r\n [options]=\"failureBehaviorOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.failureBehavior\r\n \"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n <mt-number-field\r\n [label]=\"t('timeoutSeconds')\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.timeoutSeconds\r\n \"\r\n [required]=\"true\"\r\n [min]=\"1\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n }\r\n @case (\"tab2\") {\r\n <div class=\"mt-4\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-4\">\r\n <div class=\"flex flex-1 items-center gap-3\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"file.file-check-02\"\r\n />\r\n <div class=\"flex flex-col\">\r\n <label class=\"font-medium\">{{\r\n t(\"configureForm\")\r\n }}</label>\r\n <span class=\"text-sm text-muted-color\">{{\r\n t(\"configureForm-description\")\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureForm')\"\r\n size=\"small\"\r\n (onClick)=\"openFormModal()\"\r\n ></mt-button>\r\n </div>\r\n </mt-card>\r\n </div>\r\n }\r\n @case (\"tab3\") {\r\n <div class=\"mt-4\">\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-notifications')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureNotifications')\"\r\n size=\"small\"\r\n (onClick)=\"openNotificationsModal()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-structure-builder>\r\n } @else {\r\n <p-skeleton width=\"100%\" height=\"50rem\" />\r\n }\r\n }\r\n @case (\"notification\") {\r\n <div class=\"flex-1 flex justify-center items-start p-4\">\r\n <div class=\"w-1/2 max-[1025px]:w-full\">\r\n <mt-card [title]=\"t('notifications')\">\r\n <mt-notification-template />\r\n </mt-card>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "component", type: StructureBuilder, selector: "mt-structure-builder", inputs: ["availableNodes", "availableNodesLabel", "nodeForm", "nodeDialogFooterConfig", "connectionForm", "connectionFormulaSchemaId", "connectionFormulaConfig", "nodeActions", "nodeFields", "isAutoLayout", "readonly", "addModalType", "updateModalType", "addModalStyleClass", "updateModalStyleClass", "addModalHeader", "updateModalHeader", "appendTo", "availableTabsClass", "layoutDirection", "nodes", "connections", "nodeTemplate"], outputs: ["nodeActionsEvent", "action", "nodesChange", "connectionsChange"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "size", "fluid", "disabled"], outputs: ["activeChange", "onChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { 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: "ngmodule", type: TranslocoModule }, { kind: "directive", type: i2.TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: NotificationTemplate, selector: "mt-notification-template" }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape"], outputs: ["onChange"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "icon", "iconPosition"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
1561
2480
|
}
|
|
1562
2481
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilder, decorators: [{
|
|
1563
2482
|
type: Component,
|
|
@@ -1567,14 +2486,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1567
2486
|
Tabs,
|
|
1568
2487
|
ReactiveFormsModule,
|
|
1569
2488
|
FormsModule,
|
|
1570
|
-
DynamicForm,
|
|
1571
2489
|
Skeleton,
|
|
1572
2490
|
ToggleField,
|
|
1573
2491
|
Button,
|
|
1574
2492
|
TranslocoModule,
|
|
1575
2493
|
NotificationTemplate,
|
|
1576
2494
|
SelectField,
|
|
1577
|
-
], host: {}, template: "<ng-container *transloco=\"let t; prefix: 'workflow.builder'\">\r\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\r\n <!-- Main Tabs at top center -->\r\n <div class=\"flex justify-center w-full py-2\">\r\n <mt-tabs\r\n [active]=\"mainTab()\"\r\n (activeChange)=\"onMainTabChange($event)\"\r\n [options]=\"mainTabsList\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n </div>\r\n @switch (mainTab()) {\r\n @case (\"workflow\") {\r\n @if (!loading()) {\r\n <mt-structure-builder\r\n class=\"flex-1\"\r\n [availableNodes]=\"availableNodes\"\r\n [connectionForm]=\"connectionForm()\"\r\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\r\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\r\n [nodeActions]=\"nodeActions()\"\r\n [nodes]=\"steps()\"\r\n [connections]=\"connections()\"\r\n (action)=\"onStructureAction($event)\"\r\n [addModalType]=\"'drawer'\"\r\n [addModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [updateModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [addModalHeader]=\"'workflow.builder.addStep' | transloco\"\r\n [updateModalHeader]=\"'workflow.builder.editStep' | transloco\"\r\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\r\n [appendTo]=\"'page-content'\"\r\n [nodeFields]=\"nodeFields()\"\r\n [availableTabsClass]=\"\r\n hasMultipleWorkflows() ? 'absolute top-30 start-4 z-1 w-64' : ''\r\n \"\r\n [layoutDirection]=\"'LR'\"\r\n >\r\n <div flowContent>\r\n @if (hasMultipleWorkflows()) {\r\n <div class=\"absolute top-4 start-4 z-10\">\r\n <mt-card class=\"w-64\">\r\n <!-- <div class=\"flex items-center gap-3\">\r\n <label class=\"text-lg font-medium\">{{\r\n 'workflow.builder.selectWorkflow' | transloco\r\n }}</label>\r\n </div> -->\r\n <mt-select-field\r\n [options]=\"workflows()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [ngModel]=\"selectedWorkflowId()\"\r\n (ngModelChange)=\"onWorkflowChange($event)\"\r\n class=\"mt-2\"\r\n />\r\n </mt-card>\r\n </div>\r\n }\r\n <div class=\"absolute top-4 end-4 z-10\">\r\n <div class=\"flex flex-col gap-2 w-80\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-10\">\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"dev.dataflow-01\"\r\n />\r\n <label>{{\r\n \"workflow.builder.enableWorkflow\" | transloco\r\n }}</label>\r\n </div>\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onPublishToggle($event)\"\r\n [disabled]=\"isPublishDisabled()\"\r\n ></mt-toggle-field>\r\n </div>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n <ng-template\r\n #nodeDialog\r\n let-close=\"close\"\r\n let-save=\"save\"\r\n let-node=\"node\"\r\n let-drawerController=\"drawerController\"\r\n >\r\n <div class=\"p-4\">\r\n @if (loadingStep()) {\r\n <!-- Loading skeleton -->\r\n <div class=\"space-y-4\">\r\n <!-- Tabs skeleton -->\r\n <div class=\"flex gap-2 mb-4\">\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <!-- Form fields skeleton -->\r\n <div class=\"space-y-4\">\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"3rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"4rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Actual content -->\r\n <mt-tabs\r\n [active]=\"selectedTab()\"\r\n (activeChange)=\"onNodeDialogTabChange($event)\"\r\n [options]=\"tabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n @switch (selectedTab()) {\r\n @case (\"tab1\") {\r\n <div class=\"mt-4\">\r\n <mt-dynamic-form\r\n [formConfig]=\"nodeForm()\"\r\n [formControl]=\"nodeFormControl\"\r\n ></mt-dynamic-form>\r\n </div>\r\n }\r\n @case (\"tab2\") {\r\n <div class=\"mt-4\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-4\">\r\n <div class=\"flex flex-1 items-center gap-3\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"file.file-check-02\"\r\n />\r\n <div class=\"flex flex-col\">\r\n <label class=\"font-medium\">{{\r\n t(\"configureForm\")\r\n }}</label>\r\n <span class=\"text-sm text-muted-color\">{{\r\n t(\"configureForm-description\")\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureForm')\"\r\n size=\"small\"\r\n (onClick)=\"openFormModal()\"\r\n ></mt-button>\r\n </div>\r\n </mt-card>\r\n </div>\r\n\r\n <!-- Properties table (hidden in manageProperties mode) -->\r\n <!-- <div class=\"mt-2\">\r\n <mt-table\r\n [data]=\"propertiesTableData()\"\r\n [columns]=\"propertiesColumns()\"\r\n [selectableRows]=\"false\"\r\n [showGridlines]=\"true\"\r\n [stripedRows]=\"true\"\r\n (cellChange)=\"onPropertyCellChange($event)\"\r\n ></mt-table>\r\n </div> -->\r\n }\r\n @case (\"tab3\") {\r\n <div class=\"mt-4\">\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-notifications')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureNotifications')\"\r\n size=\"small\"\r\n (onClick)=\"openNotificationsModal()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-structure-builder>\r\n } @else {\r\n <p-skeleton width=\"100%\" height=\"50rem\" />\r\n }\r\n }\r\n @case (\"notification\") {\r\n <div class=\"flex-1 flex justify-center items-start p-4\">\r\n <div class=\"w-1/2 max-[1025px]:w-full\">\r\n <mt-card [title]=\"t('notifications')\">\r\n <mt-notification-template />\r\n </mt-card>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n" }]
|
|
2495
|
+
TextField,
|
|
2496
|
+
NumberField,
|
|
2497
|
+
TextareaField,
|
|
2498
|
+
], host: {}, template: "<ng-container *transloco=\"let t; prefix: 'workflow.builder'\">\r\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\r\n <div class=\"flex justify-center w-full py-2\">\r\n <mt-tabs\r\n [active]=\"mainTab()\"\r\n (activeChange)=\"onMainTabChange($event)\"\r\n [options]=\"mainTabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n </div>\r\n @switch (mainTab()) {\r\n @case (\"workflow\") {\r\n @if (!loading()) {\r\n <mt-structure-builder\r\n class=\"flex-1\"\r\n [availableNodes]=\"availableNodes()\"\r\n [availableNodesLabel]=\"t('stepTemplates')\"\r\n [connectionForm]=\"connectionForm()\"\r\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\r\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\r\n [nodeActions]=\"nodeActions()\"\r\n [nodes]=\"steps()\"\r\n [connections]=\"connections()\"\r\n (action)=\"onStructureAction($event)\"\r\n [addModalType]=\"'drawer'\"\r\n [addModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [updateModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [addModalHeader]=\"'workflow.builder.addStep' | transloco\"\r\n [updateModalHeader]=\"'workflow.builder.editStep' | transloco\"\r\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\r\n [appendTo]=\"'page-content'\"\r\n [nodeFields]=\"nodeFields()\"\r\n [availableTabsClass]=\"hasMultipleWorkflows() ? '!mt-28' : ''\"\r\n [layoutDirection]=\"'LR'\"\r\n >\r\n <div flowContent class=\"pointer-events-none\">\r\n @if (hasMultipleWorkflows()) {\r\n <div class=\"pointer-events-auto absolute top-4 start-4 z-10\">\r\n <mt-card class=\"w-64\">\r\n <mt-select-field\r\n [options]=\"workflows()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [ngModel]=\"selectedWorkflowId()\"\r\n (ngModelChange)=\"onWorkflowChange($event)\"\r\n class=\"mt-2\"\r\n />\r\n </mt-card>\r\n </div>\r\n }\r\n <div class=\"pointer-events-auto absolute top-4 end-4 z-10\">\r\n <div class=\"flex flex-col gap-2 w-80\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-10\">\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"dev.dataflow-01\"\r\n />\r\n <label>{{\r\n \"workflow.builder.enableWorkflow\" | transloco\r\n }}</label>\r\n </div>\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onPublishToggle($event)\"\r\n [disabled]=\"isPublishDisabled()\"\r\n ></mt-toggle-field>\r\n </div>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n <ng-template\r\n #nodeTemplate\r\n let-node\r\n let-actions=\"actions\"\r\n let-onAction=\"onAction\"\r\n >\r\n @if (node.type === \"AppAction\") {\r\n <!-- Compact App Action card: narrower, tighter padding, smaller text -->\r\n <div\r\n class=\"flex min-h-[6rem] min-w-[13rem] flex-col overflow-hidden rounded-2xl bg-content\"\r\n >\r\n <div class=\"flex flex-1 flex-col gap-1.5 px-3 py-2.5\">\r\n <span [class]=\"getStepBadgeClass(node.type)\">\r\n {{ getStepTypeLabel(node.type) }}\r\n </span>\r\n <div\r\n class=\"text-sm leading-tight font-semibold text-surface-900\"\r\n >\r\n {{ getStepCardTitle(node) }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"flex min-h-[2.25rem] items-center justify-end gap-1.5 border-t border-surface-200 bg-surface-50 px-3 py-1\"\r\n >\r\n @for (action of actions; track action.key) {\r\n <mt-button\r\n size=\"small\"\r\n [variant]=\"action?.variant\"\r\n [icon]=\"action?.icon\"\r\n [tooltip]=\"action?.tooltip\"\r\n [severity]=\"action?.severity\"\r\n (onClick)=\"onAction(action, node)\"\r\n >\r\n </mt-button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Form Step card: roomier default layout -->\r\n <div\r\n class=\"min-w-[17rem] overflow-hidden rounded-2xl bg-content\"\r\n >\r\n <div class=\"space-y-3 px-4 py-4\">\r\n <span [class]=\"getStepBadgeClass(node.type)\">\r\n {{ getStepTypeLabel(node.type) }}\r\n </span>\r\n <div class=\"text-base font-semibold text-surface-900\">\r\n {{ getStepCardTitle(node) }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"flex min-h-[3rem] items-center justify-end gap-2 border-t border-surface-200 bg-surface-50 px-4 py-3\"\r\n >\r\n @for (action of actions; track action.key) {\r\n <mt-button\r\n [size]=\"action?.size || 'small'\"\r\n [variant]=\"action?.variant\"\r\n [icon]=\"action?.icon\"\r\n [tooltip]=\"action?.tooltip\"\r\n [severity]=\"action?.severity\"\r\n (onClick)=\"onAction(action, node)\"\r\n >\r\n </mt-button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </ng-template>\r\n <ng-template\r\n #nodeDialog\r\n let-close=\"close\"\r\n let-save=\"save\"\r\n let-node=\"node\"\r\n let-drawerController=\"drawerController\"\r\n >\r\n <div class=\"p-4\">\r\n @if (loadingStep()) {\r\n <div class=\"space-y-4\">\r\n <div class=\"flex gap-2 mb-4\">\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <!-- Form fields skeleton -->\r\n <div class=\"space-y-4\">\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"3rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"4rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-tabs\r\n [active]=\"selectedTab()\"\r\n (activeChange)=\"onNodeDialogTabChange($event)\"\r\n [options]=\"tabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n @switch (selectedTab()) {\r\n @case (\"tab1\") {\r\n <div class=\"mt-4 space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <mt-text-field\r\n [label]=\"t('nameEnglish')\"\r\n [placeholder]=\"t('enterNameEnglish')\"\r\n [formControl]=\"stepForm.controls.nameEn\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n <mt-text-field\r\n [label]=\"t('nameArabic')\"\r\n [placeholder]=\"t('enterNameArabic')\"\r\n [formControl]=\"stepForm.controls.nameAr\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n </div>\r\n\r\n @if (currentStepType() === \"UserInput\") {\r\n @if (!isEditingInitialNode()) {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"approver\") }}\r\n </div>\r\n <mt-select-field\r\n [label]=\"t('approver')\"\r\n [options]=\"[\r\n { label: t('group'), value: '1' },\r\n { label: t('role'), value: '2' },\r\n ]\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.targetType\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n @if (\r\n stepForm.controls.targetType.value === \"1\"\r\n ) {\r\n <mt-select-field\r\n [label]=\"t('group')\"\r\n [placeholder]=\"t('selectGroup')\"\r\n [options]=\"groups()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [formControl]=\"stepForm.controls.group\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n } @else {\r\n <mt-select-field\r\n [label]=\"t('role')\"\r\n [placeholder]=\"t('selectRole')\"\r\n [options]=\"roles()\"\r\n [group]=\"hasGroupedRoles()\"\r\n optionGroupLabel=\"label\"\r\n optionGroupChildren=\"items\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.role\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n }\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n } @else {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"app\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-700\"\r\n >\r\n {{ t(\"appLabelPplus\") }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"action\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-900\"\r\n >\r\n {{ getAppActionDisplayName() }}\r\n </div>\r\n @if (getAppActionKey()) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ getAppActionKey() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (\r\n getAppActionDescription();\r\n as description\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-muted-color\"\r\n >\r\n {{ description }}\r\n </div>\r\n }\r\n\r\n @if (\r\n getAppActionDiscoveryMessage();\r\n as discoveryMessage\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\r\n >\r\n {{ discoveryMessage }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div>\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionConfiguration\") }}\r\n </div>\r\n <div class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"appActionConfigurationDescription\") }}\r\n </div>\r\n </div>\r\n\r\n @if (\r\n loadingAppActionDetail() &&\r\n appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div class=\"space-y-3\">\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"6rem\"\r\n ></p-skeleton>\r\n </div>\r\n } @else if (\r\n !appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-dashed border-surface-300 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionUnavailable\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"schema\" &&\r\n appActionConfigDefinition().fields.length === 0\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionNoConfigRequired\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"raw\"\r\n ) {\r\n <div class=\"space-y-3\">\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\r\n >\r\n {{ t(\"appActionRawConfigFallback\") }}\r\n </div>\r\n <mt-textarea-field\r\n [label]=\"t('appActionConfigJson')\"\r\n [placeholder]=\"\r\n t('enterAppActionConfigJson')\r\n \"\r\n [formControl]=\"appActionRawConfigControl\"\r\n [rows]=\"'10'\"\r\n ></mt-textarea-field>\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (\r\n field of appActionConfigDefinition().fields;\r\n track field.key\r\n ) {\r\n <div class=\"space-y-2\">\r\n @switch (field.kind) {\r\n @case (\"text\") {\r\n <mt-text-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-text-field>\r\n }\r\n @case (\"number\") {\r\n <mt-number-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-number-field>\r\n }\r\n @case (\"toggle\") {\r\n <mt-toggle-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n ></mt-toggle-field>\r\n }\r\n @case (\"select\") {\r\n <mt-select-field\r\n [label]=\"field.title\"\r\n [options]=\"\r\n getAppActionEnumOptions(field)\r\n \"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-select-field>\r\n }\r\n @case (\"json\") {\r\n <mt-textarea-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n [rows]=\"'8'\"\r\n ></mt-textarea-field>\r\n }\r\n }\r\n\r\n @if (field.description) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ field.description }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (getAppActionConfigMessages().length) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-3 text-sm text-red-700\"\r\n >\r\n @for (\r\n message of getAppActionConfigMessages();\r\n track message\r\n ) {\r\n <div>{{ message }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"advancedExecution\") }}\r\n </div>\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n\r\n <mt-select-field\r\n [label]=\"t('failureBehavior')\"\r\n [options]=\"failureBehaviorOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.failureBehavior\r\n \"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n <mt-number-field\r\n [label]=\"t('timeoutSeconds')\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.timeoutSeconds\r\n \"\r\n [required]=\"true\"\r\n [min]=\"1\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n }\r\n @case (\"tab2\") {\r\n <div class=\"mt-4\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-4\">\r\n <div class=\"flex flex-1 items-center gap-3\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"file.file-check-02\"\r\n />\r\n <div class=\"flex flex-col\">\r\n <label class=\"font-medium\">{{\r\n t(\"configureForm\")\r\n }}</label>\r\n <span class=\"text-sm text-muted-color\">{{\r\n t(\"configureForm-description\")\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureForm')\"\r\n size=\"small\"\r\n (onClick)=\"openFormModal()\"\r\n ></mt-button>\r\n </div>\r\n </mt-card>\r\n </div>\r\n }\r\n @case (\"tab3\") {\r\n <div class=\"mt-4\">\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-notifications')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureNotifications')\"\r\n size=\"small\"\r\n (onClick)=\"openNotificationsModal()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-structure-builder>\r\n } @else {\r\n <p-skeleton width=\"100%\" height=\"50rem\" />\r\n }\r\n }\r\n @case (\"notification\") {\r\n <div class=\"flex-1 flex justify-center items-start p-4\">\r\n <div class=\"w-1/2 max-[1025px]:w-full\">\r\n <mt-card [title]=\"t('notifications')\">\r\n <mt-notification-template />\r\n </mt-card>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n" }]
|
|
1578
2499
|
}], ctorParameters: () => [] });
|
|
1579
2500
|
|
|
1580
2501
|
// store/app.state.ts
|
|
@@ -1592,5 +2513,5 @@ const REQUEST_CONTEXT = new HttpContextToken(() => ({
|
|
|
1592
2513
|
* Generated bundle index. Do not edit.
|
|
1593
2514
|
*/
|
|
1594
2515
|
|
|
1595
|
-
export { CreateConnection, CreateStep, DeleteConnection, DeleteStep, GetFormulaProperties, GetGroups, GetRolesForModule, GetStep, GetWorkflow, GetWorkflows, PublishWorkflow, REQUEST_CONTEXT, SetModuleInfo, UpdateConnection, UpdateStep, ValidateFlow, WorkflowBuilder, WorkflowFacade, WorkflowState };
|
|
2516
|
+
export { CreateConnection, CreateStep, DeleteConnection, DeleteStep, GetAppActionDetail, GetAppActions, GetFormulaProperties, GetGroups, GetRolesForModule, GetStep, GetWorkflow, GetWorkflows, PublishWorkflow, REQUEST_CONTEXT, SetModuleInfo, UpdateConnection, UpdateStep, ValidateFlow, WorkflowBuilder, WorkflowFacade, WorkflowState };
|
|
1596
2517
|
//# sourceMappingURL=masterteam-workflow.mjs.map
|