@gadmin2n/schematics 0.0.78 → 0.0.80
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/dist/lib/application/files/gadmin2-game-angle-demo/GRACEFUL-DEPLOYMENT.md +270 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.module.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/main.ts +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/health/health.controller.ts +17 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/health/health.module.ts +7 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +22 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/EnhancedFlowRenderer.tsx +388 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExecutionStatusNode.tsx +229 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/FlowRenderer.tsx +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +464 -55
- package/package.json +1 -1
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
Spin,
|
|
10
10
|
Steps,
|
|
11
11
|
Tag,
|
|
12
|
-
Tooltip,
|
|
13
12
|
Typography,
|
|
14
13
|
} from 'antd';
|
|
15
14
|
import {
|
|
@@ -23,6 +22,8 @@ import {
|
|
|
23
22
|
} from '@ant-design/icons';
|
|
24
23
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
25
24
|
import { customRequest } from 'helpers/http';
|
|
25
|
+
import { EnhancedFlowRenderer } from './components/EnhancedFlowRenderer';
|
|
26
|
+
import type { WorkflowDSL } from './types';
|
|
26
27
|
|
|
27
28
|
const { Title, Text } = Typography;
|
|
28
29
|
|
|
@@ -67,6 +68,7 @@ interface InstanceDetail {
|
|
|
67
68
|
createdAt: string;
|
|
68
69
|
workflow: { name: string };
|
|
69
70
|
nodeExecutions: NodeExecution[];
|
|
71
|
+
dsl?: WorkflowDSL;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
function formatDuration(start: string | null, end: string | null): string {
|
|
@@ -102,6 +104,8 @@ export default function WorkflowInstanceDetailPage() {
|
|
|
102
104
|
const [data, setData] = useState<InstanceDetail | null>(null);
|
|
103
105
|
const [loading, setLoading] = useState(true);
|
|
104
106
|
const [actionLoading, setActionLoading] = useState(false);
|
|
107
|
+
const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
|
|
108
|
+
const [viewMode, setViewMode] = useState<'timeline' | 'canvas'>('canvas');
|
|
105
109
|
|
|
106
110
|
const fetchData = useCallback(async () => {
|
|
107
111
|
if (!instanceId) return;
|
|
@@ -130,6 +134,13 @@ export default function WorkflowInstanceDetailPage() {
|
|
|
130
134
|
}
|
|
131
135
|
}, [data?.status, fetchData]);
|
|
132
136
|
|
|
137
|
+
// Fallback to timeline if no DSL available
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (data && !data.dsl && viewMode === 'canvas') {
|
|
140
|
+
setViewMode('timeline');
|
|
141
|
+
}
|
|
142
|
+
}, [data, viewMode]);
|
|
143
|
+
|
|
133
144
|
const handleCancel = async () => {
|
|
134
145
|
setActionLoading(true);
|
|
135
146
|
try {
|
|
@@ -193,6 +204,13 @@ export default function WorkflowInstanceDetailPage() {
|
|
|
193
204
|
if (!data) return null;
|
|
194
205
|
|
|
195
206
|
const statusCfg = STATUS_TAG[data.status] || STATUS_TAG.PENDING;
|
|
207
|
+
const selectedExecution = selectedNodeId
|
|
208
|
+
? data.nodeExecutions.find((ne) => ne.nodeId === selectedNodeId)
|
|
209
|
+
: null;
|
|
210
|
+
const selectedDslNode =
|
|
211
|
+
selectedNodeId && data.dsl
|
|
212
|
+
? data.dsl.nodes.find((n) => n.id === selectedNodeId)
|
|
213
|
+
: null;
|
|
196
214
|
|
|
197
215
|
return (
|
|
198
216
|
<div
|
|
@@ -277,65 +295,456 @@ export default function WorkflowInstanceDetailPage() {
|
|
|
277
295
|
</Descriptions>
|
|
278
296
|
</Card>
|
|
279
297
|
|
|
280
|
-
{/* Node Execution Timeline */}
|
|
281
|
-
<Card
|
|
282
|
-
{
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
298
|
+
{/* Node Execution Timeline / Canvas View */}
|
|
299
|
+
<Card
|
|
300
|
+
title={
|
|
301
|
+
viewMode === 'timeline'
|
|
302
|
+
? 'Node Execution Timeline'
|
|
303
|
+
: 'Flow Execution Visualization'
|
|
304
|
+
}
|
|
305
|
+
extra={
|
|
306
|
+
data.dsl ? (
|
|
307
|
+
<Space>
|
|
308
|
+
<Button.Group>
|
|
309
|
+
<Button
|
|
310
|
+
type={viewMode === 'canvas' ? 'primary' : 'default'}
|
|
311
|
+
onClick={() => {
|
|
312
|
+
setViewMode('canvas');
|
|
313
|
+
setSelectedNodeId(null);
|
|
314
|
+
}}
|
|
315
|
+
>
|
|
316
|
+
Canvas
|
|
317
|
+
</Button>
|
|
318
|
+
<Button
|
|
319
|
+
type={viewMode === 'timeline' ? 'primary' : 'default'}
|
|
320
|
+
onClick={() => {
|
|
321
|
+
setViewMode('timeline');
|
|
322
|
+
setSelectedNodeId(null);
|
|
323
|
+
}}
|
|
324
|
+
>
|
|
325
|
+
Timeline
|
|
326
|
+
</Button>
|
|
327
|
+
</Button.Group>
|
|
328
|
+
</Space>
|
|
329
|
+
) : null
|
|
330
|
+
}
|
|
331
|
+
>
|
|
332
|
+
{viewMode === 'timeline' ? (
|
|
333
|
+
// Timeline View
|
|
334
|
+
<div style={{ display: 'flex', gap: 16 }}>
|
|
335
|
+
<div style={{ flex: 1 }}>
|
|
336
|
+
{data.nodeExecutions.length === 0 ? (
|
|
337
|
+
<Text type="secondary">No node executions recorded yet.</Text>
|
|
338
|
+
) : (
|
|
339
|
+
<Steps
|
|
340
|
+
direction="vertical"
|
|
341
|
+
size="small"
|
|
342
|
+
current={-1}
|
|
343
|
+
items={data.nodeExecutions.map((ne) => {
|
|
344
|
+
const isApprovalPending =
|
|
345
|
+
ne.nodeType === 'approval' && ne.status === 'RUNNING';
|
|
346
|
+
const isSelected = selectedNodeId === ne.nodeId;
|
|
347
|
+
return {
|
|
348
|
+
title: (
|
|
349
|
+
<Space
|
|
350
|
+
style={{ cursor: 'pointer' }}
|
|
351
|
+
onClick={() =>
|
|
352
|
+
setSelectedNodeId(isSelected ? null : ne.nodeId)
|
|
353
|
+
}
|
|
304
354
|
>
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
355
|
+
<span style={{ fontWeight: isSelected ? 600 : 400 }}>
|
|
356
|
+
{ne.nodeLabel || ne.nodeId}
|
|
357
|
+
</span>
|
|
358
|
+
<Tag style={{ fontSize: 11 }}>{ne.nodeType}</Tag>
|
|
359
|
+
{isApprovalPending && (
|
|
360
|
+
<Space size={4}>
|
|
361
|
+
<Button
|
|
362
|
+
size="small"
|
|
363
|
+
type="primary"
|
|
364
|
+
onClick={(e) => {
|
|
365
|
+
e.stopPropagation();
|
|
366
|
+
handleApprove(ne.nodeId, true);
|
|
367
|
+
}}
|
|
368
|
+
loading={actionLoading}
|
|
369
|
+
>
|
|
370
|
+
Approve
|
|
371
|
+
</Button>
|
|
372
|
+
<Button
|
|
373
|
+
size="small"
|
|
374
|
+
danger
|
|
375
|
+
onClick={(e) => {
|
|
376
|
+
e.stopPropagation();
|
|
377
|
+
handleApprove(ne.nodeId, false);
|
|
378
|
+
}}
|
|
379
|
+
loading={actionLoading}
|
|
380
|
+
>
|
|
381
|
+
Reject
|
|
382
|
+
</Button>
|
|
383
|
+
</Space>
|
|
384
|
+
)}
|
|
385
|
+
</Space>
|
|
386
|
+
),
|
|
387
|
+
description: (
|
|
388
|
+
<div
|
|
389
|
+
style={{
|
|
390
|
+
fontSize: 12,
|
|
391
|
+
color: '#666',
|
|
392
|
+
cursor: 'pointer',
|
|
393
|
+
}}
|
|
394
|
+
onClick={() =>
|
|
395
|
+
setSelectedNodeId(isSelected ? null : ne.nodeId)
|
|
396
|
+
}
|
|
312
397
|
>
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
398
|
+
<span>
|
|
399
|
+
Duration:{' '}
|
|
400
|
+
{formatDuration(ne.startedAt, ne.finishedAt)}
|
|
401
|
+
</span>
|
|
402
|
+
{ne.error && (
|
|
403
|
+
<div style={{ color: '#ff4d4f', marginTop: 4 }}>
|
|
404
|
+
Error:{' '}
|
|
405
|
+
{typeof ne.error === 'object'
|
|
406
|
+
? JSON.stringify(ne.error)
|
|
407
|
+
: String(ne.error)}
|
|
408
|
+
</div>
|
|
409
|
+
)}
|
|
410
|
+
</div>
|
|
411
|
+
),
|
|
412
|
+
status: getStepStatus(ne.status),
|
|
413
|
+
icon:
|
|
414
|
+
ne.status === 'RUNNING' ? (
|
|
415
|
+
<LoadingOutlined />
|
|
416
|
+
) : undefined,
|
|
417
|
+
};
|
|
418
|
+
})}
|
|
419
|
+
/>
|
|
420
|
+
)}
|
|
421
|
+
</div>
|
|
422
|
+
|
|
423
|
+
{/* Node Detail Panel (shared with canvas view) */}
|
|
424
|
+
{selectedExecution && (
|
|
425
|
+
<div
|
|
426
|
+
style={{
|
|
427
|
+
width: 320,
|
|
428
|
+
borderLeft: '1px solid #f0f0f0',
|
|
429
|
+
paddingLeft: 16,
|
|
430
|
+
overflowY: 'auto',
|
|
431
|
+
}}
|
|
432
|
+
>
|
|
433
|
+
<div style={{ marginBottom: 16 }}>
|
|
434
|
+
<Title level={5}>
|
|
435
|
+
{selectedExecution.nodeLabel || selectedExecution.nodeId}
|
|
436
|
+
</Title>
|
|
437
|
+
<Tag
|
|
438
|
+
color={
|
|
439
|
+
selectedExecution.status === 'COMPLETED'
|
|
440
|
+
? 'green'
|
|
441
|
+
: selectedExecution.status === 'RUNNING'
|
|
442
|
+
? 'blue'
|
|
443
|
+
: selectedExecution.status === 'FAILED'
|
|
444
|
+
? 'red'
|
|
445
|
+
: 'gold'
|
|
446
|
+
}
|
|
447
|
+
>
|
|
448
|
+
{selectedExecution.status}
|
|
449
|
+
</Tag>
|
|
450
|
+
</div>
|
|
451
|
+
|
|
452
|
+
<Descriptions
|
|
453
|
+
column={1}
|
|
454
|
+
size="small"
|
|
455
|
+
style={{ marginBottom: 16 }}
|
|
456
|
+
>
|
|
457
|
+
<Descriptions.Item label="Node Type">
|
|
458
|
+
{selectedExecution.nodeType}
|
|
459
|
+
</Descriptions.Item>
|
|
460
|
+
<Descriptions.Item label="Started">
|
|
461
|
+
{selectedExecution.startedAt
|
|
462
|
+
? new Date(selectedExecution.startedAt).toLocaleString()
|
|
463
|
+
: '—'}
|
|
464
|
+
</Descriptions.Item>
|
|
465
|
+
<Descriptions.Item label="Finished">
|
|
466
|
+
{selectedExecution.finishedAt
|
|
467
|
+
? new Date(selectedExecution.finishedAt).toLocaleString()
|
|
468
|
+
: '—'}
|
|
469
|
+
</Descriptions.Item>
|
|
470
|
+
<Descriptions.Item label="Duration">
|
|
471
|
+
{formatDuration(
|
|
472
|
+
selectedExecution.startedAt,
|
|
473
|
+
selectedExecution.finishedAt,
|
|
316
474
|
)}
|
|
317
|
-
</
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
475
|
+
</Descriptions.Item>
|
|
476
|
+
</Descriptions>
|
|
477
|
+
|
|
478
|
+
{selectedExecution.input && (
|
|
479
|
+
<div style={{ marginBottom: 12 }}>
|
|
480
|
+
<Text strong style={{ fontSize: 12 }}>
|
|
481
|
+
Input:
|
|
482
|
+
</Text>
|
|
483
|
+
<pre
|
|
484
|
+
style={{
|
|
485
|
+
background: '#fafafa',
|
|
486
|
+
padding: 8,
|
|
487
|
+
borderRadius: 4,
|
|
488
|
+
fontSize: 11,
|
|
489
|
+
maxHeight: 150,
|
|
490
|
+
overflow: 'auto',
|
|
491
|
+
}}
|
|
492
|
+
>
|
|
493
|
+
{JSON.stringify(selectedExecution.input, null, 2)}
|
|
494
|
+
</pre>
|
|
495
|
+
</div>
|
|
496
|
+
)}
|
|
497
|
+
|
|
498
|
+
{selectedExecution.output && (
|
|
499
|
+
<div style={{ marginBottom: 12 }}>
|
|
500
|
+
<Text strong style={{ fontSize: 12 }}>
|
|
501
|
+
Output:
|
|
502
|
+
</Text>
|
|
503
|
+
<pre
|
|
504
|
+
style={{
|
|
505
|
+
background: '#fafafa',
|
|
506
|
+
padding: 8,
|
|
507
|
+
borderRadius: 4,
|
|
508
|
+
fontSize: 11,
|
|
509
|
+
maxHeight: 150,
|
|
510
|
+
overflow: 'auto',
|
|
511
|
+
}}
|
|
512
|
+
>
|
|
513
|
+
{JSON.stringify(selectedExecution.output, null, 2)}
|
|
514
|
+
</pre>
|
|
515
|
+
</div>
|
|
516
|
+
)}
|
|
517
|
+
|
|
518
|
+
{selectedExecution.error && (
|
|
519
|
+
<div style={{ marginBottom: 12 }}>
|
|
520
|
+
<Text strong style={{ fontSize: 12, color: '#ff4d4f' }}>
|
|
521
|
+
Error:
|
|
522
|
+
</Text>
|
|
523
|
+
<pre
|
|
524
|
+
style={{
|
|
525
|
+
background: '#fff1f0',
|
|
526
|
+
padding: 8,
|
|
527
|
+
borderRadius: 4,
|
|
528
|
+
fontSize: 11,
|
|
529
|
+
color: '#ff4d4f',
|
|
530
|
+
maxHeight: 150,
|
|
531
|
+
overflow: 'auto',
|
|
532
|
+
}}
|
|
533
|
+
>
|
|
534
|
+
{typeof selectedExecution.error === 'object'
|
|
535
|
+
? JSON.stringify(selectedExecution.error, null, 2)
|
|
536
|
+
: String(selectedExecution.error)}
|
|
537
|
+
</pre>
|
|
538
|
+
</div>
|
|
539
|
+
)}
|
|
540
|
+
</div>
|
|
541
|
+
)}
|
|
542
|
+
</div>
|
|
543
|
+
) : (
|
|
544
|
+
// Canvas View
|
|
545
|
+
<>
|
|
546
|
+
{data.dsl ? (
|
|
547
|
+
<div style={{ display: 'flex', gap: 16 }}>
|
|
548
|
+
{/* Canvas */}
|
|
549
|
+
<div style={{ flex: 1, height: 500, minHeight: 500 }}>
|
|
550
|
+
<EnhancedFlowRenderer
|
|
551
|
+
dsl={data.dsl}
|
|
552
|
+
nodeExecutions={data.nodeExecutions}
|
|
553
|
+
showExecutionStatus={true}
|
|
554
|
+
selectedNodeId={selectedNodeId}
|
|
555
|
+
readonly={true}
|
|
556
|
+
onNodeClick={(nodeId) => setSelectedNodeId(nodeId)}
|
|
557
|
+
/>
|
|
558
|
+
</div>
|
|
559
|
+
|
|
560
|
+
{/* Node Detail Panel */}
|
|
561
|
+
{selectedNodeId && (
|
|
562
|
+
<div
|
|
563
|
+
style={{
|
|
564
|
+
width: 320,
|
|
565
|
+
borderLeft: '1px solid #f0f0f0',
|
|
566
|
+
paddingLeft: 16,
|
|
567
|
+
overflowY: 'auto',
|
|
568
|
+
}}
|
|
569
|
+
>
|
|
570
|
+
{selectedExecution ? (
|
|
571
|
+
<>
|
|
572
|
+
<div style={{ marginBottom: 16 }}>
|
|
573
|
+
<Title level={5}>
|
|
574
|
+
{selectedExecution.nodeLabel ||
|
|
575
|
+
selectedExecution.nodeId}
|
|
576
|
+
</Title>
|
|
577
|
+
<Tag
|
|
578
|
+
color={
|
|
579
|
+
selectedExecution.status === 'COMPLETED'
|
|
580
|
+
? 'green'
|
|
581
|
+
: selectedExecution.status === 'RUNNING'
|
|
582
|
+
? 'blue'
|
|
583
|
+
: selectedExecution.status === 'FAILED'
|
|
584
|
+
? 'red'
|
|
585
|
+
: 'gold'
|
|
586
|
+
}
|
|
587
|
+
>
|
|
588
|
+
{selectedExecution.status}
|
|
589
|
+
</Tag>
|
|
590
|
+
</div>
|
|
591
|
+
|
|
592
|
+
<Descriptions
|
|
593
|
+
column={1}
|
|
594
|
+
size="small"
|
|
595
|
+
style={{ marginBottom: 16 }}
|
|
596
|
+
>
|
|
597
|
+
<Descriptions.Item label="Node Type">
|
|
598
|
+
{selectedExecution.nodeType}
|
|
599
|
+
</Descriptions.Item>
|
|
600
|
+
<Descriptions.Item label="Started">
|
|
601
|
+
{selectedExecution.startedAt
|
|
602
|
+
? new Date(
|
|
603
|
+
selectedExecution.startedAt,
|
|
604
|
+
).toLocaleString()
|
|
605
|
+
: '—'}
|
|
606
|
+
</Descriptions.Item>
|
|
607
|
+
<Descriptions.Item label="Finished">
|
|
608
|
+
{selectedExecution.finishedAt
|
|
609
|
+
? new Date(
|
|
610
|
+
selectedExecution.finishedAt,
|
|
611
|
+
).toLocaleString()
|
|
612
|
+
: '—'}
|
|
613
|
+
</Descriptions.Item>
|
|
614
|
+
<Descriptions.Item label="Duration">
|
|
615
|
+
{formatDuration(
|
|
616
|
+
selectedExecution.startedAt,
|
|
617
|
+
selectedExecution.finishedAt,
|
|
618
|
+
)}
|
|
619
|
+
</Descriptions.Item>
|
|
620
|
+
</Descriptions>
|
|
621
|
+
|
|
622
|
+
{selectedExecution.input && (
|
|
623
|
+
<div style={{ marginBottom: 12 }}>
|
|
624
|
+
<Text strong style={{ fontSize: 12 }}>
|
|
625
|
+
Input:
|
|
626
|
+
</Text>
|
|
627
|
+
<pre
|
|
628
|
+
style={{
|
|
629
|
+
background: '#fafafa',
|
|
630
|
+
padding: 8,
|
|
631
|
+
borderRadius: 4,
|
|
632
|
+
fontSize: 11,
|
|
633
|
+
maxHeight: 150,
|
|
634
|
+
overflow: 'auto',
|
|
635
|
+
}}
|
|
636
|
+
>
|
|
637
|
+
{JSON.stringify(selectedExecution.input, null, 2)}
|
|
638
|
+
</pre>
|
|
639
|
+
</div>
|
|
640
|
+
)}
|
|
641
|
+
|
|
642
|
+
{selectedExecution.output && (
|
|
643
|
+
<div style={{ marginBottom: 12 }}>
|
|
644
|
+
<Text strong style={{ fontSize: 12 }}>
|
|
645
|
+
Output:
|
|
646
|
+
</Text>
|
|
647
|
+
<pre
|
|
648
|
+
style={{
|
|
649
|
+
background: '#fafafa',
|
|
650
|
+
padding: 8,
|
|
651
|
+
borderRadius: 4,
|
|
652
|
+
fontSize: 11,
|
|
653
|
+
maxHeight: 150,
|
|
654
|
+
overflow: 'auto',
|
|
655
|
+
}}
|
|
656
|
+
>
|
|
657
|
+
{JSON.stringify(
|
|
658
|
+
selectedExecution.output,
|
|
659
|
+
null,
|
|
660
|
+
2,
|
|
661
|
+
)}
|
|
662
|
+
</pre>
|
|
663
|
+
</div>
|
|
664
|
+
)}
|
|
665
|
+
|
|
666
|
+
{selectedExecution.error && (
|
|
667
|
+
<div style={{ marginBottom: 12 }}>
|
|
668
|
+
<Text
|
|
669
|
+
strong
|
|
670
|
+
style={{ fontSize: 12, color: '#ff4d4f' }}
|
|
671
|
+
>
|
|
672
|
+
Error:
|
|
673
|
+
</Text>
|
|
674
|
+
<pre
|
|
675
|
+
style={{
|
|
676
|
+
background: '#fff1f0',
|
|
677
|
+
padding: 8,
|
|
678
|
+
borderRadius: 4,
|
|
679
|
+
fontSize: 11,
|
|
680
|
+
color: '#ff4d4f',
|
|
681
|
+
maxHeight: 150,
|
|
682
|
+
overflow: 'auto',
|
|
683
|
+
}}
|
|
684
|
+
>
|
|
685
|
+
{typeof selectedExecution.error === 'object'
|
|
686
|
+
? JSON.stringify(
|
|
687
|
+
selectedExecution.error,
|
|
688
|
+
null,
|
|
689
|
+
2,
|
|
690
|
+
)
|
|
691
|
+
: String(selectedExecution.error)}
|
|
692
|
+
</pre>
|
|
693
|
+
</div>
|
|
694
|
+
)}
|
|
695
|
+
|
|
696
|
+
{/* Approval actions */}
|
|
697
|
+
{selectedExecution.nodeType === 'approval' &&
|
|
698
|
+
selectedExecution.status === 'RUNNING' && (
|
|
699
|
+
<Space style={{ width: '100%' }}>
|
|
700
|
+
<Button
|
|
701
|
+
type="primary"
|
|
702
|
+
block
|
|
703
|
+
onClick={() =>
|
|
704
|
+
handleApprove(selectedExecution.nodeId, true)
|
|
705
|
+
}
|
|
706
|
+
loading={actionLoading}
|
|
707
|
+
>
|
|
708
|
+
Approve
|
|
709
|
+
</Button>
|
|
710
|
+
<Button
|
|
711
|
+
danger
|
|
712
|
+
block
|
|
713
|
+
onClick={() =>
|
|
714
|
+
handleApprove(selectedExecution.nodeId, false)
|
|
715
|
+
}
|
|
716
|
+
loading={actionLoading}
|
|
717
|
+
>
|
|
718
|
+
Reject
|
|
719
|
+
</Button>
|
|
720
|
+
</Space>
|
|
721
|
+
)}
|
|
722
|
+
</>
|
|
723
|
+
) : selectedDslNode ? (
|
|
724
|
+
<div>
|
|
725
|
+
<div style={{ marginBottom: 16 }}>
|
|
726
|
+
<Title level={5}>{selectedDslNode.label}</Title>
|
|
727
|
+
<Tag color="default">Not Executed</Tag>
|
|
728
|
+
</div>
|
|
729
|
+
<Descriptions column={1} size="small">
|
|
730
|
+
<Descriptions.Item label="Node Type">
|
|
731
|
+
{selectedDslNode.type}
|
|
732
|
+
</Descriptions.Item>
|
|
733
|
+
<Descriptions.Item label="Status">
|
|
734
|
+
<Text type="secondary">Waiting for execution</Text>
|
|
735
|
+
</Descriptions.Item>
|
|
736
|
+
</Descriptions>
|
|
330
737
|
</div>
|
|
331
|
-
)}
|
|
738
|
+
) : null}
|
|
332
739
|
</div>
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
740
|
+
)}
|
|
741
|
+
</div>
|
|
742
|
+
) : (
|
|
743
|
+
<Text type="secondary">
|
|
744
|
+
Workflow DSL not available for visualization.
|
|
745
|
+
</Text>
|
|
746
|
+
)}
|
|
747
|
+
</>
|
|
339
748
|
)}
|
|
340
749
|
</Card>
|
|
341
750
|
</div>
|