Continue workflow folders re-ordering (#9315)
- put actions in steps - move hooks - move states
This commit is contained in:
+16
@@ -0,0 +1,16 @@
|
||||
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
|
||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||
import { RightDrawerWorkflowEditStepContent } from '@/workflow/workflow-steps/components/RightDrawerWorkflowEditStepContent';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const RightDrawerWorkflowEditStep = () => {
|
||||
const workflowId = useRecoilValue(workflowIdState);
|
||||
const workflow = useWorkflowWithCurrentVersion(workflowId);
|
||||
|
||||
if (!isDefined(workflow)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <RightDrawerWorkflowEditStepContent workflow={workflow} />;
|
||||
};
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
|
||||
import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail';
|
||||
import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep';
|
||||
import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
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 } = useUpdateStep({
|
||||
workflow,
|
||||
});
|
||||
|
||||
return (
|
||||
<WorkflowStepDetail
|
||||
stepId={workflowSelectedNode}
|
||||
workflowVersion={workflow.currentVersion}
|
||||
onActionUpdate={updateStep}
|
||||
onTriggerUpdate={updateTrigger}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { useWorkflowVersion } from '@/workflow/hooks/useWorkflowVersion';
|
||||
import { workflowVersionIdState } from '@/workflow/states/workflowVersionIdState';
|
||||
import { RightDrawerWorkflowViewStepContent } from '@/workflow/workflow-steps/components/RightDrawerWorkflowViewStepContent';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const RightDrawerWorkflowViewStep = () => {
|
||||
const workflowVersionId = useRecoilValue(workflowVersionIdState);
|
||||
if (!isDefined(workflowVersionId)) {
|
||||
throw new Error('Expected a workflow version id');
|
||||
}
|
||||
|
||||
const workflowVersion = useWorkflowVersion(workflowVersionId);
|
||||
|
||||
if (!isDefined(workflowVersion)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<RightDrawerWorkflowViewStepContent workflowVersion={workflowVersion} />
|
||||
);
|
||||
};
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
|
||||
import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
export const RightDrawerWorkflowViewStepContent = ({
|
||||
workflowVersion,
|
||||
}: {
|
||||
workflowVersion: WorkflowVersion;
|
||||
}) => {
|
||||
const workflowSelectedNode = useRecoilValue(workflowSelectedNodeState);
|
||||
if (!isDefined(workflowSelectedNode)) {
|
||||
throw new Error(
|
||||
'Expected a node to be selected. Selecting a node is mandatory to edit it.',
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<WorkflowStepDetail
|
||||
stepId={workflowSelectedNode}
|
||||
workflowVersion={workflowVersion}
|
||||
readonly
|
||||
/>
|
||||
);
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledWorkflowStepBody = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: scroll;
|
||||
padding: ${({ theme }) => theme.spacing(6)};
|
||||
row-gap: ${({ theme }) => theme.spacing(6)};
|
||||
flex: 1 1 auto;
|
||||
`;
|
||||
|
||||
export { StyledWorkflowStepBody as WorkflowStepBody };
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
import {
|
||||
WorkflowAction,
|
||||
WorkflowTrigger,
|
||||
WorkflowVersion,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
|
||||
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
|
||||
import { WorkflowEditActionFormCreateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormCreateRecord';
|
||||
import { WorkflowEditActionFormDeleteRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormDeleteRecord';
|
||||
import { WorkflowEditActionFormSendEmail } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormSendEmail';
|
||||
import { WorkflowEditActionFormUpdateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormUpdateRecord';
|
||||
import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm';
|
||||
import { WorkflowEditTriggerManualForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerManualForm';
|
||||
import { Suspense, lazy } from 'react';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
import { RightDrawerSkeletonLoader } from '~/loading/components/RightDrawerSkeletonLoader';
|
||||
|
||||
const WorkflowEditActionFormServerlessFunction = lazy(() =>
|
||||
import(
|
||||
'@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunction'
|
||||
).then((module) => ({
|
||||
default: module.WorkflowEditActionFormServerlessFunction,
|
||||
})),
|
||||
);
|
||||
|
||||
type WorkflowStepDetailProps =
|
||||
| {
|
||||
stepId: string;
|
||||
workflowVersion: WorkflowVersion;
|
||||
readonly: true;
|
||||
onTriggerUpdate?: undefined;
|
||||
onActionUpdate?: undefined;
|
||||
}
|
||||
| {
|
||||
stepId: string;
|
||||
workflowVersion: WorkflowVersion;
|
||||
readonly?: false;
|
||||
onTriggerUpdate: (trigger: WorkflowTrigger) => void;
|
||||
onActionUpdate: (action: WorkflowAction) => void;
|
||||
};
|
||||
|
||||
export const WorkflowStepDetail = ({
|
||||
stepId,
|
||||
workflowVersion,
|
||||
...props
|
||||
}: WorkflowStepDetailProps) => {
|
||||
const stepDefinition = getStepDefinitionOrThrow({
|
||||
stepId,
|
||||
workflowVersion,
|
||||
});
|
||||
if (!isDefined(stepDefinition)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (stepDefinition.type) {
|
||||
case 'trigger': {
|
||||
if (!isDefined(stepDefinition.definition)) {
|
||||
throw new Error(
|
||||
'Expected the trigger to be defined at this point. Ensure the trigger has been set with a default value before trying to edit it.',
|
||||
);
|
||||
}
|
||||
|
||||
switch (stepDefinition.definition.type) {
|
||||
case 'DATABASE_EVENT': {
|
||||
return (
|
||||
<WorkflowEditTriggerDatabaseEventForm
|
||||
trigger={stepDefinition.definition}
|
||||
triggerOptions={props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'MANUAL': {
|
||||
return (
|
||||
<WorkflowEditTriggerManualForm
|
||||
trigger={stepDefinition.definition}
|
||||
triggerOptions={props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return assertUnreachable(
|
||||
stepDefinition.definition,
|
||||
`Expected the step to have an handler; ${JSON.stringify(stepDefinition)}`,
|
||||
);
|
||||
}
|
||||
case 'action': {
|
||||
switch (stepDefinition.definition.type) {
|
||||
case 'CODE': {
|
||||
return (
|
||||
<Suspense fallback={<RightDrawerSkeletonLoader />}>
|
||||
<WorkflowEditActionFormServerlessFunction
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
case 'SEND_EMAIL': {
|
||||
return (
|
||||
<WorkflowEditActionFormSendEmail
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'CREATE_RECORD': {
|
||||
return (
|
||||
<WorkflowEditActionFormCreateRecord
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
case 'UPDATE_RECORD': {
|
||||
return (
|
||||
<WorkflowEditActionFormUpdateRecord
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
case 'DELETE_RECORD': {
|
||||
return (
|
||||
<WorkflowEditActionFormDeleteRecord
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return assertUnreachable(
|
||||
stepDefinition,
|
||||
`Expected the step to have an handler; ${JSON.stringify(stepDefinition)}`,
|
||||
);
|
||||
};
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||
import styled from '@emotion/styled';
|
||||
import React, { useState } from 'react';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { IconComponent } from 'packages/twenty-ui';
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
background-color: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledHeaderInfo = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledHeaderTitle = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
font-size: ${({ theme }) => theme.font.size.xl};
|
||||
width: 420px;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledHeaderType = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledHeaderIconContainer = 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.sm};
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const WorkflowStepHeader = ({
|
||||
onTitleChange,
|
||||
Icon,
|
||||
iconColor,
|
||||
initialTitle,
|
||||
headerType,
|
||||
}: {
|
||||
onTitleChange: (newTitle: string) => void;
|
||||
Icon: IconComponent;
|
||||
iconColor: string;
|
||||
initialTitle: string;
|
||||
headerType: string;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [title, setTitle] = useState(initialTitle);
|
||||
const debouncedOnTitleChange = useDebouncedCallback(onTitleChange, 100);
|
||||
const handleChange = (newTitle: string) => {
|
||||
setTitle(newTitle);
|
||||
debouncedOnTitleChange(newTitle);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledHeader>
|
||||
<StyledHeaderIconContainer>
|
||||
{
|
||||
<Icon
|
||||
color={iconColor}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
size={theme.icon.size.lg}
|
||||
/>
|
||||
}
|
||||
</StyledHeaderIconContainer>
|
||||
<StyledHeaderInfo>
|
||||
<StyledHeaderTitle>
|
||||
<TextInput
|
||||
value={title}
|
||||
copyButton={false}
|
||||
hotkeyScope="workflow-step-title"
|
||||
onEnter={onTitleChange}
|
||||
onEscape={onTitleChange}
|
||||
onChange={handleChange}
|
||||
shouldTrim={false}
|
||||
/>
|
||||
</StyledHeaderTitle>
|
||||
<StyledHeaderType>{headerType}</StyledHeaderType>
|
||||
</StyledHeaderInfo>
|
||||
</StyledHeader>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user