diff --git a/.yarn/patches/css-minimizer-webpack-plugin-npm-1.3.0-c66c75884d.patch b/.yarn/patches/css-minimizer-webpack-plugin-npm-1.3.0-c66c75884d.patch new file mode 100644 index 0000000000..11ff47efd6 --- /dev/null +++ b/.yarn/patches/css-minimizer-webpack-plugin-npm-1.3.0-c66c75884d.patch @@ -0,0 +1,13 @@ +diff --git a/dist/index.js b/dist/index.js +index f50f242ce99f8894457dfe573cb5d525b8cbb800..8aa3b6da76d413834a60c7947e351c994e0a585b 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -278,7 +278,7 @@ class CssMinimizerPlugin { + cssMinimizer: _package.default.version, + 'css-minimizer-webpack-plugin-options': this.options, + name, +- contentHash: _crypto.default.createHash('md4').update(input).digest('hex') ++ contentHash: _crypto.default.createHash('md5').update(input).digest('hex') + }, name); + } + } diff --git a/.yarnrc.yml b/.yarnrc.yml index 81ab91d885..b5ba869a40 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -20,6 +20,9 @@ packageExtensions: '@whatwg-node/fetch@*': peerDependenciesMeta: '@types/node': { optional: true } + 'css-minimizer-webpack-plugin@*': + peerDependencies: + 'clean-css': '*' 'ahooks@*': peerDependencies: '@babel/runtime': '*' diff --git a/package.json b/package.json index 9af9ce7e4e..d110ba1723 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "dependencies": { "@apollo/client": "3.7.*", "@babel/runtime": "^7.23.2", + "@dagrejs/dagre": "^1.1.2", "@date-io/core": "^2.17.0", "@date-io/luxon": "^2.17.0", "@editorjs/checklist": "^1.5.0", @@ -105,6 +106,7 @@ "react-pdf": "^5.7.2", "react-router": "^6.17.0", "react-router-dom": "^6.17.0", + "reactflow": "^11.11.3", "response-time": "^2.3.2", "rifm": "^0.12.1", "serialize-query-params": "^1.3.6", @@ -220,6 +222,7 @@ }, "resolutions": { "@iarna/rtf-to-html/rtf-parser": "patch:rtf-parser@npm:1.3.3#.yarn/patches/rtf-parser-npm-1.3.3-c57888a546.patch", + "css-minimizer-webpack-plugin@npm:^1.2.0": "patch:css-minimizer-webpack-plugin@npm%3A1.3.0#~/.yarn/patches/css-minimizer-webpack-plugin-npm-1.3.0-c66c75884d.patch", "razzle/react-refresh": "^0.13.0", "razzle/@pmmmwh/react-refresh-webpack-plugin": "^0.5.6", "react-dev-utils/browserslist": "^4.14.2", diff --git a/razzle.config.js b/razzle.config.js index c85c5f21f4..1f1dc16090 100644 --- a/razzle.config.js +++ b/razzle.config.js @@ -23,7 +23,10 @@ const modifyWebpackOptions = ({ options.babelRule.include.push( require.resolve('@seedcompany/common').replace('.cjs', '.js'), require.resolve('@editorjs/editorjs').replace('.umd.js', '.mjs'), - (path) => path.includes('/@mui-') + (path) => + path.includes('/@mui-') || + path.includes('reactflow') || + path.includes('dagrejs') ); return options; diff --git a/src/common/transitionTypeStyles.tsx b/src/common/transitionTypeStyles.tsx index 89c74f4ced..76f28f9c3d 100644 --- a/src/common/transitionTypeStyles.tsx +++ b/src/common/transitionTypeStyles.tsx @@ -1,8 +1,8 @@ import { ButtonProps } from '@mui/material'; import { TransitionType } from '~/api/schema/schema.graphql'; -export const transitionTypeStyles: Record = { +export const transitionTypeStyles = { Approve: { color: 'primary', variant: 'contained' }, Neutral: { color: 'secondary', variant: 'text' }, Reject: { color: 'error', variant: 'text' }, -}; +} satisfies Record; diff --git a/src/components/PaperTooltip/PaperTooltip.tsx b/src/components/PaperTooltip/PaperTooltip.tsx index e860ba0cbb..7ce085b702 100644 --- a/src/components/PaperTooltip/PaperTooltip.tsx +++ b/src/components/PaperTooltip/PaperTooltip.tsx @@ -1,17 +1,17 @@ -import { Tooltip } from '@mui/material'; +import { Tooltip, TooltipProps } from '@mui/material'; import { styled } from '@mui/material/styles'; -export const PaperTooltip = styled(Tooltip)( - ({ theme: { palette, spacing, typography, shadows } }) => ({ - tooltip: { - backgroundColor: palette.background.paper, - color: palette.text.primary, - ...typography.body1, - padding: spacing(1), - boxShadow: shadows[8], - }, - arrow: { - color: palette.background.paper, - }, - }) -); +export const PaperTooltip = styled(({ className, ...props }: TooltipProps) => ( + +))(({ theme: { palette, spacing, typography, shadows } }) => ({ + '& .MuiTooltip-tooltip': { + backgroundColor: palette.background.paper, + color: palette.text.primary, + ...typography.body1, + padding: spacing(1), + boxShadow: shadows[8], + }, + '& .MuiTooltip-arrow': { + color: palette.background.paper, + }, +})); diff --git a/src/components/Workflow/Flowchart.tsx b/src/components/Workflow/Flowchart.tsx new file mode 100644 index 0000000000..16fbb39f8d --- /dev/null +++ b/src/components/Workflow/Flowchart.tsx @@ -0,0 +1,111 @@ +import { TypedDocumentNode as DocumentNode, useQuery } from '@apollo/client'; +import { mapEntries } from '@seedcompany/common'; +import { useDebounceFn, useLocalStorageState } from 'ahooks'; +import { OperationDefinitionNode } from 'graphql'; +import { ComponentType, useCallback, useMemo } from 'react'; +import ReactFlow, { + applyNodeChanges, + Background, + Controls, + EdgeTypes, + NodeProps, + OnNodesChange, + ReactFlowProvider, + useEdgesState, + useNodesState, + XYPosition, +} from 'reactflow'; +import { + Edge as EdgeComponent, + FlowchartStyles, + StateNode, + TransitionNode, +} from './nodes'; +import { NodeTypes, parseWorkflow } from './parse-node-edges'; +import { useAutoLayout } from './useAutoLayout'; +import { WorkflowFragment } from './workflow.graphql'; + +interface Props { + doc: DocumentNode<{ workflow: WorkflowFragment }, Record>; +} + +const WrappedFlowchart = (props: Props) => ( + + + +); +// eslint-disable-next-line react/display-name,import/no-default-export +export default WrappedFlowchart; + +const Flowchart = (props: Props) => { + const opName = useMemo( + () => + props.doc.definitions.find( + (d): d is OperationDefinitionNode => d.kind === 'OperationDefinition' + )!.name!.value, + [props.doc] + ); + + const [storedPos, setStoredPos] = useLocalStorageState< + Record + >(`${opName}-flowchart-node-position-map`); + const [nodes, setNodes] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const autoLayout = useAutoLayout(setNodes); + + useQuery(props.doc, { + onCompleted: ({ workflow }) => { + autoLayout.reset(); + const { nodes, edges } = parseWorkflow(workflow); + const persistedPosNodes = nodes.map((node) => + storedPos?.[node.id] ? { ...node, position: storedPos[node.id]! } : node + ); + setNodes(persistedPosNodes); + setEdges(edges); + }, + }); + + const persist = useDebounceFn((nextNodes: typeof nodes) => { + setStoredPos( + mapEntries(nextNodes, ({ id, position }) => [id, position]).asRecord + ); + }); + + const onNodesChange: OnNodesChange = useCallback( + (changes) => { + setNodes((prev) => { + const next = applyNodeChanges(changes, prev); + persist.run(next); + return next; + }); + }, + [setNodes, persist] + ); + + return ( + + + + + + + ); +}; + +export const nodeTypes: Record> = { + state: StateNode, + transition: TransitionNode, +}; + +const edgeTypes = { + default: EdgeComponent, +} satisfies EdgeTypes; diff --git a/src/components/Workflow/layout.ts b/src/components/Workflow/layout.ts new file mode 100644 index 0000000000..cf90092974 --- /dev/null +++ b/src/components/Workflow/layout.ts @@ -0,0 +1,120 @@ +import Dagre from '@dagrejs/dagre'; +import { mapKeys, simpleSwitch } from '@seedcompany/common'; +import { Edge as E, Node as N, Position as Side, XYPosition } from 'reactflow'; +import { NodeTypes } from './parse-node-edges'; +import { + WorkflowStateFragment as State, + WorkflowTransitionFragment as Transition, +} from './workflow.graphql'; + +type Node = N; +type Edge = E; + +export const determinePositions = (nodes: Node[], edges: Edge[]) => { + const g = new Dagre.graphlib.Graph() + .setDefaultEdgeLabel(() => ({})) + .setGraph({ + ranksep: 80, + acyclicer: 'greedy', + }); + + const nodeMap = mapKeys.fromList(nodes, (n) => n.id).asMap; + + nodes.forEach((node) => + g.setNode(node.id, { + ...node, + width: node.width!, + height: node.height!, + }) + ); + edges.forEach((edge) => + g.setEdge(edge.source, edge.target, { + weight: + simpleSwitch(edge.data!.type, { + Approve: 10, + Neutral: 2, + Reject: 1, + })! + (nodeMap.get(edge.source)!.type === 'state' ? 2 : 0), + }) + ); + + Dagre.layout(g); + + const max = { + x: Math.max(...nodes.map((n) => g.node(n.id).x)), + y: Math.max(...nodes.map((n) => g.node(n.id).y)), + }; + + return nodes.map((node) => { + // node position was persisted keep user placement. + if (node.position.x > 0 || node.position.y > 0) { + return node; + } + + const position = g.node(node.id); + // Convert anchor point from dagre to react flow + // center/center -> top/left + let x = position.x - node.width! / 2; + const y = position.y - node.height! / 2; + + // Invert x-axis because for some reason, dagre puts the starting + // point on the right. + x = max.x - x; + + return { ...node, position: { x, y } }; + }); +}; + +/** + * Returns the intersection point of the line between the center of the + * intersectionNode and the target node + */ +export function getNodeIntersection( + intersectionNode: Node, + targetNode: Node, + margin: XYPosition = { x: 10, y: 0 } +) { + // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a + const w = intersectionNode.width! / 2 - margin.x; + const h = intersectionNode.height! / 2 - margin.y; + + const x2 = intersectionNode.positionAbsolute!.x + w + margin.x; + const y2 = intersectionNode.positionAbsolute!.y + h + margin.y; + const x1 = targetNode.positionAbsolute!.x + targetNode.width! / 2; + const y1 = targetNode.positionAbsolute!.y + targetNode.height! / 2; + + const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h); + const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h); + const a = 1 / (Math.abs(xx1) + Math.abs(yy1)); + const xx3 = a * xx1; + const yy3 = a * yy1; + const x = w * (xx3 + yy3) + x2; + const y = h * (-xx3 + yy3) + y2; + + return { x, y }; +} + +/** + * Returns the Side/"Position" of the edge where the intersection point is + */ +export function getEdgeSide(node: Node, intersectsAt: XYPosition) { + const n = { ...node.positionAbsolute, ...node }; + const nx = Math.round(n.x!); + const ny = Math.round(n.y!); + const px = Math.round(intersectsAt.x); + const py = Math.round(intersectsAt.y); + + if (px <= nx + 1) { + return Side.Left; + } + if (px >= nx + n.width! - 1) { + return Side.Right; + } + if (py <= ny + 1) { + return Side.Top; + } + if (py >= n.y! + n.height! - 1) { + return Side.Bottom; + } + return Side.Top; +} diff --git a/src/components/Workflow/nodes.tsx b/src/components/Workflow/nodes.tsx new file mode 100644 index 0000000000..b2b1f136d3 --- /dev/null +++ b/src/components/Workflow/nodes.tsx @@ -0,0 +1,158 @@ +import { Box, Card, CardProps } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import { forwardRef, useCallback } from 'react'; +import { + BaseEdge, + EdgeProps, + getSimpleBezierPath as getPath, + Handle, + NodeProps, + Position, + useStore, +} from 'reactflow'; +import { extendSx } from '~/common'; +import { transitionTypeStyles } from '~/common/transitionTypeStyles'; +import { getEdgeSide, getNodeIntersection } from './layout'; +import { isBack } from './parse-node-edges'; +import { TransitionNodeExtra } from './transition-info'; +import { + WorkflowStateFragment as State, + WorkflowTransitionFragment as Transition, +} from './workflow.graphql'; + +import 'reactflow/dist/style.css'; + +export const FlowchartStyles = styled(Box)(({ theme }) => ({ + height: '100%', + '.react-flow__attribution': { display: 'none' }, + '& .react-flow': { + '.react-flow__edge-textbg': { + fill: theme.palette.background.paper, + }, + '.react-flow__handle': { + opacity: 0, + pointerEvents: 'none', + cursor: 'unset', + }, + '.react-flow__edge': { + '.react-flow__edge-path': { + stroke: 'var(--color)', + strokeWidth: 2, + }, + '&.selected, &:focus, &:focus-visible': { + '.react-flow__edge-path': { + strokeWidth: 3, + }, + }, + }, + }, +})); + +export function StateNode({ data, selected }: NodeProps) { + return ( + <> + + + {data.label} + + + + + ); +} + +export function TransitionNode({ data, selected }: NodeProps) { + const { color } = transitionTypeStyles[data.type]; + const back = isBack(data); + return ( + <> + + + + {data.label} + + + + + ); +} + +const NodeCard = forwardRef< + HTMLDivElement, + CardProps & Pick +>(function NodeCard({ children, selected, color, sx, ...rest }, ref) { + return ( + ({ + transition: theme.transitions.create(['box-shadow', 'border-color'], { + duration: theme.transitions.duration.shorter, + }), + borderColor: selected ? `${color}.dark` : 'transparent', + borderWidth: 1, + borderStyle: 'solid', + p: 2, + bgcolor: `${color}.main`, + color: `${color}.contrastText`, + }), + ...extendSx(sx), + ]} + > + {children} + + ); +}); + +export const Edge = ({ + id, + source, + target, + ...props +}: EdgeProps) => { + const sourceNode = useStore( + useCallback((store) => store.nodeInternals.get(source)!, [source]) + ); + const targetNode = useStore( + useCallback((store) => store.nodeInternals.get(target)!, [target]) + ); + + const sourceIntersectsAt = getNodeIntersection(sourceNode, targetNode); + const targetIntersectsAt = getNodeIntersection(targetNode, sourceNode); + const [path, labelX, labelY, offsetX, offsetY] = getPath({ + sourceX: sourceIntersectsAt.x, + sourceY: sourceIntersectsAt.y, + sourcePosition: getEdgeSide(sourceNode, sourceIntersectsAt), + targetX: targetIntersectsAt.x, + targetY: targetIntersectsAt.y, + targetPosition: getEdgeSide(targetNode, targetIntersectsAt), + }); + const pathProps = { path, labelX, labelY, offsetX, offsetY }; + + const back = sourceNode.type === 'transition' && isBack(sourceNode.data); + const { color } = transitionTypeStyles[props.data!.type]; + return ( + ({ + '--color': back + ? theme.palette.grey[props.selected ? 600 : 300] + : theme.palette[color][props.selected ? 'main' : 'light'], + })} + > + + + ); +}; diff --git a/src/components/Workflow/parse-node-edges.tsx b/src/components/Workflow/parse-node-edges.tsx new file mode 100644 index 0000000000..36e94d7c91 --- /dev/null +++ b/src/components/Workflow/parse-node-edges.tsx @@ -0,0 +1,109 @@ +import { cmpBy } from '@seedcompany/common'; +import { uniqBy } from 'lodash'; +import { Fragment } from 'react'; +import { Edge, Node } from 'reactflow'; +import { LiteralUnion } from 'type-fest'; +import { WorkflowTransitionDynamicTo } from '~/api/schema/schema.graphql'; +import { isTypename } from '~/common'; +import { + WorkflowStateFragment as State, + WorkflowTransitionFragment as Transition, + WorkflowFragment as Workflow, +} from './workflow.graphql'; + +export type NodeTypes = LiteralUnion<'state' | 'transition', string>; + +export function parseWorkflow(workflow: Workflow) { + const states = workflow.states.map( + (state): Node => ({ + id: state.value, + type: 'state', + data: state, + position: { x: 0, y: 0 }, + }) + ); + + const transitionEnds = uniqBy( + workflow.transitions.toSorted( + cmpBy((t) => { + const endState = + t.to.__typename === 'WorkflowTransitionStaticTo' + ? t.to.state.value + : t.to.relatedStates[0]!.value; + return workflow.states.findIndex((e) => e.value === endState); + }) + ), + transitionEndId + ); + const transitionEndNodes = transitionEnds.map( + (t): Node => ({ + id: transitionEndId(t), + type: 'transition', + data: t, + position: { x: 0, y: 0 }, + }) + ); + const nodes = [...states, ...transitionEndNodes].reverse(); + + const edges = uniqBy( + workflow.transitions + .flatMap>((t) => [ + ...t.from.map((from) => ({ + id: `${from.value} -> ${transitionEndId(t)}`, + source: from.value, + target: transitionEndId(t), + targetHandle: 'forward', + label: ( + <> + {t.conditions.map((c) => ( + + {c.label} +
+
+ ))} + + ), + data: t, + })), + ...(isDynamic(t.to) + ? t.to.relatedStates.map((state) => ({ + id: `${transitionEndId(t)} -> ${state.value}`, + source: transitionEndId(t), + target: state.value, + targetHandle: isBack(t) ? 'back' : 'forward', + label: isBack(t) ? 'Back' : undefined, + data: t, + })) + : [ + { + id: `${transitionEndId(t)} -> ${t.to.state.value}`, + source: transitionEndId(t), + target: t.to.state.value, + targetHandle: 'forward', + data: t, + }, + ]), + ]) + .map((e) => ({ + ...e, + animated: e.animated ?? true, + })), + (e) => e.id + ); + + return { nodes, edges }; +} + +export const isBack = (t: Transition) => + isDynamic(t.to) && t.to.label === 'Back'; + +const transitionEndId = (t: Transition) => { + const endId = + t.to.__typename === 'WorkflowTransitionStaticTo' + ? t.to.state.value + : t.to.id; + return `${t.label} -> ${endId}`; +}; +const isDynamic = isTypename( + 'WorkflowTransitionDynamicTo' +); diff --git a/src/components/Workflow/transition-info.tsx b/src/components/Workflow/transition-info.tsx new file mode 100644 index 0000000000..2685ebb30c --- /dev/null +++ b/src/components/Workflow/transition-info.tsx @@ -0,0 +1,136 @@ +import { + Check as CheckIcon, + Circle as CircleIcon, + Notifications as NotificationIcon, + Security as SecurityIcon, + Close as XIcon, +} from '@mui/icons-material'; +import { + ListItemIcon, + ListItemText, + MenuItem, + MenuList, + Stack, + Tooltip, + Typography, +} from '@mui/material'; +import { forwardRef, ReactElement } from 'react'; +import { RoleLabels } from '~/api/schema/enumLists'; +import { PaperTooltip } from '../PaperTooltip'; +import { WorkflowTransitionFragment as Transition } from './workflow.graphql'; + +import 'reactflow/dist/style.css'; + +interface TransitionProp { + transition: Transition; +} + +export const TransitionNodeExtra = ({ + transition, + children, +}: TransitionProp & { + children: ReactElement; +}) => ( + + {children} + +); + +const TransitionNodeExtraMenu = forwardRef( + function TransitionNodeExtraMenu({ ownerState, transition, ...props }, ref) { + return ( + + + + + ); + } +); + +const NotifiersInfo = ({ transition }: TransitionProp) => ( + } + sx={{ + '& .MuiTooltip-tooltip': { + padding: 0, + }, + }} + > + + +); + +const NotifierList = ({ transition }: TransitionProp) => ( + <> + + Notifiers + + + {transition.notifiers.map((notifier) => ( + + {notifier.label} + + ))} + + +); + +const PermissionInfo = ({ transition }: TransitionProp) => ( + } + sx={{ + '& .MuiTooltip-tooltip': { + padding: 0, + }, + }} + > + + +); + +const PermissionPopup = ({ transition }: TransitionProp) => ( + <> + + Permissions + + + {transition.permissions + .filter((p) => p.execute != null) + .map((p) => ( + + + {!p.execute ? ( + + ) : p.condition ? ( + + ) : ( + + )} + + + + ))} + + +); diff --git a/src/components/Workflow/useAutoLayout.ts b/src/components/Workflow/useAutoLayout.ts new file mode 100644 index 0000000000..ba9eafa811 --- /dev/null +++ b/src/components/Workflow/useAutoLayout.ts @@ -0,0 +1,51 @@ +import { useToggle } from 'ahooks'; +import { Dispatch, useCallback, useEffect, useRef } from 'react'; +import { Node, useReactFlow, useStoreApi } from 'reactflow'; +import { Sx } from '~/common'; +import { determinePositions } from './layout'; + +export const useAutoLayout = (setNodes: Dispatch) => { + const api = useStoreApi(); + const { fitView, getNodes, getEdges } = useReactFlow(); + const autoLayoutStage = useRef(0); + const [show, setShow] = useToggle(); + + const reset = useCallback(() => { + autoLayoutStage.current = 0; + }, []); + + useEffect( + () => + api.subscribe((state) => { + if (autoLayoutStage.current === 0 && state.getNodes()[0]?.width) { + // console.log('nodes have sized, positioning'); + autoLayoutStage.current = 1; + const positioned = determinePositions(getNodes(), getEdges()); + setNodes(positioned); + return; + } + + if (autoLayoutStage.current === 1) { + // console.log('nodes have positioned, adjusting view port'); + autoLayoutStage.current = 2; + window.requestAnimationFrame(() => fitView()); + return; + } + + if (autoLayoutStage.current === 2) { + // console.log('viewport adjusted, showing'); + autoLayoutStage.current = 3; + setShow.setRight(); + } + }), + [fitView, api, autoLayoutStage, setShow, getNodes, getEdges, setNodes] + ); + + return { + show, + showSx: !show ? showSx : undefined, + reset, + }; +}; + +const showSx: Sx = { '.react-flow__renderer': { opacity: 0 } }; diff --git a/src/components/Workflow/workflow.graphql b/src/components/Workflow/workflow.graphql new file mode 100644 index 0000000000..7389106e40 --- /dev/null +++ b/src/components/Workflow/workflow.graphql @@ -0,0 +1,51 @@ +fragment workflow on Workflow { + id + states { + ...workflowState + } + transitions { + ...workflowTransition + } +} + +fragment workflowState on WorkflowState { + value + label +} + +fragment workflowTransition on WorkflowTransition { + key + devName + label + type + from { + ...workflowState + } + to { + __typename + ... on WorkflowTransitionStaticTo { + state { + ...workflowState + } + } + ... on WorkflowTransitionDynamicTo { + id + label + relatedStates { + ...workflowState + } + } + } + conditions { + label + } + notifiers { + label + } + permissions { + role + readEvent + execute + condition + } +} diff --git a/src/scenes/Projects/Projects.tsx b/src/scenes/Projects/Projects.tsx index 8cfbb17bd2..5fd56e37c6 100644 --- a/src/scenes/Projects/Projects.tsx +++ b/src/scenes/Projects/Projects.tsx @@ -38,9 +38,14 @@ const ChangeRequestList = loadable(() => import('./ChangeRequest/List'), { resolveComponent: (m) => m.ProjectChangeRequestList, }); +const ProjectFlowchart = loadable(() => import('./Workflow/ProjectFlowchart'), { + resolveComponent: (m) => m.ProjectFlowchart, +}); + export const Projects = () => ( } /> + } /> } /> {NotFoundRoute} diff --git a/src/scenes/Projects/Workflow/ProjectFlowchart.graphql b/src/scenes/Projects/Workflow/ProjectFlowchart.graphql new file mode 100644 index 0000000000..41ba862376 --- /dev/null +++ b/src/scenes/Projects/Workflow/ProjectFlowchart.graphql @@ -0,0 +1,5 @@ +query ProjectFlowchart { + workflow: projectWorkflow { + ...workflow + } +} diff --git a/src/scenes/Projects/Workflow/ProjectFlowchart.tsx b/src/scenes/Projects/Workflow/ProjectFlowchart.tsx new file mode 100644 index 0000000000..81198bd562 --- /dev/null +++ b/src/scenes/Projects/Workflow/ProjectFlowchart.tsx @@ -0,0 +1,6 @@ +import Flowchart from '../../../components/Workflow/Flowchart'; +import { ProjectFlowchartDocument } from './ProjectFlowchart.graphql'; + +export const ProjectFlowchart = () => ( + +); diff --git a/yarn.lock b/yarn.lock index 100b4e214a..8243ef0589 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1690,6 +1690,22 @@ __metadata: languageName: node linkType: hard +"@dagrejs/dagre@npm:^1.1.2": + version: 1.1.2 + resolution: "@dagrejs/dagre@npm:1.1.2" + dependencies: + "@dagrejs/graphlib": "npm:2.2.2" + checksum: 10c0/717b3e6974b67a3839ea828228582fa3bd310fac5aadc4a68d5e4b96c3f7bcb97cb6f78518bdbd4c14e4fa1cc764ac6259fa567734916fa51f078f5747c85877 + languageName: node + linkType: hard + +"@dagrejs/graphlib@npm:2.2.2": + version: 2.2.2 + resolution: "@dagrejs/graphlib@npm:2.2.2" + checksum: 10c0/2e79a4f5c6c402054b7ef42e786459645495934476170999f13867a55a00072636a23914772cce6bc03ce51eef70de589058860b8f034c1d70804fb61e01fcfc + languageName: node + linkType: hard + "@date-io/core@npm:^2.15.0, @date-io/core@npm:^2.17.0": version: 2.17.0 resolution: "@date-io/core@npm:2.17.0" @@ -3846,6 +3862,102 @@ __metadata: languageName: node linkType: hard +"@reactflow/background@npm:11.3.13": + version: 11.3.13 + resolution: "@reactflow/background@npm:11.3.13" + dependencies: + "@reactflow/core": "npm:11.11.3" + classcat: "npm:^5.0.3" + zustand: "npm:^4.4.1" + peerDependencies: + react: ">=17" + react-dom: ">=17" + checksum: 10c0/54929a506c1b73b6406d511a0a55a89cf88eb2073bc4e72defde63b3da46794ea87638e017180492798d717e9220e833f66c7419270137ace9acaa040f790e6e + languageName: node + linkType: hard + +"@reactflow/controls@npm:11.2.13": + version: 11.2.13 + resolution: "@reactflow/controls@npm:11.2.13" + dependencies: + "@reactflow/core": "npm:11.11.3" + classcat: "npm:^5.0.3" + zustand: "npm:^4.4.1" + peerDependencies: + react: ">=17" + react-dom: ">=17" + checksum: 10c0/219285855f5a76ad77bf858e1eb3ed867d95f96b8c2f6480e6f9f89dce9e68617cd97565960455aa6fa9c190292ebf81f28da3a254abd413c358a0288773203c + languageName: node + linkType: hard + +"@reactflow/core@npm:11.11.3": + version: 11.11.3 + resolution: "@reactflow/core@npm:11.11.3" + dependencies: + "@types/d3": "npm:^7.4.0" + "@types/d3-drag": "npm:^3.0.1" + "@types/d3-selection": "npm:^3.0.3" + "@types/d3-zoom": "npm:^3.0.1" + classcat: "npm:^5.0.3" + d3-drag: "npm:^3.0.0" + d3-selection: "npm:^3.0.0" + d3-zoom: "npm:^3.0.0" + zustand: "npm:^4.4.1" + peerDependencies: + react: ">=17" + react-dom: ">=17" + checksum: 10c0/08c8353316c38ebc398e645f2e1e2d7246ea4e331485d604f8c6a8d54a83cc85e87921427b3fd7d161314258072b44508c9bba79faefa67e9d401e6b3f262b19 + languageName: node + linkType: hard + +"@reactflow/minimap@npm:11.7.13": + version: 11.7.13 + resolution: "@reactflow/minimap@npm:11.7.13" + dependencies: + "@reactflow/core": "npm:11.11.3" + "@types/d3-selection": "npm:^3.0.3" + "@types/d3-zoom": "npm:^3.0.1" + classcat: "npm:^5.0.3" + d3-selection: "npm:^3.0.0" + d3-zoom: "npm:^3.0.0" + zustand: "npm:^4.4.1" + peerDependencies: + react: ">=17" + react-dom: ">=17" + checksum: 10c0/71f89a7ae36ce6b50237401640043be20264fedfcd055f6a3e16cf84adcac343ad2f3fd74eb48875ab12538d3566aaddc21cce6160d602120e71797c79ee374e + languageName: node + linkType: hard + +"@reactflow/node-resizer@npm:2.2.13": + version: 2.2.13 + resolution: "@reactflow/node-resizer@npm:2.2.13" + dependencies: + "@reactflow/core": "npm:11.11.3" + classcat: "npm:^5.0.4" + d3-drag: "npm:^3.0.0" + d3-selection: "npm:^3.0.0" + zustand: "npm:^4.4.1" + peerDependencies: + react: ">=17" + react-dom: ">=17" + checksum: 10c0/8fa01dc3c2805af56bfb93a05d7c2aecbf1c40fdd25d768ae0a0d252cec9ac04911493103abfbe3f8fcd840a8ea4a8c8342646eed7dcfcb3147d1de2a0661dff + languageName: node + linkType: hard + +"@reactflow/node-toolbar@npm:1.3.13": + version: 1.3.13 + resolution: "@reactflow/node-toolbar@npm:1.3.13" + dependencies: + "@reactflow/core": "npm:11.11.3" + classcat: "npm:^5.0.3" + zustand: "npm:^4.4.1" + peerDependencies: + react: ">=17" + react-dom: ">=17" + checksum: 10c0/bfba042b97bcb11c11f71db3c3eab5f153fb2b666f6135d0658efb0bf2b6f210ecb52fa1da3af59eb995dd91d04fe223ef88af7c6e3bc349650fb93e17ed876c + languageName: node + linkType: hard + "@remix-run/router@npm:1.10.0": version: 1.10.0 resolution: "@remix-run/router@npm:1.10.0" @@ -4152,6 +4264,278 @@ __metadata: languageName: node linkType: hard +"@types/d3-array@npm:*": + version: 3.2.1 + resolution: "@types/d3-array@npm:3.2.1" + checksum: 10c0/38bf2c778451f4b79ec81a2288cb4312fe3d6449ecdf562970cc339b60f280f31c93a024c7ff512607795e79d3beb0cbda123bb07010167bce32927f71364bca + languageName: node + linkType: hard + +"@types/d3-axis@npm:*": + version: 3.0.6 + resolution: "@types/d3-axis@npm:3.0.6" + dependencies: + "@types/d3-selection": "npm:*" + checksum: 10c0/d756d42360261f44d8eefd0950c5bb0a4f67a46dd92069da3f723ac36a1e8cb2b9ce6347d836ef19d5b8aef725dbcf8fdbbd6cfbff676ca4b0642df2f78b599a + languageName: node + linkType: hard + +"@types/d3-brush@npm:*": + version: 3.0.6 + resolution: "@types/d3-brush@npm:3.0.6" + dependencies: + "@types/d3-selection": "npm:*" + checksum: 10c0/fd6e2ac7657a354f269f6b9c58451ffae9d01b89ccb1eb6367fd36d635d2f1990967215ab498e0c0679ff269429c57fad6a2958b68f4d45bc9f81d81672edc01 + languageName: node + linkType: hard + +"@types/d3-chord@npm:*": + version: 3.0.6 + resolution: "@types/d3-chord@npm:3.0.6" + checksum: 10c0/c5a25eb5389db01e63faec0c5c2ec7cc41c494e9b3201630b494c4e862a60f1aa83fabbc33a829e7e1403941e3c30d206c741559b14406ac2a4239cfdf4b4c17 + languageName: node + linkType: hard + +"@types/d3-color@npm:*": + version: 3.1.3 + resolution: "@types/d3-color@npm:3.1.3" + checksum: 10c0/65eb0487de606eb5ad81735a9a5b3142d30bc5ea801ed9b14b77cb14c9b909f718c059f13af341264ee189acf171508053342142bdf99338667cea26a2d8d6ae + languageName: node + linkType: hard + +"@types/d3-contour@npm:*": + version: 3.0.6 + resolution: "@types/d3-contour@npm:3.0.6" + dependencies: + "@types/d3-array": "npm:*" + "@types/geojson": "npm:*" + checksum: 10c0/e7d83e94719af4576ceb5ac7f277c5806f83ba6c3631744ae391cffc3641f09dfa279470b83053cd0b2acd6784e8749c71141d05bdffa63ca58ffb5b31a0f27c + languageName: node + linkType: hard + +"@types/d3-delaunay@npm:*": + version: 6.0.4 + resolution: "@types/d3-delaunay@npm:6.0.4" + checksum: 10c0/d154a8864f08c4ea23ecb9bdabcef1c406a25baa8895f0cb08a0ed2799de0d360e597552532ce7086ff0cdffa8f3563f9109d18f0191459d32bb620a36939123 + languageName: node + linkType: hard + +"@types/d3-dispatch@npm:*": + version: 3.0.6 + resolution: "@types/d3-dispatch@npm:3.0.6" + checksum: 10c0/405eb7d0ec139fbf72fa6a43b0f3ca8a1f913bb2cb38f607827e63fca8d4393f021f32f3b96b33c93ddbd37789453a0b3624f14f504add5308fd9aec8a46dda0 + languageName: node + linkType: hard + +"@types/d3-drag@npm:*, @types/d3-drag@npm:^3.0.1": + version: 3.0.7 + resolution: "@types/d3-drag@npm:3.0.7" + dependencies: + "@types/d3-selection": "npm:*" + checksum: 10c0/65e29fa32a87c72d26c44b5e2df3bf15af21cd128386bcc05bcacca255927c0397d0cd7e6062aed5f0abd623490544a9d061c195f5ed9f018fe0b698d99c079d + languageName: node + linkType: hard + +"@types/d3-dsv@npm:*": + version: 3.0.7 + resolution: "@types/d3-dsv@npm:3.0.7" + checksum: 10c0/c0f01da862465594c8a28278b51c850af3b4239cc22b14fd1a19d7a98f93d94efa477bf59d8071beb285dca45bf614630811451e18e7c52add3a0abfee0a1871 + languageName: node + linkType: hard + +"@types/d3-ease@npm:*": + version: 3.0.2 + resolution: "@types/d3-ease@npm:3.0.2" + checksum: 10c0/aff5a1e572a937ee9bff6465225d7ba27d5e0c976bd9eacdac2e6f10700a7cb0c9ea2597aff6b43a6ed850a3210030870238894a77ec73e309b4a9d0333f099c + languageName: node + linkType: hard + +"@types/d3-fetch@npm:*": + version: 3.0.7 + resolution: "@types/d3-fetch@npm:3.0.7" + dependencies: + "@types/d3-dsv": "npm:*" + checksum: 10c0/3d147efa52a26da1a5d40d4d73e6cebaaa964463c378068062999b93ea3731b27cc429104c21ecbba98c6090e58ef13429db6399238c5e3500162fb3015697a0 + languageName: node + linkType: hard + +"@types/d3-force@npm:*": + version: 3.0.9 + resolution: "@types/d3-force@npm:3.0.9" + checksum: 10c0/6d791a48ea570daaada6df93af8c877d58e6b940b3ab4515cde08ed6ed1d4e8e59fd8407efe37a1b3f5fe95867fe83a2974c4314a7924dc19860a5e955c26211 + languageName: node + linkType: hard + +"@types/d3-format@npm:*": + version: 3.0.4 + resolution: "@types/d3-format@npm:3.0.4" + checksum: 10c0/3ac1600bf9061a59a228998f7cd3f29e85cbf522997671ba18d4d84d10a2a1aff4f95aceb143fa9960501c3ec351e113fc75884e6a504ace44dc1744083035ee + languageName: node + linkType: hard + +"@types/d3-geo@npm:*": + version: 3.1.0 + resolution: "@types/d3-geo@npm:3.1.0" + dependencies: + "@types/geojson": "npm:*" + checksum: 10c0/3745a93439038bb5b0b38facf435f7079812921d46406f5d38deaee59e90084ff742443c7ea0a8446df81a0d81eaf622fe7068cf4117a544bd4aa3b2dc182f88 + languageName: node + linkType: hard + +"@types/d3-hierarchy@npm:*": + version: 3.1.7 + resolution: "@types/d3-hierarchy@npm:3.1.7" + checksum: 10c0/873711737d6b8e7b6f1dda0bcd21294a48f75024909ae510c5d2c21fad2e72032e0958def4d9f68319d3aaac298ad09c49807f8bfc87a145a82693b5208613c7 + languageName: node + linkType: hard + +"@types/d3-interpolate@npm:*": + version: 3.0.4 + resolution: "@types/d3-interpolate@npm:3.0.4" + dependencies: + "@types/d3-color": "npm:*" + checksum: 10c0/066ebb8da570b518dd332df6b12ae3b1eaa0a7f4f0c702e3c57f812cf529cc3500ec2aac8dc094f31897790346c6b1ebd8cd7a077176727f4860c2b181a65ca4 + languageName: node + linkType: hard + +"@types/d3-path@npm:*": + version: 3.1.0 + resolution: "@types/d3-path@npm:3.1.0" + checksum: 10c0/85e8b3aa968a60a5b33198ade06ae7ffedcf9a22d86f24859ff58e014b053ccb7141ec163b78d547bc8215bb12bb54171c666057ab6156912814005b686afb31 + languageName: node + linkType: hard + +"@types/d3-polygon@npm:*": + version: 3.0.2 + resolution: "@types/d3-polygon@npm:3.0.2" + checksum: 10c0/f46307bb32b6c2aef8c7624500e0f9b518de8f227ccc10170b869dc43e4c542560f6c8d62e9f087fac45e198d6e4b623e579c0422e34c85baf56717456d3f439 + languageName: node + linkType: hard + +"@types/d3-quadtree@npm:*": + version: 3.0.6 + resolution: "@types/d3-quadtree@npm:3.0.6" + checksum: 10c0/7eaa0a4d404adc856971c9285e1c4ab17e9135ea669d847d6db7e0066126a28ac751864e7ce99c65d526e130f56754a2e437a1617877098b3bdcc3ef23a23616 + languageName: node + linkType: hard + +"@types/d3-random@npm:*": + version: 3.0.3 + resolution: "@types/d3-random@npm:3.0.3" + checksum: 10c0/5f4fea40080cd6d4adfee05183d00374e73a10c530276a6455348983dda341003a251def28565a27c25d9cf5296a33e870e397c9d91ff83fb7495a21c96b6882 + languageName: node + linkType: hard + +"@types/d3-scale-chromatic@npm:*": + version: 3.0.3 + resolution: "@types/d3-scale-chromatic@npm:3.0.3" + checksum: 10c0/2f48c6f370edba485b57b73573884ded71914222a4580140ff87ee96e1d55ccd05b1d457f726e234a31269b803270ac95d5554229ab6c43c7e4a9894e20dd490 + languageName: node + linkType: hard + +"@types/d3-scale@npm:*": + version: 4.0.8 + resolution: "@types/d3-scale@npm:4.0.8" + dependencies: + "@types/d3-time": "npm:*" + checksum: 10c0/57de90e4016f640b83cb960b7e3a0ab3ed02e720898840ddc5105264ffcfea73336161442fdc91895377c2d2f91904d637282f16852b8535b77e15a761c8e99e + languageName: node + linkType: hard + +"@types/d3-selection@npm:*, @types/d3-selection@npm:^3.0.3": + version: 3.0.10 + resolution: "@types/d3-selection@npm:3.0.10" + checksum: 10c0/de1f99ab186a08999bf394a645fd76911add1b02316270d4c07616c8383903a2b068d7e02b73b6a99a1f26bb49a2e99ef4b55a5d2ddfa165f6f3c53144897920 + languageName: node + linkType: hard + +"@types/d3-shape@npm:*": + version: 3.1.6 + resolution: "@types/d3-shape@npm:3.1.6" + dependencies: + "@types/d3-path": "npm:*" + checksum: 10c0/0625715925d3c7ed3d44ce998b42c993f063c31605b6e4a8046c4be0fe724e2d214fc83e86d04f429a30a6e1f439053e92b0d9e59e1180c3a5327b4a6e79fa0a + languageName: node + linkType: hard + +"@types/d3-time-format@npm:*": + version: 4.0.3 + resolution: "@types/d3-time-format@npm:4.0.3" + checksum: 10c0/9ef5e8e2b96b94799b821eed5d61a3d432c7903247966d8ad951b8ce5797fe46554b425cb7888fa5bf604b4663c369d7628c0328ffe80892156671c58d1a7f90 + languageName: node + linkType: hard + +"@types/d3-time@npm:*": + version: 3.0.3 + resolution: "@types/d3-time@npm:3.0.3" + checksum: 10c0/245a8aadca504df27edf730de502e47a68f16ae795c86b5ca35e7afa91c133aa9ef4d08778f8cf1ed2be732f89a4105ba4b437ce2afbdfd17d3d937b6ba5f568 + languageName: node + linkType: hard + +"@types/d3-timer@npm:*": + version: 3.0.2 + resolution: "@types/d3-timer@npm:3.0.2" + checksum: 10c0/c644dd9571fcc62b1aa12c03bcad40571553020feeb5811f1d8a937ac1e65b8a04b759b4873aef610e28b8714ac71c9885a4d6c127a048d95118f7e5b506d9e1 + languageName: node + linkType: hard + +"@types/d3-transition@npm:*": + version: 3.0.8 + resolution: "@types/d3-transition@npm:3.0.8" + dependencies: + "@types/d3-selection": "npm:*" + checksum: 10c0/feba7845bd1e1d49e38b0d55562e01e90bfbcf0a56fbe0de4279c12e43a687032d22ed559629c0412145d25d61e4e53ddfef34c89c6bf043d48b6c2cd3a929dc + languageName: node + linkType: hard + +"@types/d3-zoom@npm:*, @types/d3-zoom@npm:^3.0.1": + version: 3.0.8 + resolution: "@types/d3-zoom@npm:3.0.8" + dependencies: + "@types/d3-interpolate": "npm:*" + "@types/d3-selection": "npm:*" + checksum: 10c0/1dbdbcafddcae12efb5beb6948546963f29599e18bc7f2a91fb69cc617c2299a65354f2d47e282dfb86fec0968406cd4fb7f76ba2d2fb67baa8e8d146eb4a547 + languageName: node + linkType: hard + +"@types/d3@npm:^7.4.0": + version: 7.4.3 + resolution: "@types/d3@npm:7.4.3" + dependencies: + "@types/d3-array": "npm:*" + "@types/d3-axis": "npm:*" + "@types/d3-brush": "npm:*" + "@types/d3-chord": "npm:*" + "@types/d3-color": "npm:*" + "@types/d3-contour": "npm:*" + "@types/d3-delaunay": "npm:*" + "@types/d3-dispatch": "npm:*" + "@types/d3-drag": "npm:*" + "@types/d3-dsv": "npm:*" + "@types/d3-ease": "npm:*" + "@types/d3-fetch": "npm:*" + "@types/d3-force": "npm:*" + "@types/d3-format": "npm:*" + "@types/d3-geo": "npm:*" + "@types/d3-hierarchy": "npm:*" + "@types/d3-interpolate": "npm:*" + "@types/d3-path": "npm:*" + "@types/d3-polygon": "npm:*" + "@types/d3-quadtree": "npm:*" + "@types/d3-random": "npm:*" + "@types/d3-scale": "npm:*" + "@types/d3-scale-chromatic": "npm:*" + "@types/d3-selection": "npm:*" + "@types/d3-shape": "npm:*" + "@types/d3-time": "npm:*" + "@types/d3-time-format": "npm:*" + "@types/d3-timer": "npm:*" + "@types/d3-transition": "npm:*" + "@types/d3-zoom": "npm:*" + checksum: 10c0/a9c6d65b13ef3b42c87f2a89ea63a6d5640221869f97d0657b0cb2f1dac96a0f164bf5605643c0794e0de3aa2bf05df198519aaf15d24ca135eb0e8bd8a9d879 + languageName: node + linkType: hard + "@types/eslint@npm:^8.44.6": version: 8.44.6 resolution: "@types/eslint@npm:8.44.6" @@ -4193,6 +4577,13 @@ __metadata: languageName: node linkType: hard +"@types/geojson@npm:*": + version: 7946.0.14 + resolution: "@types/geojson@npm:7946.0.14" + checksum: 10c0/54f3997708fa2970c03eeb31f7e4540a0eb6387b15e9f8a60513a1409c23cafec8d618525404573468b59c6fecbfd053724b3327f7fca416729c26271d799f55 + languageName: node + linkType: hard + "@types/graceful-fs@npm:^4.1.2": version: 4.1.8 resolution: "@types/graceful-fs@npm:4.1.8" @@ -6887,6 +7278,13 @@ __metadata: languageName: node linkType: hard +"classcat@npm:^5.0.3, classcat@npm:^5.0.4": + version: 5.0.5 + resolution: "classcat@npm:5.0.5" + checksum: 10c0/ff8d273055ef9b518529cfe80fd0486f7057a9917373807ff802d75ceb46e8f8e148f41fa094ee7625c8f34642cfaa98395ff182d9519898da7cbf383d4a210d + languageName: node + linkType: hard + "clean-css@npm:^4.2.3": version: 4.2.4 resolution: "clean-css@npm:4.2.4" @@ -7406,6 +7804,7 @@ __metadata: "@babel/plugin-transform-runtime": "npm:^7.23.2" "@babel/runtime": "npm:^7.23.2" "@babel/types": "npm:^7.23.0" + "@dagrejs/dagre": "npm:^1.1.2" "@date-io/core": "npm:^2.17.0" "@date-io/luxon": "npm:^2.17.0" "@editorjs/checklist": "npm:^1.5.0" @@ -7567,6 +7966,7 @@ __metadata: react-pdf: "npm:^5.7.2" react-router: "npm:^6.17.0" react-router-dom: "npm:^6.17.0" + reactflow: "npm:^11.11.3" response-time: "npm:^2.3.2" rifm: "npm:^0.12.1" rimraf: "npm:^5.0.5" @@ -7794,7 +8194,7 @@ __metadata: languageName: node linkType: hard -"css-minimizer-webpack-plugin@npm:^1.2.0": +"css-minimizer-webpack-plugin@npm:1.3.0": version: 1.3.0 resolution: "css-minimizer-webpack-plugin@npm:1.3.0" dependencies: @@ -7813,6 +8213,25 @@ __metadata: languageName: node linkType: hard +"css-minimizer-webpack-plugin@patch:css-minimizer-webpack-plugin@npm%3A1.3.0#~/.yarn/patches/css-minimizer-webpack-plugin-npm-1.3.0-c66c75884d.patch": + version: 1.3.0 + resolution: "css-minimizer-webpack-plugin@patch:css-minimizer-webpack-plugin@npm%3A1.3.0#~/.yarn/patches/css-minimizer-webpack-plugin-npm-1.3.0-c66c75884d.patch::version=1.3.0&hash=ea44b1" + dependencies: + cacache: "npm:^15.0.5" + cssnano: "npm:^4.1.10" + find-cache-dir: "npm:^3.3.1" + jest-worker: "npm:^26.3.0" + p-limit: "npm:^3.0.2" + schema-utils: "npm:^3.0.0" + serialize-javascript: "npm:^5.0.1" + source-map: "npm:^0.6.1" + webpack-sources: "npm:^1.4.3" + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + checksum: 10c0/708c2fa46c8a37bdfeff176edbe463a8b0dd6dd08257d7fd2056f470873bc7504b0b7777aae1f65c34ead3f26f54769874c6f98906ae1441a708827724d396cf + languageName: node + linkType: hard + "css-select-base-adapter@npm:^0.1.1": version: 0.1.1 resolution: "css-select-base-adapter@npm:0.1.1" @@ -8028,6 +8447,88 @@ __metadata: languageName: node linkType: hard +"d3-color@npm:1 - 3": + version: 3.1.0 + resolution: "d3-color@npm:3.1.0" + checksum: 10c0/a4e20e1115fa696fce041fbe13fbc80dc4c19150fa72027a7c128ade980bc0eeeba4bcf28c9e21f0bce0e0dbfe7ca5869ef67746541dcfda053e4802ad19783c + languageName: node + linkType: hard + +"d3-dispatch@npm:1 - 3": + version: 3.0.1 + resolution: "d3-dispatch@npm:3.0.1" + checksum: 10c0/6eca77008ce2dc33380e45d4410c67d150941df7ab45b91d116dbe6d0a3092c0f6ac184dd4602c796dc9e790222bad3ff7142025f5fd22694efe088d1d941753 + languageName: node + linkType: hard + +"d3-drag@npm:2 - 3, d3-drag@npm:^3.0.0": + version: 3.0.0 + resolution: "d3-drag@npm:3.0.0" + dependencies: + d3-dispatch: "npm:1 - 3" + d3-selection: "npm:3" + checksum: 10c0/d2556e8dc720741a443b595a30af403dd60642dfd938d44d6e9bfc4c71a962142f9a028c56b61f8b4790b65a34acad177d1263d66f103c3c527767b0926ef5aa + languageName: node + linkType: hard + +"d3-ease@npm:1 - 3": + version: 3.0.1 + resolution: "d3-ease@npm:3.0.1" + checksum: 10c0/fec8ef826c0cc35cda3092c6841e07672868b1839fcaf556e19266a3a37e6bc7977d8298c0fcb9885e7799bfdcef7db1baaba9cd4dcf4bc5e952cf78574a88b0 + languageName: node + linkType: hard + +"d3-interpolate@npm:1 - 3": + version: 3.0.1 + resolution: "d3-interpolate@npm:3.0.1" + dependencies: + d3-color: "npm:1 - 3" + checksum: 10c0/19f4b4daa8d733906671afff7767c19488f51a43d251f8b7f484d5d3cfc36c663f0a66c38fe91eee30f40327443d799be17169f55a293a3ba949e84e57a33e6a + languageName: node + linkType: hard + +"d3-selection@npm:2 - 3, d3-selection@npm:3, d3-selection@npm:^3.0.0": + version: 3.0.0 + resolution: "d3-selection@npm:3.0.0" + checksum: 10c0/e59096bbe8f0cb0daa1001d9bdd6dbc93a688019abc97d1d8b37f85cd3c286a6875b22adea0931b0c88410d025563e1643019161a883c516acf50c190a11b56b + languageName: node + linkType: hard + +"d3-timer@npm:1 - 3": + version: 3.0.1 + resolution: "d3-timer@npm:3.0.1" + checksum: 10c0/d4c63cb4bb5461d7038aac561b097cd1c5673969b27cbdd0e87fa48d9300a538b9e6f39b4a7f0e3592ef4f963d858c8a9f0e92754db73116770856f2fc04561a + languageName: node + linkType: hard + +"d3-transition@npm:2 - 3": + version: 3.0.1 + resolution: "d3-transition@npm:3.0.1" + dependencies: + d3-color: "npm:1 - 3" + d3-dispatch: "npm:1 - 3" + d3-ease: "npm:1 - 3" + d3-interpolate: "npm:1 - 3" + d3-timer: "npm:1 - 3" + peerDependencies: + d3-selection: 2 - 3 + checksum: 10c0/4e74535dda7024aa43e141635b7522bb70cf9d3dfefed975eb643b36b864762eca67f88fafc2ca798174f83ca7c8a65e892624f824b3f65b8145c6a1a88dbbad + languageName: node + linkType: hard + +"d3-zoom@npm:^3.0.0": + version: 3.0.0 + resolution: "d3-zoom@npm:3.0.0" + dependencies: + d3-dispatch: "npm:1 - 3" + d3-drag: "npm:2 - 3" + d3-interpolate: "npm:1 - 3" + d3-selection: "npm:2 - 3" + d3-transition: "npm:2 - 3" + checksum: 10c0/ee2036479049e70d8c783d594c444fe00e398246048e3f11a59755cd0e21de62ece3126181b0d7a31bf37bcf32fd726f83ae7dea4495ff86ec7736ce5ad36fd3 + languageName: node + linkType: hard + "damerau-levenshtein@npm:^1.0.8": version: 1.0.8 resolution: "damerau-levenshtein@npm:1.0.8" @@ -16620,6 +17121,23 @@ __metadata: languageName: node linkType: hard +"reactflow@npm:^11.11.3": + version: 11.11.3 + resolution: "reactflow@npm:11.11.3" + dependencies: + "@reactflow/background": "npm:11.3.13" + "@reactflow/controls": "npm:11.2.13" + "@reactflow/core": "npm:11.11.3" + "@reactflow/minimap": "npm:11.7.13" + "@reactflow/node-resizer": "npm:2.2.13" + "@reactflow/node-toolbar": "npm:1.3.13" + peerDependencies: + react: ">=17" + react-dom: ">=17" + checksum: 10c0/1de038357a3b1ec440a06f041540ec4738419366bff13829996407b963f69bec7acfd1dca475c067727146bfd8f6ce1b230ce499abdbd6a8a2a37493cf547a94 + languageName: node + linkType: hard + "read-pkg-up@npm:^7.0.1": version: 7.0.1 resolution: "read-pkg-up@npm:7.0.1" @@ -19645,7 +20163,7 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:^1.2.0": +"use-sync-external-store@npm:1.2.0, use-sync-external-store@npm:^1.2.0": version: 1.2.0 resolution: "use-sync-external-store@npm:1.2.0" peerDependencies: @@ -20577,3 +21095,23 @@ __metadata: checksum: 10c0/71cc2f2bbb537300c3f569e25693d37b3bc91f225cefce251a71c30bc6bb3e7f8e9420ca0eb57f2ac9e492b085b8dfa075fd1e8195c40b83c951dd59c6e4fbf8 languageName: node linkType: hard + +"zustand@npm:^4.4.1": + version: 4.5.2 + resolution: "zustand@npm:4.5.2" + dependencies: + use-sync-external-store: "npm:1.2.0" + peerDependencies: + "@types/react": ">=16.8" + immer: ">=9.0.6" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + checksum: 10c0/aee26f11facebb39b016e89539f72a72c2c00151208907fc909c3cedd455728240e09e01d98ebd3b63a2a3518a5917eac5de6c853743ca55a1655296d750bb48 + languageName: node + linkType: hard