@masterteam/workflow 0.0.35 → 0.0.37

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.
@@ -20,7 +20,7 @@ import { StructureBuilder } from '@masterteam/structure-builder';
20
20
  import { Tabs } from '@masterteam/components/tabs';
21
21
  import { Skeleton } from 'primeng/skeleton';
22
22
  import { Action, Selector, State, Store, select } from '@ngxs/store';
23
- import { HttpClient, HttpContextToken } from '@angular/common/http';
23
+ import { HttpClient, HttpParams, HttpContextToken } from '@angular/common/http';
24
24
  import { of } from 'rxjs';
25
25
  import { tap, catchError, finalize } from 'rxjs/operators';
26
26
 
@@ -68,6 +68,17 @@ class GetAppActionDetail {
68
68
  this.actionKey = actionKey;
69
69
  }
70
70
  }
71
+ class GetAppActionOptions {
72
+ actionKey;
73
+ fieldKey;
74
+ params;
75
+ static type = '[Workflow] Get App Action Options';
76
+ constructor(actionKey, fieldKey, params) {
77
+ this.actionKey = actionKey;
78
+ this.fieldKey = fieldKey;
79
+ this.params = params;
80
+ }
81
+ }
71
82
  class GetStep {
72
83
  stepId;
73
84
  static type = '[Workflow] Get Step';
@@ -176,6 +187,7 @@ const DEFAULT_STATE = {
176
187
  roles: [],
177
188
  appActionDescriptors: [],
178
189
  selectedAppActionDescriptor: null,
190
+ appActionOptionsCache: {},
179
191
  selectedStep: null,
180
192
  isFlowValid: null,
181
193
  loadingActive: [],
@@ -217,6 +229,9 @@ let WorkflowState = class WorkflowState {
217
229
  static selectedAppActionDescriptor(state) {
218
230
  return state.selectedAppActionDescriptor;
219
231
  }
232
+ static appActionOptionsCache(state) {
233
+ return state.appActionOptionsCache ?? {};
234
+ }
220
235
  static selectedStep(state) {
221
236
  return state.selectedStep;
222
237
  }
@@ -427,6 +442,64 @@ let WorkflowState = class WorkflowState {
427
442
  return of(null);
428
443
  }), finalize(() => endLoading(ctx, 'getAppActionDetail')));
429
444
  }
