onChange(activeTabId, newCodeValue)}
diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTab.tsx b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTab.tsx
index b2d54cbc03f9..54a565215d4b 100644
--- a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTab.tsx
+++ b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTab.tsx
@@ -2,6 +2,7 @@ import { Section } from '@/ui/layout/section/components/Section';
import { H2Title, IconPlayerPlay } from 'twenty-ui';
import { LightCopyIconButton } from '@/object-record/record-field/components/LightCopyIconButton';
+import { SettingsServerlessFunctionCodeEditorContainer } from '@/settings/serverless-functions/components/SettingsServerlessFunctionCodeEditorContainer';
import { SettingsServerlessFunctionsOutputMetadataInfo } from '@/settings/serverless-functions/components/SettingsServerlessFunctionsOutputMetadataInfo';
import { settingsServerlessFunctionCodeEditorOutputParamsState } from '@/settings/serverless-functions/states/settingsServerlessFunctionCodeEditorOutputParamsState';
import { settingsServerlessFunctionInputState } from '@/settings/serverless-functions/states/settingsServerlessFunctionInputState';
@@ -78,37 +79,30 @@ export const SettingsServerlessFunctionTestTab = ({
/>,
]}
/>
-
+
+
+
]}
rightNodes={[]}
/>
-
+
+
+
diff --git a/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx b/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx
index 723b04a9f69b..dc846b9c0834 100644
--- a/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx
+++ b/packages/twenty-front/src/modules/ui/input/code-editor/components/CodeEditor.tsx
@@ -1,148 +1,51 @@
-import { useGetAvailablePackages } from '@/settings/serverless-functions/hooks/useGetAvailablePackages';
import { codeEditorTheme } from '@/ui/input/code-editor/utils/codeEditorTheme';
import { useTheme } from '@emotion/react';
-import styled from '@emotion/styled';
-import Editor, { EditorProps, Monaco } from '@monaco-editor/react';
-import dotenv from 'dotenv';
-import { MarkerSeverity, editor } from 'monaco-editor';
-import { AutoTypings } from 'monaco-editor-auto-typings';
-import { isDefined } from '~/utils/isDefined';
-
-const StyledEditor = styled(Editor)`
- border: 1px solid ${({ theme }) => theme.border.color.medium};
- border-top: none;
- border-radius: 0 0 ${({ theme }) => theme.border.radius.sm}
- ${({ theme }) => theme.border.radius.sm};
-`;
-
-export type File = {
- language: string;
- content: string;
- path: string;
-};
+import Editor, { EditorProps } from '@monaco-editor/react';
+import { isDefined } from 'twenty-ui';
type CodeEditorProps = Omit & {
- currentFilePath: string;
- files: File[];
onChange?: (value: string) => void;
- setIsCodeValid?: (isCodeValid: boolean) => void;
};
export const CodeEditor = ({
- currentFilePath,
- files,
+ value,
+ language,
+ onMount,
onChange,
- setIsCodeValid,
+ onValidate,
height = 450,
- options = undefined,
+ options,
}: CodeEditorProps) => {
const theme = useTheme();
- const { availablePackages } = useGetAvailablePackages();
-
- const currentFile = files.find((file) => file.path === currentFilePath);
- const environmentVariablesFile = files.find((file) => file.path === '.env');
-
- const handleEditorDidMount = async (
- editor: editor.IStandaloneCodeEditor,
- monaco: Monaco,
- ) => {
- monaco.editor.defineTheme('codeEditorTheme', codeEditorTheme(theme));
- monaco.editor.setTheme('codeEditorTheme');
-
- if (files.length > 1) {
- files.forEach((file) => {
- const model = monaco.editor.getModel(monaco.Uri.file(file.path));
- if (!isDefined(model)) {
- monaco.editor.createModel(
- file.content,
- file.language,
- monaco.Uri.file(file.path),
- );
+ return (
+ {
+ monaco.editor.defineTheme('codeEditorTheme', codeEditorTheme(theme));
+ monaco.editor.setTheme('codeEditorTheme');
+
+ onMount?.(editor, monaco);
+ }}
+ onChange={(value) => {
+ if (isDefined(value)) {
+ onChange?.(value);
}
- });
-
- monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
- ...monaco.languages.typescript.typescriptDefaults.getCompilerOptions(),
- moduleResolution:
- monaco.languages.typescript.ModuleResolutionKind.NodeJs,
- baseUrl: 'file:///src',
- paths: {
- 'src/*': ['file:///src/*'],
+ }}
+ onValidate={onValidate}
+ options={{
+ overviewRulerLanes: 0,
+ scrollbar: {
+ vertical: 'hidden',
+ horizontal: 'hidden',
},
- allowSyntheticDefaultImports: true,
- esModuleInterop: true,
- noEmit: true,
- target: monaco.languages.typescript.ScriptTarget.ESNext,
- });
-
- if (isDefined(environmentVariablesFile)) {
- const environmentVariables = dotenv.parse(
- environmentVariablesFile.content,
- );
-
- const environmentDefinition = `
- declare namespace NodeJS {
- interface ProcessEnv {
- ${Object.keys(environmentVariables)
- .map((key) => `${key}: string;`)
- .join('\n')}
- }
- }
-
- declare const process: {
- env: NodeJS.ProcessEnv;
- };
- `;
-
- monaco.languages.typescript.typescriptDefaults.addExtraLib(
- environmentDefinition,
- 'ts:process-env.d.ts',
- );
- }
-
- await AutoTypings.create(editor, {
- monaco,
- preloadPackages: true,
- onlySpecifiedPackages: true,
- versions: availablePackages,
- debounceDuration: 0,
- });
- }
- };
-
- const handleEditorValidation = (markers: editor.IMarker[]) => {
- for (const marker of markers) {
- if (marker.severity === MarkerSeverity.Error) {
- setIsCodeValid?.(false);
- return;
- }
- }
- setIsCodeValid?.(true);
- };
-
- return (
- isDefined(currentFile) &&
- isDefined(availablePackages) && (
- value && onChange?.(value)}
- onValidate={handleEditorValidation}
- options={{
- ...options,
- overviewRulerLanes: 0,
- scrollbar: {
- vertical: 'hidden',
- horizontal: 'hidden',
- },
- minimap: {
- enabled: false,
- },
- }}
- />
- )
+ minimap: {
+ enabled: false,
+ },
+ ...options,
+ }}
+ />
);
};
diff --git a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageSubContainer.tsx b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageSubContainer.tsx
index 3365a170e2a6..6f2c0aa442c9 100644
--- a/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageSubContainer.tsx
+++ b/packages/twenty-front/src/modules/ui/layout/show-page/components/ShowPageSubContainer.tsx
@@ -18,6 +18,8 @@ import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPage
import { SingleTabProps, TabList } from '@/ui/layout/tab/components/TabList';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
+import { WorkflowRunOutputVisualizer } from '@/workflow/components/WorkflowRunOutputVisualizer';
+import { WorkflowRunVersionVisualizer } from '@/workflow/components/WorkflowRunVersionVisualizer';
import { WorkflowVersionVisualizer } from '@/workflow/components/WorkflowVersionVisualizer';
import { WorkflowVersionVisualizerEffect } from '@/workflow/components/WorkflowVersionVisualizerEffect';
import { WorkflowVisualizer } from '@/workflow/components/WorkflowVisualizer';
@@ -182,6 +184,14 @@ export const ShowPageSubContainer = ({
/>
>
);
+ case 'workflowRunFlow':
+ return (
+
+ );
+ case 'workflowRunOutput':
+ return (
+
+ );
default:
return <>>;
}
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowRunOutputVisualizer.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowRunOutputVisualizer.tsx
new file mode 100644
index 000000000000..1a49c030ac60
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/components/WorkflowRunOutputVisualizer.tsx
@@ -0,0 +1,32 @@
+import { CodeEditor } from '@/ui/input/code-editor/components/CodeEditor';
+import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun';
+import styled from '@emotion/styled';
+import { isDefined } from 'twenty-ui';
+
+const StyledSourceCodeContainer = styled.div`
+ border: 1px solid ${({ theme }) => theme.border.color.medium};
+ border-radius: ${({ theme }) => theme.border.radius.sm};
+ margin: ${({ theme }) => theme.spacing(4)};
+ overflow: hidden;
+`;
+
+export const WorkflowRunOutputVisualizer = ({
+ workflowRunId,
+}: {
+ workflowRunId: string;
+}) => {
+ const workflowRun = useWorkflowRun({ workflowRunId });
+ if (!isDefined(workflowRun)) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowRunVersionVisualizer.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowRunVersionVisualizer.tsx
new file mode 100644
index 000000000000..8d8f265c426c
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/components/WorkflowRunVersionVisualizer.tsx
@@ -0,0 +1,29 @@
+import { WorkflowVersionVisualizer } from '@/workflow/components/WorkflowVersionVisualizer';
+import { WorkflowVersionVisualizerEffect } from '@/workflow/components/WorkflowVersionVisualizerEffect';
+import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun';
+import { isDefined } from 'twenty-ui';
+
+export const WorkflowRunVersionVisualizer = ({
+ workflowRunId,
+}: {
+ workflowRunId: string;
+}) => {
+ const workflowRun = useWorkflowRun({
+ workflowRunId,
+ });
+ if (!isDefined(workflowRun)) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
diff --git a/packages/twenty-front/src/modules/workflow/hooks/useWorkflowRun.tsx b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowRun.tsx
new file mode 100644
index 000000000000..9bb6fa5642ed
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowRun.tsx
@@ -0,0 +1,16 @@
+import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
+import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
+import { WorkflowRun } from '@/workflow/types/Workflow';
+
+export const useWorkflowRun = ({
+ workflowRunId,
+}: {
+ workflowRunId: string;
+}) => {
+ const { record } = useFindOneRecord({
+ objectNameSingular: CoreObjectNameSingular.WorkflowRun,
+ objectRecordId: workflowRunId,
+ });
+
+ return record;
+};
diff --git a/packages/twenty-front/src/modules/workflow/types/Workflow.ts b/packages/twenty-front/src/modules/workflow/types/Workflow.ts
index 65b2e9a25a15..70e3ab197020 100644
--- a/packages/twenty-front/src/modules/workflow/types/Workflow.ts
+++ b/packages/twenty-front/src/modules/workflow/types/Workflow.ts
@@ -84,6 +84,28 @@ export type WorkflowVersion = {
__typename: 'WorkflowVersion';
};
+type StepRunOutput = {
+ id: string;
+ name: string;
+ type: string;
+ outputs: {
+ attemptCount: number;
+ result: object | undefined;
+ error: string | undefined;
+ }[];
+};
+
+export type WorkflowRunOutput = {
+ steps: Record;
+};
+
+export type WorkflowRun = {
+ __typename: 'WorkflowRun';
+ id: string;
+ workflowVersionId: string;
+ output: WorkflowRunOutput;
+};
+
export type Workflow = {
__typename: 'Workflow';
id: string;
diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts
index ffef2fdeda79..5244849f69b1 100644
--- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts
+++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts
@@ -215,6 +215,7 @@ export {
IconTimelineEvent,
IconTool,
IconTrash,
+ IconPrinter,
IconUnlink,
IconUpload,
IconUser,