Update workflow nodes configuration (#6861)
- Improve the design of the right drawer - Allow to update the trigger of the workflow: the object and the event listened to - Allow to update the selected serverless function that a code action should execute - Change how we determine which workflow version to display in the visualizer. We fetch the selected workflow's data, including whether it has a draft or a published version. If the workflow has a draft version, it gets displayed; otherwise, we display the last published version. - I used the type `WorkflowWithCurrentVersion` to forward the currently edited workflow with its _current_ version embedded across the app. - I created single-responsibility hooks like `useFindWorkflowWithCurrentVersion`, `useFindShowPageWorkflow`, `useUpdateWorkflowVersionTrigger` or `useUpdateWorkflowVersionStep`. - I updated the types for workflow related objects, like `Workflow` and `WorkflowVersion`. See `packages/twenty-front/src/modules/workflow/types/Workflow.ts`. - This introduced the possibility to have `null` values for triggers and steps. I made the according changes in the codebase and in the tests. - I created a utility function to extract both parts of object-event format (`company.created`): `packages/twenty-front/src/modules/workflow/utils/splitWorkflowTriggerEventName.ts`
This commit is contained in:
committed by
GitHub
parent
c55dfbde6e
commit
a2b1062db6
@ -1,10 +1,16 @@
|
||||
import { showPageWorkflowSelectedNodeState } from '@/workflow/states/showPageWorkflowSelectedNodeState';
|
||||
import { RightDrawerWorkflowEditStepContent } from '@/workflow/components/RightDrawerWorkflowEditStepContent';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const RightDrawerWorkflowEditStep = () => {
|
||||
const showPageWorkflowSelectedNode = useRecoilValue(
|
||||
showPageWorkflowSelectedNodeState,
|
||||
);
|
||||
const workflowId = useRecoilValue(workflowIdState);
|
||||
const workflow = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
return <p>{showPageWorkflowSelectedNode}</p>;
|
||||
if (!isDefined(workflow)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <RightDrawerWorkflowEditStepContent workflow={workflow} />;
|
||||
};
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
import { WorkflowEditActionForm } from '@/workflow/components/WorkflowEditActionForm';
|
||||
import { WorkflowEditTriggerForm } from '@/workflow/components/WorkflowEditTriggerForm';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
|
||||
import { useUpdateWorkflowVersionStep } from '@/workflow/hooks/useUpdateWorkflowVersionStep';
|
||||
import { useUpdateWorkflowVersionTrigger } from '@/workflow/hooks/useUpdateWorkflowVersionTrigger';
|
||||
import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
|
||||
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||
import { findStepPositionOrThrow } from '@/workflow/utils/findStepPositionOrThrow';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
const getStepDefinitionOrThrow = ({
|
||||
stepId,
|
||||
workflow,
|
||||
}: {
|
||||
stepId: string;
|
||||
workflow: WorkflowWithCurrentVersion;
|
||||
}) => {
|
||||
const currentVersion = workflow.currentVersion;
|
||||
if (!isDefined(currentVersion)) {
|
||||
throw new Error('Expected to find a current version');
|
||||
}
|
||||
|
||||
if (stepId === TRIGGER_STEP_ID) {
|
||||
if (!isDefined(currentVersion.trigger)) {
|
||||
throw new Error('Expected to find the definition of the trigger');
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'trigger',
|
||||
definition: currentVersion.trigger,
|
||||
} as const;
|
||||
}
|
||||
|
||||
if (!isDefined(currentVersion.steps)) {
|
||||
throw new Error('Expected to find an array of steps');
|
||||
}
|
||||
|
||||
const selectedNodePosition = findStepPositionOrThrow({
|
||||
steps: currentVersion.steps,
|
||||
stepId: stepId,
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'action',
|
||||
definition: selectedNodePosition.steps[selectedNodePosition.index],
|
||||
} as const;
|
||||
};
|
||||
|
||||
export const RightDrawerWorkflowEditStepContent = ({
|
||||
workflow,
|
||||
}: {
|
||||
workflow: WorkflowWithCurrentVersion;
|
||||
}) => {
|
||||
const workflowSelectedNode = useRecoilValue(workflowSelectedNodeState);
|
||||
if (!isDefined(workflowSelectedNode)) {
|
||||
throw new Error(
|
||||
'Expected a node to be selected. Selecting a node is mandatory to edit it.',
|
||||
);
|
||||
}
|
||||
|
||||
const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow });
|
||||
const { updateStep } = useUpdateWorkflowVersionStep({
|
||||
workflow,
|
||||
stepId: workflowSelectedNode,
|
||||
});
|
||||
|
||||
const stepDefinition = getStepDefinitionOrThrow({
|
||||
stepId: workflowSelectedNode,
|
||||
workflow,
|
||||
});
|
||||
|
||||
if (stepDefinition.type === 'trigger') {
|
||||
return (
|
||||
<WorkflowEditTriggerForm
|
||||
trigger={stepDefinition.definition}
|
||||
onUpdateTrigger={updateTrigger}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<WorkflowEditActionForm
|
||||
action={stepDefinition.definition}
|
||||
onUpdateAction={updateStep}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,24 +1,12 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { RightDrawerWorkflowSelectActionContent } from '@/workflow/components/RightDrawerWorkflowSelectActionContent';
|
||||
import { showPageWorkflowIdState } from '@/workflow/states/showPageWorkflowIdState';
|
||||
import { Workflow } from '@/workflow/types/Workflow';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const RightDrawerWorkflowSelectAction = () => {
|
||||
const showPageWorkflowId = useRecoilValue(showPageWorkflowIdState);
|
||||
|
||||
const { record: workflow } = useFindOneRecord<Workflow>({
|
||||
objectNameSingular: CoreObjectNameSingular.Workflow,
|
||||
objectRecordId: showPageWorkflowId,
|
||||
recordGqlFields: {
|
||||
id: true,
|
||||
name: true,
|
||||
versions: true,
|
||||
publishedVersionId: true,
|
||||
},
|
||||
});
|
||||
const workflowId = useRecoilValue(workflowIdState);
|
||||
const workflow = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
if (!isDefined(workflow)) {
|
||||
return null;
|
||||
|
||||
@ -1,19 +1,9 @@
|
||||
import { TabList } from '@/ui/layout/tab/components/TabList';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useRightDrawerWorkflowSelectAction } from '@/workflow/hooks/useRightDrawerWorkflowSelectAction';
|
||||
import { Workflow } from '@/workflow/types/Workflow';
|
||||
import { ACTIONS } from '@/workflow/constants/Actions';
|
||||
import { useCreateStep } from '@/workflow/hooks/useCreateStep';
|
||||
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
// FIXME: copy-pasted
|
||||
const StyledTabListContainer = styled.div`
|
||||
align-items: center;
|
||||
border-bottom: ${({ theme }) => `1px solid ${theme.border.color.light}`};
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
height: 40px;
|
||||
`;
|
||||
|
||||
const StyledActionListContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -24,33 +14,24 @@ const StyledActionListContainer = styled.div`
|
||||
padding-inline: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const TAB_LIST_COMPONENT_ID =
|
||||
'workflow-select-action-page-right-tab-list';
|
||||
|
||||
export const RightDrawerWorkflowSelectActionContent = ({
|
||||
workflow,
|
||||
}: {
|
||||
workflow: Workflow;
|
||||
workflow: WorkflowWithCurrentVersion;
|
||||
}) => {
|
||||
const tabListId = `${TAB_LIST_COMPONENT_ID}`;
|
||||
|
||||
const { tabs, options, handleActionClick } =
|
||||
useRightDrawerWorkflowSelectAction({ tabListId, workflow });
|
||||
const { createStep } = useCreateStep({
|
||||
workflow,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTabListContainer>
|
||||
<TabList loading={false} tabListId={tabListId} tabs={tabs} />
|
||||
</StyledTabListContainer>
|
||||
|
||||
<StyledActionListContainer>
|
||||
{options.map((option) => (
|
||||
{ACTIONS.map((action) => (
|
||||
<MenuItem
|
||||
key={option.id}
|
||||
LeftIcon={option.icon}
|
||||
text={option.name}
|
||||
LeftIcon={action.icon}
|
||||
text={action.label}
|
||||
onClick={() => {
|
||||
handleActionClick(option.id);
|
||||
return createStep(action.type);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { WorkflowShowPageDiagramCreateStepNode } from '@/workflow/components/WorkflowShowPageDiagramCreateStepNode';
|
||||
import { WorkflowShowPageDiagramEffect } from '@/workflow/components/WorkflowShowPageDiagramEffect';
|
||||
import { WorkflowShowPageDiagramStepNode } from '@/workflow/components/WorkflowShowPageDiagramStepNode';
|
||||
import { showPageWorkflowDiagramState } from '@/workflow/states/showPageWorkflowDiagramState';
|
||||
import { WorkflowDiagramCanvasEffect } from '@/workflow/components/WorkflowDiagramCanvasEffect';
|
||||
import { WorkflowDiagramCreateStepNode } from '@/workflow/components/WorkflowDiagramCreateStepNode';
|
||||
import { WorkflowDiagramStepNode } from '@/workflow/components/WorkflowDiagramStepNode';
|
||||
import { workflowDiagramState } from '@/workflow/states/workflowDiagramState';
|
||||
import {
|
||||
WorkflowDiagram,
|
||||
WorkflowDiagramEdge,
|
||||
@ -21,7 +21,7 @@ import { useMemo } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { GRAY_SCALE, isDefined } from 'twenty-ui';
|
||||
|
||||
export const WorkflowShowPageDiagram = ({
|
||||
export const WorkflowDiagramCanvas = ({
|
||||
diagram,
|
||||
}: {
|
||||
diagram: WorkflowDiagram;
|
||||
@ -31,14 +31,12 @@ export const WorkflowShowPageDiagram = ({
|
||||
[diagram],
|
||||
);
|
||||
|
||||
const setShowPageWorkflowDiagram = useSetRecoilState(
|
||||
showPageWorkflowDiagramState,
|
||||
);
|
||||
const setWorkflowDiagram = useSetRecoilState(workflowDiagramState);
|
||||
|
||||
const handleNodesChange = (
|
||||
nodeChanges: Array<NodeChange<WorkflowDiagramNode>>,
|
||||
) => {
|
||||
setShowPageWorkflowDiagram((diagram) => {
|
||||
setWorkflowDiagram((diagram) => {
|
||||
if (isDefined(diagram) === false) {
|
||||
throw new Error(
|
||||
'It must be impossible for the nodes to be updated if the diagram is not defined yet. Be sure the diagram is rendered only when defined.',
|
||||
@ -55,7 +53,7 @@ export const WorkflowShowPageDiagram = ({
|
||||
const handleEdgesChange = (
|
||||
edgeChanges: Array<EdgeChange<WorkflowDiagramEdge>>,
|
||||
) => {
|
||||
setShowPageWorkflowDiagram((diagram) => {
|
||||
setWorkflowDiagram((diagram) => {
|
||||
if (isDefined(diagram) === false) {
|
||||
throw new Error(
|
||||
'It must be impossible for the edges to be updated if the diagram is not defined yet. Be sure the diagram is rendered only when defined.',
|
||||
@ -72,8 +70,8 @@ export const WorkflowShowPageDiagram = ({
|
||||
return (
|
||||
<ReactFlow
|
||||
nodeTypes={{
|
||||
default: WorkflowShowPageDiagramStepNode,
|
||||
'create-step': WorkflowShowPageDiagramCreateStepNode,
|
||||
default: WorkflowDiagramStepNode,
|
||||
'create-step': WorkflowDiagramCreateStepNode,
|
||||
}}
|
||||
fitView
|
||||
nodes={nodes.map((node) => ({ ...node, draggable: false }))}
|
||||
@ -81,7 +79,7 @@ export const WorkflowShowPageDiagram = ({
|
||||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={handleEdgesChange}
|
||||
>
|
||||
<WorkflowShowPageDiagramEffect />
|
||||
<WorkflowDiagramCanvasEffect />
|
||||
|
||||
<Background color={GRAY_SCALE.gray25} size={2} />
|
||||
</ReactFlow>
|
||||
@ -1,8 +1,8 @@
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||
import { useStartNodeCreation } from '@/workflow/hooks/useStartNodeCreation';
|
||||
import { showPageWorkflowDiagramTriggerNodeSelectionState } from '@/workflow/states/showPageWorkflowDiagramTriggerNodeSelectionState';
|
||||
import { showPageWorkflowSelectedNodeState } from '@/workflow/states/showPageWorkflowSelectedNodeState';
|
||||
import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/states/workflowDiagramTriggerNodeSelectionState';
|
||||
import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
|
||||
import {
|
||||
WorkflowDiagramEdge,
|
||||
WorkflowDiagramNode,
|
||||
@ -16,18 +16,16 @@ import { useCallback, useEffect } from 'react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const WorkflowShowPageDiagramEffect = () => {
|
||||
export const WorkflowDiagramCanvasEffect = () => {
|
||||
const reactflow = useReactFlow<WorkflowDiagramNode, WorkflowDiagramEdge>();
|
||||
|
||||
const { startNodeCreation } = useStartNodeCreation();
|
||||
|
||||
const { openRightDrawer, closeRightDrawer } = useRightDrawer();
|
||||
const setShowPageWorkflowSelectedNode = useSetRecoilState(
|
||||
showPageWorkflowSelectedNodeState,
|
||||
);
|
||||
const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState);
|
||||
|
||||
const showPageWorkflowDiagramTriggerNodeSelection = useRecoilValue(
|
||||
showPageWorkflowDiagramTriggerNodeSelectionState,
|
||||
const workflowDiagramTriggerNodeSelection = useRecoilValue(
|
||||
workflowDiagramTriggerNodeSelectionState,
|
||||
);
|
||||
|
||||
const handleSelectionChange = useCallback(
|
||||
@ -52,13 +50,13 @@ export const WorkflowShowPageDiagramEffect = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
setShowPageWorkflowSelectedNode(selectedNode.id);
|
||||
setWorkflowSelectedNode(selectedNode.id);
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit);
|
||||
},
|
||||
[
|
||||
closeRightDrawer,
|
||||
openRightDrawer,
|
||||
setShowPageWorkflowSelectedNode,
|
||||
setWorkflowSelectedNode,
|
||||
startNodeCreation,
|
||||
],
|
||||
);
|
||||
@ -68,14 +66,14 @@ export const WorkflowShowPageDiagramEffect = () => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDefined(showPageWorkflowDiagramTriggerNodeSelection)) {
|
||||
if (!isDefined(workflowDiagramTriggerNodeSelection)) {
|
||||
return;
|
||||
}
|
||||
|
||||
reactflow.updateNode(showPageWorkflowDiagramTriggerNodeSelection, {
|
||||
reactflow.updateNode(workflowDiagramTriggerNodeSelection, {
|
||||
selected: true,
|
||||
});
|
||||
}, [reactflow, showPageWorkflowDiagramTriggerNodeSelection]);
|
||||
}, [reactflow, workflowDiagramTriggerNodeSelection]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@ -7,7 +7,7 @@ export const StyledTargetHandle = styled(Handle)`
|
||||
visibility: hidden;
|
||||
`;
|
||||
|
||||
export const WorkflowShowPageDiagramCreateStepNode = () => {
|
||||
export const WorkflowDiagramCreateStepNode = () => {
|
||||
return (
|
||||
<>
|
||||
<StyledTargetHandle type="target" position={Position.Top} />
|
||||
@ -64,7 +64,7 @@ export const StyledTargetHandle = styled(Handle)`
|
||||
visibility: hidden;
|
||||
`;
|
||||
|
||||
export const WorkflowShowPageDiagramStepNode = ({
|
||||
export const WorkflowDiagramStepNode = ({
|
||||
data,
|
||||
}: {
|
||||
data: WorkflowDiagramStepNodeData;
|
||||
@ -0,0 +1,103 @@
|
||||
import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions';
|
||||
import { Select, SelectOption } from '@/ui/input/components/Select';
|
||||
import { WorkflowAction } from '@/workflow/types/Workflow';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { IconCode, isDefined } from 'twenty-ui';
|
||||
|
||||
const StyledTriggerHeader = styled.div`
|
||||
background-color: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => theme.spacing(6)};
|
||||
`;
|
||||
|
||||
const StyledTriggerHeaderTitle = styled.p`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
font-size: ${({ theme }) => theme.font.size.xl};
|
||||
|
||||
margin: ${({ theme }) => theme.spacing(3)} 0;
|
||||
`;
|
||||
|
||||
const StyledTriggerHeaderType = styled.p`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const StyledTriggerHeaderIconContainer = styled.div`
|
||||
align-self: flex-start;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.xs};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledTriggerSettings = styled.div`
|
||||
padding: ${({ theme }) => theme.spacing(6)};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
export const WorkflowEditActionForm = ({
|
||||
action,
|
||||
onUpdateAction,
|
||||
}: {
|
||||
action: WorkflowAction;
|
||||
onUpdateAction: (trigger: WorkflowAction) => void;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const { serverlessFunctions } = useGetManyServerlessFunctions();
|
||||
|
||||
const availableFunctions: Array<SelectOption<string>> = [
|
||||
{ label: 'None', value: '' },
|
||||
...serverlessFunctions
|
||||
.filter((serverlessFunction) =>
|
||||
isDefined(serverlessFunction.latestVersion),
|
||||
)
|
||||
.map((serverlessFunction) => ({
|
||||
label: serverlessFunction.name,
|
||||
value: serverlessFunction.id,
|
||||
})),
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTriggerHeader>
|
||||
<StyledTriggerHeaderIconContainer>
|
||||
<IconCode color={theme.color.orange} />
|
||||
</StyledTriggerHeaderIconContainer>
|
||||
|
||||
<StyledTriggerHeaderTitle>
|
||||
Code - Serverless Function
|
||||
</StyledTriggerHeaderTitle>
|
||||
|
||||
<StyledTriggerHeaderType>Code</StyledTriggerHeaderType>
|
||||
</StyledTriggerHeader>
|
||||
|
||||
<StyledTriggerSettings>
|
||||
<Select
|
||||
dropdownId="workflow-edit-action-function"
|
||||
label="Function"
|
||||
fullWidth
|
||||
value={action.settings.serverlessFunctionId}
|
||||
options={availableFunctions}
|
||||
onChange={(updatedFunction) => {
|
||||
onUpdateAction({
|
||||
...action,
|
||||
settings: {
|
||||
...action.settings,
|
||||
serverlessFunctionId: updatedFunction,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</StyledTriggerSettings>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,136 @@
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { Select, SelectOption } from '@/ui/input/components/Select';
|
||||
import { OBJECT_EVENT_TRIGGERS } from '@/workflow/constants/ObjectEventTriggers';
|
||||
import { WorkflowTrigger } from '@/workflow/types/Workflow';
|
||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { IconPlaylistAdd, isDefined } from 'twenty-ui';
|
||||
|
||||
const StyledTriggerHeader = styled.div`
|
||||
background-color: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${({ theme }) => theme.spacing(6)};
|
||||
`;
|
||||
|
||||
const StyledTriggerHeaderTitle = styled.p`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
font-size: ${({ theme }) => theme.font.size.xl};
|
||||
|
||||
margin: ${({ theme }) => theme.spacing(3)} 0;
|
||||
`;
|
||||
|
||||
const StyledTriggerHeaderType = styled.p`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const StyledTriggerHeaderIconContainer = styled.div`
|
||||
align-self: flex-start;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.xs};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledTriggerSettings = styled.div`
|
||||
padding: ${({ theme }) => theme.spacing(6)};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
export const WorkflowEditTriggerForm = ({
|
||||
trigger,
|
||||
onUpdateTrigger,
|
||||
}: {
|
||||
trigger: WorkflowTrigger;
|
||||
onUpdateTrigger: (trigger: WorkflowTrigger) => void;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
|
||||
const triggerEvent = splitWorkflowTriggerEventName(
|
||||
trigger.settings.eventName,
|
||||
);
|
||||
|
||||
const availableMetadata: Array<SelectOption<string>> =
|
||||
activeObjectMetadataItems.map((item) => ({
|
||||
label: item.labelPlural,
|
||||
value: item.nameSingular,
|
||||
}));
|
||||
const recordTypeMetadata = activeObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === triggerEvent.objectType,
|
||||
);
|
||||
if (!isDefined(recordTypeMetadata)) {
|
||||
throw new Error(
|
||||
'Expected to find the metadata configuration for the currently selected record type of the trigger.',
|
||||
);
|
||||
}
|
||||
|
||||
const selectedEvent = OBJECT_EVENT_TRIGGERS.find(
|
||||
(availableEvent) => availableEvent.value === triggerEvent.event,
|
||||
);
|
||||
if (!isDefined(selectedEvent)) {
|
||||
throw new Error('Expected to find the currently selected event type.');
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTriggerHeader>
|
||||
<StyledTriggerHeaderIconContainer>
|
||||
<IconPlaylistAdd color={theme.font.color.tertiary} />
|
||||
</StyledTriggerHeaderIconContainer>
|
||||
|
||||
<StyledTriggerHeaderTitle>
|
||||
When a {recordTypeMetadata.labelSingular} is {selectedEvent.label}
|
||||
</StyledTriggerHeaderTitle>
|
||||
|
||||
<StyledTriggerHeaderType>
|
||||
Trigger . Record is {selectedEvent.label}
|
||||
</StyledTriggerHeaderType>
|
||||
</StyledTriggerHeader>
|
||||
|
||||
<StyledTriggerSettings>
|
||||
<Select
|
||||
dropdownId="workflow-edit-trigger-record-type"
|
||||
label="Record Type"
|
||||
fullWidth
|
||||
value={triggerEvent.objectType}
|
||||
options={availableMetadata}
|
||||
onChange={(updatedRecordType) => {
|
||||
onUpdateTrigger({
|
||||
...trigger,
|
||||
settings: {
|
||||
...trigger.settings,
|
||||
eventName: `${updatedRecordType}.${triggerEvent.event}`,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
dropdownId="workflow-edit-trigger-event-type"
|
||||
label="Event type"
|
||||
fullWidth
|
||||
value={triggerEvent.event}
|
||||
options={OBJECT_EVENT_TRIGGERS}
|
||||
onChange={(updatedEvent) => {
|
||||
onUpdateTrigger({
|
||||
...trigger,
|
||||
settings: {
|
||||
...trigger.settings,
|
||||
eventName: `${triggerEvent.objectType}.${updatedEvent}`,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</StyledTriggerSettings>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,12 +1,8 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { showPageWorkflowDiagramState } from '@/workflow/states/showPageWorkflowDiagramState';
|
||||
import { showPageWorkflowErrorState } from '@/workflow/states/showPageWorkflowErrorState';
|
||||
import { showPageWorkflowIdState } from '@/workflow/states/showPageWorkflowIdState';
|
||||
import { showPageWorkflowLoadingState } from '@/workflow/states/showPageWorkflowLoadingState';
|
||||
import { Workflow } from '@/workflow/types/Workflow';
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { workflowDiagramState } from '@/workflow/states/workflowDiagramState';
|
||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||
import { addCreateStepNodes } from '@/workflow/utils/addCreateStepNodes';
|
||||
import { getWorkflowLastDiagramVersion } from '@/workflow/utils/getWorkflowLastDiagramVersion';
|
||||
import { getWorkflowVersionDiagram } from '@/workflow/utils/getWorkflowVersionDiagram';
|
||||
import { useEffect } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
@ -18,50 +14,29 @@ type WorkflowShowPageEffectProps = {
|
||||
export const WorkflowShowPageEffect = ({
|
||||
workflowId,
|
||||
}: WorkflowShowPageEffectProps) => {
|
||||
const {
|
||||
record: workflow,
|
||||
loading,
|
||||
error,
|
||||
} = useFindOneRecord<Workflow>({
|
||||
objectNameSingular: CoreObjectNameSingular.Workflow,
|
||||
objectRecordId: workflowId,
|
||||
recordGqlFields: {
|
||||
id: true,
|
||||
name: true,
|
||||
versions: true,
|
||||
publishedVersionId: true,
|
||||
},
|
||||
});
|
||||
const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
const setShowPageWorkflowId = useSetRecoilState(showPageWorkflowIdState);
|
||||
const setCurrentWorkflowData = useSetRecoilState(
|
||||
showPageWorkflowDiagramState,
|
||||
);
|
||||
const setCurrentWorkflowLoading = useSetRecoilState(
|
||||
showPageWorkflowLoadingState,
|
||||
);
|
||||
const setCurrentWorkflowError = useSetRecoilState(showPageWorkflowErrorState);
|
||||
const setWorkflowId = useSetRecoilState(workflowIdState);
|
||||
const setWorkflowDiagram = useSetRecoilState(workflowDiagramState);
|
||||
|
||||
useEffect(() => {
|
||||
setShowPageWorkflowId(workflowId);
|
||||
}, [setShowPageWorkflowId, workflowId]);
|
||||
setWorkflowId(workflowId);
|
||||
}, [setWorkflowId, workflowId]);
|
||||
|
||||
useEffect(() => {
|
||||
const flowLastVersion = getWorkflowLastDiagramVersion(workflow);
|
||||
const flowWithCreateStepNodes = addCreateStepNodes(flowLastVersion);
|
||||
const currentVersion = workflowWithCurrentVersion?.currentVersion;
|
||||
if (!isDefined(currentVersion)) {
|
||||
setWorkflowDiagram(undefined);
|
||||
|
||||
setCurrentWorkflowData(
|
||||
isDefined(workflow) ? flowWithCreateStepNodes : undefined,
|
||||
);
|
||||
}, [setCurrentWorkflowData, workflow]);
|
||||
return;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentWorkflowLoading(loading);
|
||||
}, [loading, setCurrentWorkflowLoading]);
|
||||
const lastWorkflowDiagram = getWorkflowVersionDiagram(currentVersion);
|
||||
const workflowDiagramWithCreateStepNodes =
|
||||
addCreateStepNodes(lastWorkflowDiagram);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentWorkflowError(error);
|
||||
}, [error, setCurrentWorkflowError]);
|
||||
setWorkflowDiagram(workflowDiagramWithCreateStepNodes);
|
||||
}, [setWorkflowDiagram, workflowWithCurrentVersion?.currentVersion]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user