@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.
@@ -1,25 +1,28 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, computed, Component, signal, linkedSignal, effect } from '@angular/core';
3
- import { Card } from '@masterteam/components/card';
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 ?? null;
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 ?? null;
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 ? { ...createdStep, loading: false } : step);
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.0.3", ngImport: i0, type: WorkflowState, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
748
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkflowState });
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.0.3", ngImport: i0, type: WorkflowState, decorators: [{
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.0.3", ngImport: i0, type: WorkflowFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
970
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkflowFacade, providedIn: 'root' });
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.0.3", ngImport: i0, type: WorkflowFacade, decorators: [{
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
- class WorkflowNotificationsDrawer {
982
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkflowNotificationsDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
983
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.3", type: WorkflowNotificationsDrawer, isStandalone: true, selector: "mt-workflow-notifications-drawer", ngImport: i0, 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", styles: [""], dependencies: [{ kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: NotificationTemplate, selector: "mt-notification-template" }] });
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.0.3", ngImport: i0, type: WorkflowFormDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
993
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.3", type: WorkflowFormDrawer, isStandalone: true, selector: "mt-workflow-form-drawer", ngImport: i0, template: "<div [class]=\"modal.contentClass + ' p-4'\">\r\n <mt-form-builder\r\n class=\"flex-1\"\r\n canvasStyleClass=\"!w-full\"\r\n mode=\"manageProperties\"\r\n />\r\n</div>\r\n", styles: [""], dependencies: [{ kind: "component", type: FormBuilder, selector: "mt-form-builder", inputs: ["canvasStyleClass", "mode"] }] });
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.0.3", ngImport: i0, type: WorkflowFormDrawer, decorators: [{
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'\">\r\n <mt-form-builder\r\n class=\"flex-1\"\r\n canvasStyleClass=\"!w-full\"\r\n mode=\"manageProperties\"\r\n />\r\n</div>\r\n" }]
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
- // Main tabs for Workflow and Notification
1007
- mainTab = signal('workflow', ...(ngDevMode ? [{ debugName: "mainTab" }] : []));
1008
- mainTabsList = [
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
- groups = this.workflowFacade.groups;
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
- key: 'isVisible',
1066
- label: this.transloco.translate('workflow.builder.columnVisible'),
1067
- type: 'boolean',
1753
+ label: this.transloco.translate('workflow.builder.failureBehaviorStop'),
1754
+ value: 'Stop',
1068
1755
  },
1069
1756
  {
1070
- key: 'isEditable',
1071
- label: this.transloco.translate('workflow.builder.columnEditable'),
1072
- type: 'boolean',
1757
+ label: this.transloco.translate('workflow.builder.failureBehaviorContinue'),
1758
+ value: 'Continue',
1073
1759
  },
