-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Visualize Workflows #6697
Visualize Workflows #6697
Changes from all commits
3908549
9d33288
ef3b409
c85f43e
e546d19
140a84d
1913265
510741e
041a40f
8fc7b10
1a1a3d8
1926923
cfc3af1
aa69097
554b54b
20bcee7
bdf0677
7685838
9119dce
b9544af
bec87c5
fc23fe9
e97e2fc
a5354c8
b1b24c1
02b3939
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ | |
"workerDirectory": "public" | ||
}, | ||
"dependencies": { | ||
"@xyflow/react": "^12.0.4", | ||
"transliteration": "^2.3.5" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import styled from '@emotion/styled'; | ||
|
||
const StyledContainer = styled.div` | ||
box-sizing: border-box; | ||
display: flex; | ||
flex-direction: column; | ||
height: 100%; | ||
justify-content: flex-start; | ||
overflow-y: auto; | ||
position: relative; | ||
`; | ||
|
||
const StyledChatArea = styled.div` | ||
flex: 1; | ||
display: flex; | ||
flex-direction: column; | ||
overflow-y: scroll; | ||
padding: ${({ theme }) => theme.spacing(6)}; | ||
padding-bottom: 0px; | ||
`; | ||
|
||
const StyledNewMessageArea = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
padding: ${({ theme }) => theme.spacing(6)}; | ||
padding-top: 0px; | ||
`; | ||
|
||
export const RightDrawerWorkflow = () => { | ||
const handleCreateCodeBlock = () => {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: handleCreateCodeBlock is empty. Implement the logic for creating a code block. |
||
|
||
return ( | ||
<StyledContainer> | ||
<StyledChatArea>{/* TODO */}</StyledChatArea> | ||
<StyledNewMessageArea> | ||
<button onClick={handleCreateCodeBlock}>Create code block</button> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Consider using a more specific button component from your UI library instead of a plain HTML button. |
||
</StyledNewMessageArea> | ||
</StyledContainer> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { WorkflowShowPageDiagramCreateStepNode } from '@/workflow/components/WorkflowShowPageDiagramCreateStepNode.tsx'; | ||
import { WorkflowShowPageDiagramStepNode } from '@/workflow/components/WorkflowShowPageDiagramStepNode'; | ||
import { showPageWorkflowDiagramState } from '@/workflow/states/showPageWorkflowDiagramState'; | ||
import { | ||
WorkflowDiagram, | ||
WorkflowDiagramEdge, | ||
WorkflowDiagramNode, | ||
} from '@/workflow/types/WorkflowDiagram'; | ||
import { getOrganizedDiagram } from '@/workflow/utils/getOrganizedDiagram'; | ||
import { | ||
applyEdgeChanges, | ||
applyNodeChanges, | ||
Background, | ||
EdgeChange, | ||
NodeChange, | ||
ReactFlow, | ||
} from '@xyflow/react'; | ||
import '@xyflow/react/dist/style.css'; | ||
import { useMemo } from 'react'; | ||
import { useSetRecoilState } from 'recoil'; | ||
import { GRAY_SCALE, isDefined } from 'twenty-ui'; | ||
|
||
export const WorkflowShowPageDiagram = ({ | ||
diagram, | ||
}: { | ||
diagram: WorkflowDiagram; | ||
}) => { | ||
const { nodes, edges } = useMemo( | ||
() => getOrganizedDiagram(diagram), | ||
[diagram], | ||
); | ||
|
||
const setShowPageWorkflowDiagram = useSetRecoilState( | ||
showPageWorkflowDiagramState, | ||
); | ||
|
||
const handleNodesChange = ( | ||
nodeChanges: Array<NodeChange<WorkflowDiagramNode>>, | ||
) => { | ||
setShowPageWorkflowDiagram((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.', | ||
); | ||
} | ||
|
||
return { | ||
...diagram, | ||
nodes: applyNodeChanges(nodeChanges, diagram.nodes), | ||
}; | ||
}); | ||
}; | ||
|
||
const handleEdgesChange = ( | ||
edgeChanges: Array<EdgeChange<WorkflowDiagramEdge>>, | ||
) => { | ||
setShowPageWorkflowDiagram((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.', | ||
); | ||
} | ||
|
||
return { | ||
...diagram, | ||
edges: applyEdgeChanges(edgeChanges, diagram.edges), | ||
}; | ||
}); | ||
}; | ||
|
||
return ( | ||
<ReactFlow | ||
nodeTypes={{ | ||
default: WorkflowShowPageDiagramStepNode, | ||
'create-step': WorkflowShowPageDiagramCreateStepNode, | ||
}} | ||
fitView | ||
nodes={nodes.map((node) => ({ ...node, draggable: false }))} | ||
edges={edges} | ||
onNodesChange={handleNodesChange} | ||
onEdgesChange={handleEdgesChange} | ||
> | ||
<Background color={GRAY_SCALE.gray25} size={2} /> | ||
</ReactFlow> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { IconButton } from '@/ui/input/button/components/IconButton'; | ||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer'; | ||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages'; | ||
import styled from '@emotion/styled'; | ||
import { Handle, Position } from '@xyflow/react'; | ||
import { IconPlus } from 'twenty-ui'; | ||
|
||
export const StyledTargetHandle = styled(Handle)` | ||
visibility: hidden; | ||
`; | ||
|
||
export const WorkflowShowPageDiagramCreateStepNode = () => { | ||
const { openRightDrawer } = useRightDrawer(); | ||
|
||
const handleCreateStepNodeButtonClick = () => { | ||
openRightDrawer(RightDrawerPages.Workflow); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<StyledTargetHandle type="target" position={Position.Top} /> | ||
|
||
<IconButton Icon={IconPlus} onClick={handleCreateStepNodeButtonClick} /> | ||
</div> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { WorkflowDiagramStepNodeData } from '@/workflow/types/WorkflowDiagram'; | ||
import styled from '@emotion/styled'; | ||
import { Handle, Position } from '@xyflow/react'; | ||
|
||
const StyledStepNodeContainer = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
|
||
padding-bottom: 12px; | ||
padding-top: 6px; | ||
`; | ||
|
||
const StyledStepNodeType = styled.div` | ||
background-color: ${({ theme }) => theme.background.tertiary}; | ||
border-radius: ${({ theme }) => theme.border.radius.sm} | ||
${({ theme }) => theme.border.radius.sm} 0 0; | ||
|
||
color: ${({ theme }) => theme.color.gray50}; | ||
font-size: ${({ theme }) => theme.font.size.xs}; | ||
font-weight: ${({ theme }) => theme.font.weight.semiBold}; | ||
|
||
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)}; | ||
position: absolute; | ||
top: 0; | ||
transform: translateY(-100%); | ||
|
||
.selectable.selected &, | ||
.selectable:focus &, | ||
.selectable:focus-visible & { | ||
background-color: ${({ theme }) => theme.color.blue}; | ||
color: ${({ theme }) => theme.font.color.inverted}; | ||
} | ||
`; | ||
|
||
const StyledStepNodeInnerContainer = styled.div` | ||
background-color: ${({ theme }) => theme.background.secondary}; | ||
border: 1px solid ${({ theme }) => theme.border.color.medium}; | ||
border-radius: ${({ theme }) => theme.border.radius.md}; | ||
display: flex; | ||
gap: ${({ theme }) => theme.spacing(2)}; | ||
padding: ${({ theme }) => theme.spacing(2)}; | ||
|
||
position: relative; | ||
box-shadow: ${({ theme }) => theme.boxShadow.superHeavy}; | ||
|
||
.selectable.selected &, | ||
.selectable:focus &, | ||
.selectable:focus-visible & { | ||
background-color: ${({ theme }) => theme.color.blue10}; | ||
border-color: ${({ theme }) => theme.color.blue}; | ||
} | ||
`; | ||
|
||
const StyledStepNodeLabel = styled.div` | ||
font-size: ${({ theme }) => theme.font.size.md}; | ||
font-weight: ${({ theme }) => theme.font.weight.medium}; | ||
`; | ||
|
||
const StyledSourceHandle = styled(Handle)` | ||
background-color: ${({ theme }) => theme.color.gray50}; | ||
`; | ||
|
||
export const StyledTargetHandle = styled(Handle)` | ||
visibility: hidden; | ||
`; | ||
|
||
export const WorkflowShowPageDiagramStepNode = ({ | ||
data, | ||
}: { | ||
data: WorkflowDiagramStepNodeData; | ||
}) => { | ||
return ( | ||
<StyledStepNodeContainer> | ||
{data.nodeType !== 'trigger' ? ( | ||
<StyledTargetHandle type="target" position={Position.Top} /> | ||
) : null} | ||
|
||
<StyledStepNodeInnerContainer> | ||
<StyledStepNodeType>{data.nodeType}</StyledStepNodeType> | ||
|
||
<StyledStepNodeLabel>{data.label}</StyledStepNodeLabel> | ||
</StyledStepNodeInnerContainer> | ||
|
||
<StyledSourceHandle type="source" position={Position.Bottom} /> | ||
</StyledStepNodeContainer> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introcuce RightDrawerPages.Action which a generic component for any object
and WorkflowNodeEdit / WorflowConditionEdit...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will fix this issue in my next PR as this PR doesn't implement much things about the Right Drawer.
Things that remain to do: