@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.
- package/assets/i18n/ar.json +16 -1
- package/assets/i18n/en.json +16 -1
- package/assets/workflow.css +1 -1
- package/fesm2022/masterteam-workflow.mjs +561 -24
- package/fesm2022/masterteam-workflow.mjs.map +1 -1
- package/package.json +5 -5
- package/types/masterteam-workflow.d.ts +168 -10
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1876
|
-
icon
|
|
1877
|
-
|
|
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
|
-
? '
|
|
1880
|
-
:
|
|
1881
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|