445
+ getAppActionOptions(ctx, action) {
446
+ const cacheKey = buildAppActionOptionsCacheKey(action.actionKey, action.fieldKey, action.params);
447
+ const cached = ctx.getState().appActionOptionsCache?.[cacheKey];
448
+ if (cached && !cached.errorCode) {
449
+ return of(cached.options);
450
+ }
451
+ startLoading(ctx, 'getAppActionOptions');
452
+ let params = new HttpParams();
453
+ if (action.params) {
454
+ for (const [key, value] of Object.entries(action.params)) {
455
+ if (value === null || value === undefined || value === '')
456
+ continue;
457
+ params = params.set(key, String(value));
458
+ }
459
+ }
460
+ const url = `${this.workflowAppActionsUrl}/actions/${encodeURIComponent(action.actionKey)}/options/${encodeURIComponent(action.fieldKey)}`;
461
+ return this.http
462
+ .get(url, params.keys().length ? { params } : {})
463
+ .pipe(tap((response) => {
464
+ const options = normalizeWorkflowAppActionOptions(response?.data);
465
+ const entry = {
466
+ options,
467
+ errorCode: null,
468
+ errorMessage: null,
469
+ loadedAt: Date.now(),
470
+ };
471
+ const state = ctx.getState();
472
+ ctx.patchState({
473
+ appActionOptionsCache: {
474
+ ...(state.appActionOptionsCache ?? {}),
475
+ [cacheKey]: entry,
476
+ },
477
+ });
478
+ }), catchError((error) => {
479
+ const code = error?.error?.errors?.code ??
480
+ error?.error?.code ??
481
+ 'workflow_app_action_options_error';
482
+ const message = error?.error?.errors?.message ??
483
+ error?.error?.message ??
484
+ error?.message ??
485
+ 'Failed to load options';
486
+ const state = ctx.getState();
487
+ const entry = {
488
+ options: [],
489
+ errorCode: String(code),
490
+ errorMessage: String(message),
491
+ loadedAt: Date.now(),
492
+ };
493
+ ctx.patchState({
494
+ appActionOptionsCache: {
495
+ ...(state.appActionOptionsCache ?? {}),
496
+ [cacheKey]: entry,
497
+ },
498
+ });
499
+ setLoadingError(ctx, 'getAppActionOptions', String(message));
500
+ return of([]);
501
+ }), finalize(() => endLoading(ctx, 'getAppActionOptions')));
502
+ }
430
503
  getStep(ctx, action) {
431
504
  startLoading(ctx, 'getStep');
432
505
  return this.http
@@ -858,6 +931,9 @@ __decorate([
858
931
  __decorate([
859
932
  Action(GetAppActionDetail)
860
933
  ], WorkflowState.prototype, "getAppActionDetail", null);
934
+ __decorate([
935
+ Action(GetAppActionOptions)
936
+ ], WorkflowState.prototype, "getAppActionOptions", null);
861
937
  __decorate([
862
938
  Action(GetStep)
863
939
  ], WorkflowState.prototype, "getStep", null);
@@ -915,6 +991,9 @@ __decorate([
915
991
  __decorate([
916
992
  Selector()
917
993
  ], WorkflowState, "selectedAppActionDescriptor", null);
994
+ __decorate([
995
+ Selector()
996
+ ], WorkflowState, "appActionOptionsCache", null);
918
997
  __decorate([
919
998
  Selector()
920
999
  ], WorkflowState, "selectedStep", null);
@@ -941,7 +1020,7 @@ WorkflowState = __decorate([
941
1020
  ], WorkflowState);
942
1021
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowState, decorators: [{
943
1022
  type: Injectable
944
- }], propDecorators: { setModuleInfo: [], getWorkflows: [], getWorkflow: [], getFormulaProperties: [], getGroups: [], getRolesForModule: [], getAppActions: [], getAppActionDetail: [], getStep: [], validateFlow: [], createStep: [], updateStep: [], createConnection: [], updateConnection: [], publishWorkflow: [], deleteStep: [], deleteConnection: [] } });
1023
+ }], propDecorators: { setModuleInfo: [], getWorkflows: [], getWorkflow: [], getFormulaProperties: [], getGroups: [], getRolesForModule: [], getAppActions: [], getAppActionDetail: [], getAppActionOptions: [], getStep: [], validateFlow: [], createStep: [], updateStep: [], createConnection: [], updateConnection: [], publishWorkflow: [], deleteStep: [], deleteConnection: [] } });
945
1024
  function normalizeWorkflowRoles(data) {
946
1025
  if (!Array.isArray(data)) {
947
1026
  return [];
@@ -989,6 +1068,13 @@ function normalizeWorkflowStepSchema(input) {
989
1068
  function normalizeWorkflowStepDetail(input) {
990
1069
  const type = normalizeWorkflowStepType$1(input?.type);
991
1070
  const appAction = normalizeWorkflowAppAction(input, type);
1071
+ const systemKind = typeof input?.systemKind === 'string' && input.systemKind.length
1072
+ ? input.systemKind
1073
+ : type === 'ApprovalCommit'
1074
+ ? 'ApprovalCommit'
1075
+ : null;
1076
+ const isSystem = !!input?.isSystem || type === 'ApprovalCommit';
1077
+ const isLocked = !!input?.isLocked || type === 'ApprovalCommit';
992
1078
  return {
993
1079
  ...input,
994
1080
  id: Number(input?.id ?? 0),
@@ -1006,10 +1092,17 @@ function normalizeWorkflowStepDetail(input) {
1006
1092
  ? undefined
1007
1093
  : !!input.hasForm,
1008
1094
  appAction,
1095
+ systemKind,
1096
+ isSystem,
1097
+ isLocked,
1009
1098
  };
1010
1099
  }
1011
1100
  function normalizeWorkflowStepType$1(type) {
1012
- return type === 'AppAction' ? 'AppAction' : 'UserInput';
1101
+ if (type === 'AppAction')
1102
+ return 'AppAction';
1103
+ if (type === 'ApprovalCommit')
1104
+ return 'ApprovalCommit';
1105
+ return 'UserInput';
1013
1106
  }
1014
1107
  function normalizeWorkflowStepName(name) {
1015
1108
  if (typeof name === 'string') {
@@ -1064,6 +1157,14 @@ function normalizeWorkflowAppAction(input, type) {
1064
1157
  supportedScopes: Array.isArray(candidate?.supportedScopes)
1065
1158
  ? candidate.supportedScopes.map(String)
1066
1159
  : [],
1160
+ configFields: normalizeAppActionConfigFields(candidate?.configFields),
1161
+ runtimeInputs: normalizeAppActionRuntimeInputs(candidate?.runtimeInputs),
1162
+ outputs: normalizeAppActionOutputs(candidate?.outputs),
1163
+ supportedPhases: Array.isArray(candidate?.supportedPhases)
1164
+ ? candidate.supportedPhases
1165
+ .filter((value) => typeof value === 'string')
1166
+ .map((value) => value)
1167
+ : [],
1067
1168
  };
1068
1169
  }
1069
1170
  function normalizeWorkflowAppActionDescriptors(data) {
@@ -1088,8 +1189,111 @@ function normalizeWorkflowAppActionDescriptor(data) {
1088
1189
  supportedScopes: Array.isArray(record['supportedScopes'])
1089
1190
  ? record['supportedScopes'].map(String)
1090
1191
  : [],
1192
+ configFields: normalizeAppActionConfigFields(record['configFields']),
1193
+ runtimeInputs: normalizeAppActionRuntimeInputs(record['runtimeInputs']),
1194
+ outputs: normalizeAppActionOutputs(record['outputs']),
1195
+ supportedPhases: Array.isArray(record['supportedPhases'])
1196
+ ? record['supportedPhases']
1197
+ .filter((value) => typeof value === 'string')
1198
+ .map((value) => value)
1199
+ : [],
1091
1200
  };
1092
1201
  }
1202
+ function normalizeAppActionConfigFields(value) {
1203
+ if (!Array.isArray(value)) {
1204
+ return [];
1205
+ }
1206
+ return value.filter(isRecord$1).map((record) => ({
1207
+ key: readString(record, 'key') ?? '',
1208
+ displayName: readString(record, 'displayName') ?? readString(record, 'key') ?? '',
1209
+ description: readString(record, 'description') ?? null,
1210
+ type: (readString(record, 'type') ?? 'string'),
1211
+ required: !!record['required'],
1212
+ uiControl: readString(record, 'uiControl') ?? null,
1213
+ optionsProviderKey: readString(record, 'optionsProviderKey') ?? null,
1214
+ designerVisible: record['designerVisible'] === undefined ||
1215
+ record['designerVisible'] === null
1216
+ ? true
1217
+ : !!record['designerVisible'],
1218
+ enumValues: Array.isArray(record['enumValues'])
1219
+ ? record['enumValues']
1220
+ : undefined,
1221
+ defaultValue: record['defaultValue'],
1222
+ }));
1223
+ }
1224
+ function normalizeAppActionRuntimeInputs(value) {
1225
+ if (!Array.isArray(value)) {
1226
+ return [];
1227
+ }
1228
+ return value.filter(isRecord$1).map((record) => ({
1229
+ key: readString(record, 'key') ?? '',
1230
+ source: (readString(record, 'source') ?? 'Context'),
1231
+ contextKey: readString(record, 'contextKey') ?? null,
1232
+ resolverKey: readString(record, 'resolverKey') ?? null,
1233
+ required: !!record['required'],
1234
+ designerVisible: !!record['designerVisible'],
1235
+ }));
1236
+ }
1237
+ function normalizeAppActionOutputs(value) {
1238
+ if (!Array.isArray(value)) {
1239
+ return [];
1240
+ }
1241
+ return value.filter(isRecord$1).map((record) => ({
1242
+ key: readString(record, 'key') ?? '',
1243
+ type: readString(record, 'type') ?? 'string',
1244
+ persistToWorkflowContext: !!record['persistToWorkflowContext'],
1245
+ description: readString(record, 'description') ?? null,
1246
+ }));
1247
+ }
1248
+ function normalizeWorkflowAppActionOptions(value) {
1249
+ if (!Array.isArray(value)) {
1250
+ return [];
1251
+ }
1252
+ const result = [];
1253
+ for (const item of value) {
1254
+ if (!isRecord$1(item))
1255
+ continue;
1256
+ const rawValue = item['value'];
1257
+ if (rawValue === undefined ||
1258
+ rawValue === null ||
1259
+ (typeof rawValue !== 'string' &&
1260
+ typeof rawValue !== 'number' &&
1261
+ typeof rawValue !== 'boolean')) {
1262
+ continue;
1263
+ }
1264
+ const labelRaw = item['label'];
1265
+ const label = typeof labelRaw === 'string'
1266
+ ? labelRaw
1267
+ : isRecord$1(labelRaw)
1268
+ ? labelRaw
1269
+ : String(rawValue);
1270
+ const metadata = isRecord$1(item['metadata'])
1271
+ ? item['metadata']
1272
+ : undefined;
1273
+ result.push({
1274
+ value: rawValue,
1275
+ label,
1276
+ disabled: !!item['disabled'],
1277
+ metadata,
1278
+ });
1279
+ }
1280
+ return result;
1281
+ }
1282
+ /**
1283
+ * Build a deterministic cache key for an options request:
1284
+ * `{actionKey}::{fieldKey}::{paramHash}`. The param hash is a sorted
1285
+ * `key=value&...` string of non-empty params, or `_` if none.
1286
+ */
1287
+ function buildAppActionOptionsCacheKey(actionKey, fieldKey, params) {
1288
+ const paramHash = !params
1289
+ ? '_'
1290
+ : Object.entries(params)
1291
+ .filter(([, value]) => value !== null && value !== undefined && value !== '')
1292
+ .sort(([a], [b]) => a.localeCompare(b))
1293
+ .map(([key, value]) => `${key}=${String(value)}`)
1294
+ .join('&') || '_';
1295
+ return `${actionKey}::${fieldKey}::${paramHash}`;
1296
+ }
1093
1297
  function upsertWorkflowAppActionDescriptor(descriptors, descriptor) {
1094
1298
  const index = descriptors.findIndex((item) => item.actionKey === descriptor.actionKey);
1095
1299
  if (index < 0) {
@@ -1176,6 +1380,7 @@ class WorkflowFacade {
1176
1380
  roles = select(WorkflowState.roles);
1177
1381
  appActionDescriptors = select(WorkflowState.appActionDescriptors);
1178
1382
  selectedAppActionDescriptor = select(WorkflowState.selectedAppActionDescriptor);
1383
+ appActionOptionsCache = select(WorkflowState.appActionOptionsCache);
1179
1384
  selectedStep = select(WorkflowState.selectedStep);
1180
1385
  isFlowValid = select(WorkflowState.isFlowValid);
1181
1386
  steps = select(WorkflowState.stepsSchema);
@@ -1215,6 +1420,18 @@ class WorkflowFacade {
1215
1420
  loadAppActionDetail(actionKey) {
1216
1421
  return this.store.dispatch(new GetAppActionDetail(actionKey));
1217
1422
  }
1423
+ loadAppActionOptions(actionKey, fieldKey, params) {
1424
+ return this.store.dispatch(new GetAppActionOptions(actionKey, fieldKey, params));
1425
+ }
1426
+ /**
1427
+ * Synchronous lookup helper: returns the cached options entry for a given
1428
+ * (actionKey, fieldKey, params) combination, or `undefined` if the request
1429
+ * has not been issued yet. Pair with `loadAppActionOptions` to fetch.
1430
+ */
1431
+ getCachedAppActionOptions(actionKey, fieldKey, params) {
1432
+ const cacheKey = buildAppActionOptionsCacheKey(actionKey, fieldKey, params);
1433
+ return this.appActionOptionsCache()?.[cacheKey];
1434
+ }
1218
1435
  loadStep(stepId) {
1219
1436
  return this.store.dispatch(new GetStep(stepId));
1220
1437
  }
@@ -1261,6 +1478,114 @@ const SUPPORTED_PROPERTY_TYPES = [
1261
1478
  'array',
1262
1479
  'object',
1263
1480
  ];
1481
+ /**
1482
+ * Build the editor definition from a descriptor's explicit `configFields[]`.
1483
+ * Prefer this over `buildWorkflowAppActionConfigEditorDefinition` whenever
1484
+ * the backend ships descriptor-driven fields (per the AppActions handoff).
1485
+ *
1486
+ * Only `designerVisible !== false` fields are rendered. Returns an empty
1487
+ * schema when the input is empty so callers can fall back to JSON-Schema
1488
+ * parsing when needed.
1489
+ */
1490
+ function buildWorkflowAppActionConfigEditorDefinitionFromFields(configFields) {
1491
+ const visibleFields = (configFields ?? []).filter((field) => field?.designerVisible !== false);
1492
+ if (!visibleFields.length) {
1493
+ return {
1494
+ mode: 'schema',
1495
+ schema: { type: 'object', properties: [], required: [] },
1496
+ fields: [],
1497
+ errors: [],
1498
+ };
1499
+ }
1500
+ const properties = visibleFields.map((field) => ({
1501
+ key: field.key,
1502
+ title: field.displayName || field.key,
1503
+ description: field.description ?? null,
1504
+ type: field.type ?? 'string',
1505
+ required: !!field.required,
1506
+ enumValues: field.enumValues,
1507
+ rawSchema: {
1508
+ type: field.type,
1509
+ title: field.displayName,
1510
+ description: field.description ?? undefined,
1511
+ ...(field.enumValues ? { enum: field.enumValues } : {}),
1512
+ },
1513
+ }));
1514
+ const schema = {
1515
+ type: 'object',
1516
+ properties,
1517
+ required: visibleFields.filter((f) => f.required).map((f) => f.key),
1518
+ };
1519
+ return {
1520
+ mode: 'schema',
1521
+ schema,
1522
+ fields: visibleFields.map((field) => mapDescriptorFieldToEditorField(field)),
1523
+ errors: [],
1524
+ };
1525
+ }
1526
+ function mapDescriptorFieldToEditorField(field) {
1527
+ const title = field.displayName || field.key;
1528
+ const required = !!field.required;
1529
+ const description = field.description ?? null;
1530
+ const type = field.type ?? 'string';
1531
+ // Explicit uiControl from the descriptor takes precedence — including
1532
+ // `select` driven by an `optionsProviderKey` (no enum needed).
1533
+ if (field.uiControl === 'select' || field.optionsProviderKey) {
1534
+ return {
1535
+ key: field.key,
1536
+ title,
1537
+ description,
1538
+ type,
1539
+ kind: 'select',
1540
+ required,
1541
+ enumValues: field.enumValues,
1542
+ optionsProviderKey: field.optionsProviderKey ?? null,
1543
+ };
1544
+ }
1545
+ if (field.uiControl === 'json' || type === 'array' || type === 'object') {
1546
+ return {
1547
+ key: field.key,
1548
+ title,
1549
+ description,
1550
+ type,
1551
+ kind: 'json',
1552
+ required,
1553
+ jsonValueType: type === 'array' ? 'array' : 'object',
1554
+ };
1555
+ }
1556
+ if (field.uiControl === 'toggle' || type === 'boolean') {
1557
+ return {
1558
+ key: field.key,
1559
+ title,
1560
+ description,
1561
+ type,
1562
+ kind: 'toggle',
1563
+ required,
1564
+ };
1565
+ }
1566
+ if (field.uiControl === 'number' || type === 'number') {
1567
+ return {
1568
+ key: field.key,
1569
+ title,
1570
+ description,
1571
+ type,
1572
+ kind: 'number',
1573
+ required,
1574
+ };
1575
+ }
1576
+ if (field.enumValues?.length) {
1577
+ return {
1578
+ key: field.key,
1579
+ title,
1580
+ description,
1581
+ type,
1582
+ kind: 'select',
1583
+ required,
1584
+ enumValues: field.enumValues,
1585
+ };
1586
+ }
1587
+ return { key: field.key, title, description, type, kind: 'text', required };
1588
+ }
1264
1589
  function buildWorkflowAppActionConfigEditorDefinition(configSchemaJson) {
1265
1590
  if (!configSchemaJson?.trim()) {
1266
1591
  return {
@@ -1618,7 +1943,11 @@ function buildWorkflowStepPayload({ formValue, properties, hasNotification, appA
1618
1943
  };
1619
1944
  }
1620
1945
  function normalizeWorkflowStepType(type) {
1621
- return type === 'AppAction' ? 'AppAction' : 'UserInput';
1946
+ if (type === 'AppAction')
1947
+ return 'AppAction';
1948
+ if (type === 'ApprovalCommit')
1949
+ return 'ApprovalCommit';
1950
+ return 'UserInput';
1622
1951
  }
1623
1952
  function normalizeStepName(name) {
1624
1953
  if (name && typeof name === 'object') {
@@ -1666,6 +1995,8 @@ class WorkflowBuilder {
1666
1995
  lastAppActionDescriptorSignature = '';
1667
1996
  mainTab = signal('workflow', ...(ngDevMode ? [{ debugName: "mainTab" }] : /* istanbul ignore next */ []));
1668
1997
  selectedTab = signal('tab1', ...(ngDevMode ? [{ debugName: "selectedTab" }] : /* istanbul ignore next */ []));
1998
+ resolvedAutomaticallyOpen = signal(false, ...(ngDevMode ? [{ debugName: "resolvedAutomaticallyOpen" }] : /* istanbul ignore next */ []));
1999
+ outputsOpen = signal(false, ...(ngDevMode ? [{ debugName: "outputsOpen" }] : /* istanbul ignore next */ []));
1669
2000
  isPublished = signal(false, ...(ngDevMode ? [{ debugName: "isPublished" }] : /* istanbul ignore next */ []));
1670
2001
  isEditingInitialNode = signal(false, ...(ngDevMode ? [{ debugName: "isEditingInitialNode" }] : /* istanbul ignore next */ []));
1671
2002
  isCreatingStep = signal(false, ...(ngDevMode ? [{ debugName: "isCreatingStep" }] : /* istanbul ignore next */ []));
@@ -1693,6 +2024,7 @@ class WorkflowBuilder {
1693
2024
  loadingAppActions = this.workflowFacade.isLoading('getAppActions');
1694
2025
  loadingAppActionDetail = this.workflowFacade.isLoading('getAppActionDetail');
1695
2026
  appActionListError = this.workflowFacade.error('getAppActions');
2027
+ publishError = this.workflowFacade.error('publishWorkflow');
1696
2028
  appActionDetailError = this.workflowFacade.error('getAppActionDetail');
1697
2029
  selectedWorkflowId = computed(() => this.workflow()?.id, ...(ngDevMode ? [{ debugName: "selectedWorkflowId" }] : /* istanbul ignore next */ []));
1698
2030
  connectionFormulaSchemaId = computed(() => this.workflow()?.id, ...(ngDevMode ? [{ debugName: "connectionFormulaSchemaId" }] : /* istanbul ignore next */ []));
@@ -1764,6 +2096,40 @@ class WorkflowBuilder {
1764
2096
  value: 'Continue',
1765
2097
  },
1766
2098
  ], ...(ngDevMode ? [{ debugName: "failureBehaviorOptions" }] : /* istanbul ignore next */ []));
2099
+ /**
2100
+ * Modal-config providers — these are functions because Angular's input
2101
+ * binding doesn't re-evaluate inside a synchronous emit→read sequence,
2102
+ * so reading `currentStepType()` from a `computed` would still see the
2103
+ * stale value at the moment structure-builder reads the input. Passing
2104
+ * functions lets structure-builder call them at dialog-open time with
2105
+ * the node data, where we read `node.type` directly.
2106
+ */
2107
+ editorDrawerStyleClass = (node) => {
2108
+ const type = node?.type === 'AppAction' || node?.type === 'UserInput'
2109
+ ? node.type
2110
+ : this.currentStepType();
2111
+ return type === 'AppAction'
2112
+ ? '!w-[42rem] !absolute !shadow-none'
2113
+ : '!w-[25rem] !absolute !shadow-none';
2114
+ };
2115
+ editorAddHeader = (node) => {
2116
+ const type = node?.type === 'AppAction' || node?.type === 'UserInput'
2117
+ ? node.type
2118
+ : this.currentStepType();
2119
+ const key = type === 'AppAction'
2120
+ ? 'workflow.builder.addAction'
2121
+ : 'workflow.builder.addStep';
2122
+ return this.transloco.translate(key);
2123
+ };
2124
+ editorUpdateHeader = (node) => {
2125
+ const type = node?.type === 'AppAction' || node?.type === 'UserInput'
2126
+ ? node.type
2127
+ : this.currentStepType();
2128
+ const key = type === 'AppAction'
2129
+ ? 'workflow.builder.editAction'
2130
+ : 'workflow.builder.editStep';
2131
+ return this.transloco.translate(key);
2132
+ };
1767
2133
  tabsList = computed(() => {
1768
2134
  const tabs = [
1769
2135
  {
@@ -1830,6 +2196,8 @@ class WorkflowBuilder {
1830
2196
  variant: 'outlined',
1831
2197
  size: 'small',
1832
2198
  tooltip: 'Edit',
2199
+ // Locked system steps (e.g. Commit Approved Form) are not editable.
2200
+ condition: (node) => !node.isLocked,
1833
2201
  },
1834
2202
  {
1835
2203
  key: 'delete',
@@ -1838,7 +2206,8 @@ class WorkflowBuilder {
1838
2206
  size: 'small',
1839
2207
  severity: 'danger',
1840
2208
  tooltip: 'Delete',
1841
- condition: (node) => !node.isInitial,
2209
+ // Initial step + locked system steps are not deletable.
2210
+ condition: (node) => !node.isInitial && !node.isLocked,
1842
2211
  },
1843
2212
  ], ...(ngDevMode ? [{ debugName: "nodeActions" }] : /* istanbul ignore next */ []));
1844
2213
  nodeDialogFooterConfig = {
@@ -1870,19 +2239,33 @@ class WorkflowBuilder {
1870
2239
  }, ...(ngDevMode ? [{ debugName: "connectionFormulaConfig" }] : /* istanbul ignore next */ []));
1871
2240
  steps = computed(() => this.workflowFacade.steps().map((step) => {
1872
2241
  const isAppAction = step.type === 'AppAction';
2242
+ const isApprovalCommit = step.type === 'ApprovalCommit';
1873
2243
  return {
1874
2244
  ...step,
1875
- color: isAppAction ? '#B45309' : '#0369A1',
1876
- icon: isAppAction
1877
- ? 'general.zap'
2245
+ // System Approval-Commit step gets a distinct emerald accent + a
2246
+ // verified-check icon. Authoring is locked; the step is rendered
2247
+ // in icon mode like AppActions to communicate "system boundary".
2248
+ color: isApprovalCommit
2249
+ ? '#059669'
2250
+ : isAppAction
2251
+ ? '#B45309'
2252
+ : '#0369A1',
2253
+ icon: isApprovalCommit
2254
+ ? 'general.check-verified-01'
2255
+ : isAppAction
2256
+ ? 'general.zap'
2257
+ : step.isInitial
2258
+ ? 'map.flag-04'
2259
+ : 'file.clipboard-check',
2260
+ subtitle: isApprovalCommit
2261
+ ? this.transloco.translate('workflow.builder.approvalCommitSubtitle')
2262
+ : this.getStepTypeLabel(step.type),
2263
+ badge: isApprovalCommit
2264
+ ? null
1878
2265
  : step.isInitial
1879
- ? 'map.flag-04'
1880
- : 'file.clipboard-check',
1881
- subtitle: this.getStepTypeLabel(step.type),
1882
- badge: step.isInitial
1883
- ? this.transloco.translate('workflow.builder.initial')
1884
- : null,
1885
- style: isAppAction ? 'icon' : 'detail',
2266
+ ? this.transloco.translate('workflow.builder.initial')
2267
+ : null,
2268
+ style: isAppAction || isApprovalCommit ? 'icon' : 'detail',
1886
2269
  };
1887
2270
  }), ...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
1888
2271
  connections = computed(() => this.workflowFacade.connections().map((connection) => ({
@@ -1955,7 +2338,7 @@ class WorkflowBuilder {
1955
2338
  if (signature === this.lastAppActionDescriptorSignature) {
1956
2339
  return;
1957
2340
  }
1958
- this.rebuildAppActionConfigEditor(this.getCurrentAppActionConfigJson(), descriptor?.configSchemaJson ?? null);
2341
+ this.rebuildAppActionConfigEditor(this.getCurrentAppActionConfigJson(), descriptor?.configSchemaJson ?? null, descriptor);
1959
2342
  this.lastAppActionDescriptorSignature = signature;
1960
2343
  });
1961
2344
  this.stepForm.controls.targetType.valueChanges
@@ -2010,6 +2393,12 @@ class WorkflowBuilder {
2010
2393
  if (!event.data) {
2011
2394
  return;
2012
2395
  }
2396
+ // Locked / system steps (e.g. Commit Approved Form) are
2397
+ // backend-owned and not editable from the FE builder. Hard
2398
+ // short-circuit before opening the editor dialog.
2399
+ if (event.data.isLocked || event.data.type === 'ApprovalCommit') {
2400
+ return;
2401
+ }
2013
2402
  this.clearForm(normalizeWorkflowStepType(event.data.type));
2014
2403
  this.isCreatingStep.set(false);
2015
2404
  this.isEditingInitialNode.set(!!event.data.isInitial);
@@ -2052,6 +2441,12 @@ class WorkflowBuilder {
2052
2441
  if (!event.data) {
2053
2442
  return;
2054
2443
  }
2444
+ // Defensive guard — UI hides the delete button on locked steps
2445
+ // already; this short-circuit is for any caller that bypasses
2446
+ // the canvas chrome (e.g. keyboard shortcuts).
2447
+ if (event.data.isLocked || event.data.type === 'ApprovalCommit') {
2448
+ return;
2449
+ }
2055
2450
  this.confirmationService.confirmDelete({
2056
2451
  type: 'dialog',
2057
2452
  accept: () => {
@@ -2136,14 +2531,132 @@ class WorkflowBuilder {
2136
2531
  return this.appActionConfigForm.get(key);
2137
2532
  }
2138
2533
  getAppActionEnumOptions(field) {
2534
+ // Dynamic options (loaded from the backend options endpoint) take
2535
+ // precedence over a descriptor's static enumValues. The cache lookup
2536
+ // depends on the current actionKey + the param context (requestSchemaId).
2537
+ if (field.optionsProviderKey) {
2538
+ const dynamic = this.getDynamicAppActionOptions(field.key);
2539
+ if (dynamic !== undefined) {
2540
+ return dynamic;
2541
+ }
2542
+ }
2139
2543
  return (field.enumValues ?? []).map((value) => ({
2140
2544
  label: String(value),
2141
2545
  value,
2142
2546
  }));
2143
2547
  }
2548
+ /**
2549
+ * Lookup the cached dynamic options for a given config field. Returns
2550
+ * an empty array on backend error so the template can render an error
2551
+ * state, or `undefined` when no fetch has been issued yet.
2552
+ */
2553
+ getDynamicAppActionOptions(fieldKey) {
2554
+ const actionKey = this.appActionSettingsForm.controls.actionKey.value;
2555
+ if (!actionKey)
2556
+ return undefined;
2557
+ const params = this.buildAppActionOptionsParams();
2558
+ const cached = this.workflowFacade.getCachedAppActionOptions(actionKey, fieldKey, params);
2559
+ if (!cached)
2560
+ return undefined;
2561
+ const locale = this.transloco.getActiveLang();
2562
+ return cached.options.map((opt) => {
2563
+ const label = typeof opt.label === 'string'
2564
+ ? opt.label
2565
+ : (opt.label?.[locale] ?? opt.label?.['en'] ?? String(opt.value));
2566
+ return {
2567
+ label,
2568
+ value: opt.value,
2569
+ disabled: !!opt.disabled,
2570
+ };
2571
+ });
2572
+ }
2573
+ hasAppActionOptionsError(fieldKey) {
2574
+ const actionKey = this.appActionSettingsForm.controls.actionKey.value;
2575
+ if (!actionKey)
2576
+ return null;
2577
+ const cached = this.workflowFacade.getCachedAppActionOptions(actionKey, fieldKey, this.buildAppActionOptionsParams());
2578
+ return cached?.errorMessage ?? null;
2579
+ }
2580
+ buildAppActionOptionsParams() {
2581
+ const params = {};
2582
+ const workflowId = this.workflow()?.id;
2583
+ if (workflowId) {
2584
+ params['requestSchemaId'] = workflowId;
2585
+ }
2586
+ return params;
2587
+ }
2588
+ /**
2589
+ * Fire option-loading for every descriptor `select` field that uses
2590
+ * an `optionsProviderKey`. Called whenever the editor is rebuilt or
2591
+ * the active actionKey changes.
2592
+ */
2593
+ loadDynamicAppActionOptions() {
2594
+ const actionKey = this.appActionSettingsForm.controls.actionKey.value;
2595
+ if (!actionKey)
2596
+ return;
2597
+ const definition = this.appActionConfigDefinition();
2598
+ const params = this.buildAppActionOptionsParams();
2599
+ for (const field of definition.fields) {
2600
+ if (field.kind !== 'select' || !field.optionsProviderKey)
2601
+ continue;
2602
+ this.workflowFacade
2603
+ .loadAppActionOptions(actionKey, field.key, params)
2604
+ .subscribe();
2605
+ }
2606
+ }
2144
2607
  getAppActionDescription() {
2145
2608
  return this.currentAppActionDescriptor()?.description ?? null;
2146
2609
  }
2610
+ /** Runtime inputs are non-editable (resolved at runtime); the editor
2611
+ * shows them as read-only "Resolved automatically" hints. */
2612
+ getAppActionRuntimeInputs() {
2613
+ return (this.currentAppActionDescriptor()?.runtimeInputs ?? []).filter((input) => input.designerVisible !== true);
2614
+ }
2615
+ /** Outputs are read-only informational hints. */
2616
+ getAppActionOutputs() {
2617
+ return this.currentAppActionDescriptor()?.outputs ?? [];
2618
+ }
2619
+ /** Phases the action supports (e.g. ['AfterCommit']). Drives the
2620
+ * placement warning when the step is placed before the commit step. */
2621
+ getAppActionSupportedPhases() {
2622
+ return this.currentAppActionDescriptor()?.supportedPhases ?? [];
2623
+ }
2624
+ /**
2625
+ * `true` when the current action is restricted to `AfterCommit` and
2626
+ * therefore should be placed downstream of the `Commit Approved Form`
2627
+ * step. Surfaces an inline guidance hint in the editor.
2628
+ */
2629
+ isAppActionAfterCommitOnly() {
2630
+ const phases = this.getAppActionSupportedPhases();
2631
+ return phases.length === 1 && phases[0] === 'AfterCommit';
2632
+ }
2633
+ /**
2634
+ * `true` when an actionKey is set, descriptor lookup is not in flight,
2635
+ * and we still have no resolvable descriptor — i.e. the backend returned
2636
+ * `data: null` for the descriptor describe-one call. The saved config
2637
+ * is preserved per the AppActions handoff §5.3.
2638
+ */
2639
+ isAppActionMissing() {
2640
+ const actionKey = this.appActionSettingsForm.controls.actionKey.value;
2641
+ if (!actionKey)
2642
+ return false;
2643
+ if (this.loadingAppActionDetail())
2644
+ return false;
2645
+ return !this.currentAppActionDescriptor();
2646
+ }
2647
+ describeRuntimeInputSource(input) {
2648
+ if (input.source === 'Context') {
2649
+ return input.contextKey
2650
+ ? `Resolved from workflow context (${input.contextKey})`
2651
+ : 'Resolved from workflow context';
2652
+ }
2653
+ if (input.source === 'InternalResolver') {
2654
+ return input.resolverKey
2655
+ ? `Resolved internally (${input.resolverKey})`
2656
+ : 'Resolved internally';
2657
+ }
2658
+ return input.source;
2659
+ }
2147
2660
  getAppActionDisplayName() {
2148
2661
  return (this.currentAppActionDescriptor()?.displayName ||
2149
2662
  this.appActionSettingsForm.controls.actionKey.value ||
@@ -2225,7 +2738,22 @@ class WorkflowBuilder {
2225
2738
  failureBehavior: editorState.appActionValue.failureBehavior,
2226
2739
  timeoutSeconds: editorState.appActionValue.timeoutSeconds,
2227
2740
  }, { emitEvent: false });
2228
- this.rebuildAppActionConfigEditor(editorState.appActionValue.configJson, descriptor?.configSchemaJson ?? step.appAction?.configSchemaJson ?? null);
2741
+ this.rebuildAppActionConfigEditor(editorState.appActionValue.configJson, descriptor?.configSchemaJson ?? step.appAction?.configSchemaJson ?? null, descriptor ??
2742
+ (step.appAction
2743
+ ? {
2744
+ appCode: step.appAction.appCode,
2745
+ actionKey: step.appAction.actionKey,
2746
+ displayName: step.appAction.displayName ?? step.appAction.actionKey,
2747
+ description: step.appAction.description ?? null,
2748
+ configSchemaJson: step.appAction.configSchemaJson ?? null,
2749
+ requiredContextKeys: step.appAction.requiredContextKeys ?? [],
2750
+ supportedScopes: step.appAction.supportedScopes ?? [],
2751
+ configFields: step.appAction.configFields,
2752
+ runtimeInputs: step.appAction.runtimeInputs,
2753
+ outputs: step.appAction.outputs,
2754
+ supportedPhases: step.appAction.supportedPhases,
2755
+ }
2756
+ : null));
2229
2757
  this.lastAppActionDescriptorSignature =
2230
2758
  this.createAppActionDescriptorSignature(editorState.appActionValue.actionKey, descriptor?.configSchemaJson ??
2231
2759
  step.appAction?.configSchemaJson ??
@@ -2240,7 +2768,8 @@ class WorkflowBuilder {
2240
2768
  onAppActionActionChanged(actionKey) {
2241
2769
  this.selectedTab.set('tab1');
2242
2770
  this.lastAppActionDescriptorSignature = '';
2243
- this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, this.resolveDescriptorForActionKey(actionKey)?.configSchemaJson ?? null);
2771
+ const descriptor = this.resolveDescriptorForActionKey(actionKey);
2772
+ this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, descriptor?.configSchemaJson ?? null, descriptor);
2244
2773
  if (actionKey) {
2245
2774
  this.ensureAppActionDetailLoaded(actionKey);
2246
2775
  }
@@ -2286,7 +2815,7 @@ class WorkflowBuilder {
2286
2815
  timeoutSeconds: DEFAULT_APP_ACTION_EDITOR_VALUE.timeoutSeconds,
2287
2816
  }, { emitEvent: false });
2288
2817
  this.isPatchingStepForm = false;
2289
- this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, this.draftAppActionDescriptor()?.configSchemaJson ?? null);
2818
+ this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, this.draftAppActionDescriptor()?.configSchemaJson ?? null, this.draftAppActionDescriptor());
2290
2819
  this.ensureAppActionDetailLoaded(actionKey);
2291
2820
  }
2292
2821
  resetPropertiesTable() {
@@ -2347,8 +2876,12 @@ class WorkflowBuilder {
2347
2876
  this.rebuildAppActionConfigEditor(DEFAULT_APP_ACTION_EDITOR_VALUE.configJson, null);
2348
2877
  this.lastAppActionDescriptorSignature = '';
2349
2878
  }
2350
- rebuildAppActionConfigEditor(configJson, configSchemaJson) {
2351
- const definition = buildWorkflowAppActionConfigEditorDefinition(configSchemaJson);
2879
+ rebuildAppActionConfigEditor(configJson, configSchemaJson, descriptor) {
2880
+ // Per the AppActions handoff: prefer descriptor `configFields[]` when
2881
+ // present; fall back to JSON-Schema parsing for legacy descriptors.
2882
+ const definition = descriptor?.configFields && descriptor.configFields.length > 0
2883
+ ? buildWorkflowAppActionConfigEditorDefinitionFromFields(descriptor.configFields)
2884
+ : buildWorkflowAppActionConfigEditorDefinition(configSchemaJson);
2352
2885
  const editorValue = mapWorkflowAppActionConfigJsonToEditorValue(configJson, definition);
2353
2886
  Object.keys(this.appActionConfigForm.controls).forEach((key) => {
2354
2887
  this.appActionConfigForm.removeControl(key);
@@ -2362,6 +2895,10 @@ class WorkflowBuilder {
2362
2895
  emitEvent: false,
2363
2896
  });
2364
2897
  this.appActionConfigDefinition.set(definition);
2898
+ // Kick off backend option-loading for any select field driven by an
2899
+ // `optionsProviderKey`. The cache lookup in `getAppActionEnumOptions`
2900
+ // picks up the populated options once they arrive.
2901
+ this.loadDynamicAppActionOptions();
2365
2902
  }
2366
2903
  ensureAppActionDiscoveryLoaded() {
2367
2904
  this.workflowFacade.loadAppActions();
@@ -2498,7 +3035,7 @@ class WorkflowBuilder {
2498
3035
  return '';
2499
3036
  }
2500
3037
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
2501
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowBuilder, isStandalone: true, selector: "mt-workflow-builder", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'workflow.builder'\">\r\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\r\n <div class=\"flex justify-center w-full py-2\">\r\n <mt-tabs\r\n [active]=\"mainTab()\"\r\n (activeChange)=\"onMainTabChange($event)\"\r\n [options]=\"mainTabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n </div>\r\n @switch (mainTab()) {\r\n @case (\"workflow\") {\r\n @if (!loading()) {\r\n <mt-structure-builder\r\n class=\"flex-1\"\r\n [structureMode]=\"'workflow'\"\r\n [availableNodes]=\"availableNodes()\"\r\n [availableNodesLabel]=\"t('stepTemplates')\"\r\n [connectionForm]=\"connectionForm()\"\r\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\r\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\r\n [nodeActions]=\"nodeActions()\"\r\n [nodes]=\"steps()\"\r\n [connections]=\"connections()\"\r\n (action)=\"onStructureAction($event)\"\r\n [addModalType]=\"'drawer'\"\r\n [addModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [updateModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [addModalHeader]=\"'workflow.builder.addStep' | transloco\"\r\n [updateModalHeader]=\"'workflow.builder.editStep' | transloco\"\r\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\r\n [appendTo]=\"'page-content'\"\r\n [nodeFields]=\"nodeFields()\"\r\n [availableTabsClass]=\"hasMultipleWorkflows() ? '!mt-28' : ''\"\r\n [layoutDirection]=\"'LR'\"\r\n >\r\n <div flowContent class=\"pointer-events-none\">\r\n @if (hasMultipleWorkflows()) {\r\n <div class=\"pointer-events-auto absolute top-4 start-4 z-10\">\r\n <mt-card class=\"w-64\">\r\n <mt-select-field\r\n [options]=\"workflows()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [ngModel]=\"selectedWorkflowId()\"\r\n (ngModelChange)=\"onWorkflowChange($event)\"\r\n class=\"mt-2\"\r\n />\r\n </mt-card>\r\n </div>\r\n }\r\n <div class=\"pointer-events-auto absolute top-4 end-4 z-10\">\r\n <div class=\"flex flex-col gap-2 w-80\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-10\">\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"dev.dataflow-01\"\r\n />\r\n <label>{{\r\n \"workflow.builder.enableWorkflow\" | transloco\r\n }}</label>\r\n </div>\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onPublishToggle($event)\"\r\n [disabled]=\"isPublishDisabled()\"\r\n ></mt-toggle-field>\r\n </div>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n <ng-template\r\n #nodeDialog\r\n let-close=\"close\"\r\n let-save=\"save\"\r\n let-node=\"node\"\r\n let-drawerController=\"drawerController\"\r\n >\r\n <div class=\"p-4\">\r\n @if (loadingStep()) {\r\n <div class=\"space-y-4\">\r\n <div class=\"flex gap-2 mb-4\">\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <!-- Form fields skeleton -->\r\n <div class=\"space-y-4\">\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"3rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"4rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-tabs\r\n [active]=\"selectedTab()\"\r\n (activeChange)=\"onNodeDialogTabChange($event)\"\r\n [options]=\"tabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n @switch (selectedTab()) {\r\n @case (\"tab1\") {\r\n <div class=\"mt-4 space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <mt-text-field\r\n [label]=\"t('nameEnglish')\"\r\n [placeholder]=\"t('enterNameEnglish')\"\r\n [formControl]=\"stepForm.controls.nameEn\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n <mt-text-field\r\n [label]=\"t('nameArabic')\"\r\n [placeholder]=\"t('enterNameArabic')\"\r\n [formControl]=\"stepForm.controls.nameAr\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n </div>\r\n\r\n @if (currentStepType() === \"UserInput\") {\r\n @if (!isEditingInitialNode()) {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"approver\") }}\r\n </div>\r\n <mt-select-field\r\n [label]=\"t('approver')\"\r\n [options]=\"[\r\n { label: t('group'), value: '1' },\r\n { label: t('role'), value: '2' },\r\n ]\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.targetType\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n @if (\r\n stepForm.controls.targetType.value === \"1\"\r\n ) {\r\n <mt-select-field\r\n [label]=\"t('group')\"\r\n [placeholder]=\"t('selectGroup')\"\r\n [options]=\"groups()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [formControl]=\"stepForm.controls.group\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n } @else {\r\n <mt-select-field\r\n [label]=\"t('role')\"\r\n [placeholder]=\"t('selectRole')\"\r\n [options]=\"roles()\"\r\n [group]=\"hasGroupedRoles()\"\r\n optionGroupLabel=\"label\"\r\n optionGroupChildren=\"items\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.role\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n }\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n } @else {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"app\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-700\"\r\n >\r\n {{ t(\"appLabelPplus\") }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"action\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-900\"\r\n >\r\n {{ getAppActionDisplayName() }}\r\n </div>\r\n @if (getAppActionKey()) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ getAppActionKey() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (\r\n getAppActionDescription();\r\n as description\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-muted-color\"\r\n >\r\n {{ description }}\r\n </div>\r\n }\r\n\r\n @if (\r\n getAppActionDiscoveryMessage();\r\n as discoveryMessage\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\r\n >\r\n {{ discoveryMessage }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div>\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionConfiguration\") }}\r\n </div>\r\n <div class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"appActionConfigurationDescription\") }}\r\n </div>\r\n </div>\r\n\r\n @if (\r\n loadingAppActionDetail() &&\r\n appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div class=\"space-y-3\">\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"6rem\"\r\n ></p-skeleton>\r\n </div>\r\n } @else if (\r\n !appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-dashed border-surface-300 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionUnavailable\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"schema\" &&\r\n appActionConfigDefinition().fields.length === 0\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionNoConfigRequired\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"raw\"\r\n ) {\r\n <div class=\"space-y-3\">\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\r\n >\r\n {{ t(\"appActionRawConfigFallback\") }}\r\n </div>\r\n <mt-textarea-field\r\n [label]=\"t('appActionConfigJson')\"\r\n [placeholder]=\"\r\n t('enterAppActionConfigJson')\r\n \"\r\n [formControl]=\"appActionRawConfigControl\"\r\n [rows]=\"'10'\"\r\n ></mt-textarea-field>\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (\r\n field of appActionConfigDefinition().fields;\r\n track field.key\r\n ) {\r\n <div class=\"space-y-2\">\r\n @switch (field.kind) {\r\n @case (\"text\") {\r\n <mt-text-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-text-field>\r\n }\r\n @case (\"number\") {\r\n <mt-number-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-number-field>\r\n }\r\n @case (\"toggle\") {\r\n <mt-toggle-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n ></mt-toggle-field>\r\n }\r\n @case (\"select\") {\r\n <mt-select-field\r\n [label]=\"field.title\"\r\n [options]=\"\r\n getAppActionEnumOptions(field)\r\n \"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-select-field>\r\n }\r\n @case (\"json\") {\r\n <mt-textarea-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n [rows]=\"'8'\"\r\n ></mt-textarea-field>\r\n }\r\n }\r\n\r\n @if (field.description) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ field.description }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (getAppActionConfigMessages().length) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-3 text-sm text-red-700\"\r\n >\r\n @for (\r\n message of getAppActionConfigMessages();\r\n track message\r\n ) {\r\n <div>{{ message }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"advancedExecution\") }}\r\n </div>\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n\r\n <mt-select-field\r\n [label]=\"t('failureBehavior')\"\r\n [options]=\"failureBehaviorOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.failureBehavior\r\n \"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n <mt-number-field\r\n [label]=\"t('timeoutSeconds')\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.timeoutSeconds\r\n \"\r\n [required]=\"true\"\r\n [min]=\"1\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n }\r\n @case (\"tab2\") {\r\n <div class=\"mt-4\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-4\">\r\n <div class=\"flex flex-1 items-center gap-3\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"file.file-check-02\"\r\n />\r\n <div class=\"flex flex-col\">\r\n <label class=\"font-medium\">{{\r\n t(\"configureForm\")\r\n }}</label>\r\n <span class=\"text-sm text-muted-color\">{{\r\n t(\"configureForm-description\")\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureForm')\"\r\n size=\"small\"\r\n (onClick)=\"openFormModal()\"\r\n ></mt-button>\r\n </div>\r\n </mt-card>\r\n </div>\r\n }\r\n @case (\"tab3\") {\r\n <div class=\"mt-4\">\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-notifications')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureNotifications')\"\r\n size=\"small\"\r\n (onClick)=\"openNotificationsModal()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-structure-builder>\r\n } @else {\r\n <p-skeleton width=\"100%\" height=\"50rem\" />\r\n }\r\n }\r\n @case (\"notification\") {\r\n <div class=\"flex-1 flex justify-center items-start p-4\">\r\n <div class=\"w-1/2 max-[1025px]:w-full\">\r\n <mt-card [title]=\"t('notifications')\">\r\n <mt-notification-template />\r\n </mt-card>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "component", type: StructureBuilder, selector: "mt-structure-builder", inputs: ["availableNodes", "availableNodesLabel", "nodeForm", "nodeDialogFooterConfig", "connectionForm", "connectionFormulaSchemaId", "connectionFormulaConfig", "nodeActions", "nodeFields", "isAutoLayout", "readonly", "structureMode", "nodeStyle", "addModalType", "updateModalType", "addModalStyleClass", "updateModalStyleClass", "addModalHeader", "updateModalHeader", "appendTo", "availableTabsClass", "layoutDirection", "nodes", "connections", "nodeTemplate"], outputs: ["nodeActionsEvent", "action", "nodesChange", "connectionsChange"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "size", "fluid", "disabled"], outputs: ["activeChange", "onChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "directive", type: i2.TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: NotificationTemplate, selector: "mt-notification-template" }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape"], outputs: ["onChange"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "icon", "iconPosition"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
3038
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowBuilder, isStandalone: true, selector: "mt-workflow-builder", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'workflow.builder'\">\r\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\r\n <div class=\"flex justify-center w-full py-2\">\r\n <mt-tabs\r\n [active]=\"mainTab()\"\r\n (activeChange)=\"onMainTabChange($event)\"\r\n [options]=\"mainTabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n </div>\r\n @switch (mainTab()) {\r\n @case (\"workflow\") {\r\n @if (!loading()) {\r\n <mt-structure-builder\r\n class=\"flex-1\"\r\n [structureMode]=\"'workflow'\"\r\n [availableNodes]=\"availableNodes()\"\r\n [availableNodesLabel]=\"t('stepTemplates')\"\r\n [connectionForm]=\"connectionForm()\"\r\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\r\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\r\n [nodeActions]=\"nodeActions()\"\r\n [nodes]=\"steps()\"\r\n [connections]=\"connections()\"\r\n (action)=\"onStructureAction($event)\"\r\n [addModalType]=\"'drawer'\"\r\n [addModalStyleClass]=\"editorDrawerStyleClass\"\r\n [updateModalStyleClass]=\"editorDrawerStyleClass\"\r\n [addModalHeader]=\"editorAddHeader\"\r\n [updateModalHeader]=\"editorUpdateHeader\"\r\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\r\n [appendTo]=\"'page-content'\"\r\n [nodeFields]=\"nodeFields()\"\r\n [availableTabsClass]=\"hasMultipleWorkflows() ? '!mt-28' : ''\"\r\n [layoutDirection]=\"'LR'\"\r\n >\r\n <div flowContent class=\"pointer-events-none\">\r\n @if (hasMultipleWorkflows()) {\r\n <div class=\"pointer-events-auto absolute top-4 start-4 z-10\">\r\n <mt-card class=\"w-64\">\r\n <mt-select-field\r\n [options]=\"workflows()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [ngModel]=\"selectedWorkflowId()\"\r\n (ngModelChange)=\"onWorkflowChange($event)\"\r\n class=\"mt-2\"\r\n />\r\n </mt-card>\r\n </div>\r\n }\r\n <div class=\"pointer-events-auto absolute top-4 end-4 z-10\">\r\n <div class=\"flex flex-col gap-2 w-80\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-10\">\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"dev.dataflow-01\"\r\n />\r\n <label>{{\r\n \"workflow.builder.enableWorkflow\" | transloco\r\n }}</label>\r\n </div>\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onPublishToggle($event)\"\r\n [disabled]=\"isPublishDisabled()\"\r\n ></mt-toggle-field>\r\n </div>\r\n </mt-card>\r\n\r\n @if (publishError()) {\r\n <mt-card>\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\r\n >\r\n {{ publishError() }}\r\n </div>\r\n </mt-card>\r\n }\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 <div class=\"space-y-4\">\r\n <div class=\"flex gap-2 mb-4\">\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <!-- Form fields skeleton -->\r\n <div class=\"space-y-4\">\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"3rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"4rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-tabs\r\n [active]=\"selectedTab()\"\r\n (activeChange)=\"onNodeDialogTabChange($event)\"\r\n [options]=\"tabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n @switch (selectedTab()) {\r\n @case (\"tab1\") {\r\n <div class=\"mt-4 space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <mt-text-field\r\n [label]=\"t('nameEnglish')\"\r\n [placeholder]=\"t('enterNameEnglish')\"\r\n [formControl]=\"stepForm.controls.nameEn\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n <mt-text-field\r\n [label]=\"t('nameArabic')\"\r\n [placeholder]=\"t('enterNameArabic')\"\r\n [formControl]=\"stepForm.controls.nameAr\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n </div>\r\n\r\n @if (currentStepType() === \"UserInput\") {\r\n @if (!isEditingInitialNode()) {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"approver\") }}\r\n </div>\r\n <mt-select-field\r\n [label]=\"t('approver')\"\r\n [options]=\"[\r\n { label: t('group'), value: '1' },\r\n { label: t('role'), value: '2' },\r\n ]\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.targetType\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n @if (\r\n stepForm.controls.targetType.value === \"1\"\r\n ) {\r\n <mt-select-field\r\n [label]=\"t('group')\"\r\n [placeholder]=\"t('selectGroup')\"\r\n [options]=\"groups()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [formControl]=\"stepForm.controls.group\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n } @else {\r\n <mt-select-field\r\n [label]=\"t('role')\"\r\n [placeholder]=\"t('selectRole')\"\r\n [options]=\"roles()\"\r\n [group]=\"hasGroupedRoles()\"\r\n optionGroupLabel=\"label\"\r\n optionGroupChildren=\"items\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.role\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n }\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n } @else {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"app\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-700\"\r\n >\r\n {{ t(\"appLabelPplus\") }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"action\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-900\"\r\n [title]=\"getAppActionKey()\"\r\n >\r\n {{ getAppActionDisplayName() }}\r\n </div>\r\n </div>\r\n\r\n @if (\r\n getAppActionDescription();\r\n as description\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-muted-color\"\r\n >\r\n {{ description }}\r\n </div>\r\n }\r\n\r\n @if (\r\n getAppActionDiscoveryMessage();\r\n as discoveryMessage\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\r\n >\r\n {{ discoveryMessage }}\r\n </div>\r\n }\r\n\r\n @if (isAppActionMissing()) {\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\r\n >\r\n <div class=\"font-semibold\">\r\n {{ t(\"unknownAppAction\") }}\r\n </div>\r\n <div class=\"mt-1 text-xs\">\r\n {{ t(\"unknownAppActionHint\") }}\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div>\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionConfiguration\") }}\r\n </div>\r\n <div class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"appActionConfigurationDescription\") }}\r\n </div>\r\n </div>\r\n\r\n @if (\r\n loadingAppActionDetail() &&\r\n appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div class=\"space-y-3\">\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"6rem\"\r\n ></p-skeleton>\r\n </div>\r\n } @else if (\r\n !appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-dashed border-surface-300 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionUnavailable\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"schema\" &&\r\n appActionConfigDefinition().fields.length === 0\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionNoConfigRequired\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"raw\"\r\n ) {\r\n <div class=\"space-y-3\">\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\r\n >\r\n {{ t(\"appActionRawConfigFallback\") }}\r\n </div>\r\n <mt-textarea-field\r\n [label]=\"t('appActionConfigJson')\"\r\n [placeholder]=\"\r\n t('enterAppActionConfigJson')\r\n \"\r\n [formControl]=\"appActionRawConfigControl\"\r\n [rows]=\"'10'\"\r\n ></mt-textarea-field>\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (\r\n field of appActionConfigDefinition().fields;\r\n track field.key\r\n ) {\r\n <div class=\"space-y-2\">\r\n @switch (field.kind) {\r\n @case (\"text\") {\r\n <mt-text-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-text-field>\r\n }\r\n @case (\"number\") {\r\n <mt-number-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-number-field>\r\n }\r\n @case (\"toggle\") {\r\n <mt-toggle-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n ></mt-toggle-field>\r\n }\r\n @case (\"select\") {\r\n <mt-select-field\r\n [label]=\"field.title\"\r\n [options]=\"\r\n getAppActionEnumOptions(field)\r\n \"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-select-field>\r\n }\r\n @case (\"json\") {\r\n <mt-textarea-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n [rows]=\"'8'\"\r\n ></mt-textarea-field>\r\n }\r\n }\r\n\r\n @if (field.description) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ field.description }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (getAppActionConfigMessages().length) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-3 text-sm text-red-700\"\r\n >\r\n @for (\r\n message of getAppActionConfigMessages();\r\n track message\r\n ) {\r\n <div>{{ message }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </mt-card>\r\n\r\n @if (isAppActionAfterCommitOnly()) {\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-3 text-sm text-amber-800\"\r\n >\r\n {{ t(\"appActionAfterCommitGuidance\") }}\r\n </div>\r\n }\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"advancedExecution\") }}\r\n </div>\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n\r\n <mt-select-field\r\n [label]=\"t('failureBehavior')\"\r\n [options]=\"failureBehaviorOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.failureBehavior\r\n \"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n <mt-number-field\r\n [label]=\"t('timeoutSeconds')\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.timeoutSeconds\r\n \"\r\n [required]=\"true\"\r\n [min]=\"1\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n\r\n <!--\r\n Read-only informational sections moved to the\r\n end of the form (lowest priority). Both default\r\n collapsed; the entire header is a click target\r\n with a visible \"Show / Hide\" affordance.\r\n -->\r\n @if (getAppActionRuntimeInputs().length) {\r\n <button\r\n type=\"button\"\r\n class=\"group flex w-full items-center justify-between gap-3 rounded-xl border border-surface-200 bg-surface-50 px-3 py-2.5 text-start transition-colors hover:bg-surface-100\"\r\n (click)=\"\r\n resolvedAutomaticallyOpen.set(\r\n !resolvedAutomaticallyOpen()\r\n )\r\n \"\r\n >\r\n <div class=\"min-w-0\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionResolvedAutomatically\") }}\r\n <span\r\n class=\"ms-1 text-xs font-normal text-muted-color\"\r\n >\r\n ({{ getAppActionRuntimeInputs().length }})\r\n </span>\r\n </div>\r\n <div class=\"text-xs text-muted-color\">\r\n {{ t(\"appActionResolvedAutomaticallyHint\") }}\r\n </div>\r\n </div>\r\n <span\r\n class=\"shrink-0 inline-flex items-center gap-1 text-xs font-medium text-primary\"\r\n >\r\n {{\r\n resolvedAutomaticallyOpen()\r\n ? t(\"hideDetails\")\r\n : t(\"showDetails\")\r\n }}\r\n <mt-icon\r\n [icon]=\"\r\n resolvedAutomaticallyOpen()\r\n ? 'arrow.chevron-up'\r\n : 'arrow.chevron-down'\r\n \"\r\n />\r\n </span>\r\n </button>\r\n @if (resolvedAutomaticallyOpen()) {\r\n <mt-card>\r\n <div class=\"space-y-2\">\r\n @for (\r\n input of getAppActionRuntimeInputs();\r\n track input.key\r\n ) {\r\n <div\r\n class=\"flex items-start justify-between gap-3 rounded-lg border border-surface-200 bg-surface-50 px-3 py-2\"\r\n >\r\n <div class=\"min-w-0\">\r\n <div\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ input.key }}\r\n </div>\r\n <div class=\"text-xs text-muted-color\">\r\n {{\r\n describeRuntimeInputSource(input)\r\n }}\r\n </div>\r\n </div>\r\n <span\r\n class=\"shrink-0 rounded-full bg-surface-200 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-surface-600\"\r\n >\r\n {{ t(\"appActionRuntime\") }}\r\n </span>\r\n </div>\r\n }\r\n </div>\r\n </mt-card>\r\n }\r\n }\r\n\r\n @if (getAppActionOutputs().length) {\r\n <button\r\n type=\"button\"\r\n class=\"group flex w-full items-center justify-between gap-3 rounded-xl border border-surface-200 bg-surface-50 px-3 py-2.5 text-start transition-colors hover:bg-surface-100\"\r\n (click)=\"outputsOpen.set(!outputsOpen())\"\r\n >\r\n <div class=\"min-w-0\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionOutputs\") }}\r\n <span\r\n class=\"ms-1 text-xs font-normal text-muted-color\"\r\n >\r\n ({{ getAppActionOutputs().length }})\r\n </span>\r\n </div>\r\n <div class=\"text-xs text-muted-color\">\r\n {{ t(\"appActionOutputsHint\") }}\r\n </div>\r\n </div>\r\n <span\r\n class=\"shrink-0 inline-flex items-center gap-1 text-xs font-medium text-primary\"\r\n >\r\n {{\r\n outputsOpen()\r\n ? t(\"hideDetails\")\r\n : t(\"showDetails\")\r\n }}\r\n <mt-icon\r\n [icon]=\"\r\n outputsOpen()\r\n ? 'arrow.chevron-up'\r\n : 'arrow.chevron-down'\r\n \"\r\n />\r\n </span>\r\n </button>\r\n @if (outputsOpen()) {\r\n <mt-card>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (\r\n output of getAppActionOutputs();\r\n track output.key\r\n ) {\r\n <span\r\n class=\"rounded-md bg-surface-100 px-2 py-0.5 text-xs font-mono text-surface-700\"\r\n [title]=\"output.description ?? ''\"\r\n >\r\n {{ output.key }}:{{ output.type }}\r\n </span>\r\n }\r\n </div>\r\n </mt-card>\r\n }\r\n }\r\n }\r\n </div>\r\n }\r\n @case (\"tab2\") {\r\n <div class=\"mt-4\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-4\">\r\n <div class=\"flex flex-1 items-center gap-3\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"file.file-check-02\"\r\n />\r\n <div class=\"flex flex-col\">\r\n <label class=\"font-medium\">{{\r\n t(\"configureForm\")\r\n }}</label>\r\n <span class=\"text-sm text-muted-color\">{{\r\n t(\"configureForm-description\")\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureForm')\"\r\n size=\"small\"\r\n (onClick)=\"openFormModal()\"\r\n ></mt-button>\r\n </div>\r\n </mt-card>\r\n </div>\r\n }\r\n @case (\"tab3\") {\r\n <div class=\"mt-4\">\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-notifications')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureNotifications')\"\r\n size=\"small\"\r\n (onClick)=\"openNotificationsModal()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-structure-builder>\r\n } @else {\r\n <p-skeleton width=\"100%\" height=\"50rem\" />\r\n }\r\n }\r\n @case (\"notification\") {\r\n <div class=\"flex-1 flex justify-center items-start p-4\">\r\n <div class=\"w-1/2 max-[1025px]:w-full\">\r\n <mt-card [title]=\"t('notifications')\">\r\n <mt-notification-template />\r\n </mt-card>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "component", type: StructureBuilder, selector: "mt-structure-builder", inputs: ["availableNodes", "availableNodesLabel", "nodeForm", "nodeDialogFooterConfig", "connectionForm", "connectionFormulaSchemaId", "connectionFormulaConfig", "nodeActions", "nodeFields", "isAutoLayout", "readonly", "structureMode", "nodeStyle", "addModalType", "updateModalType", "addModalStyleClass", "updateModalStyleClass", "addModalHeader", "updateModalHeader", "appendTo", "availableTabsClass", "layoutDirection", "nodes", "connections", "nodeTemplate"], outputs: ["nodeActionsEvent", "action", "nodesChange", "connectionsChange"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "size", "fluid", "disabled"], outputs: ["activeChange", "onChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "directive", type: i2.TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: NotificationTemplate, selector: "mt-notification-template" }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], 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" }] });
2502
3039
  }
2503
3040
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilder, decorators: [{
2504
3041
  type: Component,
@@ -2517,7 +3054,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
2517
3054
  TextField,
2518
3055
  NumberField,
2519
3056
  TextareaField,
2520
- ], host: {}, template: "<ng-container *transloco=\"let t; prefix: 'workflow.builder'\">\r\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\r\n <div class=\"flex justify-center w-full py-2\">\r\n <mt-tabs\r\n [active]=\"mainTab()\"\r\n (activeChange)=\"onMainTabChange($event)\"\r\n [options]=\"mainTabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n </div>\r\n @switch (mainTab()) {\r\n @case (\"workflow\") {\r\n @if (!loading()) {\r\n <mt-structure-builder\r\n class=\"flex-1\"\r\n [structureMode]=\"'workflow'\"\r\n [availableNodes]=\"availableNodes()\"\r\n [availableNodesLabel]=\"t('stepTemplates')\"\r\n [connectionForm]=\"connectionForm()\"\r\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\r\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\r\n [nodeActions]=\"nodeActions()\"\r\n [nodes]=\"steps()\"\r\n [connections]=\"connections()\"\r\n (action)=\"onStructureAction($event)\"\r\n [addModalType]=\"'drawer'\"\r\n [addModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [updateModalStyleClass]=\"'!w-[25rem] !absolute !shadow-none'\"\r\n [addModalHeader]=\"'workflow.builder.addStep' | transloco\"\r\n [updateModalHeader]=\"'workflow.builder.editStep' | transloco\"\r\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\r\n [appendTo]=\"'page-content'\"\r\n [nodeFields]=\"nodeFields()\"\r\n [availableTabsClass]=\"hasMultipleWorkflows() ? '!mt-28' : ''\"\r\n [layoutDirection]=\"'LR'\"\r\n >\r\n <div flowContent class=\"pointer-events-none\">\r\n @if (hasMultipleWorkflows()) {\r\n <div class=\"pointer-events-auto absolute top-4 start-4 z-10\">\r\n <mt-card class=\"w-64\">\r\n <mt-select-field\r\n [options]=\"workflows()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [ngModel]=\"selectedWorkflowId()\"\r\n (ngModelChange)=\"onWorkflowChange($event)\"\r\n class=\"mt-2\"\r\n />\r\n </mt-card>\r\n </div>\r\n }\r\n <div class=\"pointer-events-auto absolute top-4 end-4 z-10\">\r\n <div class=\"flex flex-col gap-2 w-80\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-10\">\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"dev.dataflow-01\"\r\n />\r\n <label>{{\r\n \"workflow.builder.enableWorkflow\" | transloco\r\n }}</label>\r\n </div>\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onPublishToggle($event)\"\r\n [disabled]=\"isPublishDisabled()\"\r\n ></mt-toggle-field>\r\n </div>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n <ng-template\r\n #nodeDialog\r\n let-close=\"close\"\r\n let-save=\"save\"\r\n let-node=\"node\"\r\n let-drawerController=\"drawerController\"\r\n >\r\n <div class=\"p-4\">\r\n @if (loadingStep()) {\r\n <div class=\"space-y-4\">\r\n <div class=\"flex gap-2 mb-4\">\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <!-- Form fields skeleton -->\r\n <div class=\"space-y-4\">\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"3rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"4rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-tabs\r\n [active]=\"selectedTab()\"\r\n (activeChange)=\"onNodeDialogTabChange($event)\"\r\n [options]=\"tabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n @switch (selectedTab()) {\r\n @case (\"tab1\") {\r\n <div class=\"mt-4 space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <mt-text-field\r\n [label]=\"t('nameEnglish')\"\r\n [placeholder]=\"t('enterNameEnglish')\"\r\n [formControl]=\"stepForm.controls.nameEn\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n <mt-text-field\r\n [label]=\"t('nameArabic')\"\r\n [placeholder]=\"t('enterNameArabic')\"\r\n [formControl]=\"stepForm.controls.nameAr\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n </div>\r\n\r\n @if (currentStepType() === \"UserInput\") {\r\n @if (!isEditingInitialNode()) {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"approver\") }}\r\n </div>\r\n <mt-select-field\r\n [label]=\"t('approver')\"\r\n [options]=\"[\r\n { label: t('group'), value: '1' },\r\n { label: t('role'), value: '2' },\r\n ]\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.targetType\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n @if (\r\n stepForm.controls.targetType.value === \"1\"\r\n ) {\r\n <mt-select-field\r\n [label]=\"t('group')\"\r\n [placeholder]=\"t('selectGroup')\"\r\n [options]=\"groups()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [formControl]=\"stepForm.controls.group\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n } @else {\r\n <mt-select-field\r\n [label]=\"t('role')\"\r\n [placeholder]=\"t('selectRole')\"\r\n [options]=\"roles()\"\r\n [group]=\"hasGroupedRoles()\"\r\n optionGroupLabel=\"label\"\r\n optionGroupChildren=\"items\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.role\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n }\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n } @else {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"app\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-700\"\r\n >\r\n {{ t(\"appLabelPplus\") }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"action\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-900\"\r\n >\r\n {{ getAppActionDisplayName() }}\r\n </div>\r\n @if (getAppActionKey()) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ getAppActionKey() }}\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (\r\n getAppActionDescription();\r\n as description\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-muted-color\"\r\n >\r\n {{ description }}\r\n </div>\r\n }\r\n\r\n @if (\r\n getAppActionDiscoveryMessage();\r\n as discoveryMessage\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\r\n >\r\n {{ discoveryMessage }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div>\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionConfiguration\") }}\r\n </div>\r\n <div class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"appActionConfigurationDescription\") }}\r\n </div>\r\n </div>\r\n\r\n @if (\r\n loadingAppActionDetail() &&\r\n appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div class=\"space-y-3\">\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"6rem\"\r\n ></p-skeleton>\r\n </div>\r\n } @else if (\r\n !appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-dashed border-surface-300 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionUnavailable\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"schema\" &&\r\n appActionConfigDefinition().fields.length === 0\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionNoConfigRequired\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"raw\"\r\n ) {\r\n <div class=\"space-y-3\">\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\r\n >\r\n {{ t(\"appActionRawConfigFallback\") }}\r\n </div>\r\n <mt-textarea-field\r\n [label]=\"t('appActionConfigJson')\"\r\n [placeholder]=\"\r\n t('enterAppActionConfigJson')\r\n \"\r\n [formControl]=\"appActionRawConfigControl\"\r\n [rows]=\"'10'\"\r\n ></mt-textarea-field>\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (\r\n field of appActionConfigDefinition().fields;\r\n track field.key\r\n ) {\r\n <div class=\"space-y-2\">\r\n @switch (field.kind) {\r\n @case (\"text\") {\r\n <mt-text-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-text-field>\r\n }\r\n @case (\"number\") {\r\n <mt-number-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-number-field>\r\n }\r\n @case (\"toggle\") {\r\n <mt-toggle-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n ></mt-toggle-field>\r\n }\r\n @case (\"select\") {\r\n <mt-select-field\r\n [label]=\"field.title\"\r\n [options]=\"\r\n getAppActionEnumOptions(field)\r\n \"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-select-field>\r\n }\r\n @case (\"json\") {\r\n <mt-textarea-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n [rows]=\"'8'\"\r\n ></mt-textarea-field>\r\n }\r\n }\r\n\r\n @if (field.description) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ field.description }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (getAppActionConfigMessages().length) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-3 text-sm text-red-700\"\r\n >\r\n @for (\r\n message of getAppActionConfigMessages();\r\n track message\r\n ) {\r\n <div>{{ message }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"advancedExecution\") }}\r\n </div>\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n\r\n <mt-select-field\r\n [label]=\"t('failureBehavior')\"\r\n [options]=\"failureBehaviorOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.failureBehavior\r\n \"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n <mt-number-field\r\n [label]=\"t('timeoutSeconds')\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.timeoutSeconds\r\n \"\r\n [required]=\"true\"\r\n [min]=\"1\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n </div>\r\n }\r\n @case (\"tab2\") {\r\n <div class=\"mt-4\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-4\">\r\n <div class=\"flex flex-1 items-center gap-3\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"file.file-check-02\"\r\n />\r\n <div class=\"flex flex-col\">\r\n <label class=\"font-medium\">{{\r\n t(\"configureForm\")\r\n }}</label>\r\n <span class=\"text-sm text-muted-color\">{{\r\n t(\"configureForm-description\")\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureForm')\"\r\n size=\"small\"\r\n (onClick)=\"openFormModal()\"\r\n ></mt-button>\r\n </div>\r\n </mt-card>\r\n </div>\r\n }\r\n @case (\"tab3\") {\r\n <div class=\"mt-4\">\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-notifications')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureNotifications')\"\r\n size=\"small\"\r\n (onClick)=\"openNotificationsModal()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-structure-builder>\r\n } @else {\r\n <p-skeleton width=\"100%\" height=\"50rem\" />\r\n }\r\n }\r\n @case (\"notification\") {\r\n <div class=\"flex-1 flex justify-center items-start p-4\">\r\n <div class=\"w-1/2 max-[1025px]:w-full\">\r\n <mt-card [title]=\"t('notifications')\">\r\n <mt-notification-template />\r\n </mt-card>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n" }]
3057
+ ], host: {}, template: "<ng-container *transloco=\"let t; prefix: 'workflow.builder'\">\r\n <div class=\"h-full flex flex-col\" id=\"workflow-builder-card\">\r\n <div class=\"flex justify-center w-full py-2\">\r\n <mt-tabs\r\n [active]=\"mainTab()\"\r\n (activeChange)=\"onMainTabChange($event)\"\r\n [options]=\"mainTabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n </div>\r\n @switch (mainTab()) {\r\n @case (\"workflow\") {\r\n @if (!loading()) {\r\n <mt-structure-builder\r\n class=\"flex-1\"\r\n [structureMode]=\"'workflow'\"\r\n [availableNodes]=\"availableNodes()\"\r\n [availableNodesLabel]=\"t('stepTemplates')\"\r\n [connectionForm]=\"connectionForm()\"\r\n [connectionFormulaSchemaId]=\"connectionFormulaSchemaId()\"\r\n [connectionFormulaConfig]=\"connectionFormulaConfig()\"\r\n [nodeActions]=\"nodeActions()\"\r\n [nodes]=\"steps()\"\r\n [connections]=\"connections()\"\r\n (action)=\"onStructureAction($event)\"\r\n [addModalType]=\"'drawer'\"\r\n [addModalStyleClass]=\"editorDrawerStyleClass\"\r\n [updateModalStyleClass]=\"editorDrawerStyleClass\"\r\n [addModalHeader]=\"editorAddHeader\"\r\n [updateModalHeader]=\"editorUpdateHeader\"\r\n [nodeDialogFooterConfig]=\"nodeDialogFooterConfig\"\r\n [appendTo]=\"'page-content'\"\r\n [nodeFields]=\"nodeFields()\"\r\n [availableTabsClass]=\"hasMultipleWorkflows() ? '!mt-28' : ''\"\r\n [layoutDirection]=\"'LR'\"\r\n >\r\n <div flowContent class=\"pointer-events-none\">\r\n @if (hasMultipleWorkflows()) {\r\n <div class=\"pointer-events-auto absolute top-4 start-4 z-10\">\r\n <mt-card class=\"w-64\">\r\n <mt-select-field\r\n [options]=\"workflows()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [ngModel]=\"selectedWorkflowId()\"\r\n (ngModelChange)=\"onWorkflowChange($event)\"\r\n class=\"mt-2\"\r\n />\r\n </mt-card>\r\n </div>\r\n }\r\n <div class=\"pointer-events-auto absolute top-4 end-4 z-10\">\r\n <div class=\"flex flex-col gap-2 w-80\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-10\">\r\n <div class=\"flex items-center gap-2\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"dev.dataflow-01\"\r\n />\r\n <label>{{\r\n \"workflow.builder.enableWorkflow\" | transloco\r\n }}</label>\r\n </div>\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onPublishToggle($event)\"\r\n [disabled]=\"isPublishDisabled()\"\r\n ></mt-toggle-field>\r\n </div>\r\n </mt-card>\r\n\r\n @if (publishError()) {\r\n <mt-card>\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\r\n >\r\n {{ publishError() }}\r\n </div>\r\n </mt-card>\r\n }\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 <div class=\"space-y-4\">\r\n <div class=\"flex gap-2 mb-4\">\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <!-- Form fields skeleton -->\r\n <div class=\"space-y-4\">\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"8rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"3rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"6rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n <div>\r\n <p-skeleton\r\n width=\"4rem\"\r\n height=\"1rem\"\r\n styleClass=\"mb-2\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n borderRadius=\"0.5rem\"\r\n ></p-skeleton>\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-tabs\r\n [active]=\"selectedTab()\"\r\n (activeChange)=\"onNodeDialogTabChange($event)\"\r\n [options]=\"tabsList()\"\r\n size=\"large\"\r\n ></mt-tabs>\r\n @switch (selectedTab()) {\r\n @case (\"tab1\") {\r\n <div class=\"mt-4 space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <mt-text-field\r\n [label]=\"t('nameEnglish')\"\r\n [placeholder]=\"t('enterNameEnglish')\"\r\n [formControl]=\"stepForm.controls.nameEn\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n <mt-text-field\r\n [label]=\"t('nameArabic')\"\r\n [placeholder]=\"t('enterNameArabic')\"\r\n [formControl]=\"stepForm.controls.nameAr\"\r\n [required]=\"true\"\r\n ></mt-text-field>\r\n </div>\r\n\r\n @if (currentStepType() === \"UserInput\") {\r\n @if (!isEditingInitialNode()) {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"approver\") }}\r\n </div>\r\n <mt-select-field\r\n [label]=\"t('approver')\"\r\n [options]=\"[\r\n { label: t('group'), value: '1' },\r\n { label: t('role'), value: '2' },\r\n ]\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.targetType\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n @if (\r\n stepForm.controls.targetType.value === \"1\"\r\n ) {\r\n <mt-select-field\r\n [label]=\"t('group')\"\r\n [placeholder]=\"t('selectGroup')\"\r\n [options]=\"groups()\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"id\"\r\n [formControl]=\"stepForm.controls.group\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n } @else {\r\n <mt-select-field\r\n [label]=\"t('role')\"\r\n [placeholder]=\"t('selectRole')\"\r\n [options]=\"roles()\"\r\n [group]=\"hasGroupedRoles()\"\r\n optionGroupLabel=\"label\"\r\n optionGroupChildren=\"items\"\r\n optionLabel=\"name.display\"\r\n optionValue=\"value\"\r\n [formControl]=\"stepForm.controls.role\"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n }\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n }\r\n } @else {\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div class=\"grid gap-3\">\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"app\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-700\"\r\n >\r\n {{ t(\"appLabelPplus\") }}\r\n </div>\r\n </div>\r\n\r\n <div class=\"grid gap-1\">\r\n <label\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ t(\"action\") }}\r\n </label>\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-surface-900\"\r\n [title]=\"getAppActionKey()\"\r\n >\r\n {{ getAppActionDisplayName() }}\r\n </div>\r\n </div>\r\n\r\n @if (\r\n getAppActionDescription();\r\n as description\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-2 text-sm text-muted-color\"\r\n >\r\n {{ description }}\r\n </div>\r\n }\r\n\r\n @if (\r\n getAppActionDiscoveryMessage();\r\n as discoveryMessage\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700\"\r\n >\r\n {{ discoveryMessage }}\r\n </div>\r\n }\r\n\r\n @if (isAppActionMissing()) {\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\r\n >\r\n <div class=\"font-semibold\">\r\n {{ t(\"unknownAppAction\") }}\r\n </div>\r\n <div class=\"mt-1 text-xs\">\r\n {{ t(\"unknownAppActionHint\") }}\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </mt-card>\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div>\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionConfiguration\") }}\r\n </div>\r\n <div class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"appActionConfigurationDescription\") }}\r\n </div>\r\n </div>\r\n\r\n @if (\r\n loadingAppActionDetail() &&\r\n appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div class=\"space-y-3\">\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"2.5rem\"\r\n ></p-skeleton>\r\n <p-skeleton\r\n width=\"100%\"\r\n height=\"6rem\"\r\n ></p-skeleton>\r\n </div>\r\n } @else if (\r\n !appActionSettingsForm.controls.actionKey.value\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-dashed border-surface-300 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionUnavailable\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"schema\" &&\r\n appActionConfigDefinition().fields.length === 0\r\n ) {\r\n <div\r\n class=\"rounded-xl border border-surface-200 bg-surface-50 px-3 py-4 text-sm text-muted-color\"\r\n >\r\n {{ t(\"appActionNoConfigRequired\") }}\r\n </div>\r\n } @else if (\r\n appActionConfigDefinition().mode === \"raw\"\r\n ) {\r\n <div class=\"space-y-3\">\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800\"\r\n >\r\n {{ t(\"appActionRawConfigFallback\") }}\r\n </div>\r\n <mt-textarea-field\r\n [label]=\"t('appActionConfigJson')\"\r\n [placeholder]=\"\r\n t('enterAppActionConfigJson')\r\n \"\r\n [formControl]=\"appActionRawConfigControl\"\r\n [rows]=\"'10'\"\r\n ></mt-textarea-field>\r\n </div>\r\n } @else {\r\n <div class=\"space-y-3\">\r\n @for (\r\n field of appActionConfigDefinition().fields;\r\n track field.key\r\n ) {\r\n <div class=\"space-y-2\">\r\n @switch (field.kind) {\r\n @case (\"text\") {\r\n <mt-text-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-text-field>\r\n }\r\n @case (\"number\") {\r\n <mt-number-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-number-field>\r\n }\r\n @case (\"toggle\") {\r\n <mt-toggle-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n ></mt-toggle-field>\r\n }\r\n @case (\"select\") {\r\n <mt-select-field\r\n [label]=\"field.title\"\r\n [options]=\"\r\n getAppActionEnumOptions(field)\r\n \"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n ></mt-select-field>\r\n }\r\n @case (\"json\") {\r\n <mt-textarea-field\r\n [label]=\"field.title\"\r\n [formControl]=\"\r\n getAppActionConfigControl(\r\n field.key\r\n )\r\n \"\r\n [required]=\"field.required\"\r\n [rows]=\"'8'\"\r\n ></mt-textarea-field>\r\n }\r\n }\r\n\r\n @if (field.description) {\r\n <div class=\"text-xs text-muted-color\">\r\n {{ field.description }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (getAppActionConfigMessages().length) {\r\n <div\r\n class=\"rounded-xl border border-red-200 bg-red-50 px-3 py-3 text-sm text-red-700\"\r\n >\r\n @for (\r\n message of getAppActionConfigMessages();\r\n track message\r\n ) {\r\n <div>{{ message }}</div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </mt-card>\r\n\r\n @if (isAppActionAfterCommitOnly()) {\r\n <div\r\n class=\"rounded-xl border border-amber-200 bg-amber-50 px-3 py-3 text-sm text-amber-800\"\r\n >\r\n {{ t(\"appActionAfterCommitGuidance\") }}\r\n </div>\r\n }\r\n\r\n <mt-card>\r\n <div class=\"space-y-4\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"advancedExecution\") }}\r\n </div>\r\n\r\n <mt-number-field\r\n [label]=\"t('sla')\"\r\n [placeholder]=\"t('enterSla')\"\r\n [formControl]=\"stepForm.controls.sla\"\r\n [required]=\"true\"\r\n [min]=\"0\"\r\n ></mt-number-field>\r\n\r\n <mt-select-field\r\n [label]=\"t('failureBehavior')\"\r\n [options]=\"failureBehaviorOptions()\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.failureBehavior\r\n \"\r\n [required]=\"true\"\r\n ></mt-select-field>\r\n\r\n <mt-number-field\r\n [label]=\"t('timeoutSeconds')\"\r\n [formControl]=\"\r\n appActionSettingsForm.controls.timeoutSeconds\r\n \"\r\n [required]=\"true\"\r\n [min]=\"1\"\r\n ></mt-number-field>\r\n </div>\r\n </mt-card>\r\n\r\n <!--\r\n Read-only informational sections moved to the\r\n end of the form (lowest priority). Both default\r\n collapsed; the entire header is a click target\r\n with a visible \"Show / Hide\" affordance.\r\n -->\r\n @if (getAppActionRuntimeInputs().length) {\r\n <button\r\n type=\"button\"\r\n class=\"group flex w-full items-center justify-between gap-3 rounded-xl border border-surface-200 bg-surface-50 px-3 py-2.5 text-start transition-colors hover:bg-surface-100\"\r\n (click)=\"\r\n resolvedAutomaticallyOpen.set(\r\n !resolvedAutomaticallyOpen()\r\n )\r\n \"\r\n >\r\n <div class=\"min-w-0\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionResolvedAutomatically\") }}\r\n <span\r\n class=\"ms-1 text-xs font-normal text-muted-color\"\r\n >\r\n ({{ getAppActionRuntimeInputs().length }})\r\n </span>\r\n </div>\r\n <div class=\"text-xs text-muted-color\">\r\n {{ t(\"appActionResolvedAutomaticallyHint\") }}\r\n </div>\r\n </div>\r\n <span\r\n class=\"shrink-0 inline-flex items-center gap-1 text-xs font-medium text-primary\"\r\n >\r\n {{\r\n resolvedAutomaticallyOpen()\r\n ? t(\"hideDetails\")\r\n : t(\"showDetails\")\r\n }}\r\n <mt-icon\r\n [icon]=\"\r\n resolvedAutomaticallyOpen()\r\n ? 'arrow.chevron-up'\r\n : 'arrow.chevron-down'\r\n \"\r\n />\r\n </span>\r\n </button>\r\n @if (resolvedAutomaticallyOpen()) {\r\n <mt-card>\r\n <div class=\"space-y-2\">\r\n @for (\r\n input of getAppActionRuntimeInputs();\r\n track input.key\r\n ) {\r\n <div\r\n class=\"flex items-start justify-between gap-3 rounded-lg border border-surface-200 bg-surface-50 px-3 py-2\"\r\n >\r\n <div class=\"min-w-0\">\r\n <div\r\n class=\"text-sm font-medium text-surface-900\"\r\n >\r\n {{ input.key }}\r\n </div>\r\n <div class=\"text-xs text-muted-color\">\r\n {{\r\n describeRuntimeInputSource(input)\r\n }}\r\n </div>\r\n </div>\r\n <span\r\n class=\"shrink-0 rounded-full bg-surface-200 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-surface-600\"\r\n >\r\n {{ t(\"appActionRuntime\") }}\r\n </span>\r\n </div>\r\n }\r\n </div>\r\n </mt-card>\r\n }\r\n }\r\n\r\n @if (getAppActionOutputs().length) {\r\n <button\r\n type=\"button\"\r\n class=\"group flex w-full items-center justify-between gap-3 rounded-xl border border-surface-200 bg-surface-50 px-3 py-2.5 text-start transition-colors hover:bg-surface-100\"\r\n (click)=\"outputsOpen.set(!outputsOpen())\"\r\n >\r\n <div class=\"min-w-0\">\r\n <div\r\n class=\"text-sm font-semibold text-surface-900\"\r\n >\r\n {{ t(\"appActionOutputs\") }}\r\n <span\r\n class=\"ms-1 text-xs font-normal text-muted-color\"\r\n >\r\n ({{ getAppActionOutputs().length }})\r\n </span>\r\n </div>\r\n <div class=\"text-xs text-muted-color\">\r\n {{ t(\"appActionOutputsHint\") }}\r\n </div>\r\n </div>\r\n <span\r\n class=\"shrink-0 inline-flex items-center gap-1 text-xs font-medium text-primary\"\r\n >\r\n {{\r\n outputsOpen()\r\n ? t(\"hideDetails\")\r\n : t(\"showDetails\")\r\n }}\r\n <mt-icon\r\n [icon]=\"\r\n outputsOpen()\r\n ? 'arrow.chevron-up'\r\n : 'arrow.chevron-down'\r\n \"\r\n />\r\n </span>\r\n </button>\r\n @if (outputsOpen()) {\r\n <mt-card>\r\n <div class=\"flex flex-wrap gap-1.5\">\r\n @for (\r\n output of getAppActionOutputs();\r\n track output.key\r\n ) {\r\n <span\r\n class=\"rounded-md bg-surface-100 px-2 py-0.5 text-xs font-mono text-surface-700\"\r\n [title]=\"output.description ?? ''\"\r\n >\r\n {{ output.key }}:{{ output.type }}\r\n </span>\r\n }\r\n </div>\r\n </mt-card>\r\n }\r\n }\r\n }\r\n </div>\r\n }\r\n @case (\"tab2\") {\r\n <div class=\"mt-4\">\r\n <mt-card>\r\n <div class=\"flex items-center gap-4\">\r\n <div class=\"flex flex-1 items-center gap-3\">\r\n <mt-button\r\n [severity]=\"'secondary'\"\r\n icon=\"file.file-check-02\"\r\n />\r\n <div class=\"flex flex-col\">\r\n <label class=\"font-medium\">{{\r\n t(\"configureForm\")\r\n }}</label>\r\n <span class=\"text-sm text-muted-color\">{{\r\n t(\"configureForm-description\")\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureForm')\"\r\n size=\"small\"\r\n (onClick)=\"openFormModal()\"\r\n ></mt-button>\r\n </div>\r\n </mt-card>\r\n </div>\r\n }\r\n @case (\"tab3\") {\r\n <div class=\"mt-4\">\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-hide')\"\r\n [descriptionCard]=\"t('show-hide-notifications')\"\r\n icon=\"general.eye\"\r\n [formControl]=\"showHideControl\"\r\n >\r\n <ng-template #toggleCardBottom>\r\n @if (showHideControl.value) {\r\n <div class=\"mt-3\">\r\n <mt-button\r\n [label]=\"t('configureNotifications')\"\r\n size=\"small\"\r\n (onClick)=\"openNotificationsModal()\"\r\n ></mt-button>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-toggle-field>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-structure-builder>\r\n } @else {\r\n <p-skeleton width=\"100%\" height=\"50rem\" />\r\n }\r\n }\r\n @case (\"notification\") {\r\n <div class=\"flex-1 flex justify-center items-start p-4\">\r\n <div class=\"w-1/2 max-[1025px]:w-full\">\r\n <mt-card [title]=\"t('notifications')\">\r\n <mt-notification-template />\r\n </mt-card>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</ng-container>\r\n" }]
2521
3058
  }], ctorParameters: () => [] });
2522
3059
 
2523
3060
  // store/app.state.ts
@@ -2535,5 +3072,5 @@ const REQUEST_CONTEXT = new HttpContextToken(() => ({
2535
3072
  * Generated bundle index. Do not edit.
2536
3073
  */
2537
3074
 
2538
- export { CreateConnection, CreateStep, DeleteConnection, DeleteStep, GetAppActionDetail, GetAppActions, GetFormulaProperties, GetGroups, GetRolesForModule, GetStep, GetWorkflow, GetWorkflows, PublishWorkflow, REQUEST_CONTEXT, SetModuleInfo, UpdateConnection, UpdateStep, ValidateFlow, WorkflowBuilder, WorkflowFacade, WorkflowState };
3075
+ export { CreateConnection, CreateStep, DeleteConnection, DeleteStep, GetAppActionDetail, GetAppActionOptions, GetAppActions, GetFormulaProperties, GetGroups, GetRolesForModule, GetStep, GetWorkflow, GetWorkflows, PublishWorkflow, REQUEST_CONTEXT, SetModuleInfo, UpdateConnection, UpdateStep, ValidateFlow, WorkflowBuilder, WorkflowFacade, WorkflowState, buildAppActionOptionsCacheKey };
2539
3076
  //# sourceMappingURL=masterteam-workflow.mjs.map