index.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { FC, useEffect, useState } from 'react';
  2. import { WorkflowInputs, WorkflowOutputs } from '@flowgram.ai/runtime-interface';
  3. import { useService } from '@flowgram.ai/free-layout-editor';
  4. import { Button, JsonViewer, SideSheet } from '@douyinfe/semi-ui';
  5. import { IconPlay, IconSpin, IconStop } from '@douyinfe/semi-icons';
  6. import { NodeStatusGroup } from '../node-status-bar/group';
  7. import { WorkflowRuntimeService } from '../../../plugins/runtime-plugin/runtime-service';
  8. interface TestRunSideSheetProps {
  9. visible: boolean;
  10. onCancel: () => void;
  11. }
  12. export const TestRunSideSheet: FC<TestRunSideSheetProps> = ({ visible, onCancel }) => {
  13. const runtimeService = useService(WorkflowRuntimeService);
  14. const [isRunning, setRunning] = useState(false);
  15. const [value, setValue] = useState<string>(`{}`);
  16. const [error, setError] = useState<string | undefined>();
  17. const [result, setResult] = useState<
  18. | {
  19. inputs: WorkflowInputs;
  20. outputs: WorkflowOutputs;
  21. }
  22. | undefined
  23. >();
  24. const onTestRun = async () => {
  25. if (isRunning) {
  26. await runtimeService.taskCancel();
  27. return;
  28. }
  29. setResult(undefined);
  30. setError(undefined);
  31. setRunning(true);
  32. try {
  33. await runtimeService.taskRun(value);
  34. } catch (e: any) {
  35. setError(e.message);
  36. }
  37. };
  38. const onClose = async () => {
  39. await runtimeService.taskCancel();
  40. setValue(`{}`);
  41. setRunning(false);
  42. onCancel();
  43. };
  44. useEffect(() => {
  45. const disposer = runtimeService.onTerminated(({ result }) => {
  46. setRunning(false);
  47. setResult(result);
  48. });
  49. return () => disposer.dispose();
  50. }, []);
  51. const renderRunning = (
  52. <div
  53. style={{
  54. width: '100%',
  55. height: '80%',
  56. display: 'flex',
  57. flexDirection: 'column',
  58. justifyContent: 'center',
  59. alignItems: 'center',
  60. gap: 16,
  61. }}
  62. >
  63. <IconSpin spin size="large" />
  64. <div
  65. style={{
  66. fontSize: '18px',
  67. }}
  68. >
  69. Running...
  70. </div>
  71. </div>
  72. );
  73. const renderForm = (
  74. <div>
  75. <div
  76. style={{
  77. fontSize: '15px',
  78. fontWeight: '500',
  79. marginBottom: '10px',
  80. color: '#333',
  81. }}
  82. >
  83. Input
  84. </div>
  85. <JsonViewer showSearch={false} height={300} value={value} onChange={setValue} />
  86. <div
  87. style={{
  88. color: 'red',
  89. fontSize: '14px',
  90. marginTop: '30px',
  91. }}
  92. >
  93. {error}
  94. </div>
  95. <NodeStatusGroup title="Inputs" data={result?.inputs} optional disableCollapse />
  96. <NodeStatusGroup title="Outputs" data={result?.outputs} optional disableCollapse />
  97. </div>
  98. );
  99. const renderButton = (
  100. <Button
  101. onClick={onTestRun}
  102. icon={isRunning ? <IconStop size="small" /> : <IconPlay size="small" />}
  103. style={{
  104. backgroundColor: isRunning ? 'rgba(87,104,161,0.08)' : 'rgba(0,178,60,1)',
  105. borderRadius: '8px',
  106. color: isRunning ? 'rgba(15,21,40,0.82)' : '#fff',
  107. marginBottom: '16px',
  108. width: '100%',
  109. height: '40px',
  110. }}
  111. >
  112. {isRunning ? 'Cancel' : 'Test Run'}
  113. </Button>
  114. );
  115. return (
  116. <SideSheet
  117. title="Test Run"
  118. visible={visible}
  119. mask={false}
  120. onCancel={onClose}
  121. footer={renderButton}
  122. >
  123. {isRunning ? renderRunning : renderForm}
  124. </SideSheet>
  125. );
  126. };