@masterteam/workflow 0.0.30 → 0.0.32
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 +68 -45
- package/assets/i18n/en.json +34 -11
- package/assets/workflow.css +2 -2
- package/fesm2022/masterteam-workflow.mjs +1393 -466
- package/fesm2022/masterteam-workflow.mjs.map +1 -1
- package/package.json +17 -16
- package/types/masterteam-workflow.d.ts +239 -76
|
@@ -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
|
}
|
|
@@ -744,8 +831,8 @@ let WorkflowState = class WorkflowState {
|
|
|
744
831
|
return of(null);
|
|
745
832
|
}), finalize(() => endLoading(ctx, 'deleteConnection')));
|
|
746
833
|
}
|
|
747
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
748
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
834
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowState, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
835
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowState });
|
|
749
836
|
};
|
|
750
837
|
__decorate([
|
|
751
838
|
Action(SetModuleInfo)
|
|
@@ -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);
|
|
@@ -840,9 +939,9 @@ WorkflowState = __decorate([
|
|
|
840
939
|
defaults: DEFAULT_STATE,
|
|
841
940
|
})
|
|
842
941
|
], WorkflowState);
|
|
843
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
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
|
}
|
|
@@ -966,10 +1242,10 @@ class WorkflowFacade {
|
|
|
966
1242
|
deleteConnection(connectionId) {
|
|
967
1243
|
return this.store.dispatch(new DeleteConnection(connectionId));
|
|
968
1244
|
}
|
|
969
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
970
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
1245
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1246
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowFacade, providedIn: 'root' });
|
|
971
1247
|
}
|
|
972
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1248
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowFacade, decorators: [{
|
|
973
1249
|
type: Injectable,
|
|
974
1250
|
args: [{
|
|
975
1251
|
providedIn: 'root',
|
|
@@ -978,23 +1254,405 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", 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.0.3", 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
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
993
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.
|
|
1641
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowFormDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
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,10 +1660,85 @@ 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
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1665
|
+
isPatchingStepForm = false;
|
|
1666
|
+
lastAppActionDescriptorSignature = '';
|
|
1667
|
+
mainTab = signal('workflow', ...(ngDevMode ? [{ debugName: "mainTab" }] : /* istanbul ignore next */ []));
|
|
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;
|
|
1680
|
+
groups = this.workflowFacade.groups;
|
|
1681
|
+
roles = this.workflowFacade.roles;
|
|
1682
|
+
appActionDescriptors = this.workflowFacade.appActionDescriptors;
|
|
1683
|
+
selectedAppActionDescriptor = this.workflowFacade.selectedAppActionDescriptor;
|
|
1684
|
+
isFlowValid = this.workflowFacade.isFlowValid;
|
|
1685
|
+
loading = this.workflowFacade.isLoading('getWorkflow');
|
|
1686
|
+
loadingStep = this.workflowFacade.isLoading('getStep');
|
|
1687
|
+
loadingAppActions = this.workflowFacade.isLoading('getAppActions');
|
|
1688
|
+
loadingAppActionDetail = this.workflowFacade.isLoading('getAppActionDetail');
|
|
1689
|
+
appActionListError = this.workflowFacade.error('getAppActions');
|
|
1690
|
+
appActionDetailError = this.workflowFacade.error('getAppActionDetail');
|
|
1691
|
+
selectedWorkflowId = computed(() => this.workflow()?.id, ...(ngDevMode ? [{ debugName: "selectedWorkflowId" }] : /* istanbul ignore next */ []));
|
|
1692
|
+
connectionFormulaSchemaId = computed(() => this.workflow()?.id, ...(ngDevMode ? [{ debugName: "connectionFormulaSchemaId" }] : /* istanbul ignore next */ []));
|
|
1693
|
+
hasMultipleWorkflows = computed(() => this.workflows().length > 1, ...(ngDevMode ? [{ debugName: "hasMultipleWorkflows" }] : /* istanbul ignore next */ []));
|
|
1694
|
+
isPublishDisabled = computed(() => !this.isFlowValid(), ...(ngDevMode ? [{ debugName: "isPublishDisabled" }] : /* istanbul ignore next */ []));
|
|
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(() => [
|
|
1009
1742
|
{
|
|
1010
1743
|
label: this.transloco.translate('workflow.builder.workflow'),
|
|
1011
1744
|
value: 'workflow',
|
|
@@ -1014,224 +1747,95 @@ class WorkflowBuilder {
|
|
|
1014
1747
|
label: this.transloco.translate('workflow.builder.notification'),
|
|
1015
1748
|
value: 'notification',
|
|
1016
1749
|
},
|
|
1017
|
-
];
|
|
1018
|
-
|
|
1019
|
-
roles = this.workflowFacade.roles;
|
|
1020
|
-
hasGroupedRoles = computed(() => this.roles().some((role) => Array.isArray(role?.items)), ...(ngDevMode ? [{ debugName: "hasGroupedRoles" }] : []));
|
|
1021
|
-
workflows = this.workflowFacade.workflows;
|
|
1022
|
-
workflow = this.workflowFacade.workflow;
|
|
1023
|
-
loading = this.workflowFacade.isLoading('getWorkflow');
|
|
1024
|
-
loadingStep = this.workflowFacade.isLoading('getStep');
|
|
1025
|
-
isFlowValid = this.workflowFacade.isFlowValid;
|
|
1026
|
-
selectedStep = this.workflowFacade.selectedStep;
|
|
1027
|
-
selectedWorkflowId = computed(() => this.workflow()?.id, ...(ngDevMode ? [{ debugName: "selectedWorkflowId" }] : []));
|
|
1028
|
-
connectionFormulaSchemaId = computed(() => this.workflow()?.id, ...(ngDevMode ? [{ debugName: "connectionFormulaSchemaId" }] : []));
|
|
1029
|
-
hasMultipleWorkflows = computed(() => this.workflows().length > 1, ...(ngDevMode ? [{ debugName: "hasMultipleWorkflows" }] : []));
|
|
1030
|
-
confirmationService = inject(ConfirmationService);
|
|
1031
|
-
selectedTab = signal('tab1', ...(ngDevMode ? [{ debugName: "selectedTab" }] : []));
|
|
1032
|
-
nodeFields = signal({
|
|
1033
|
-
name: 'name.display',
|
|
1034
|
-
}, ...(ngDevMode ? [{ debugName: "nodeFields" }] : []));
|
|
1035
|
-
nodeFormControl = new FormControl();
|
|
1036
|
-
isPublished = signal(false, ...(ngDevMode ? [{ debugName: "isPublished" }] : []));
|
|
1037
|
-
isPublishDisabled = computed(() => !this.isFlowValid(), ...(ngDevMode ? [{ debugName: "isPublishDisabled" }] : []));
|
|
1038
|
-
isEditingInitialNode = signal(false, ...(ngDevMode ? [{ debugName: "isEditingInitialNode" }] : []));
|
|
1039
|
-
isCreatingStep = signal(false, ...(ngDevMode ? [{ debugName: "isCreatingStep" }] : []));
|
|
1040
|
-
// Properties table data - writable signal that can be updated when loading step details
|
|
1041
|
-
_propertiesTableData = signal([], ...(ngDevMode ? [{ debugName: "_propertiesTableData" }] : []));
|
|
1042
|
-
// Computed that initializes from workflow properties
|
|
1043
|
-
propertiesTableData = computed(() => {
|
|
1044
|
-
const currentData = this._propertiesTableData();
|
|
1045
|
-
if (currentData.length > 0) {
|
|
1046
|
-
return currentData;
|
|
1047
|
-
}
|
|
1048
|
-
const properties = this.workflow()?.properties || [];
|
|
1049
|
-
return properties.map((prop) => ({
|
|
1050
|
-
id: prop.id,
|
|
1051
|
-
name: prop.property.name,
|
|
1052
|
-
isVisible: false,
|
|
1053
|
-
isEditable: false,
|
|
1054
|
-
_original: prop, // Keep original for reference
|
|
1055
|
-
}));
|
|
1056
|
-
}, ...(ngDevMode ? [{ debugName: "propertiesTableData" }] : []));
|
|
1057
|
-
// Table columns configuration
|
|
1058
|
-
propertiesColumns = signal([
|
|
1059
|
-
{
|
|
1060
|
-
key: 'name',
|
|
1061
|
-
label: this.transloco.translate('workflow.builder.columnName'),
|
|
1062
|
-
type: 'text',
|
|
1063
|
-
},
|
|
1750
|
+
], ...(ngDevMode ? [{ debugName: "mainTabsList" }] : /* istanbul ignore next */ []));
|
|
1751
|
+
failureBehaviorOptions = computed(() => [
|
|
1064
1752
|
{
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
type: 'boolean',
|
|
1753
|
+
label: this.transloco.translate('workflow.builder.failureBehaviorStop'),
|
|
1754
|
+
value: 'Stop',
|
|
1068
1755
|
},
|
|
1069
1756
|
{
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
type: 'boolean',
|
|
1757
|
+
label: this.transloco.translate('workflow.builder.failureBehaviorContinue'),
|
|
1758
|
+
value: 'Continue',
|
|
1073
1759
|
},
|
|
1074
|
-
], ...(ngDevMode ? [{ debugName: "
|
|
1075
|
-
// Dynamic tabs list - notification tab only shown for non-initial nodes
|
|
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
|
|
1098
|
-
}, ...(ngDevMode ? [{ debugName: "tabsList" }] : []));
|
|
1099
|
-
|
|
1100
|
-
showSaveButton: () => this.selectedTab() === 'tab1',
|
|
1101
|
-
disableSaveButton: () => this.selectedTab() !== 'tab1' || this.nodeFormControl.invalid,
|
|
1102
|
-
};
|
|
1103
|
-
availableNodes = [
|
|
1780
|
+
return tabs;
|
|
1781
|
+
}, ...(ngDevMode ? [{ debugName: "tabsList" }] : /* istanbul ignore next */ []));
|
|
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" }] : []));
|
|
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" }] : []));
|
|
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" }] : []));
|
|
1234
|
-
// Sample connection form configuration
|
|
1235
1839
|
connectionForm = signal({
|
|
1236
1840
|
sections: [
|
|
1237
1841
|
{
|
|
@@ -1249,85 +1853,101 @@ class WorkflowBuilder {
|
|
|
1249
1853
|
],
|
|
1250
1854
|
},
|
|
1251
1855
|
],
|
|
1252
|
-
}, ...(ngDevMode ? [{ debugName: "connectionForm" }] : []));
|
|
1856
|
+
}, ...(ngDevMode ? [{ debugName: "connectionForm" }] : /* istanbul ignore next */ []));
|
|
1253
1857
|
connectionFormulaConfig = signal({
|
|
1254
1858
|
codeOnly: true,
|
|
1255
1859
|
toolbarTabs: ['functions', 'operators'],
|
|
1256
1860
|
isProcessBuilder: true,
|
|
1257
|
-
}, ...(ngDevMode ? [{ debugName: "connectionFormulaConfig" }] : []));
|
|
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
|
-
|
|
1861
|
+
}, ...(ngDevMode ? [{ debugName: "connectionFormulaConfig" }] : /* istanbul ignore next */ []));
|
|
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;
|
|
2045
|
+
}
|
|
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;
|
|
1519
2057
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
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);
|
|
1526
2069
|
}
|
|
1527
|
-
|
|
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,10 +2097,394 @@ class WorkflowBuilder {
|
|
|
1556
2097
|
});
|
|
1557
2098
|
formDialogRef.onClose.subscribe(() => { });
|
|
1558
2099
|
}
|
|
1559
|
-
|
|
1560
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", 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", "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" }] });
|
|
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
|
+
getStepCardSubtitle(step) {
|
|
2109
|
+
if (step.type !== 'AppAction') {
|
|
2110
|
+
return null;
|
|
2111
|
+
}
|
|
2112
|
+
return step.appAction?.displayName || step.appAction?.actionKey || null;
|
|
2113
|
+
}
|
|
2114
|
+
getStepTypeLabel(type) {
|
|
2115
|
+
return this.transloco.translate(type === 'AppAction'
|
|
2116
|
+
? 'workflow.builder.stepTypeAppAction'
|
|
2117
|
+
: 'workflow.builder.stepTypeUserInput');
|
|
2118
|
+
}
|
|
2119
|
+
getAppActionConfigControl(key) {
|
|
2120
|
+
return this.appActionConfigForm.get(key);
|
|
2121
|
+
}
|
|
2122
|
+
getAppActionEnumOptions(field) {
|
|
2123
|
+
return (field.enumValues ?? []).map((value) => ({
|
|
2124
|
+
label: String(value),
|
|
2125
|
+
value,
|
|
2126
|
+
}));
|
|
2127
|
+
}
|
|
2128
|
+
getAppActionDescription() {
|
|
2129
|
+
return this.currentAppActionDescriptor()?.description ?? null;
|
|
2130
|
+
}
|
|
2131
|
+
getAppActionDisplayName() {
|
|
2132
|
+
return (this.currentAppActionDescriptor()?.displayName ||
|
|
2133
|
+
this.appActionSettingsForm.controls.actionKey.value ||
|
|
2134
|
+
this.transloco.translate('workflow.builder.appActionUnavailable'));
|
|
2135
|
+
}
|
|
2136
|
+
getAppActionKey() {
|
|
2137
|
+
return this.appActionSettingsForm.controls.actionKey.value ?? '';
|
|
2138
|
+
}
|
|
2139
|
+
getAppActionConfigMessages() {
|
|
2140
|
+
const definition = this.appActionConfigDefinition();
|
|
2141
|
+
const serialization = serializeWorkflowAppActionConfigValue(this.appActionConfigForm.getRawValue(), definition, this.appActionRawConfigControl.value);
|
|
2142
|
+
return Array.from(new Set([
|
|
2143
|
+
...definition.errors,
|
|
2144
|
+
...(this.appActionDetailError() ? [this.appActionDetailError()] : []),
|
|
2145
|
+
...serialization.errors,
|
|
2146
|
+
]));
|
|
2147
|
+
}
|
|
2148
|
+
getAppActionDiscoveryMessage() {
|
|
2149
|
+
if (this.currentStepType() !== 'AppAction') {
|
|
2150
|
+
return null;
|
|
2151
|
+
}
|
|
2152
|
+
if (this.appActionListError()) {
|
|
2153
|
+
return this.appActionListError();
|
|
2154
|
+
}
|
|
2155
|
+
return null;
|
|
2156
|
+
}
|
|
2157
|
+
clearForm(stepType = 'UserInput') {
|
|
2158
|
+
const defaults = createDefaultWorkflowStepEditorValue();
|
|
2159
|
+
this.isPatchingStepForm = true;
|
|
2160
|
+
this.isEditingInitialNode.set(false);
|
|
2161
|
+
this.currentStepType.set(stepType);
|
|
2162
|
+
this.draftAppActionDescriptor.set(null);
|
|
2163
|
+
this.selectedTab.set('tab1');
|
|
2164
|
+
this.stepForm.reset({
|
|
2165
|
+
nameEn: defaults.name['en'],
|
|
2166
|
+
nameAr: defaults.name['ar'],
|
|
2167
|
+
type: stepType,
|
|
2168
|
+
targetType: defaults.targetType,
|
|
2169
|
+
group: defaults.group,
|
|
2170
|
+
role: defaults.role,
|
|
2171
|
+
sla: defaults.sla,
|
|
2172
|
+
}, { emitEvent: false });
|
|
2173
|
+
this.configureStepTypeControl();
|
|
2174
|
+
this.resetPropertiesTable();
|
|
2175
|
+
this.showHideControl.setValue(false, { emitEvent: false });
|
|
2176
|
+
this.resetAppActionState();
|
|
2177
|
+
this.isPatchingStepForm = false;
|
|
2178
|
+
this.syncUserInputValidators();
|
|
2179
|
+
if (stepType === 'AppAction') {
|
|
2180
|
+
this.ensureAppActionDiscoveryLoaded();
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
applyLoadedStep(step) {
|
|
2184
|
+
const editorState = mapWorkflowStepToEditorState(step);
|
|
2185
|
+
const stepType = step.isInitial
|
|
2186
|
+
? 'UserInput'
|
|
2187
|
+
: editorState.stepFormValue.type;
|
|
2188
|
+
const descriptor = this.resolveDescriptorForActionKey(editorState.appActionValue.actionKey, step);
|
|
2189
|
+
this.isPatchingStepForm = true;
|
|
2190
|
+
this.isEditingInitialNode.set(!!step.isInitial);
|
|
2191
|
+
this.currentStepType.set(stepType);
|
|
2192
|
+
this.selectedTab.set('tab1');
|
|
2193
|
+
this.stepForm.reset({
|
|
2194
|
+
nameEn: editorState.stepFormValue.name['en'] ?? '',
|
|
2195
|
+
nameAr: editorState.stepFormValue.name['ar'] ?? '',
|
|
2196
|
+
type: stepType,
|
|
2197
|
+
targetType: editorState.stepFormValue.targetType ?? '1',
|
|
2198
|
+
group: editorState.stepFormValue.group,
|
|
2199
|
+
role: editorState.stepFormValue.role,
|
|
2200
|
+
sla: editorState.stepFormValue.sla,
|
|
2201
|
+
}, { emitEvent: false });
|
|
2202
|
+
this.configureStepTypeControl();
|
|
2203
|
+
this.populatePropertiesTable(step);
|
|
2204
|
+
this.showHideControl.setValue(step.hasNotification ?? false, {
|
|
2205
|
+
emitEvent: false,
|
|
2206
|
+
});
|
|
2207
|
+
this.appActionSettingsForm.reset({
|
|
2208
|
+
actionKey: editorState.appActionValue.actionKey,
|
|
2209
|
+
failureBehavior: editorState.appActionValue.failureBehavior,
|
|
2210
|
+
timeoutSeconds: editorState.appActionValue.timeoutSeconds,
|
|
2211
|
+
}, { emitEvent: false });
|
|
2212
|
+
this.rebuildAppActionConfigEditor(editorState.appActionValue.configJson, descriptor?.configSchemaJson ?? step.appAction?.configSchemaJson ?? null);
|
|
2213
|
+
this.lastAppActionDescriptorSignature =
|
|
2214
|
+
this.createAppActionDescriptorSignature(editorState.appActionValue.actionKey, descriptor?.configSchemaJson ??
|
|
2215
|
+
step.appAction?.configSchemaJson ??
|
|
2216
|
+
null);
|
|
2217
|
+
this.isPatchingStepForm = false;
|
|
2218
|
+
this.syncUserInputValidators();
|
|
2219
|
+
if (stepType === 'AppAction') {
|
|
2220
|
+
this.ensureAppActionDiscoveryLoaded();
|
|
2221
|
+
this.ensureAppActionDetailLoaded(editorState.appActionValue.actionKey);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
onAppActionActionChanged(actionKey) {
|
|
2225
|
+
this.selectedTab.set('tab1');
|
|
2226
|
+
this.lastAppActionDescriptorSignature = '';
|
|
2227
|
+
this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, this.resolveDescriptorForActionKey(actionKey)?.configSchemaJson ?? null);
|
|
2228
|
+
if (actionKey) {
|
|
2229
|
+
this.ensureAppActionDetailLoaded(actionKey);
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
applyCreateNodeDefaults(nodeData) {
|
|
2233
|
+
if (!nodeData) {
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
const stepType = normalizeWorkflowStepType(nodeData.type);
|
|
2237
|
+
if (stepType !== 'AppAction') {
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
2240
|
+
const actionKey = typeof nodeData.actionKey === 'string' ? nodeData.actionKey : null;
|
|
2241
|
+
const displayName = typeof nodeData.displayName === 'string'
|
|
2242
|
+
? nodeData.displayName
|
|
2243
|
+
: typeof nodeData.label === 'string'
|
|
2244
|
+
? nodeData.label
|
|
2245
|
+
: '';
|
|
2246
|
+
this.draftAppActionDescriptor.set({
|
|
2247
|
+
appCode: typeof nodeData.appCode === 'string' ? nodeData.appCode : 'pplus',
|
|
2248
|
+
actionKey: actionKey ?? '',
|
|
2249
|
+
displayName,
|
|
2250
|
+
description: typeof nodeData.description === 'string' ? nodeData.description : null,
|
|
2251
|
+
configSchemaJson: typeof nodeData.configSchemaJson === 'string'
|
|
2252
|
+
? nodeData.configSchemaJson
|
|
2253
|
+
: null,
|
|
2254
|
+
requiredContextKeys: Array.isArray(nodeData.requiredContextKeys)
|
|
2255
|
+
? nodeData.requiredContextKeys.map(String)
|
|
2256
|
+
: [],
|
|
2257
|
+
supportedScopes: Array.isArray(nodeData.supportedScopes)
|
|
2258
|
+
? nodeData.supportedScopes.map(String)
|
|
2259
|
+
: [],
|
|
2260
|
+
});
|
|
2261
|
+
this.isPatchingStepForm = true;
|
|
2262
|
+
this.stepForm.patchValue({
|
|
2263
|
+
nameEn: displayName,
|
|
2264
|
+
nameAr: displayName,
|
|
2265
|
+
type: 'AppAction',
|
|
2266
|
+
}, { emitEvent: false });
|
|
2267
|
+
this.appActionSettingsForm.patchValue({
|
|
2268
|
+
actionKey,
|
|
2269
|
+
failureBehavior: DEFAULT_APP_ACTION_EDITOR_VALUE.failureBehavior,
|
|
2270
|
+
timeoutSeconds: DEFAULT_APP_ACTION_EDITOR_VALUE.timeoutSeconds,
|
|
2271
|
+
}, { emitEvent: false });
|
|
2272
|
+
this.isPatchingStepForm = false;
|
|
2273
|
+
this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, this.draftAppActionDescriptor()?.configSchemaJson ?? null);
|
|
2274
|
+
this.ensureAppActionDetailLoaded(actionKey);
|
|
2275
|
+
}
|
|
2276
|
+
resetPropertiesTable() {
|
|
2277
|
+
const properties = this.workflow()?.properties ?? [];
|
|
2278
|
+
this.propertiesTableDataState.set(properties.map((property) => ({
|
|
2279
|
+
id: property.id,
|
|
2280
|
+
name: property.property.name,
|
|
2281
|
+
isVisible: false,
|
|
2282
|
+
isEditable: false,
|
|
2283
|
+
_original: property,
|
|
2284
|
+
})));
|
|
2285
|
+
}
|
|
2286
|
+
populatePropertiesTable(step) {
|
|
2287
|
+
const properties = this.workflow()?.properties ?? [];
|
|
2288
|
+
this.propertiesTableDataState.set(properties.map((property) => {
|
|
2289
|
+
const stepProperty = step.properties?.find((item) => item.propertyId === property.id);
|
|
2290
|
+
return {
|
|
2291
|
+
id: property.id,
|
|
2292
|
+
name: property.property.name,
|
|
2293
|
+
isVisible: stepProperty?.isRead || false,
|
|
2294
|
+
isEditable: stepProperty?.isWrite || false,
|
|
2295
|
+
_original: property,
|
|
2296
|
+
};
|
|
2297
|
+
}));
|
|
2298
|
+
}
|
|
2299
|
+
configureStepTypeControl() {
|
|
2300
|
+
if (this.isEditingInitialNode()) {
|
|
2301
|
+
this.stepForm.controls.type.disable({ emitEvent: false });
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
this.stepForm.controls.type.enable({ emitEvent: false });
|
|
2305
|
+
}
|
|
2306
|
+
syncUserInputValidators() {
|
|
2307
|
+
const groupControl = this.stepForm.controls.group;
|
|
2308
|
+
const roleControl = this.stepForm.controls.role;
|
|
2309
|
+
const targetType = this.stepForm.controls.targetType.value;
|
|
2310
|
+
groupControl.clearValidators();
|
|
2311
|
+
roleControl.clearValidators();
|
|
2312
|
+
if (this.currentStepType() === 'UserInput' &&
|
|
2313
|
+
!this.isEditingInitialNode() &&
|
|
2314
|
+
targetType === '1') {
|
|
2315
|
+
groupControl.addValidators(Validators.required);
|
|
2316
|
+
}
|
|
2317
|
+
if (this.currentStepType() === 'UserInput' &&
|
|
2318
|
+
!this.isEditingInitialNode() &&
|
|
2319
|
+
targetType === '2') {
|
|
2320
|
+
roleControl.addValidators(Validators.required);
|
|
2321
|
+
}
|
|
2322
|
+
groupControl.updateValueAndValidity({ emitEvent: false });
|
|
2323
|
+
roleControl.updateValueAndValidity({ emitEvent: false });
|
|
2324
|
+
}
|
|
2325
|
+
resetAppActionState() {
|
|
2326
|
+
this.appActionSettingsForm.reset({
|
|
2327
|
+
actionKey: DEFAULT_APP_ACTION_EDITOR_VALUE.actionKey,
|
|
2328
|
+
failureBehavior: DEFAULT_APP_ACTION_EDITOR_VALUE.failureBehavior,
|
|
2329
|
+
timeoutSeconds: DEFAULT_APP_ACTION_EDITOR_VALUE.timeoutSeconds,
|
|
2330
|
+
}, { emitEvent: false });
|
|
2331
|
+
this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, null);
|
|
2332
|
+
this.lastAppActionDescriptorSignature = '';
|
|
2333
|
+
}
|
|
2334
|
+
rebuildAppActionConfigEditor(configJson, configSchemaJson) {
|
|
2335
|
+
const definition = buildWorkflowAppActionConfigEditorDefinition(configSchemaJson);
|
|
2336
|
+
const editorValue = mapWorkflowAppActionConfigJsonToEditorValue(configJson, definition);
|
|
2337
|
+
Object.keys(this.appActionConfigForm.controls).forEach((key) => {
|
|
2338
|
+
this.appActionConfigForm.removeControl(key);
|
|
2339
|
+
});
|
|
2340
|
+
definition.fields.forEach((field) => {
|
|
2341
|
+
this.appActionConfigForm.addControl(field.key, new FormControl(editorValue.fields[field.key] ?? null, {
|
|
2342
|
+
validators: field.required ? [Validators.required] : [],
|
|
2343
|
+
}));
|
|
2344
|
+
});
|
|
2345
|
+
this.appActionRawConfigControl.setValue(editorValue.rawConfigJson, {
|
|
2346
|
+
emitEvent: false,
|
|
2347
|
+
});
|
|
2348
|
+
this.appActionConfigDefinition.set(definition);
|
|
2349
|
+
}
|
|
2350
|
+
ensureAppActionDiscoveryLoaded() {
|
|
2351
|
+
this.workflowFacade.loadAppActions();
|
|
2352
|
+
}
|
|
2353
|
+
ensureAppActionDetailLoaded(actionKey) {
|
|
2354
|
+
if (!actionKey) {
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
const cachedDescriptor = this.resolveDescriptorForActionKey(actionKey);
|
|
2358
|
+
if (cachedDescriptor?.configSchemaJson) {
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
this.workflowFacade.loadAppActionDetail(actionKey);
|
|
2362
|
+
}
|
|
2363
|
+
resolveDescriptorForActionKey(actionKey, step) {
|
|
2364
|
+
if (!actionKey) {
|
|
2365
|
+
return null;
|
|
2366
|
+
}
|
|
2367
|
+
const selectedDescriptor = this.selectedAppActionDescriptor();
|
|
2368
|
+
if (selectedDescriptor?.actionKey === actionKey) {
|
|
2369
|
+
return selectedDescriptor;
|
|
2370
|
+
}
|
|
2371
|
+
const cachedDescriptor = this.appActionDescriptors().find((descriptor) => descriptor.actionKey === actionKey);
|
|
2372
|
+
if (cachedDescriptor) {
|
|
2373
|
+
return cachedDescriptor;
|
|
2374
|
+
}
|
|
2375
|
+
const currentStep = step ?? this.selectedStep();
|
|
2376
|
+
if (currentStep?.type === 'AppAction' &&
|
|
2377
|
+
currentStep.appAction?.actionKey === actionKey) {
|
|
2378
|
+
return {
|
|
2379
|
+
appCode: currentStep.appAction.appCode,
|
|
2380
|
+
actionKey,
|
|
2381
|
+
displayName: currentStep.appAction.displayName ?? actionKey,
|
|
2382
|
+
description: currentStep.appAction.description ?? null,
|
|
2383
|
+
configSchemaJson: currentStep.appAction.configSchemaJson ?? null,
|
|
2384
|
+
requiredContextKeys: currentStep.appAction.requiredContextKeys ?? [],
|
|
2385
|
+
supportedScopes: currentStep.appAction.supportedScopes ?? [],
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
return null;
|
|
2389
|
+
}
|
|
2390
|
+
buildStepPayload() {
|
|
2391
|
+
if (this.isNodeDialogSaveDisabled()) {
|
|
2392
|
+
return null;
|
|
2393
|
+
}
|
|
2394
|
+
const formValue = this.stepForm.getRawValue();
|
|
2395
|
+
const appActionConfig = serializeWorkflowAppActionConfigValue(this.appActionConfigForm.getRawValue(), this.appActionConfigDefinition(), this.appActionRawConfigControl.value);
|
|
2396
|
+
return buildWorkflowStepPayload({
|
|
2397
|
+
formValue: {
|
|
2398
|
+
name: {
|
|
2399
|
+
en: formValue.nameEn,
|
|
2400
|
+
ar: formValue.nameAr,
|
|
2401
|
+
},
|
|
2402
|
+
type: normalizeWorkflowStepType(formValue.type),
|
|
2403
|
+
targetType: formValue.targetType || '1',
|
|
2404
|
+
group: formValue.group,
|
|
2405
|
+
role: this.normalizeRoleValue(formValue.role),
|
|
2406
|
+
sla: Number(formValue.sla ?? 0),
|
|
2407
|
+
},
|
|
2408
|
+
properties: this.buildPropertiesPayload(),
|
|
2409
|
+
hasNotification: this.showHideControl.value,
|
|
2410
|
+
appActionValue: this.currentStepType() === 'AppAction'
|
|
2411
|
+
? {
|
|
2412
|
+
actionKey: this.appActionSettingsForm.controls.actionKey.value,
|
|
2413
|
+
failureBehavior: this.appActionSettingsForm.controls.failureBehavior.value,
|
|
2414
|
+
timeoutSeconds: Number(this.appActionSettingsForm.controls.timeoutSeconds.value ?? 300),
|
|
2415
|
+
configJson: appActionConfig.configJson,
|
|
2416
|
+
}
|
|
2417
|
+
: null,
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
buildPropertiesPayload() {
|
|
2421
|
+
return this.propertiesTableData()
|
|
2422
|
+
.filter((property) => property.isVisible || property.isEditable)
|
|
2423
|
+
.map((property) => ({
|
|
2424
|
+
PropertyId: property.id,
|
|
2425
|
+
isRead: property.isVisible,
|
|
2426
|
+
isWrite: property.isEditable,
|
|
2427
|
+
}));
|
|
2428
|
+
}
|
|
2429
|
+
getCurrentAppActionConfigJson() {
|
|
2430
|
+
const definition = this.appActionConfigDefinition();
|
|
2431
|
+
if (definition.mode === 'raw') {
|
|
2432
|
+
return this.appActionRawConfigControl.value;
|
|
2433
|
+
}
|
|
2434
|
+
return serializeWorkflowAppActionConfigValue(this.appActionConfigForm.getRawValue(), definition, this.appActionRawConfigControl.value).configJson;
|
|
2435
|
+
}
|
|
2436
|
+
createAppActionDescriptorSignature(actionKey, configSchemaJson) {
|
|
2437
|
+
return `${actionKey ?? ''}::${configSchemaJson ?? ''}`;
|
|
2438
|
+
}
|
|
2439
|
+
isNodeDialogSaveDisabled() {
|
|
2440
|
+
if (this.selectedTab() !== 'tab1' || this.loadingStep()) {
|
|
2441
|
+
return true;
|
|
2442
|
+
}
|
|
2443
|
+
if (this.stepForm.controls.nameEn.invalid ||
|
|
2444
|
+
this.stepForm.controls.nameAr.invalid) {
|
|
2445
|
+
return true;
|
|
2446
|
+
}
|
|
2447
|
+
if (this.currentStepType() === 'UserInput') {
|
|
2448
|
+
if (!this.isEditingInitialNode() && this.stepForm.controls.sla.invalid) {
|
|
2449
|
+
return true;
|
|
2450
|
+
}
|
|
2451
|
+
if (this.isEditingInitialNode()) {
|
|
2452
|
+
return false;
|
|
2453
|
+
}
|
|
2454
|
+
return this.stepForm.controls.targetType.value === '1'
|
|
2455
|
+
? this.stepForm.controls.group.invalid
|
|
2456
|
+
: this.stepForm.controls.role.invalid;
|
|
2457
|
+
}
|
|
2458
|
+
if (this.stepForm.controls.sla.invalid) {
|
|
2459
|
+
return true;
|
|
2460
|
+
}
|
|
2461
|
+
if (!!this.getAppActionDiscoveryMessage()) {
|
|
2462
|
+
return true;
|
|
2463
|
+
}
|
|
2464
|
+
if (this.appActionSettingsForm.invalid) {
|
|
2465
|
+
return true;
|
|
2466
|
+
}
|
|
2467
|
+
return this.getAppActionConfigMessages().length > 0;
|
|
2468
|
+
}
|
|
2469
|
+
normalizeRoleValue(value) {
|
|
2470
|
+
if (value === null || value === undefined || value === '') {
|
|
2471
|
+
return null;
|
|
2472
|
+
}
|
|
2473
|
+
return String(value);
|
|
2474
|
+
}
|
|
2475
|
+
resolveDisplayName(name) {
|
|
2476
|
+
if (typeof name === 'string') {
|
|
2477
|
+
return name;
|
|
2478
|
+
}
|
|
2479
|
+
if (name && typeof name === 'object') {
|
|
2480
|
+
return name['display'] || name['en'] || name['ar'] || '';
|
|
2481
|
+
}
|
|
2482
|
+
return '';
|
|
2483
|
+
}
|
|
2484
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2485
|
+
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'\">\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\n <div class=\"flex justify-center w-full py-2\">\n <mt-tabs\n [active]=\"mainTab()\"\n (activeChange)=\"onMainTabChange($event)\"\n [options]=\"mainTabsList()\"\n size=\"large\"\n ></mt-tabs>\n </div>\n @switch (mainTab()) {\n @case (\"workflow\") {\n @if (!loading()) {\n <mt-structure-builder\n class=\"flex-1\"\n [availableNodes]=\"availableNodes()\"\n [availableNodesLabel]=\"t('stepTemplates')\"\n [connectionForm]=\"connectionForm()\"\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\n [nodeActions]=\"nodeActions()\"\n [nodes]=\"steps()\"\n [connections]=\"connections()\"\n (action)=\"onStructureAction($event)\"\n [addModalType]=\"'drawer'\"\n [addModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\n [updateModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\n [addModalHeader]=\"'workflow.builder.addStep' | transloco\"\n [updateModalHeader]=\"'workflow.builder.editStep' | transloco\"\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\n [appendTo]=\"'page-content'\"\n [nodeFields]=\"nodeFields()\"\n [availableTabsClass]=\"hasMultipleWorkflows() ? '!mt-28' : ''\"\n [layoutDirection]=\"'LR'\"\n >\n <div flowContent class=\"pointer-events-none\">\n @if (hasMultipleWorkflows()) {\n <div class=\"pointer-events-auto absolute top-4 start-4 z-10\">\n <mt-card class=\"w-64\">\n <mt-select-field\n [options]=\"workflows()\"\n optionLabel=\"name.display\"\n optionValue=\"id\"\n [ngModel]=\"selectedWorkflowId()\"\n (ngModelChange)=\"onWorkflowChange($event)\"\n class=\"mt-2\"\n />\n </mt-card>\n </div>\n }\n <div class=\"pointer-events-auto absolute top-4 end-4 z-10\">\n <div class=\"flex flex-col gap-2 w-80\">\n <mt-card>\n <div class=\"flex items-center gap-10\">\n <div class=\"flex items-center gap-2\">\n <mt-button\n [severity]=\"'secondary'\"\n icon=\"dev.dataflow-01\"\n />\n <label>{{\n \"workflow.builder.enableWorkflow\" | transloco\n }}</label>\n </div>\n <mt-toggle-field\n [ngModel]=\"isPublished()\"\n (ngModelChange)=\"onPublishToggle($event)\"\n [disabled]=\"isPublishDisabled()\"\n ></mt-toggle-field>\n </div>\n </mt-card>\n </div>\n </div>\n </div>\n <ng-template\n #nodeTemplate\n let-node\n let-actions=\"actions\"\n let-onAction=\"onAction\"\n >\n <div class=\"min-w-[17rem] overflow-hidden rounded-2xl bg-content\">\n <div class=\"space-y-3 px-4 py-4\">\n <span [class]=\"getStepBadgeClass(node.type)\">\n {{ getStepTypeLabel(node.type) }}\n </span>\n <div class=\"space-y-1\">\n <div class=\"text-base font-semibold text-surface-900\">\n {{ getStepCardTitle(node) }}\n </div>\n @if (getStepCardSubtitle(node); as subtitle) {\n <div class=\"text-sm text-muted-color\">\n {{ subtitle }}\n </div>\n }\n </div>\n </div>\n <div\n class=\"flex min-h-[3rem] items-center justify-end gap-2 border-t border-surface-200 bg-surface-50 px-4 py-3\"\n >\n @for (action of actions; track action.key) {\n <mt-button\n [size]=\"action?.size || 'small'\"\n [variant]=\"action?.variant\"\n [icon]=\"action?.icon\"\n [tooltip]=\"action?.tooltip\"\n [severity]=\"action?.severity\"\n (onClick)=\"onAction(action, node)\"\n >\n </mt-button>\n }\n </div>\n </div>\n </ng-template>\n <ng-template\n #nodeDialog\n let-close=\"close\"\n let-save=\"save\"\n let-node=\"node\"\n let-drawerController=\"drawerController\"\n >\n <div class=\"p-4\">\n @if (loadingStep()) {\n <div class=\"space-y-4\">\n <div class=\"flex gap-2 mb-4\">\n <p-skeleton\n width=\"6rem\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n <p-skeleton\n width=\"8rem\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <!-- Form fields skeleton -->\n <div class=\"space-y-4\">\n <div>\n <p-skeleton\n width=\"8rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <div>\n <p-skeleton\n width=\"8rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <div>\n <p-skeleton\n width=\"10rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"3rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <div>\n <p-skeleton\n width=\"6rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <div>\n <p-skeleton\n width=\"4rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n </div>\n </div>\n } @else {\n <mt-tabs\n [active]=\"selectedTab()\"\n (activeChange)=\"onNodeDialogTabChange($event)\"\n [options]=\"tabsList()\"\n size=\"large\"\n ></mt-tabs>\n @switch (selectedTab()) {\n @case (\"tab1\") {\n <div class=\"mt-4 space-y-4\">\n <div class=\"grid gap-3\">\n <mt-text-field\n [label]=\"t('nameEnglish')\"\n [placeholder]=\"t('enterNameEnglish')\"\n [formControl]=\"stepForm.controls.nameEn\"\n [required]=\"true\"\n ></mt-text-field>\n <mt-text-field\n [label]=\"t('nameArabic')\"\n [placeholder]=\"t('enterNameArabic')\"\n [formControl]=\"stepForm.controls.nameAr\"\n [required]=\"true\"\n ></mt-text-field>\n </div>\n\n @if (currentStepType() === \"UserInput\") {\n @if (!isEditingInitialNode()) {\n <mt-card>\n <div class=\"space-y-4\">\n <div\n class=\"text-sm font-semibold text-surface-900\"\n >\n {{ t(\"approver\") }}\n </div>\n <mt-select-field\n [label]=\"t('approver')\"\n [options]=\"[\n { label: t('group'), value: '1' },\n { label: t('role'), value: '2' },\n ]\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [formControl]=\"stepForm.controls.targetType\"\n [required]=\"true\"\n ></mt-select-field>\n\n @if (\n stepForm.controls.targetType.value === \"1\"\n ) {\n <mt-select-field\n [label]=\"t('group')\"\n [placeholder]=\"t('selectGroup')\"\n [options]=\"groups()\"\n optionLabel=\"name.display\"\n optionValue=\"id\"\n [formControl]=\"stepForm.controls.group\"\n [required]=\"true\"\n ></mt-select-field>\n } @else {\n <mt-select-field\n [label]=\"t('role')\"\n [placeholder]=\"t('selectRole')\"\n [options]=\"roles()\"\n [group]=\"hasGroupedRoles()\"\n optionGroupLabel=\"label\"\n optionGroupChildren=\"items\"\n optionLabel=\"name.display\"\n optionValue=\"value\"\n [formControl]=\"stepForm.controls.role\"\n [required]=\"true\"\n ></mt-select-field>\n }\n\n <mt-number-field\n [label]=\"t('sla')\"\n [placeholder]=\"t('enterSla')\"\n [formControl]=\"stepForm.controls.sla\"\n [required]=\"true\"\n [min]=\"0\"\n ></mt-number-field>\n </div>\n </mt-card>\n }\n } @else {\n <mt-card>\n <div class=\"space-y-4\">\n <div class=\"grid gap-3\">\n <div class=\"grid gap-1\">\n <label\n class=\"text-sm font-medium text-surface-900\"\n >\n {{ t(\"app\") }}\n </label>\n <div\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-700\"\n >\n {{ t(\"appLabelPplus\") }}\n </div>\n </div>\n\n <div class=\"grid gap-1\">\n <label\n class=\"text-sm font-medium text-surface-900\"\n >\n {{ t(\"action\") }}\n </label>\n <div\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-900\"\n >\n {{ getAppActionDisplayName() }}\n </div>\n @if (getAppActionKey()) {\n <div class=\"text-xs text-muted-color\">\n {{ getAppActionKey() }}\n </div>\n }\n </div>\n\n @if (\n getAppActionDescription();\n as description\n ) {\n <div\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-muted-color\"\n >\n {{ description }}\n </div>\n }\n\n @if (\n getAppActionDiscoveryMessage();\n as discoveryMessage\n ) {\n <div\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\n >\n {{ discoveryMessage }}\n </div>\n }\n </div>\n </div>\n </mt-card>\n\n <mt-card>\n <div class=\"space-y-4\">\n <div>\n <div\n class=\"text-sm font-semibold text-surface-900\"\n >\n {{ t(\"appActionConfiguration\") }}\n </div>\n <div class=\"mt-1 text-sm text-muted-color\">\n {{ t(\"appActionConfigurationDescription\") }}\n </div>\n </div>\n\n @if (\n loadingAppActionDetail() &&\n appActionSettingsForm.controls.actionKey.value\n ) {\n <div class=\"space-y-3\">\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"6rem\"\n ></p-skeleton>\n </div>\n } @else if (\n !appActionSettingsForm.controls.actionKey.value\n ) {\n <div\n class=\"rounded-xl border border-dashed border-surface-300 px-3 py-4 text-sm text-muted-color\"\n >\n {{ t(\"appActionUnavailable\") }}\n </div>\n } @else if (\n appActionConfigDefinition().mode === \"schema\" &&\n appActionConfigDefinition().fields.length === 0\n ) {\n <div\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-4 text-sm text-muted-color\"\n >\n {{ t(\"appActionNoConfigRequired\") }}\n </div>\n } @else if (\n appActionConfigDefinition().mode === \"raw\"\n ) {\n <div class=\"space-y-3\">\n <div\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\n >\n {{ t(\"appActionRawConfigFallback\") }}\n </div>\n <mt-textarea-field\n [label]=\"t('appActionConfigJson')\"\n [placeholder]=\"\n t('enterAppActionConfigJson')\n \"\n [formControl]=\"appActionRawConfigControl\"\n [rows]=\"'10'\"\n ></mt-textarea-field>\n </div>\n } @else {\n <div class=\"space-y-3\">\n @for (\n field of appActionConfigDefinition().fields;\n track field.key\n ) {\n <div class=\"space-y-2\">\n @switch (field.kind) {\n @case (\"text\") {\n <mt-text-field\n [label]=\"field.title\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n [required]=\"field.required\"\n ></mt-text-field>\n }\n @case (\"number\") {\n <mt-number-field\n [label]=\"field.title\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n [required]=\"field.required\"\n ></mt-number-field>\n }\n @case (\"toggle\") {\n <mt-toggle-field\n [label]=\"field.title\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n ></mt-toggle-field>\n }\n @case (\"select\") {\n <mt-select-field\n [label]=\"field.title\"\n [options]=\"\n getAppActionEnumOptions(field)\n \"\n optionLabel=\"label\"\n optionValue=\"value\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n [required]=\"field.required\"\n ></mt-select-field>\n }\n @case (\"json\") {\n <mt-textarea-field\n [label]=\"field.title\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n [required]=\"field.required\"\n [rows]=\"'8'\"\n ></mt-textarea-field>\n }\n }\n\n @if (field.description) {\n <div class=\"text-xs text-muted-color\">\n {{ field.description }}\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (getAppActionConfigMessages().length) {\n <div\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-3 text-sm text-red-700\"\n >\n @for (\n message of getAppActionConfigMessages();\n track message\n ) {\n <div>{{ message }}</div>\n }\n </div>\n }\n </div>\n </mt-card>\n\n <mt-card>\n <div class=\"space-y-4\">\n <div\n class=\"text-sm font-semibold text-surface-900\"\n >\n {{ t(\"advancedExecution\") }}\n </div>\n\n <mt-number-field\n [label]=\"t('sla')\"\n [placeholder]=\"t('enterSla')\"\n [formControl]=\"stepForm.controls.sla\"\n [required]=\"true\"\n [min]=\"0\"\n ></mt-number-field>\n\n <mt-select-field\n [label]=\"t('failureBehavior')\"\n [options]=\"failureBehaviorOptions()\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [formControl]=\"\n appActionSettingsForm.controls.failureBehavior\n \"\n [required]=\"true\"\n ></mt-select-field>\n\n <mt-number-field\n [label]=\"t('timeoutSeconds')\"\n [formControl]=\"\n appActionSettingsForm.controls.timeoutSeconds\n \"\n [required]=\"true\"\n [min]=\"1\"\n ></mt-number-field>\n </div>\n </mt-card>\n }\n </div>\n }\n @case (\"tab2\") {\n <div class=\"mt-4\">\n <mt-card>\n <div class=\"flex items-center gap-4\">\n <div class=\"flex flex-1 items-center gap-3\">\n <mt-button\n [severity]=\"'secondary'\"\n icon=\"file.file-check-02\"\n />\n <div class=\"flex flex-col\">\n <label class=\"font-medium\">{{\n t(\"configureForm\")\n }}</label>\n <span class=\"text-sm text-muted-color\">{{\n t(\"configureForm-description\")\n }}</span>\n </div>\n </div>\n </div>\n <div class=\"mt-3\">\n <mt-button\n [label]=\"t('configureForm')\"\n size=\"small\"\n (onClick)=\"openFormModal()\"\n ></mt-button>\n </div>\n </mt-card>\n </div>\n }\n @case (\"tab3\") {\n <div class=\"mt-4\">\n <mt-toggle-field\n toggleShape=\"card\"\n [label]=\"t('show-hide')\"\n [descriptionCard]=\"t('show-hide-notifications')\"\n icon=\"general.eye\"\n [formControl]=\"showHideControl\"\n >\n <ng-template #toggleCardBottom>\n @if (showHideControl.value) {\n <div class=\"mt-3\">\n <mt-button\n [label]=\"t('configureNotifications')\"\n size=\"small\"\n (onClick)=\"openNotificationsModal()\"\n ></mt-button>\n </div>\n }\n </ng-template>\n </mt-toggle-field>\n </div>\n }\n }\n }\n </div>\n </ng-template>\n </mt-structure-builder>\n } @else {\n <p-skeleton width=\"100%\" height=\"50rem\" />\n }\n }\n @case (\"notification\") {\n <div class=\"flex-1 flex justify-center items-start p-4\">\n <div class=\"w-1/2 max-[1025px]:w-full\">\n <mt-card [title]=\"t('notifications')\">\n <mt-notification-template />\n </mt-card>\n </div>\n </div>\n }\n }\n </div>\n</ng-container>\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", "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
2486
|
}
|
|
1562
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
2487
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilder, decorators: [{
|
|
1563
2488
|
type: Component,
|
|
1564
2489
|
args: [{ selector: 'mt-workflow-builder', imports: [
|
|
1565
2490
|
StructureBuilder,
|
|
@@ -1567,14 +2492,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImpor
|
|
|
1567
2492
|
Tabs,
|
|
1568
2493
|
ReactiveFormsModule,
|
|
1569
2494
|
FormsModule,
|
|
1570
|
-
DynamicForm,
|
|
1571
2495
|
Skeleton,
|
|
1572
2496
|
ToggleField,
|
|
1573
2497
|
Button,
|
|
1574
2498
|
TranslocoModule,
|
|
1575
2499
|
NotificationTemplate,
|
|
1576
2500
|
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" }]
|
|
2501
|
+
TextField,
|
|
2502
|
+
NumberField,
|
|
2503
|
+
TextareaField,
|
|
2504
|
+
], host: {}, template: "<ng-container *transloco=\"let t; prefix: 'workflow.builder'\">\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\n <div class=\"flex justify-center w-full py-2\">\n <mt-tabs\n [active]=\"mainTab()\"\n (activeChange)=\"onMainTabChange($event)\"\n [options]=\"mainTabsList()\"\n size=\"large\"\n ></mt-tabs>\n </div>\n @switch (mainTab()) {\n @case (\"workflow\") {\n @if (!loading()) {\n <mt-structure-builder\n class=\"flex-1\"\n [availableNodes]=\"availableNodes()\"\n [availableNodesLabel]=\"t('stepTemplates')\"\n [connectionForm]=\"connectionForm()\"\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\n [nodeActions]=\"nodeActions()\"\n [nodes]=\"steps()\"\n [connections]=\"connections()\"\n (action)=\"onStructureAction($event)\"\n [addModalType]=\"'drawer'\"\n [addModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\n [updateModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\n [addModalHeader]=\"'workflow.builder.addStep' | transloco\"\n [updateModalHeader]=\"'workflow.builder.editStep' | transloco\"\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\n [appendTo]=\"'page-content'\"\n [nodeFields]=\"nodeFields()\"\n [availableTabsClass]=\"hasMultipleWorkflows() ? '!mt-28' : ''\"\n [layoutDirection]=\"'LR'\"\n >\n <div flowContent class=\"pointer-events-none\">\n @if (hasMultipleWorkflows()) {\n <div class=\"pointer-events-auto absolute top-4 start-4 z-10\">\n <mt-card class=\"w-64\">\n <mt-select-field\n [options]=\"workflows()\"\n optionLabel=\"name.display\"\n optionValue=\"id\"\n [ngModel]=\"selectedWorkflowId()\"\n (ngModelChange)=\"onWorkflowChange($event)\"\n class=\"mt-2\"\n />\n </mt-card>\n </div>\n }\n <div class=\"pointer-events-auto absolute top-4 end-4 z-10\">\n <div class=\"flex flex-col gap-2 w-80\">\n <mt-card>\n <div class=\"flex items-center gap-10\">\n <div class=\"flex items-center gap-2\">\n <mt-button\n [severity]=\"'secondary'\"\n icon=\"dev.dataflow-01\"\n />\n <label>{{\n \"workflow.builder.enableWorkflow\" | transloco\n }}</label>\n </div>\n <mt-toggle-field\n [ngModel]=\"isPublished()\"\n (ngModelChange)=\"onPublishToggle($event)\"\n [disabled]=\"isPublishDisabled()\"\n ></mt-toggle-field>\n </div>\n </mt-card>\n </div>\n </div>\n </div>\n <ng-template\n #nodeTemplate\n let-node\n let-actions=\"actions\"\n let-onAction=\"onAction\"\n >\n <div class=\"min-w-[17rem] overflow-hidden rounded-2xl bg-content\">\n <div class=\"space-y-3 px-4 py-4\">\n <span [class]=\"getStepBadgeClass(node.type)\">\n {{ getStepTypeLabel(node.type) }}\n </span>\n <div class=\"space-y-1\">\n <div class=\"text-base font-semibold text-surface-900\">\n {{ getStepCardTitle(node) }}\n </div>\n @if (getStepCardSubtitle(node); as subtitle) {\n <div class=\"text-sm text-muted-color\">\n {{ subtitle }}\n </div>\n }\n </div>\n </div>\n <div\n class=\"flex min-h-[3rem] items-center justify-end gap-2 border-t border-surface-200 bg-surface-50 px-4 py-3\"\n >\n @for (action of actions; track action.key) {\n <mt-button\n [size]=\"action?.size || 'small'\"\n [variant]=\"action?.variant\"\n [icon]=\"action?.icon\"\n [tooltip]=\"action?.tooltip\"\n [severity]=\"action?.severity\"\n (onClick)=\"onAction(action, node)\"\n >\n </mt-button>\n }\n </div>\n </div>\n </ng-template>\n <ng-template\n #nodeDialog\n let-close=\"close\"\n let-save=\"save\"\n let-node=\"node\"\n let-drawerController=\"drawerController\"\n >\n <div class=\"p-4\">\n @if (loadingStep()) {\n <div class=\"space-y-4\">\n <div class=\"flex gap-2 mb-4\">\n <p-skeleton\n width=\"6rem\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n <p-skeleton\n width=\"8rem\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <!-- Form fields skeleton -->\n <div class=\"space-y-4\">\n <div>\n <p-skeleton\n width=\"8rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <div>\n <p-skeleton\n width=\"8rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <div>\n <p-skeleton\n width=\"10rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"3rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <div>\n <p-skeleton\n width=\"6rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n <div>\n <p-skeleton\n width=\"4rem\"\n height=\"1rem\"\n styleClass=\"mb-2\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n borderRadius=\"0.5rem\"\n ></p-skeleton>\n </div>\n </div>\n </div>\n } @else {\n <mt-tabs\n [active]=\"selectedTab()\"\n (activeChange)=\"onNodeDialogTabChange($event)\"\n [options]=\"tabsList()\"\n size=\"large\"\n ></mt-tabs>\n @switch (selectedTab()) {\n @case (\"tab1\") {\n <div class=\"mt-4 space-y-4\">\n <div class=\"grid gap-3\">\n <mt-text-field\n [label]=\"t('nameEnglish')\"\n [placeholder]=\"t('enterNameEnglish')\"\n [formControl]=\"stepForm.controls.nameEn\"\n [required]=\"true\"\n ></mt-text-field>\n <mt-text-field\n [label]=\"t('nameArabic')\"\n [placeholder]=\"t('enterNameArabic')\"\n [formControl]=\"stepForm.controls.nameAr\"\n [required]=\"true\"\n ></mt-text-field>\n </div>\n\n @if (currentStepType() === \"UserInput\") {\n @if (!isEditingInitialNode()) {\n <mt-card>\n <div class=\"space-y-4\">\n <div\n class=\"text-sm font-semibold text-surface-900\"\n >\n {{ t(\"approver\") }}\n </div>\n <mt-select-field\n [label]=\"t('approver')\"\n [options]=\"[\n { label: t('group'), value: '1' },\n { label: t('role'), value: '2' },\n ]\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [formControl]=\"stepForm.controls.targetType\"\n [required]=\"true\"\n ></mt-select-field>\n\n @if (\n stepForm.controls.targetType.value === \"1\"\n ) {\n <mt-select-field\n [label]=\"t('group')\"\n [placeholder]=\"t('selectGroup')\"\n [options]=\"groups()\"\n optionLabel=\"name.display\"\n optionValue=\"id\"\n [formControl]=\"stepForm.controls.group\"\n [required]=\"true\"\n ></mt-select-field>\n } @else {\n <mt-select-field\n [label]=\"t('role')\"\n [placeholder]=\"t('selectRole')\"\n [options]=\"roles()\"\n [group]=\"hasGroupedRoles()\"\n optionGroupLabel=\"label\"\n optionGroupChildren=\"items\"\n optionLabel=\"name.display\"\n optionValue=\"value\"\n [formControl]=\"stepForm.controls.role\"\n [required]=\"true\"\n ></mt-select-field>\n }\n\n <mt-number-field\n [label]=\"t('sla')\"\n [placeholder]=\"t('enterSla')\"\n [formControl]=\"stepForm.controls.sla\"\n [required]=\"true\"\n [min]=\"0\"\n ></mt-number-field>\n </div>\n </mt-card>\n }\n } @else {\n <mt-card>\n <div class=\"space-y-4\">\n <div class=\"grid gap-3\">\n <div class=\"grid gap-1\">\n <label\n class=\"text-sm font-medium text-surface-900\"\n >\n {{ t(\"app\") }}\n </label>\n <div\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-700\"\n >\n {{ t(\"appLabelPplus\") }}\n </div>\n </div>\n\n <div class=\"grid gap-1\">\n <label\n class=\"text-sm font-medium text-surface-900\"\n >\n {{ t(\"action\") }}\n </label>\n <div\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-900\"\n >\n {{ getAppActionDisplayName() }}\n </div>\n @if (getAppActionKey()) {\n <div class=\"text-xs text-muted-color\">\n {{ getAppActionKey() }}\n </div>\n }\n </div>\n\n @if (\n getAppActionDescription();\n as description\n ) {\n <div\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-muted-color\"\n >\n {{ description }}\n </div>\n }\n\n @if (\n getAppActionDiscoveryMessage();\n as discoveryMessage\n ) {\n <div\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\n >\n {{ discoveryMessage }}\n </div>\n }\n </div>\n </div>\n </mt-card>\n\n <mt-card>\n <div class=\"space-y-4\">\n <div>\n <div\n class=\"text-sm font-semibold text-surface-900\"\n >\n {{ t(\"appActionConfiguration\") }}\n </div>\n <div class=\"mt-1 text-sm text-muted-color\">\n {{ t(\"appActionConfigurationDescription\") }}\n </div>\n </div>\n\n @if (\n loadingAppActionDetail() &&\n appActionSettingsForm.controls.actionKey.value\n ) {\n <div class=\"space-y-3\">\n <p-skeleton\n width=\"100%\"\n height=\"2.5rem\"\n ></p-skeleton>\n <p-skeleton\n width=\"100%\"\n height=\"6rem\"\n ></p-skeleton>\n </div>\n } @else if (\n !appActionSettingsForm.controls.actionKey.value\n ) {\n <div\n class=\"rounded-xl border border-dashed border-surface-300 px-3 py-4 text-sm text-muted-color\"\n >\n {{ t(\"appActionUnavailable\") }}\n </div>\n } @else if (\n appActionConfigDefinition().mode === \"schema\" &&\n appActionConfigDefinition().fields.length === 0\n ) {\n <div\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-4 text-sm text-muted-color\"\n >\n {{ t(\"appActionNoConfigRequired\") }}\n </div>\n } @else if (\n appActionConfigDefinition().mode === \"raw\"\n ) {\n <div class=\"space-y-3\">\n <div\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\n >\n {{ t(\"appActionRawConfigFallback\") }}\n </div>\n <mt-textarea-field\n [label]=\"t('appActionConfigJson')\"\n [placeholder]=\"\n t('enterAppActionConfigJson')\n \"\n [formControl]=\"appActionRawConfigControl\"\n [rows]=\"'10'\"\n ></mt-textarea-field>\n </div>\n } @else {\n <div class=\"space-y-3\">\n @for (\n field of appActionConfigDefinition().fields;\n track field.key\n ) {\n <div class=\"space-y-2\">\n @switch (field.kind) {\n @case (\"text\") {\n <mt-text-field\n [label]=\"field.title\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n [required]=\"field.required\"\n ></mt-text-field>\n }\n @case (\"number\") {\n <mt-number-field\n [label]=\"field.title\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n [required]=\"field.required\"\n ></mt-number-field>\n }\n @case (\"toggle\") {\n <mt-toggle-field\n [label]=\"field.title\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n ></mt-toggle-field>\n }\n @case (\"select\") {\n <mt-select-field\n [label]=\"field.title\"\n [options]=\"\n getAppActionEnumOptions(field)\n \"\n optionLabel=\"label\"\n optionValue=\"value\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n [required]=\"field.required\"\n ></mt-select-field>\n }\n @case (\"json\") {\n <mt-textarea-field\n [label]=\"field.title\"\n [formControl]=\"\n getAppActionConfigControl(\n field.key\n )\n \"\n [required]=\"field.required\"\n [rows]=\"'8'\"\n ></mt-textarea-field>\n }\n }\n\n @if (field.description) {\n <div class=\"text-xs text-muted-color\">\n {{ field.description }}\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (getAppActionConfigMessages().length) {\n <div\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-3 text-sm text-red-700\"\n >\n @for (\n message of getAppActionConfigMessages();\n track message\n ) {\n <div>{{ message }}</div>\n }\n </div>\n }\n </div>\n </mt-card>\n\n <mt-card>\n <div class=\"space-y-4\">\n <div\n class=\"text-sm font-semibold text-surface-900\"\n >\n {{ t(\"advancedExecution\") }}\n </div>\n\n <mt-number-field\n [label]=\"t('sla')\"\n [placeholder]=\"t('enterSla')\"\n [formControl]=\"stepForm.controls.sla\"\n [required]=\"true\"\n [min]=\"0\"\n ></mt-number-field>\n\n <mt-select-field\n [label]=\"t('failureBehavior')\"\n [options]=\"failureBehaviorOptions()\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [formControl]=\"\n appActionSettingsForm.controls.failureBehavior\n \"\n [required]=\"true\"\n ></mt-select-field>\n\n <mt-number-field\n [label]=\"t('timeoutSeconds')\"\n [formControl]=\"\n appActionSettingsForm.controls.timeoutSeconds\n \"\n [required]=\"true\"\n [min]=\"1\"\n ></mt-number-field>\n </div>\n </mt-card>\n }\n </div>\n }\n @case (\"tab2\") {\n <div class=\"mt-4\">\n <mt-card>\n <div class=\"flex items-center gap-4\">\n <div class=\"flex flex-1 items-center gap-3\">\n <mt-button\n [severity]=\"'secondary'\"\n icon=\"file.file-check-02\"\n />\n <div class=\"flex flex-col\">\n <label class=\"font-medium\">{{\n t(\"configureForm\")\n }}</label>\n <span class=\"text-sm text-muted-color\">{{\n t(\"configureForm-description\")\n }}</span>\n </div>\n </div>\n </div>\n <div class=\"mt-3\">\n <mt-button\n [label]=\"t('configureForm')\"\n size=\"small\"\n (onClick)=\"openFormModal()\"\n ></mt-button>\n </div>\n </mt-card>\n </div>\n }\n @case (\"tab3\") {\n <div class=\"mt-4\">\n <mt-toggle-field\n toggleShape=\"card\"\n [label]=\"t('show-hide')\"\n [descriptionCard]=\"t('show-hide-notifications')\"\n icon=\"general.eye\"\n [formControl]=\"showHideControl\"\n >\n <ng-template #toggleCardBottom>\n @if (showHideControl.value) {\n <div class=\"mt-3\">\n <mt-button\n [label]=\"t('configureNotifications')\"\n size=\"small\"\n (onClick)=\"openNotificationsModal()\"\n ></mt-button>\n </div>\n }\n </ng-template>\n </mt-toggle-field>\n </div>\n }\n }\n }\n </div>\n </ng-template>\n </mt-structure-builder>\n } @else {\n <p-skeleton width=\"100%\" height=\"50rem\" />\n }\n }\n @case (\"notification\") {\n <div class=\"flex-1 flex justify-center items-start p-4\">\n <div class=\"w-1/2 max-[1025px]:w-full\">\n <mt-card [title]=\"t('notifications')\">\n <mt-notification-template />\n </mt-card>\n </div>\n </div>\n }\n }\n </div>\n</ng-container>\n" }]
|
|
1578
2505
|
}], ctorParameters: () => [] });
|
|
1579
2506
|
|
|
1580
2507
|
// store/app.state.ts
|
|
@@ -1592,5 +2519,5 @@ const REQUEST_CONTEXT = new HttpContextToken(() => ({
|
|
|
1592
2519
|
* Generated bundle index. Do not edit.
|
|
1593
2520
|
*/
|
|
1594
2521
|
|
|
1595
|
-
export { CreateConnection, CreateStep, DeleteConnection, DeleteStep, GetFormulaProperties, GetGroups, GetRolesForModule, GetStep, GetWorkflow, GetWorkflows, PublishWorkflow, REQUEST_CONTEXT, SetModuleInfo, UpdateConnection, UpdateStep, ValidateFlow, WorkflowBuilder, WorkflowFacade, WorkflowState };
|
|
2522
|
+
export { CreateConnection, CreateStep, DeleteConnection, DeleteStep, GetAppActionDetail, GetAppActions, GetFormulaProperties, GetGroups, GetRolesForModule, GetStep, GetWorkflow, GetWorkflows, PublishWorkflow, REQUEST_CONTEXT, SetModuleInfo, UpdateConnection, UpdateStep, ValidateFlow, WorkflowBuilder, WorkflowFacade, WorkflowState };
|
|
1596
2523
|
//# sourceMappingURL=masterteam-workflow.mjs.map
|