999dcd4468
## Before <img width="1031" height="836" alt="image" src="https://github.com/user-attachments/assets/475ca1be-f7c4-49d0-b329-649dbe8da489" /> ## After <img width="1195" height="862" alt="image" src="https://github.com/user-attachments/assets/b1bac131-e562-4439-8f8e-bda4d6e2a646" /> --------- Co-authored-by: Charles Bochet <charles@twenty.com>
150 lines
3.8 KiB
TypeScript
150 lines
3.8 KiB
TypeScript
import { LightCopyIconButton } from '@/object-record/record-field/ui/components/LightCopyIconButton';
|
|
import { styled } from '@linaria/react';
|
|
import { t } from '@lingui/core/macro';
|
|
import {
|
|
IconLoader,
|
|
IconSquareRoundedCheck,
|
|
IconSquareRoundedX,
|
|
} from 'twenty-ui/display';
|
|
import { CodeEditor, CoreEditorHeader } from 'twenty-ui/input';
|
|
import { AnimatedCircleLoading } from 'twenty-ui/utilities';
|
|
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
|
import { useContext } from 'react';
|
|
import { ThemeContext } from 'twenty-ui/theme';
|
|
|
|
const StyledContainer = styled.div`
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
`;
|
|
|
|
const StyledCodeEditorWrapper = styled.div`
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
`;
|
|
|
|
type OutputAccent = 'default' | 'success' | 'error';
|
|
|
|
const StyledInfoContainer = styled.div`
|
|
display: flex;
|
|
font-size: ${themeCssVariables.font.size.md};
|
|
`;
|
|
|
|
const StyledOutput = styled.div<{ accent?: OutputAccent }>`
|
|
align-items: center;
|
|
gap: ${themeCssVariables.spacing[1]};
|
|
color: ${({ accent }) =>
|
|
accent === 'success'
|
|
? themeCssVariables.color.turquoise
|
|
: accent === 'error'
|
|
? themeCssVariables.color.red
|
|
: themeCssVariables.font.color.secondary};
|
|
display: flex;
|
|
`;
|
|
|
|
const StyledStatusInfo = styled.div`
|
|
display: flex;
|
|
gap: ${themeCssVariables.spacing[2]};
|
|
font-size: ${themeCssVariables.font.size.sm};
|
|
color: ${themeCssVariables.font.color.tertiary};
|
|
`;
|
|
|
|
export type ExecutionStatus = {
|
|
isSuccess: boolean;
|
|
isError: boolean;
|
|
successMessage?: string;
|
|
errorMessage?: string;
|
|
additionalInfo?: string;
|
|
};
|
|
|
|
type WorkflowStepExecutionResultProps = {
|
|
result: string;
|
|
language: 'plaintext' | 'json';
|
|
height?: string | number;
|
|
status: ExecutionStatus;
|
|
isTesting?: boolean;
|
|
loadingMessage?: string;
|
|
idleMessage?: string;
|
|
};
|
|
|
|
export const WorkflowStepExecutionResult = ({
|
|
result,
|
|
language,
|
|
height = '100%',
|
|
status,
|
|
isTesting = false,
|
|
loadingMessage = t`Processing...`,
|
|
idleMessage = t`Output`,
|
|
}: WorkflowStepExecutionResultProps) => {
|
|
const { theme } = useContext(ThemeContext);
|
|
|
|
const SuccessLeftNode = (
|
|
<StyledOutput accent="success">
|
|
<IconSquareRoundedCheck size={theme.icon.size.md} />
|
|
<div>
|
|
<div>{status.successMessage}</div>
|
|
{status.additionalInfo && (
|
|
<StyledStatusInfo>{status.additionalInfo}</StyledStatusInfo>
|
|
)}
|
|
</div>
|
|
</StyledOutput>
|
|
);
|
|
|
|
const ErrorLeftNode = (
|
|
<StyledOutput accent="error">
|
|
<IconSquareRoundedX size={theme.icon.size.md} />
|
|
<div>
|
|
<div>{status.errorMessage}</div>
|
|
{status.additionalInfo && (
|
|
<StyledStatusInfo>{status.additionalInfo}</StyledStatusInfo>
|
|
)}
|
|
</div>
|
|
</StyledOutput>
|
|
);
|
|
|
|
const IdleLeftNode = idleMessage;
|
|
|
|
const PendingLeftNode = isTesting && (
|
|
<StyledOutput>
|
|
<AnimatedCircleLoading>
|
|
<IconLoader size={theme.icon.size.md} />
|
|
</AnimatedCircleLoading>
|
|
<StyledInfoContainer>{loadingMessage}</StyledInfoContainer>
|
|
</StyledOutput>
|
|
);
|
|
|
|
const computeLeftNode = () => {
|
|
if (isTesting) {
|
|
return PendingLeftNode;
|
|
}
|
|
if (status.isError) {
|
|
return ErrorLeftNode;
|
|
}
|
|
if (status.isSuccess) {
|
|
return SuccessLeftNode;
|
|
}
|
|
return IdleLeftNode;
|
|
};
|
|
|
|
return (
|
|
<StyledContainer>
|
|
<CoreEditorHeader
|
|
leftNodes={[computeLeftNode()]}
|
|
rightNodes={[<LightCopyIconButton copyText={result} />]}
|
|
/>
|
|
<StyledCodeEditorWrapper>
|
|
<CodeEditor
|
|
resizable={true}
|
|
value={result}
|
|
language={language}
|
|
height={height}
|
|
options={{ readOnly: true, domReadOnly: true }}
|
|
isLoading={isTesting}
|
|
variant="with-header"
|
|
/>
|
|
</StyledCodeEditorWrapper>
|
|
</StyledContainer>
|
|
);
|
|
};
|