@memberjunction/ng-core-entity-forms 2.111.1 → 2.112.0
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/README.md +10 -10
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-diagram.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-diagram.component.js +45 -45
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-diagram.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-form-section.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-form-section.component.js +6 -7
- package/dist/lib/custom/AIAgents/FlowAgentType/flow-agent-form-section.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/FlowAgentType/mj-integrated-flow-editor.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/FlowAgentType/mj-integrated-flow-editor.component.js +17 -32
- package/dist/lib/custom/AIAgents/FlowAgentType/mj-integrated-flow-editor.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/add-action-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/add-action-dialog.component.js +37 -36
- package/dist/lib/custom/AIAgents/add-action-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/agent-advanced-settings-dialog.component.js +2 -2
- package/dist/lib/custom/AIAgents/agent-advanced-settings-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/agent-permissions-dialog.component.d.ts +1 -1
- package/dist/lib/custom/AIAgents/agent-permissions-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/agent-permissions-dialog.component.js +18 -25
- package/dist/lib/custom/AIAgents/agent-permissions-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.js +10 -11
- package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/ai-agent-form.component.js +159 -147
- package/dist/lib/custom/AIAgents/ai-agent-form.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/create-prompt-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/create-prompt-dialog.component.js +11 -10
- package/dist/lib/custom/AIAgents/create-prompt-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.js +36 -32
- package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/new-agent-dialog.component.js +5 -5
- package/dist/lib/custom/AIAgents/new-agent-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.js +15 -13
- package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.js +13 -15
- package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.js +28 -23
- package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.d.ts.map +1 -1
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js +15 -12
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js.map +1 -1
- package/dist/lib/custom/AIPrompts/ai-prompt-form.component.d.ts.map +1 -1
- package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js +105 -86
- package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js.map +1 -1
- package/dist/lib/custom/AIPrompts/template-selector-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIPrompts/template-selector-dialog.component.js +13 -20
- package/dist/lib/custom/AIPrompts/template-selector-dialog.component.js.map +1 -1
- package/dist/lib/custom/Actions/action-execution-log-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Actions/action-execution-log-form.component.js +4 -7
- package/dist/lib/custom/Actions/action-execution-log-form.component.js.map +1 -1
- package/dist/lib/custom/Actions/action-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Actions/action-form.component.js +99 -80
- package/dist/lib/custom/Actions/action-form.component.js.map +1 -1
- package/dist/lib/custom/Actions/action-test-harness.component.d.ts.map +1 -1
- package/dist/lib/custom/Actions/action-test-harness.component.js +24 -17
- package/dist/lib/custom/Actions/action-test-harness.component.js.map +1 -1
- package/dist/lib/custom/Queries/query-category-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/Queries/query-category-dialog.component.js +8 -8
- package/dist/lib/custom/Queries/query-category-dialog.component.js.map +1 -1
- package/dist/lib/custom/Queries/query-form.component.d.ts +2 -2
- package/dist/lib/custom/Queries/query-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Queries/query-form.component.js +32 -44
- package/dist/lib/custom/Queries/query-form.component.js.map +1 -1
- package/dist/lib/custom/Queries/query-run-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/Queries/query-run-dialog.component.js +24 -22
- package/dist/lib/custom/Queries/query-run-dialog.component.js.map +1 -1
- package/dist/lib/custom/Templates/template-param-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/Templates/template-param-dialog.component.js +15 -21
- package/dist/lib/custom/Templates/template-param-dialog.component.js.map +1 -1
- package/dist/lib/custom/Templates/template-params-grid.component.d.ts.map +1 -1
- package/dist/lib/custom/Templates/template-params-grid.component.js +29 -17
- package/dist/lib/custom/Templates/template-params-grid.component.js.map +1 -1
- package/dist/lib/custom/Templates/templates-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Templates/templates-form.component.js +25 -26
- package/dist/lib/custom/Templates/templates-form.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.js +335 -274
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.js +8 -8
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js +24 -28
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js +25 -30
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js +20 -21
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js.map +1 -1
- package/dist/lib/custom/shared/entity-selector-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/shared/entity-selector-dialog.component.js +6 -7
- package/dist/lib/custom/shared/entity-selector-dialog.component.js.map +1 -1
- package/dist/lib/generated/Entities/AIAgent/sections/aiagent-form-overview.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/AIAgent/sections/aiagent-form-overview.component.js +47 -33
- package/dist/lib/generated/Entities/AIAgent/sections/aiagent-form-overview.component.js.map +1 -1
- package/dist/lib/shared/components/template-editor.component.d.ts.map +1 -1
- package/dist/lib/shared/components/template-editor.component.js +26 -27
- package/dist/lib/shared/components/template-editor.component.js.map +1 -1
- package/package.json +16 -17
|
@@ -8,7 +8,7 @@ import { Component, inject, ViewContainerRef } from '@angular/core';
|
|
|
8
8
|
import { RegisterClass } from '@memberjunction/global';
|
|
9
9
|
import { BaseFormComponent } from '@memberjunction/ng-base-forms';
|
|
10
10
|
import { SharedService } from '@memberjunction/ng-shared';
|
|
11
|
-
import { Metadata, RunView, CompositeKey } from '@memberjunction/
|
|
11
|
+
import { Metadata, RunView, CompositeKey } from '@memberjunction/global';
|
|
12
12
|
import { ActionFormComponent } from '../../generated/Entities/Action/action.form.component';
|
|
13
13
|
import { DialogService } from '@progress/kendo-angular-dialog';
|
|
14
14
|
import { ActionParamDialogComponent } from './action-param-dialog.component';
|
|
@@ -1076,7 +1076,7 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1076
1076
|
params: true,
|
|
1077
1077
|
resultCodes: true,
|
|
1078
1078
|
execution: false,
|
|
1079
|
-
configuration: false
|
|
1079
|
+
configuration: false,
|
|
1080
1080
|
};
|
|
1081
1081
|
// Test harness state
|
|
1082
1082
|
this.showTestHarness = false;
|
|
@@ -1085,7 +1085,7 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1085
1085
|
totalRuns: 0,
|
|
1086
1086
|
successRate: 0,
|
|
1087
1087
|
avgDuration: 0,
|
|
1088
|
-
lastRun: null
|
|
1088
|
+
lastRun: null,
|
|
1089
1089
|
};
|
|
1090
1090
|
// Code editor config
|
|
1091
1091
|
this.codeLanguage = 'typescript';
|
|
@@ -1103,7 +1103,7 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1103
1103
|
this.loadResultCodes(),
|
|
1104
1104
|
this.loadRecentExecutions(),
|
|
1105
1105
|
this.loadActionLibraries(),
|
|
1106
|
-
this.loadExecutionStats()
|
|
1106
|
+
this.loadExecutionStats(),
|
|
1107
1107
|
]);
|
|
1108
1108
|
}
|
|
1109
1109
|
}
|
|
@@ -1182,10 +1182,7 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1182
1182
|
this.paramsToDelete = [];
|
|
1183
1183
|
this.resultCodesToDelete = [];
|
|
1184
1184
|
// Reload params and result codes to get updated data
|
|
1185
|
-
await Promise.all([
|
|
1186
|
-
this.loadActionParams(),
|
|
1187
|
-
this.loadResultCodes()
|
|
1188
|
-
]);
|
|
1185
|
+
await Promise.all([this.loadActionParams(), this.loadResultCodes()]);
|
|
1189
1186
|
// Show success message
|
|
1190
1187
|
this.sharedService.CreateSimpleNotification('Action and related records saved successfully', 'success', 3000);
|
|
1191
1188
|
}
|
|
@@ -1219,16 +1216,16 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1219
1216
|
EntityName: 'Action Params',
|
|
1220
1217
|
ExtraFilter: `ActionID='${this.record.ID}'`,
|
|
1221
1218
|
OrderBy: 'Name',
|
|
1222
|
-
ResultType: 'entity_object' // This ensures we get proper entity instances
|
|
1219
|
+
ResultType: 'entity_object', // This ensures we get proper entity instances
|
|
1223
1220
|
});
|
|
1224
1221
|
if (result.Success) {
|
|
1225
1222
|
this.actionParams = result.Results || [];
|
|
1226
1223
|
// Update cached filtered params - trim and lowercase Type values to handle any whitespace and case
|
|
1227
|
-
this._inputParams = this.actionParams.filter(p => {
|
|
1224
|
+
this._inputParams = this.actionParams.filter((p) => {
|
|
1228
1225
|
const type = p.Type?.trim().toLowerCase();
|
|
1229
1226
|
return type === 'input' || type === 'both';
|
|
1230
1227
|
});
|
|
1231
|
-
this._outputParams = this.actionParams.filter(p => {
|
|
1228
|
+
this._outputParams = this.actionParams.filter((p) => {
|
|
1232
1229
|
const type = p.Type?.trim().toLowerCase();
|
|
1233
1230
|
return type === 'output' || type === 'both';
|
|
1234
1231
|
});
|
|
@@ -1259,7 +1256,7 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1259
1256
|
EntityName: 'Action Result Codes',
|
|
1260
1257
|
ExtraFilter: `ActionID='${this.record.ID}'`,
|
|
1261
1258
|
OrderBy: 'IsSuccess DESC, ResultCode',
|
|
1262
|
-
ResultType: 'entity_object' // This ensures we get proper entity instances
|
|
1259
|
+
ResultType: 'entity_object', // This ensures we get proper entity instances
|
|
1263
1260
|
});
|
|
1264
1261
|
if (result.Success) {
|
|
1265
1262
|
this.resultCodes = result.Results || [];
|
|
@@ -1285,7 +1282,7 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1285
1282
|
EntityName: 'Action Execution Logs',
|
|
1286
1283
|
ExtraFilter: `ActionID='${this.record.ID}'`,
|
|
1287
1284
|
OrderBy: 'StartedAt DESC',
|
|
1288
|
-
MaxRows: 10
|
|
1285
|
+
MaxRows: 10,
|
|
1289
1286
|
});
|
|
1290
1287
|
if (result.Success) {
|
|
1291
1288
|
this.recentExecutions = result.Results || [];
|
|
@@ -1310,13 +1307,13 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1310
1307
|
const result = await rv.RunView({
|
|
1311
1308
|
EntityName: 'Action Libraries',
|
|
1312
1309
|
ExtraFilter: `ActionID='${this.record.ID}'`,
|
|
1313
|
-
OrderBy: 'Library'
|
|
1310
|
+
OrderBy: 'Library',
|
|
1314
1311
|
});
|
|
1315
1312
|
if (result.Success) {
|
|
1316
1313
|
this.actionLibraries = result.Results || [];
|
|
1317
1314
|
// Load library details
|
|
1318
1315
|
if (this.actionLibraries.length > 0) {
|
|
1319
|
-
const libraryIds = this.actionLibraries.map(al => al.LibraryID).filter(id => id);
|
|
1316
|
+
const libraryIds = this.actionLibraries.map((al) => al.LibraryID).filter((id) => id);
|
|
1320
1317
|
const md = new Metadata();
|
|
1321
1318
|
this.libraries = [];
|
|
1322
1319
|
for (const libId of libraryIds) {
|
|
@@ -1343,21 +1340,20 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1343
1340
|
const result = await rv.RunView({
|
|
1344
1341
|
EntityName: 'Action Execution Logs',
|
|
1345
1342
|
ExtraFilter: `ActionID='${this.record.ID}'`,
|
|
1346
|
-
OrderBy: 'StartedAt DESC'
|
|
1343
|
+
OrderBy: 'StartedAt DESC',
|
|
1347
1344
|
});
|
|
1348
1345
|
if (result.Success && result.Results && result.Results.length > 0) {
|
|
1349
1346
|
const allExecutions = result.Results;
|
|
1350
1347
|
this.executionStats.totalRuns = allExecutions.length;
|
|
1351
1348
|
// Calculate success rate based on result codes
|
|
1352
|
-
const successfulRuns = allExecutions.filter(e => {
|
|
1353
|
-
const resultCode = this.resultCodes.find(rc => rc.ResultCode === e.ResultCode);
|
|
1349
|
+
const successfulRuns = allExecutions.filter((e) => {
|
|
1350
|
+
const resultCode = this.resultCodes.find((rc) => rc.ResultCode === e.ResultCode);
|
|
1354
1351
|
return resultCode?.IsSuccess || false;
|
|
1355
1352
|
});
|
|
1356
|
-
this.executionStats.successRate =
|
|
1357
|
-
? (successfulRuns.length / this.executionStats.totalRuns) * 100
|
|
1358
|
-
: 0;
|
|
1353
|
+
this.executionStats.successRate =
|
|
1354
|
+
this.executionStats.totalRuns > 0 ? (successfulRuns.length / this.executionStats.totalRuns) * 100 : 0;
|
|
1359
1355
|
// Calculate average duration from ALL completed executions
|
|
1360
|
-
const completedExecutions = allExecutions.filter(e => e.StartedAt && e.EndedAt);
|
|
1356
|
+
const completedExecutions = allExecutions.filter((e) => e.StartedAt && e.EndedAt);
|
|
1361
1357
|
if (completedExecutions.length > 0) {
|
|
1362
1358
|
const totalDuration = completedExecutions.reduce((sum, e) => {
|
|
1363
1359
|
const duration = new Date(e.EndedAt).getTime() - new Date(e.StartedAt).getTime();
|
|
@@ -1377,18 +1373,26 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1377
1373
|
// UI Helper Methods
|
|
1378
1374
|
getStatusColor() {
|
|
1379
1375
|
switch (this.record.Status) {
|
|
1380
|
-
case 'Active':
|
|
1381
|
-
|
|
1382
|
-
case '
|
|
1383
|
-
|
|
1376
|
+
case 'Active':
|
|
1377
|
+
return '#28a745';
|
|
1378
|
+
case 'Pending':
|
|
1379
|
+
return '#ffc107';
|
|
1380
|
+
case 'Disabled':
|
|
1381
|
+
return '#dc3545';
|
|
1382
|
+
default:
|
|
1383
|
+
return '#6c757d';
|
|
1384
1384
|
}
|
|
1385
1385
|
}
|
|
1386
1386
|
getStatusIcon() {
|
|
1387
1387
|
switch (this.record.Status) {
|
|
1388
|
-
case 'Active':
|
|
1389
|
-
|
|
1390
|
-
case '
|
|
1391
|
-
|
|
1388
|
+
case 'Active':
|
|
1389
|
+
return 'fa-check-circle';
|
|
1390
|
+
case 'Pending':
|
|
1391
|
+
return 'fa-clock';
|
|
1392
|
+
case 'Disabled':
|
|
1393
|
+
return 'fa-ban';
|
|
1394
|
+
default:
|
|
1395
|
+
return 'fa-question-circle';
|
|
1392
1396
|
}
|
|
1393
1397
|
}
|
|
1394
1398
|
getTypeColor() {
|
|
@@ -1399,34 +1403,50 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1399
1403
|
}
|
|
1400
1404
|
getApprovalStatusColor() {
|
|
1401
1405
|
switch (this.record.CodeApprovalStatus) {
|
|
1402
|
-
case 'Approved':
|
|
1403
|
-
|
|
1404
|
-
case '
|
|
1405
|
-
|
|
1406
|
+
case 'Approved':
|
|
1407
|
+
return '#28a745';
|
|
1408
|
+
case 'Pending':
|
|
1409
|
+
return '#ffc107';
|
|
1410
|
+
case 'Rejected':
|
|
1411
|
+
return '#dc3545';
|
|
1412
|
+
default:
|
|
1413
|
+
return '#6c757d';
|
|
1406
1414
|
}
|
|
1407
1415
|
}
|
|
1408
1416
|
getApprovalStatusIcon() {
|
|
1409
1417
|
switch (this.record.CodeApprovalStatus) {
|
|
1410
|
-
case 'Approved':
|
|
1411
|
-
|
|
1412
|
-
case '
|
|
1413
|
-
|
|
1418
|
+
case 'Approved':
|
|
1419
|
+
return 'fa-check-circle';
|
|
1420
|
+
case 'Pending':
|
|
1421
|
+
return 'fa-clock';
|
|
1422
|
+
case 'Rejected':
|
|
1423
|
+
return 'fa-times-circle';
|
|
1424
|
+
default:
|
|
1425
|
+
return 'fa-question-circle';
|
|
1414
1426
|
}
|
|
1415
1427
|
}
|
|
1416
1428
|
getParamTypeIcon(type) {
|
|
1417
1429
|
switch (type) {
|
|
1418
|
-
case 'Input':
|
|
1419
|
-
|
|
1420
|
-
case '
|
|
1421
|
-
|
|
1430
|
+
case 'Input':
|
|
1431
|
+
return 'fa-sign-in-alt';
|
|
1432
|
+
case 'Output':
|
|
1433
|
+
return 'fa-sign-out-alt';
|
|
1434
|
+
case 'Both':
|
|
1435
|
+
return 'fa-exchange-alt';
|
|
1436
|
+
default:
|
|
1437
|
+
return 'fa-question';
|
|
1422
1438
|
}
|
|
1423
1439
|
}
|
|
1424
1440
|
getParamTypeColor(type) {
|
|
1425
1441
|
switch (type) {
|
|
1426
|
-
case 'Input':
|
|
1427
|
-
|
|
1428
|
-
case '
|
|
1429
|
-
|
|
1442
|
+
case 'Input':
|
|
1443
|
+
return '#007bff';
|
|
1444
|
+
case 'Output':
|
|
1445
|
+
return '#28a745';
|
|
1446
|
+
case 'Both':
|
|
1447
|
+
return '#6f42c1';
|
|
1448
|
+
default:
|
|
1449
|
+
return '#6c757d';
|
|
1430
1450
|
}
|
|
1431
1451
|
}
|
|
1432
1452
|
formatDuration(ms) {
|
|
@@ -1537,7 +1557,7 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1537
1557
|
isExecutionSuccess(execution) {
|
|
1538
1558
|
const code = execution.ResultCode?.toLowerCase();
|
|
1539
1559
|
// First check if we have a result code definition
|
|
1540
|
-
const resultCode = this.resultCodes.find(rc => rc.ResultCode === execution.ResultCode);
|
|
1560
|
+
const resultCode = this.resultCodes.find((rc) => rc.ResultCode === execution.ResultCode);
|
|
1541
1561
|
if (resultCode) {
|
|
1542
1562
|
return resultCode.IsSuccess;
|
|
1543
1563
|
}
|
|
@@ -1577,13 +1597,13 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1577
1597
|
const dialogRef = this.dialogService.open({
|
|
1578
1598
|
content: ActionParamDialogComponent,
|
|
1579
1599
|
width: 500,
|
|
1580
|
-
appendTo: this.viewContainerRef
|
|
1600
|
+
appendTo: this.viewContainerRef,
|
|
1581
1601
|
});
|
|
1582
1602
|
const dialog = dialogRef.content.instance;
|
|
1583
1603
|
dialog.param = newParam;
|
|
1584
1604
|
dialog.isNew = true;
|
|
1585
1605
|
dialog.editMode = true;
|
|
1586
|
-
dialogRef.result.subscribe(result => {
|
|
1606
|
+
dialogRef.result.subscribe((result) => {
|
|
1587
1607
|
if (result && result.save) {
|
|
1588
1608
|
// The dialog has already modified the newParam entity directly
|
|
1589
1609
|
// New entities are automatically dirty (IsSaved = false)
|
|
@@ -1592,7 +1612,7 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1592
1612
|
// Add to pending records for saving
|
|
1593
1613
|
this.PendingRecords.push({
|
|
1594
1614
|
entityObject: newParam,
|
|
1595
|
-
action: 'save'
|
|
1615
|
+
action: 'save',
|
|
1596
1616
|
});
|
|
1597
1617
|
// Update the filtered arrays
|
|
1598
1618
|
this.updateParamArrays();
|
|
@@ -1604,22 +1624,22 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1604
1624
|
const dialogRef = this.dialogService.open({
|
|
1605
1625
|
content: ActionParamDialogComponent,
|
|
1606
1626
|
width: 500,
|
|
1607
|
-
appendTo: this.viewContainerRef
|
|
1627
|
+
appendTo: this.viewContainerRef,
|
|
1608
1628
|
});
|
|
1609
1629
|
const dialog = dialogRef.content.instance;
|
|
1610
1630
|
dialog.param = param;
|
|
1611
1631
|
dialog.isNew = false;
|
|
1612
1632
|
dialog.editMode = this.EditMode;
|
|
1613
|
-
dialogRef.result.subscribe(result => {
|
|
1633
|
+
dialogRef.result.subscribe((result) => {
|
|
1614
1634
|
if (result && result.save && this.EditMode) {
|
|
1615
1635
|
// Param will be dirty from property changes in dialog
|
|
1616
1636
|
// Ensure it's in pending records if modified
|
|
1617
1637
|
if (param.Dirty) {
|
|
1618
|
-
const exists = this.PendingRecords.some(pr => pr.entityObject === param && pr.action === 'save');
|
|
1638
|
+
const exists = this.PendingRecords.some((pr) => pr.entityObject === param && pr.action === 'save');
|
|
1619
1639
|
if (!exists) {
|
|
1620
1640
|
this.PendingRecords.push({
|
|
1621
1641
|
entityObject: param,
|
|
1622
|
-
action: 'save'
|
|
1642
|
+
action: 'save',
|
|
1623
1643
|
});
|
|
1624
1644
|
}
|
|
1625
1645
|
}
|
|
@@ -1640,12 +1660,12 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1640
1660
|
}
|
|
1641
1661
|
async updateParamArrays() {
|
|
1642
1662
|
// Update cached filtered params - exclude deleted items
|
|
1643
|
-
const activeParams = this.actionParams.filter(p => !this.paramsToDelete || !this.paramsToDelete.includes(p));
|
|
1644
|
-
this._inputParams = activeParams.filter(p => {
|
|
1663
|
+
const activeParams = this.actionParams.filter((p) => !this.paramsToDelete || !this.paramsToDelete.includes(p));
|
|
1664
|
+
this._inputParams = activeParams.filter((p) => {
|
|
1645
1665
|
const type = p.Type?.trim().toLowerCase();
|
|
1646
1666
|
return type === 'input' || type === 'both';
|
|
1647
1667
|
});
|
|
1648
|
-
this._outputParams = activeParams.filter(p => {
|
|
1668
|
+
this._outputParams = activeParams.filter((p) => {
|
|
1649
1669
|
const type = p.Type?.trim().toLowerCase();
|
|
1650
1670
|
return type === 'output' || type === 'both';
|
|
1651
1671
|
});
|
|
@@ -1659,9 +1679,8 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1659
1679
|
// Re-add our preserved records
|
|
1660
1680
|
for (const record of currentPendingRecords) {
|
|
1661
1681
|
// Only re-add if it's an Action Param or Result Code (avoid duplicates)
|
|
1662
|
-
if (record.entityObject.EntityInfo.Name === 'Action Params' ||
|
|
1663
|
-
|
|
1664
|
-
const exists = this.PendingRecords.some(pr => pr.entityObject === record.entityObject);
|
|
1682
|
+
if (record.entityObject.EntityInfo.Name === 'Action Params' || record.entityObject.EntityInfo.Name === 'Action Result Codes') {
|
|
1683
|
+
const exists = this.PendingRecords.some((pr) => pr.entityObject === record.entityObject);
|
|
1665
1684
|
if (!exists) {
|
|
1666
1685
|
this.PendingRecords.push(record);
|
|
1667
1686
|
}
|
|
@@ -1671,11 +1690,11 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1671
1690
|
for (const param of this.actionParams) {
|
|
1672
1691
|
if (!param.IsSaved || param.Dirty) {
|
|
1673
1692
|
// Check if not already in pending records
|
|
1674
|
-
const exists = this.PendingRecords.some(pr => pr.entityObject === param);
|
|
1693
|
+
const exists = this.PendingRecords.some((pr) => pr.entityObject === param);
|
|
1675
1694
|
if (!exists) {
|
|
1676
1695
|
this.PendingRecords.push({
|
|
1677
1696
|
entityObject: param,
|
|
1678
|
-
action: 'save'
|
|
1697
|
+
action: 'save',
|
|
1679
1698
|
});
|
|
1680
1699
|
}
|
|
1681
1700
|
}
|
|
@@ -1684,11 +1703,11 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1684
1703
|
for (const param of this.paramsToDelete) {
|
|
1685
1704
|
if (param.IsSaved) {
|
|
1686
1705
|
// Check if not already in pending records
|
|
1687
|
-
const exists = this.PendingRecords.some(pr => pr.entityObject === param);
|
|
1706
|
+
const exists = this.PendingRecords.some((pr) => pr.entityObject === param);
|
|
1688
1707
|
if (!exists) {
|
|
1689
1708
|
this.PendingRecords.push({
|
|
1690
1709
|
entityObject: param,
|
|
1691
|
-
action: 'delete'
|
|
1710
|
+
action: 'delete',
|
|
1692
1711
|
});
|
|
1693
1712
|
}
|
|
1694
1713
|
}
|
|
@@ -1697,11 +1716,11 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1697
1716
|
for (const resultCode of this.resultCodes) {
|
|
1698
1717
|
if (!resultCode.IsSaved || resultCode.Dirty) {
|
|
1699
1718
|
// Check if not already in pending records
|
|
1700
|
-
const exists = this.PendingRecords.some(pr => pr.entityObject === resultCode);
|
|
1719
|
+
const exists = this.PendingRecords.some((pr) => pr.entityObject === resultCode);
|
|
1701
1720
|
if (!exists) {
|
|
1702
1721
|
this.PendingRecords.push({
|
|
1703
1722
|
entityObject: resultCode,
|
|
1704
|
-
action: 'save'
|
|
1723
|
+
action: 'save',
|
|
1705
1724
|
});
|
|
1706
1725
|
}
|
|
1707
1726
|
}
|
|
@@ -1710,11 +1729,11 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1710
1729
|
for (const resultCode of this.resultCodesToDelete) {
|
|
1711
1730
|
if (resultCode.IsSaved) {
|
|
1712
1731
|
// Check if not already in pending records
|
|
1713
|
-
const exists = this.PendingRecords.some(pr => pr.entityObject === resultCode);
|
|
1732
|
+
const exists = this.PendingRecords.some((pr) => pr.entityObject === resultCode);
|
|
1714
1733
|
if (!exists) {
|
|
1715
1734
|
this.PendingRecords.push({
|
|
1716
1735
|
entityObject: resultCode,
|
|
1717
|
-
action: 'delete'
|
|
1736
|
+
action: 'delete',
|
|
1718
1737
|
});
|
|
1719
1738
|
}
|
|
1720
1739
|
}
|
|
@@ -1741,20 +1760,20 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1741
1760
|
const dialogRef = this.dialogService.open({
|
|
1742
1761
|
content: ActionResultCodeDialogComponent,
|
|
1743
1762
|
width: 500,
|
|
1744
|
-
appendTo: this.viewContainerRef
|
|
1763
|
+
appendTo: this.viewContainerRef,
|
|
1745
1764
|
});
|
|
1746
1765
|
const dialog = dialogRef.content.instance;
|
|
1747
1766
|
dialog.resultCode = newResultCode;
|
|
1748
1767
|
dialog.isNew = true;
|
|
1749
1768
|
dialog.editMode = true;
|
|
1750
|
-
dialogRef.result.subscribe(result => {
|
|
1769
|
+
dialogRef.result.subscribe((result) => {
|
|
1751
1770
|
if (result && result.save) {
|
|
1752
1771
|
// Add to local array
|
|
1753
1772
|
this.resultCodes.push(newResultCode);
|
|
1754
1773
|
// Add to pending records for saving
|
|
1755
1774
|
this.PendingRecords.push({
|
|
1756
1775
|
entityObject: newResultCode,
|
|
1757
|
-
action: 'save'
|
|
1776
|
+
action: 'save',
|
|
1758
1777
|
});
|
|
1759
1778
|
this.cdr.detectChanges();
|
|
1760
1779
|
}
|
|
@@ -1764,21 +1783,21 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1764
1783
|
const dialogRef = this.dialogService.open({
|
|
1765
1784
|
content: ActionResultCodeDialogComponent,
|
|
1766
1785
|
width: 500,
|
|
1767
|
-
appendTo: this.viewContainerRef
|
|
1786
|
+
appendTo: this.viewContainerRef,
|
|
1768
1787
|
});
|
|
1769
1788
|
const dialog = dialogRef.content.instance;
|
|
1770
1789
|
dialog.resultCode = resultCode;
|
|
1771
1790
|
dialog.isNew = false;
|
|
1772
1791
|
dialog.editMode = this.EditMode;
|
|
1773
|
-
dialogRef.result.subscribe(result => {
|
|
1792
|
+
dialogRef.result.subscribe((result) => {
|
|
1774
1793
|
if (result && result.save && this.EditMode) {
|
|
1775
1794
|
// Ensure it's in pending records if modified
|
|
1776
1795
|
if (resultCode.Dirty) {
|
|
1777
|
-
const exists = this.PendingRecords.some(pr => pr.entityObject === resultCode && pr.action === 'save');
|
|
1796
|
+
const exists = this.PendingRecords.some((pr) => pr.entityObject === resultCode && pr.action === 'save');
|
|
1778
1797
|
if (!exists) {
|
|
1779
1798
|
this.PendingRecords.push({
|
|
1780
1799
|
entityObject: resultCode,
|
|
1781
|
-
action: 'save'
|
|
1800
|
+
action: 'save',
|
|
1782
1801
|
});
|
|
1783
1802
|
}
|
|
1784
1803
|
}
|
|
@@ -1813,12 +1832,12 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1813
1832
|
// Add to pending records for deletion
|
|
1814
1833
|
this.PendingRecords.push({
|
|
1815
1834
|
entityObject: resultCode,
|
|
1816
|
-
action: 'delete'
|
|
1835
|
+
action: 'delete',
|
|
1817
1836
|
});
|
|
1818
1837
|
}
|
|
1819
1838
|
else {
|
|
1820
1839
|
// For unsaved result codes, just remove from pending records
|
|
1821
|
-
const pendingIndex = this.PendingRecords.findIndex(pr => pr.entityObject === resultCode && pr.action === 'save');
|
|
1840
|
+
const pendingIndex = this.PendingRecords.findIndex((pr) => pr.entityObject === resultCode && pr.action === 'save');
|
|
1822
1841
|
if (pendingIndex >= 0) {
|
|
1823
1842
|
this.PendingRecords.splice(pendingIndex, 1);
|
|
1824
1843
|
}
|
|
@@ -1843,12 +1862,12 @@ let ActionFormComponentExtended = class ActionFormComponentExtended extends Acti
|
|
|
1843
1862
|
// Add to pending records for deletion
|
|
1844
1863
|
this.PendingRecords.push({
|
|
1845
1864
|
entityObject: param,
|
|
1846
|
-
action: 'delete'
|
|
1865
|
+
action: 'delete',
|
|
1847
1866
|
});
|
|
1848
1867
|
}
|
|
1849
1868
|
else {
|
|
1850
1869
|
// For unsaved params, just remove from pending records
|
|
1851
|
-
const pendingIndex = this.PendingRecords.findIndex(pr => pr.entityObject === param && pr.action === 'save');
|
|
1870
|
+
const pendingIndex = this.PendingRecords.findIndex((pr) => pr.entityObject === param && pr.action === 'save');
|
|
1852
1871
|
if (pendingIndex >= 0) {
|
|
1853
1872
|
this.PendingRecords.splice(pendingIndex, 1);
|
|
1854
1873
|
}
|
|
@@ -1878,7 +1897,7 @@ export { ActionFormComponentExtended };
|
|
|
1878
1897
|
type: Component,
|
|
1879
1898
|
args: [{ selector: 'mj-action-form', template: "<div class=\"record-form-container\">\n @if (record) {\n <form class=\"record-form\" #form=\"ngForm\">\n <mj-form-toolbar [form]=\"this\"></mj-form-toolbar>\n\n <!-- Main Content Area -->\n <div class=\"action-main-area\" style=\"display: flex; flex-direction: column; height: 100%; overflow-y: auto;\">\n \n <!-- Header Section -->\n <div class=\"action-header\" style=\"flex-shrink: 0; padding: 20px; background: #f8f9fa; border-bottom: 2px solid #e9ecef;\">\n <div style=\"display: flex; justify-content: space-between; align-items: flex-start; gap: 20px;\">\n \n <!-- Left: Action Info -->\n <div style=\"flex: 1; min-width: 0;\">\n <div style=\"display: flex; align-items: center; gap: 12px; margin-bottom: 8px;\">\n <i [class]=\"getActionIcon()\" [style.color]=\"getTypeColor()\" style=\"font-size: 1.4em;\"></i>\n @if (EditMode) {\n <kendo-textbox [(ngModel)]=\"record.Name\" \n name=\"actionName\"\n placeholder=\"Enter action name...\"\n style=\"font-size: 1.2em; font-weight: 600; min-width: 300px; flex: 1;\">\n </kendo-textbox>\n } @else {\n <h4 style=\"margin: 0; color: #495057; font-weight: 600; flex: 1;\">{{ record.Name || 'Untitled Action' }}</h4>\n <span class=\"status-badge\" [style.background-color]=\"getStatusColor()\" \n style=\"color: white; padding: 4px 10px; border-radius: 12px; font-size: 0.75em; font-weight: 500;\">\n {{ record.Status }}\n </span>\n }\n </div>\n \n <!-- Status and Type Editors when in edit mode -->\n @if (EditMode) {\n <div style=\"display: flex; gap: 16px; margin-bottom: 12px; flex-wrap: wrap;\">\n <div>\n <label style=\"display: block; margin-bottom: 4px; font-weight: 600; color: #495057; font-size: 0.9em;\">Status</label>\n <kendo-dropdownlist [(ngModel)]=\"record.Status\"\n name=\"actionStatus\"\n [data]=\"[{text: 'Active', value: 'Active'}, {text: 'Pending', value: 'Pending'}, {text: 'Disabled', value: 'Disabled'}]\"\n textField=\"text\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n style=\"width: 150px;\">\n </kendo-dropdownlist>\n </div>\n <div>\n <label style=\"display: block; margin-bottom: 4px; font-weight: 600; color: #495057; font-size: 0.9em;\">\n Type <span style=\"color: #dc3545;\">*</span>\n </label>\n <kendo-dropdownlist [(ngModel)]=\"record.Type\"\n name=\"actionType\"\n [data]=\"[{text: 'Generated', value: 'Generated'}, {text: 'Custom', value: 'Custom'}]\"\n textField=\"text\"\n valueField=\"value\"\n [valuePrimitive]=\"true\"\n style=\"width: 150px;\">\n </kendo-dropdownlist>\n </div>\n <div>\n <label style=\"display: block; margin-bottom: 4px; font-weight: 600; color: #495057; font-size: 0.9em;\">\n Category <span style=\"color: #dc3545;\">*</span>\n </label>\n <kendo-dropdownlist [(ngModel)]=\"record.CategoryID\"\n name=\"categoryID\"\n [data]=\"[]\"\n placeholder=\"Select category...\"\n style=\"width: 200px;\">\n </kendo-dropdownlist>\n </div>\n </div>\n }\n \n @if (EditMode) {\n <kendo-textarea [(ngModel)]=\"record.Description\" \n name=\"actionDescription\"\n [rows]=\"2\"\n placeholder=\"Enter action description...\"\n style=\"width: 100%; max-width: 600px; margin-bottom: 12px;\">\n </kendo-textarea>\n } @else if (record.Description) {\n <p style=\"margin: 0 0 12px 0; color: #6c757d; font-size: 0.9em; line-height: 1.4;\">{{ record.Description }}</p>\n }\n \n <!-- Quick Config Row -->\n <div class=\"quick-config\" style=\"display: flex; align-items: center; gap: 16px; flex-wrap: wrap;\">\n @if (category) {\n <div class=\"config-item\" style=\"display: flex; align-items: center; gap: 6px; font-size: 0.85em;\">\n <i class=\"fa-solid fa-folder\" style=\"color: #6c757d;\"></i>\n <span style=\"color: #6c757d;\">Category:</span>\n <span style=\"color: #495057; font-weight: 500; cursor: pointer;\" (click)=\"navigateToCategory()\">\n {{ category.Name }}\n <i class=\"fa-solid fa-external-link\" style=\"font-size: 0.75em; margin-left: 4px;\"></i>\n </span>\n </div>\n }\n \n <div class=\"config-item\" style=\"display: flex; align-items: center; gap: 6px; font-size: 0.85em;\">\n <i class=\"fa-solid fa-cube\" style=\"color: #6c757d;\"></i>\n <span style=\"color: #6c757d;\">Type:</span>\n <span style=\"color: #495057; font-weight: 500;\">{{ record.Type }}</span>\n </div>\n \n @if (record.Type === 'Generated' && record.CodeApprovalStatus) {\n <div class=\"config-item\" style=\"display: flex; align-items: center; gap: 6px; font-size: 0.85em;\">\n <i [class]=\"'fa-solid ' + getApprovalStatusIcon()\" [style.color]=\"getApprovalStatusColor()\"></i>\n <span style=\"color: #6c757d;\">Code:</span>\n <span [style.color]=\"getApprovalStatusColor()\" style=\"font-weight: 500;\">{{ record.CodeApprovalStatus }}</span>\n </div>\n }\n \n @if (actionParams.length > 0) {\n <div class=\"config-item\" style=\"display: flex; align-items: center; gap: 6px; font-size: 0.85em;\">\n <i class=\"fa-solid fa-sliders\" style=\"color: #6c757d;\"></i>\n <span style=\"color: #495057; font-weight: 500;\">{{ actionParams.length }} Parameters</span>\n </div>\n }\n </div>\n </div>\n \n <!-- Right: Action Buttons -->\n <div class=\"action-buttons\" style=\"display: flex; flex-direction: column; gap: 8px; align-items: flex-end;\">\n <div style=\"display: flex; gap: 8px;\">\n @if (record.ID) {\n <button kendoButton themeColor=\"primary\" size=\"large\"\n (click)=\"openTestHarness()\"\n title=\"Open Run Harness\"\n [disabled]=\"record.Status !== 'Active'\">\n <i class=\"fa-solid fa-play\"></i> Run\n </button>\n }\n @if (EditMode && record.Type === 'Generated') {\n <button kendoButton themeColor=\"info\" size=\"medium\"\n (click)=\"regenerateCode()\"\n title=\"Regenerate Code\">\n <i class=\"fa-solid fa-robot\"></i> Regenerate\n </button>\n }\n </div>\n \n @if (EditMode && record.CodeApprovalStatus === 'Pending' && record.Type === 'Generated') {\n <div style=\"display: flex; gap: 8px;\">\n <button kendoButton themeColor=\"success\" size=\"small\"\n (click)=\"approveCode()\">\n <i class=\"fa-solid fa-check\"></i> Approve\n </button>\n <button kendoButton themeColor=\"error\" size=\"small\"\n (click)=\"rejectCode()\">\n <i class=\"fa-solid fa-times\"></i> Reject\n </button>\n </div>\n }\n \n @if (!EditMode && record.Status !== 'Active' && record.ID) {\n <div style=\"font-size: 0.75em; color: #dc3545; text-align: right;\">\n Action must be Active to execute\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Configuration Sections with Expansion Panels -->\n <div class=\"configuration-sections\" style=\"flex: 1; background: white; border-top: 2px solid #e9ecef; padding: 16px; min-height: 0;\">\n \n <!-- Parameters Section (FIRST) -->\n <kendo-expansionpanel \n [expanded]=\"true\"\n style=\"margin-bottom: 12px;\">\n <ng-template kendoExpansionPanelTitleDirective>\n <span style=\"display: flex; align-items: center; gap: 8px; font-weight: 600;\">\n <i class=\"fa-solid fa-sliders\" style=\"color: #6c757d;\"></i>\n Parameters\n @if (actionParams.length > 0) {\n <span style=\"background: #17a2b8; color: white; padding: 2px 6px; border-radius: 10px; font-size: 0.7em;\">\n {{ actionParams.length }}\n </span>\n }\n </span>\n </ng-template>\n \n <div style=\"padding: 16px;\">\n @if (isLoadingParams) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Loading parameters...\n </div>\n } @else if (actionParams.length === 0) {\n <div class=\"empty-state\" style=\"padding: 30px; text-align: center;\">\n <i class=\"fa-solid fa-sliders\" style=\"font-size: 2em; color: #6c757d; opacity: 0.3;\"></i>\n <p style=\"margin: 12px 0 0 0; color: #6c757d;\">No parameters defined</p>\n @if (EditMode && record.IsSaved) {\n <p style=\"margin: 8px 0 0 0; font-size: 0.85em; color: #6c757d;\">Add parameters to define inputs and outputs for this action</p>\n }\n </div>\n } @else {\n <!-- Input Parameters Grid -->\n <div class=\"params-section\">\n <div class=\"params-header\">\n <h3><i class=\"fa-solid fa-sign-in-alt\"></i> Input Parameters</h3>\n @if (EditMode && record.IsSaved) {\n <button kendoButton fillMode=\"flat\" size=\"small\" (click)=\"addParameter('Input')\">\n <i class=\"fa-solid fa-plus\"></i> Add Input\n </button>\n }\n </div>\n \n @if (getInputParams().length === 0) {\n <div class=\"empty-state\" style=\"padding: 30px; text-align: center;\">\n <i class=\"fa-solid fa-inbox\" style=\"font-size: 2em; color: #6c757d; opacity: 0.3;\"></i>\n <p style=\"margin: 12px 0 0 0; color: #6c757d;\">No input parameters defined</p>\n </div>\n } @else {\n <div class=\"params-grid\">\n @for (param of getInputParams(); track param.ID) {\n <div class=\"param-card\" [class.required]=\"param.IsRequired\" \n [class.clickable]=\"true\"\n (click)=\"onParamClick(param, $event)\">\n <div class=\"param-header\">\n <span class=\"param-name\">{{ param.Name }}</span>\n <div class=\"param-badges\">\n @if (param.IsRequired) {\n <span class=\"required-badge\">Required</span>\n }\n @if (param.IsArray) {\n <span class=\"array-badge\">Array</span>\n }\n @if (EditMode) {\n <button class=\"param-edit-btn\" (click)=\"editParameter(param)\" title=\"Edit parameter\">\n <i class=\"fa-solid fa-edit\"></i>\n </button>\n <button class=\"param-delete-btn\" (click)=\"deleteParameter(param)\" title=\"Delete parameter\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n </div>\n </div>\n <div class=\"param-details\">\n <div class=\"param-type\">{{ param.ValueType }}</div>\n @if (param.Description) {\n <div class=\"param-description\">{{ param.Description }}</div>\n }\n @if (param.DefaultValue) {\n <div class=\"param-default\">\n <span class=\"default-label\">Default:</span>\n <code>{{ param.DefaultValue }}</code>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Output Parameters Grid -->\n <div class=\"params-section\" style=\"margin-top: 24px;\" \n [class.params-section-compact]=\"getOutputParams().length === 0\">\n <div class=\"params-header\">\n <h3><i class=\"fa-solid fa-sign-out-alt\"></i> Output Parameters</h3>\n @if (EditMode && record.IsSaved) {\n <button kendoButton fillMode=\"flat\" size=\"small\" (click)=\"addParameter('Output')\">\n <i class=\"fa-solid fa-plus\"></i> Add Output\n </button>\n }\n </div>\n \n @if (getOutputParams().length === 0) {\n <div class=\"empty-state\" style=\"padding: 20px; text-align: center;\">\n <i class=\"fa-solid fa-inbox\" style=\"font-size: 2em; color: #6c757d; opacity: 0.3;\"></i>\n <p style=\"margin: 12px 0 0 0; color: #6c757d;\">No output parameters defined</p>\n </div>\n } @else {\n <div class=\"params-grid\">\n @for (param of getOutputParams(); track param.ID) {\n <div class=\"param-card\"\n [class.clickable]=\"true\"\n (click)=\"onParamClick(param, $event)\">\n <div class=\"param-header\">\n <span class=\"param-name\">{{ param.Name }}</span>\n <div class=\"param-badges\">\n @if (param.IsArray) {\n <span class=\"array-badge\">Array</span>\n }\n @if (EditMode) {\n <button class=\"param-edit-btn\" (click)=\"editParameter(param)\" title=\"Edit parameter\">\n <i class=\"fa-solid fa-edit\"></i>\n </button>\n <button class=\"param-delete-btn\" (click)=\"deleteParameter(param)\" title=\"Delete parameter\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n }\n </div>\n </div>\n <div class=\"param-details\">\n <div class=\"param-type\">{{ param.ValueType }}</div>\n @if (param.Description) {\n <div class=\"param-description\">{{ param.Description }}</div>\n }\n @if (param.DefaultValue) {\n <div class=\"param-default\">\n <span class=\"default-label\">Default:</span>\n <code>{{ param.DefaultValue }}</code>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </kendo-expansionpanel>\n\n <!-- Code & Generation Section (Only for Generated type) -->\n @if (record.Type === 'Generated') {\n <kendo-expansionpanel \n [expanded]=\"expandedSections.code\"\n style=\"margin-bottom: 12px;\">\n <ng-template kendoExpansionPanelTitleDirective>\n <span style=\"display: flex; align-items: center; gap: 8px; font-weight: 600;\">\n <i class=\"fa-solid fa-code\" style=\"color: #6c757d;\"></i>\n Code & Generation\n @if (record.CodeLocked) {\n <span style=\"background: #ffc107; color: #856404; padding: 2px 6px; border-radius: 10px; font-size: 0.7em;\">\n <i class=\"fa-solid fa-lock\"></i> Locked\n </span>\n }\n </span>\n </ng-template>\n \n <div style=\"padding: 16px;\">\n @if (EditMode) {\n <div class=\"generation-panel\">\n <h3><i class=\"fa-solid fa-robot\"></i> AI Generation</h3>\n <div class=\"prompt-section\">\n <label>User Prompt</label>\n <kendo-textarea [(ngModel)]=\"record.UserPrompt\"\n name=\"userPrompt\"\n [rows]=\"8\"\n placeholder=\"Describe what this action should do...\"\n style=\"width: 100%; min-height: 120px;\">\n </kendo-textarea>\n </div>\n <div class=\"comments-section\">\n <label>Internal Comments (not sent to AI)</label>\n <kendo-textarea [(ngModel)]=\"record.UserComments\"\n name=\"userComments\"\n [rows]=\"2\"\n placeholder=\"Internal notes...\"\n style=\"width: 100%;\">\n </kendo-textarea>\n </div>\n <div class=\"generation-controls\">\n <kendo-switch [(ngModel)]=\"record.CodeLocked\"\n name=\"codeLocked\">\n </kendo-switch>\n <label style=\"margin-left: 8px;\">Lock Code (prevent regeneration)</label>\n </div>\n </div>\n }\n \n <div class=\"code-editor-section\">\n <div class=\"code-toolbar\">\n <h3><i class=\"fa-solid fa-file-code\"></i> Action Code</h3>\n <div class=\"code-actions\">\n @if (record.CodeComments) {\n <button kendoButton fillMode=\"flat\" size=\"small\" (click)=\"toggleCodeComments()\">\n <i class=\"fa-solid fa-comment\"></i> \n {{ showCodeComments ? 'Hide' : 'Show' }} AI Comments\n </button>\n }\n <button kendoButton fillMode=\"flat\" size=\"small\" (click)=\"copyToClipboard(record.Code || '')\">\n <i class=\"fa-solid fa-copy\"></i> Copy\n </button>\n </div>\n </div>\n <mj-code-editor \n [(ngModel)]=\"record.Code\"\n name=\"actionCode\"\n [readonly]=\"!EditMode || record.CodeLocked\"\n [language]=\"codeLanguage\"\n [lineWrapping]=\"true\"\n style=\"height: 400px; width: 100%;\">\n </mj-code-editor>\n \n @if (showCodeComments && record.CodeComments) {\n <div class=\"code-comments\">\n <h4><i class=\"fa-solid fa-robot\"></i> AI Explanation</h4>\n <p>{{ record.CodeComments }}</p>\n </div>\n }\n \n @if (EditMode && record.CodeApprovalStatus === 'Rejected') {\n <div class=\"approval-comments\">\n <label>Rejection Comments</label>\n <kendo-textarea [(ngModel)]=\"record.CodeApprovalComments\"\n name=\"codeApprovalComments\"\n [rows]=\"2\"\n placeholder=\"Explain why the code was rejected...\"\n style=\"width: 100%;\">\n </kendo-textarea>\n </div>\n }\n </div>\n </div>\n </kendo-expansionpanel>\n }\n\n <!-- Result Codes Section -->\n <kendo-expansionpanel \n [expanded]=\"expandedSections.resultCodes\"\n style=\"margin-bottom: 12px;\">\n <ng-template kendoExpansionPanelTitleDirective>\n <span style=\"display: flex; align-items: center; gap: 8px; font-weight: 600;\">\n <i class=\"fa-solid fa-flag-checkered\" style=\"color: #6c757d;\"></i>\n Result Codes\n @if (resultCodes.length > 0) {\n <span style=\"background: #28a745; color: white; padding: 2px 6px; border-radius: 10px; font-size: 0.7em;\">\n {{ resultCodes.length }}\n </span>\n }\n </span>\n </ng-template>\n \n <div style=\"padding: 16px;\">\n <!-- Add Result Code Button -->\n @if (EditMode && record.IsSaved) {\n <div style=\"display: flex; justify-content: flex-end; margin-bottom: 12px;\">\n <button kendoButton [primary]=\"true\" size=\"small\" (click)=\"addResultCode()\">\n <i class=\"fa-solid fa-plus\"></i> Add Result Code\n </button>\n </div>\n }\n \n @if (isLoadingResultCodes) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Loading result codes...\n </div>\n } @else if (resultCodes.length === 0) {\n <div class=\"empty-state\" style=\"padding: 30px; text-align: center;\">\n <i class=\"fa-solid fa-flag-checkered\" style=\"font-size: 2em; color: #6c757d; opacity: 0.3;\"></i>\n <p style=\"margin: 12px 0 0 0; color: #6c757d;\">No result codes defined</p>\n @if (EditMode && record.IsSaved) {\n <p style=\"margin: 8px 0 0 0; font-size: 0.85em; color: #6c757d;\">Add result codes to define possible outcomes</p>\n }\n </div>\n } @else {\n <div class=\"result-codes-grid\">\n @for (code of resultCodes; track code.ID) {\n <div class=\"result-code-card\" \n [class.success]=\"code.IsSuccess\" \n [class.failure]=\"!code.IsSuccess\"\n [class.clickable]=\"true\"\n (click)=\"onResultCodeClick(code, $event)\">\n <div class=\"result-icon\">\n <i [class]=\"code.IsSuccess ? 'fa-solid fa-check-circle' : 'fa-solid fa-times-circle'\"></i>\n </div>\n <div class=\"result-content\">\n <div class=\"result-code\">{{ code.ResultCode }}</div>\n @if (code.Description) {\n <div class=\"result-description\">{{ code.Description }}</div>\n }\n </div>\n @if (EditMode) {\n <div class=\"result-actions\">\n <button class=\"result-edit-btn\" (click)=\"editResultCode(code); $event.stopPropagation()\" title=\"Edit result code\">\n <i class=\"fa-solid fa-edit\"></i>\n </button>\n <button class=\"result-delete-btn\" (click)=\"deleteResultCode(code); $event.stopPropagation()\" title=\"Delete result code\">\n <i class=\"fa-solid fa-trash\"></i>\n </button>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </kendo-expansionpanel>\n\n <!-- Execution & Monitoring Section -->\n @if (record.IsSaved) {\n <kendo-expansionpanel \n [expanded]=\"expandedSections.execution\"\n style=\"margin-bottom: 12px;\">\n <ng-template kendoExpansionPanelTitleDirective>\n <span style=\"display: flex; align-items: center; gap: 8px; font-weight: 600;\">\n <i class=\"fa-solid fa-chart-line\" style=\"color: #6c757d;\"></i>\n Execution & Monitoring\n </span>\n </ng-template>\n \n <div style=\"padding: 16px;\">\n @if (isLoadingExecutions) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Loading executions...\n </div>\n } @else if (recentExecutions.length === 0) {\n <div class=\"empty-state\" style=\"padding: 30px; text-align: center;\">\n <i class=\"fa-solid fa-chart-line\" style=\"font-size: 2em; color: #6c757d; opacity: 0.3;\"></i>\n <p style=\"margin: 12px 0 0 0; color: #6c757d;\">No executions yet</p>\n </div>\n } @else {\n <div class=\"execution-stats-row\">\n <div class=\"stat-box\">\n <i class=\"fa-solid fa-tachometer-alt\"></i>\n <div class=\"stat-info\">\n <div class=\"stat-label\" style=\"color: #212529;\">Avg Duration</div>\n <div class=\"stat-value\" style=\"color: #212529;\">{{ formatDuration(executionStats.avgDuration) }}</div>\n </div>\n </div>\n <div class=\"stat-box\">\n <i class=\"fa-solid fa-check-circle\" [style.color]=\"getSuccessRateColor()\"></i>\n <div class=\"stat-info\">\n <div class=\"stat-label\" style=\"color: #212529;\">Success Rate</div>\n <div class=\"stat-value\" [style.color]=\"getSuccessRateColor()\" style=\"font-weight: 600;\">{{ executionStats.successRate.toFixed(0) }}%</div>\n </div>\n </div>\n <div class=\"stat-box\">\n <i class=\"fa-solid fa-play-circle\"></i>\n <div class=\"stat-info\">\n <div class=\"stat-label\" style=\"color: #212529;\">Total Runs</div>\n <div class=\"stat-value\" style=\"color: #212529;\">{{ executionStats.totalRuns }}</div>\n </div>\n </div>\n </div>\n\n <h3 style=\"margin-top: 24px;\"><i class=\"fa-solid fa-history\"></i> Recent Executions</h3>\n <div class=\"executions-table\">\n <table>\n <thead>\n <tr>\n <th>Started</th>\n <th>Duration</th>\n <th>User</th>\n <th>Result</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (execution of recentExecutions; track execution.ID) {\n <tr class=\"execution-row\" [class.success]=\"isExecutionSuccess(execution)\">\n <td>{{ execution.StartedAt | date:'short' }}</td>\n <td>\n @if (execution.EndedAt) {\n {{ formatDuration(getExecutionDuration(execution)) }}\n } @else {\n <span class=\"running\">Running...</span>\n }\n </td>\n <td>{{ execution.User }}</td>\n <td>\n <span class=\"result-code\" \n [class.success]=\"isExecutionSuccess(execution)\"\n [class.failure]=\"!isExecutionSuccess(execution)\">\n {{ execution.ResultCode }}\n </span>\n </td>\n <td>\n <button kendoButton fillMode=\"flat\" size=\"small\" (click)=\"navigateToExecution(execution.ID)\">\n <i class=\"fa-solid fa-external-link\"></i>\n </button>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n </kendo-expansionpanel>\n }\n\n <!-- Related Configuration Section -->\n @if (record.IsSaved) {\n <kendo-expansionpanel \n [expanded]=\"expandedSections.configuration\"\n style=\"margin-bottom: 12px;\">\n <ng-template kendoExpansionPanelTitleDirective>\n <span style=\"display: flex; align-items: center; gap: 8px; font-weight: 600;\">\n <i class=\"fa-solid fa-cogs\" style=\"color: #6c757d;\"></i>\n Related Configuration\n </span>\n </ng-template>\n \n <div style=\"padding: 16px;\">\n <!-- Libraries -->\n <div class=\"config-subsection\">\n <h3><i class=\"fa-solid fa-book\"></i> Libraries</h3>\n @if (isLoadingLibraries) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Loading libraries...\n </div>\n } @else if (actionLibraries.length === 0) {\n <div class=\"empty-state mini\">\n <p>No libraries configured</p>\n </div>\n } @else {\n <div class=\"library-cards\">\n @for (lib of libraries; track lib.ID; let i = $index) {\n <div class=\"library-card\" (click)=\"navigateToLibrary(lib.ID)\">\n <i class=\"fa-solid fa-book\"></i>\n <div class=\"library-info\">\n <div class=\"library-name\">{{ lib.Name }}</div>\n @if (actionLibraries[i].ItemsUsed) {\n <div class=\"library-items\">{{ actionLibraries[i].ItemsUsed }}</div>\n }\n </div>\n <i class=\"fa-solid fa-external-link\"></i>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Other Related Entities -->\n <div class=\"related-entities-grid\">\n <div class=\"related-entity-link\">\n <i class=\"fa-solid fa-shield-alt\"></i>\n <span>Authorizations</span>\n <span class=\"entity-count\">View</span>\n </div>\n <div class=\"related-entity-link\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>Contexts</span>\n <span class=\"entity-count\">View</span>\n </div>\n <div class=\"related-entity-link\">\n <i class=\"fa-solid fa-calendar\"></i>\n <span>Scheduled Actions</span>\n <span class=\"entity-count\">View</span>\n </div>\n <div class=\"related-entity-link\">\n <i class=\"fa-solid fa-cube\"></i>\n <span>Entity Actions</span>\n <span class=\"entity-count\">View</span>\n </div>\n </div>\n </div>\n </kendo-expansionpanel>\n }\n </div>\n </div>\n </form>\n }\n</div>\n\n<!-- Action Test Harness -->\n@if (showTestHarness) {\n <kendo-window \n [width]=\"1200\" \n [height]=\"800\" \n [minWidth]=\"800\"\n [minHeight]=\"600\"\n [draggable]=\"true\"\n [resizable]=\"true\"\n [state]=\"'default'\"\n (close)=\"onTestHarnessVisibilityChanged(false)\"\n title=\"Run Action - {{ record.Name || 'Untitled' }}\">\n <mj-action-test-harness\n [action]=\"record\"\n [actionParams]=\"actionParams\"\n [isVisible]=\"showTestHarness\"\n (visibilityChange)=\"onTestHarnessVisibilityChanged($event)\">\n </mj-action-test-harness>\n </kendo-window>\n}", styles: ["/* Hero Header Section */\n.action-hero-header {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n padding: 32px;\n margin: -20px -20px 24px -20px;\n border-radius: 0 0 16px 16px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.1);\n}\n\n.hero-content {\n max-width: 1400px;\n margin: 0 auto;\n}\n\n.action-identity {\n display: flex;\n align-items: flex-start;\n gap: 24px;\n margin-bottom: 24px;\n}\n\n.action-icon-wrapper {\n width: 80px;\n height: 80px;\n border-radius: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n background: rgba(255,255,255,0.2);\n backdrop-filter: blur(10px);\n}\n\n.action-icon-wrapper i {\n font-size: 36px;\n}\n\n.action-info {\n flex: 1;\n min-width: 0;\n}\n\n.action-title-row {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-bottom: 12px;\n flex-wrap: wrap;\n}\n\n.action-title {\n margin: 0;\n font-size: 2em;\n font-weight: 600;\n color: white;\n}\n\n.action-name-input {\n font-size: 1.8em;\n font-weight: 600;\n background: rgba(255,255,255,0.2);\n border: 2px solid rgba(255,255,255,0.3);\n color: white;\n padding: 8px 16px;\n border-radius: 8px;\n min-width: 400px;\n}\n\n.action-name-input::placeholder {\n color: rgba(255,255,255,0.6);\n}\n\n.action-badges {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.status-badge, .type-badge, .approval-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border-radius: 20px;\n font-size: 0.85em;\n font-weight: 500;\n background: rgba(255,255,255,0.2);\n backdrop-filter: blur(10px);\n border: 1px solid rgba(255,255,255,0.3);\n}\n\n.action-category {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: rgba(255,255,255,0.9);\n font-size: 0.95em;\n cursor: pointer;\n transition: all 0.2s;\n margin-bottom: 12px;\n}\n\n.action-category:hover {\n color: white;\n transform: translateX(4px);\n}\n\n.action-description {\n color: rgba(255,255,255,0.9);\n font-size: 1.05em;\n line-height: 1.5;\n margin: 0;\n}\n\n.action-description-input {\n width: 100%;\n max-width: 600px;\n background: rgba(255,255,255,0.2);\n border: 2px solid rgba(255,255,255,0.3);\n color: white;\n border-radius: 8px;\n}\n\n.action-description-input::placeholder {\n color: rgba(255,255,255,0.6);\n}\n\n/* Quick Stats */\n.action-stats {\n display: flex;\n gap: 24px;\n margin-bottom: 24px;\n flex-wrap: wrap;\n}\n\n.stat-card {\n background: rgba(255,255,255,0.1);\n backdrop-filter: blur(10px);\n border: 1px solid rgba(255,255,255,0.2);\n border-radius: 12px;\n padding: 16px 24px;\n display: flex;\n align-items: center;\n gap: 16px;\n min-width: 160px;\n}\n\n.stat-card i {\n font-size: 24px;\n color: rgba(255,255,255,0.8);\n}\n\n.stat-content {\n display: flex;\n flex-direction: column;\n}\n\n.stat-value {\n font-size: 1.4em;\n font-weight: 700;\n color: white;\n}\n\n.stat-label {\n font-size: 0.85em;\n color: rgba(255,255,255,0.7);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n/* Hero Actions */\n.hero-actions {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.hero-actions kendo-button {\n backdrop-filter: blur(10px);\n}\n\n/* Main Content Sections */\n.action-content {\n padding: 0 20px 20px;\n}\n\n.content-section {\n background: white;\n border: 1px solid #e9ecef;\n border-radius: 12px;\n margin-bottom: 20px;\n overflow: hidden;\n transition: all 0.3s ease;\n}\n\n.content-section:hover {\n box-shadow: 0 4px 12px rgba(0,0,0,0.08);\n}\n\n.section-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 20px;\n background: #f8f9fa;\n cursor: pointer;\n user-select: none;\n transition: background 0.2s;\n}\n\n.section-header:hover {\n background: #e9ecef;\n}\n\n.section-header h2 {\n margin: 0;\n font-size: 1.3em;\n color: #2c3e50;\n flex: 1;\n}\n\n.section-header i:first-child {\n font-size: 1.2em;\n color: #6c757d;\n}\n\n.section-count {\n background: #007bff;\n color: white;\n padding: 2px 8px;\n border-radius: 12px;\n font-size: 0.85em;\n font-weight: 500;\n}\n\n.toggle-icon {\n color: #6c757d;\n transition: transform 0.3s;\n}\n\n.toggle-icon.rotated {\n transform: rotate(-90deg);\n}\n\n.section-content {\n padding: 24px;\n animation: slideDown 0.3s ease;\n}\n\n@keyframes slideDown {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Overview Section */\n.overview-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n gap: 20px;\n}\n\n.overview-field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.overview-field label {\n font-weight: 600;\n color: #495057;\n font-size: 0.9em;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.required {\n color: #dc3545;\n}\n\n.full-width {\n width: 100%;\n}\n\n.overview-display {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 24px;\n}\n\n.display-field {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.display-field label {\n font-size: 0.85em;\n color: #6c757d;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.display-value {\n font-size: 1.1em;\n color: #2c3e50;\n}\n\n.display-value.code {\n font-family: 'Courier New', monospace;\n background: #f8f9fa;\n padding: 4px 8px;\n border-radius: 4px;\n}\n\n.approval-date {\n color: #6c757d;\n font-size: 0.85em;\n margin-left: 8px;\n}\n\n/* Code Section */\n.generation-panel {\n background: #f8f9fa;\n border: 1px solid #e9ecef;\n border-radius: 8px;\n padding: 20px;\n margin-bottom: 24px;\n}\n\n.generation-panel h3 {\n margin: 0 0 16px 0;\n color: #6f42c1;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.prompt-section, .comments-section {\n margin-bottom: 16px;\n}\n\n.prompt-section label, .comments-section label {\n display: block;\n margin-bottom: 8px;\n font-weight: 600;\n color: #495057;\n}\n\n.generation-controls {\n display: flex;\n align-items: center;\n gap: 12px;\n padding-top: 12px;\n border-top: 1px solid #dee2e6;\n}\n\n.code-locked-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n background: #ffc107;\n color: #856404;\n border-radius: 12px;\n font-size: 0.8em;\n font-weight: 500;\n}\n\n.code-editor-section {\n border: 1px solid #e9ecef;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.code-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e9ecef;\n}\n\n.code-toolbar h3 {\n margin: 0;\n font-size: 1.1em;\n color: #495057;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.code-actions {\n display: flex;\n gap: 8px;\n}\n\n.code-comments {\n background: #e3f2fd;\n border: 1px solid #90caf9;\n border-radius: 8px;\n padding: 16px;\n margin-top: 16px;\n}\n\n.code-comments h4 {\n margin: 0 0 8px 0;\n color: #1976d2;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.code-comments p {\n margin: 0;\n color: #424242;\n line-height: 1.5;\n}\n\n.approval-comments {\n margin-top: 16px;\n}\n\n.approval-comments label {\n display: block;\n margin-bottom: 8px;\n font-weight: 600;\n color: #dc3545;\n}\n\n/* Parameters Section */\n.params-section {\n background: #f8f9fa;\n border-radius: 12px;\n padding: 20px;\n}\n\n.params-section.params-section-compact {\n padding: 12px 20px;\n}\n\n.params-section-compact .empty-state {\n padding: 12px !important;\n}\n\n.params-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n\n.params-header h3 {\n margin: 0;\n color: #495057;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.params-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 16px;\n}\n\n.param-card {\n background: white;\n border: 2px solid #e9ecef;\n border-radius: 8px;\n padding: 16px;\n transition: all 0.2s;\n}\n\n.param-card:hover {\n border-color: #007bff;\n box-shadow: 0 2px 8px rgba(0,123,255,0.1);\n}\n\n.param-card.required {\n border-color: #ffc107;\n}\n\n.param-card.clickable {\n cursor: pointer;\n}\n\n.param-card.clickable:hover {\n background: #e7f3ff;\n border-color: #2196f3;\n transform: translateY(-1px);\n}\n\n.param-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n\n.param-name {\n font-weight: 600;\n color: #2c3e50;\n}\n\n.param-badges {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.param-edit-btn,\n.param-delete-btn {\n background: none;\n border: none;\n padding: 4px 8px;\n cursor: pointer;\n color: #6c757d;\n border-radius: 4px;\n transition: all 0.2s;\n}\n\n.param-edit-btn:hover {\n background: #e3f2fd;\n color: #2196f3;\n}\n\n.param-delete-btn:hover {\n background: #ffebee;\n color: #f44336;\n}\n\n.param-details {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.required-badge {\n background: #ffc107;\n color: #856404;\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 0.75em;\n font-weight: 500;\n}\n\n.array-badge {\n background: #6f42c1;\n color: white;\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 0.75em;\n font-weight: 500;\n}\n\n.param-type {\n color: #6c757d;\n font-size: 0.9em;\n margin-bottom: 8px;\n}\n\n.param-description {\n color: #495057;\n font-size: 0.9em;\n line-height: 1.4;\n margin-bottom: 8px;\n}\n\n.param-default {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px solid #e9ecef;\n}\n\n.default-label {\n color: #6c757d;\n font-size: 0.85em;\n}\n\n.param-default code {\n background: #f8f9fa;\n padding: 2px 6px;\n border-radius: 4px;\n font-family: 'Courier New', monospace;\n color: #e83e8c;\n}\n\n\n/* Result Codes Section */\n.result-codes-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 16px;\n}\n\n.result-code-card {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n padding: 16px;\n border: 2px solid #e9ecef;\n border-radius: 8px;\n background: white;\n transition: all 0.2s;\n position: relative;\n}\n\n.result-code-card.clickable {\n cursor: pointer;\n}\n\n.result-code-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n}\n\n.result-code-card.success {\n border-color: #28a745;\n background: #d4edda;\n}\n\n.result-code-card.failure {\n border-color: #dc3545;\n background: #f8d7da;\n}\n\n.result-actions {\n position: absolute;\n top: 8px;\n right: 8px;\n display: flex;\n gap: 4px;\n opacity: 0;\n transition: opacity 0.2s;\n}\n\n.result-code-card:hover .result-actions {\n opacity: 1;\n}\n\n.result-edit-btn,\n.result-delete-btn {\n background: white;\n border: 1px solid #dee2e6;\n border-radius: 4px;\n padding: 4px 8px;\n cursor: pointer;\n transition: all 0.2s;\n font-size: 0.85em;\n}\n\n.result-edit-btn:hover {\n background: #007bff;\n color: white;\n border-color: #007bff;\n}\n\n.result-delete-btn:hover {\n background: #dc3545;\n color: white;\n border-color: #dc3545;\n}\n\n.result-icon {\n font-size: 1.5em;\n flex-shrink: 0;\n}\n\n.result-code-card.success .result-icon {\n color: #28a745;\n}\n\n.result-code-card.failure .result-icon {\n color: #dc3545;\n}\n\n.result-content {\n flex: 1;\n min-width: 0;\n}\n\n.result-code {\n font-weight: 600;\n color: #2c3e50;\n margin-bottom: 4px;\n font-family: 'Courier New', monospace;\n}\n\n.result-description {\n color: #495057;\n font-size: 0.9em;\n line-height: 1.4;\n}\n\n/* Execution Section */\n.execution-stats-row {\n display: flex;\n gap: 20px;\n margin-bottom: 32px;\n flex-wrap: wrap;\n}\n\n.stat-box {\n flex: 1;\n min-width: 200px;\n background: #f8f9fa;\n border: 1px solid #e9ecef;\n border-radius: 8px;\n padding: 20px;\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.stat-box i {\n font-size: 2em;\n color: #6c757d;\n}\n\n.stat-info {\n flex: 1;\n}\n\n.executions-table {\n overflow-x: auto;\n border: 1px solid #e9ecef;\n border-radius: 8px;\n}\n\n.executions-table table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.executions-table th {\n background: #f8f9fa;\n padding: 12px;\n text-align: left;\n font-weight: 600;\n color: #495057;\n border-bottom: 2px solid #e9ecef;\n}\n\n.executions-table td {\n padding: 12px;\n border-bottom: 1px solid #e9ecef;\n}\n\n.execution-row {\n transition: background 0.2s;\n}\n\n.execution-row:hover {\n background: #f8f9fa;\n}\n\n.execution-row.success {\n background: #d4edda20;\n}\n\n.running {\n color: #ffc107;\n font-style: italic;\n}\n\n.result-code {\n display: inline-block;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 0.85em;\n font-weight: 500;\n font-family: 'Courier New', monospace;\n}\n\n.result-code.success {\n background: #d4edda;\n color: #155724;\n}\n\n.result-code.failure {\n background: #f8d7da;\n color: #721c24;\n}\n\n/* Configuration Section */\n.config-subsection {\n margin-bottom: 32px;\n}\n\n.config-subsection h3 {\n margin: 0 0 16px 0;\n color: #495057;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.library-cards {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.library-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 16px;\n background: #f8f9fa;\n border: 1px solid #e9ecef;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.library-card:hover {\n background: #e9ecef;\n transform: translateX(4px);\n}\n\n.library-card i:first-child {\n font-size: 1.5em;\n color: #6c757d;\n}\n\n.library-info {\n flex: 1;\n min-width: 0;\n}\n\n.library-name {\n font-weight: 600;\n color: #2c3e50;\n margin-bottom: 4px;\n}\n\n.library-items {\n color: #6c757d;\n font-size: 0.9em;\n font-family: 'Courier New', monospace;\n}\n\n.related-entities-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 12px;\n}\n\n.related-entity-link {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n background: #f8f9fa;\n border: 1px solid #e9ecef;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.related-entity-link:hover {\n background: #e9ecef;\n border-color: #007bff;\n}\n\n.related-entity-link i {\n font-size: 1.2em;\n color: #6c757d;\n}\n\n.related-entity-link span:first-of-type {\n flex: 1;\n font-weight: 500;\n color: #495057;\n}\n\n.entity-count {\n color: #007bff;\n font-size: 0.9em;\n}\n\n/* Common States */\n.loading-state {\n text-align: center;\n padding: 60px;\n color: #6c757d;\n}\n\n.loading-state i {\n font-size: 2em;\n margin-bottom: 12px;\n}\n\n.empty-state {\n text-align: center;\n padding: 60px;\n color: #6c757d;\n}\n\n.empty-state.mini {\n padding: 20px;\n}\n\n.empty-state i {\n font-size: 3em;\n margin-bottom: 16px;\n opacity: 0.3;\n}\n\n.empty-state p {\n margin: 0;\n font-size: 1.1em;\n}\n\n.empty-hint {\n margin-top: 8px !important;\n font-size: 0.9em !important;\n opacity: 0.7;\n}\n\n.no-params {\n text-align: center;\n padding: 20px;\n color: #6c757d;\n font-style: italic;\n}\n\n.no-params p {\n margin: 0;\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n .action-hero-header {\n padding: 20px;\n }\n \n .action-identity {\n flex-direction: column;\n text-align: center;\n }\n \n .action-icon-wrapper {\n margin: 0 auto;\n }\n \n .action-title-row {\n justify-content: center;\n }\n \n .action-stats {\n justify-content: center;\n }\n \n .params-grid {\n grid-template-columns: 1fr;\n }\n}"] }]
|
|
1880
1899
|
}], () => [{ type: i0.ElementRef }, { type: i1.SharedService }, { type: i2.Router }, { type: i2.ActivatedRoute }, { type: i0.ChangeDetectorRef }], null); })();
|
|
1881
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ActionFormComponentExtended, { className: "ActionFormComponentExtended", filePath: "src/lib/custom/Actions/action-form.component.ts", lineNumber:
|
|
1900
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ActionFormComponentExtended, { className: "ActionFormComponentExtended", filePath: "src/lib/custom/Actions/action-form.component.ts", lineNumber: 27 }); })();
|
|
1882
1901
|
// Loader function required for the component to be properly registered
|
|
1883
1902
|
export function LoadActionFormComponentExtended() {
|
|
1884
1903
|
// This function is called to ensure the form is loaded and registered
|