1074
- ], ...(ngDevMode ? [{ debugName: "propertiesColumns" }] : []));
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 baseTabs = [
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
- return baseTabs;
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
- baseTabs.push({
1087
- label: this.transloco.translate('workflow.builder.tabProperties'),
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 baseTabs;
1098
- }, ...(ngDevMode ? [{ debugName: "tabsList" }] : []));
1099
- nodeDialogFooterConfig = {
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: '#000000',
1794
+ color: '#0369A1',
1114
1795
  },
1115
- ];
1116
- // Base form sections - defined once for reusability
1117
- baseNameFields = {
1118
- key: 'settings',
1119
- type: 'none',
1120
- bodyClass: 'space-y-2',
1121
- fields: [
1122
- {
1123
- key: 'name.en',
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
- // Nodes currently in the flow (with positions) - linked to store via selectors
1259
- steps = computed(() => {
1260
- return this.workflowFacade.steps().map((step) => ({
1261
- ...step,
1262
- color: '#000000',
1263
- }));
1264
- }, ...(ngDevMode ? [{ debugName: "steps" }] : []));
1265
- // Connections between nodes (matching your design) - linked to store via selectors
1266
- connections = computed(() => {
1267
- return this.workflowFacade.connections().map((connection) => ({
1268
- ...connection,
1269
- from: connection.source,
1270
- to: connection.target,
1271
- }));
1272
- }, ...(ngDevMode ? [{ debugName: "connections" }] : []));
1273
- // Node Actions
1274
- nodeActions = signal([
1275
- {
1276
- key: 'edit',
1277
- icon: 'general.edit-05',
1278
- variant: 'outlined',
1279
- size: 'small',
1280
- tooltip: 'Edit',
1281
- },
1282
- {
1283
- key: 'delete',
1284
- icon: 'general.trash-01',
1285
- variant: 'outlined',
1286
- size: 'small',
1287
- severity: 'danger',
1288
- tooltip: 'Delete',
1289
- condition: (node) => !node.isInitial,
1290
- },
1291
- ], ...(ngDevMode ? [{ debugName: "nodeActions" }] : []));
1292
- showHideControl = new FormControl(false);
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 && workflow.isPublished !== undefined) {
1906
+ if (workflow?.isPublished !== undefined) {
1298
1907
  this.isPublished.set(workflow.isPublished);
1299
1908
  }
1300
1909
  });
1301
- // Effect to watch selectedStep and patch form when it changes
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
- // Patch form with complete step data including targetType and targetValue
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 'createNode':
1404
- if (event.data) {
1405
- this.isCreatingStep.set(false);
1406
- // Get form values
1407
- const formValue = this.nodeFormControl.value;
1408
- // Get selected properties with isVisible and isEditable flags
1409
- const propertiesPayload = this.propertiesTableData()
1410
- .filter((prop) => prop.isVisible || prop.isEditable)
1411
- .map((prop) => ({
1412
- PropertyId: prop.id,
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
- break;
1430
- case 'startUpdating':
1431
- if (event.data) {
1432
- this.clearForm();
1433
- this.isCreatingStep.set(false);
1434
- const stepId = event.data.id;
1435
- // Set the flag based on whether node is initial
1436
- // This will trigger nodeForm to rebuild automatically via linkedSignal
1437
- this.isEditingInitialNode.set(!!event.data.isInitial);
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', stepId, 'processschema', workflowId);
2002
+ this.formBuilderFacade.setModuleInfo('processstep', event.data.id, 'processschema', workflowId);
1446
2003
  this.formBuilderFacade.getFormConfiguration();
1447
2004
  }
1448
2005
  }
1449
2006
  }
1450
- break;
1451
- case 'startCreating':
1452
- // Reset form and properties for new step
1453
- this.clearForm();
1454
- this.isCreatingStep.set(true);
1455
- break;
1456
- case 'updateNode':
1457
- if (event.data) {
1458
- // Get form values
1459
- const formValue = this.nodeFormControl.value;
1460
- const stepId = event.data.id;
1461
- const isInitial = !!event.data.isInitial;
1462
- // Get selected properties with isVisible and isEditable flags
1463
- const propertiesPayload = this.propertiesTableData()
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
- break;
1484
- case 'deleteNode':
1485
- if (event.data) {
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
- break;
1496
- case 'createConnection':
1497
- if (event.data) {
1498
- // Create connection in store
1499
- const connectionPayload = {
1500
- sourceStepId: event.data.from,
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
- break;
1508
- case 'updateConnection':
1509
- if (event.data) {
1510
- // Update connection in store
1511
- const connectionId = event.data.id;
1512
- const updatePayload = {
1513
- sourceStepId: event.data.from,
1514
- targetStepId: event.data.to,
1515
- priority: event.data.priority ?? 1,
1516
- formula: event.data.formula ?? null,
1517
- };
1518
- this.workflowFacade.updateConnection(connectionId, updatePayload);
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
- break;
1521
- case 'deleteConnection':
1522
- if (event.data) {
1523
- const connectionIdToDelete = event.data.id;
1524
- // Delete connection from store
1525
- this.workflowFacade.deleteConnection(connectionIdToDelete);
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
- break;
2070
+ return;
2071
+ }
1528
2072
  default:
1529
- break;
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((result) => {
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
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: WorkflowBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
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.0.3", ngImport: i0, type: WorkflowBuilder, decorators: [{
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