From 22c69fef40580dd05d8dd1bdcb3eb18a3b72cf9e Mon Sep 17 00:00:00 2001 From: 1AhmedYasser <26207361+1AhmedYasser@users.noreply.github.com> Date: Tue, 19 May 2026 14:08:06 +0300 Subject: [PATCH 01/18] chore(982): Added mcq branch manual connection support --- .../components/Flow/McqBranchSelectModal.tsx | 29 ++++ .../components/Flow/NodeTypes/CustomNode.tsx | 32 ++++- .../components/FlowBuilder/FlowBuilder.tsx | 48 +++---- .../components/FlowElementsPopup/index.tsx | 4 +- GUI/src/hooks/flow/useEdgeAdd.ts | 9 ++ GUI/src/hooks/flow/useMcqConnect.ts | 121 ++++++++++++++++ GUI/src/i18n/en/common.json | 4 + GUI/src/i18n/et/common.json | 4 + GUI/src/store/new-services.store.ts | 1 + GUI/src/utils/mcq-flow-utils.test.ts | 47 ++++++ GUI/src/utils/mcq-flow-utils.ts | 134 ++++++++++++++++++ 11 files changed, 398 insertions(+), 35 deletions(-) create mode 100644 GUI/src/components/Flow/McqBranchSelectModal.tsx create mode 100644 GUI/src/hooks/flow/useMcqConnect.ts create mode 100644 GUI/src/utils/mcq-flow-utils.test.ts create mode 100644 GUI/src/utils/mcq-flow-utils.ts diff --git a/GUI/src/components/Flow/McqBranchSelectModal.tsx b/GUI/src/components/Flow/McqBranchSelectModal.tsx new file mode 100644 index 000000000..aa0b95b2b --- /dev/null +++ b/GUI/src/components/Flow/McqBranchSelectModal.tsx @@ -0,0 +1,29 @@ +import { Button, Modal, Track } from 'components'; +import { FC } from 'react'; +import { useTranslation } from 'react-i18next'; +import { McqEmptyBranch } from 'utils/mcq-flow-utils'; + +type McqBranchSelectModalProps = { + emptyBranches: McqEmptyBranch[]; + onSelect: (branch: McqEmptyBranch) => void; + onClose: () => void; +}; + +const McqBranchSelectModal: FC = ({ emptyBranches, onSelect, onClose }) => { + const { t } = useTranslation(); + + return ( + +

{t('serviceFlow.mcq.emptyBranchesMessage', { count: emptyBranches.length })}

+ + {emptyBranches.map((branch) => ( + + ))} + +
+ ); +}; + +export default McqBranchSelectModal; diff --git a/GUI/src/components/Flow/NodeTypes/CustomNode.tsx b/GUI/src/components/Flow/NodeTypes/CustomNode.tsx index d75ffb5f0..47b3198dc 100644 --- a/GUI/src/components/Flow/NodeTypes/CustomNode.tsx +++ b/GUI/src/components/Flow/NodeTypes/CustomNode.tsx @@ -1,12 +1,14 @@ -import { Handle, NodeProps, Position, useUpdateNodeInternals } from '@xyflow/react'; +import { Handle, NodeProps, Position, useStore, useUpdateNodeInternals } from '@xyflow/react'; import './Node.scss'; import Button from 'components/Button'; import Icon from 'components/Icon'; import Track from 'components/Track'; -import React, { FC, useEffect } from 'react'; +import React, { FC, useEffect, useMemo } from 'react'; import { MdDeleteOutline, MdOutlineEdit, MdOutlineRemoveRedEye } from 'react-icons/md'; import useServiceStore from 'store/services.store'; +import { StepType } from 'types'; import { NodeDataProps } from 'types/service-flow'; +import { MCQ_SOURCE_HANDLE_ID, mcqHasEmptyBranches } from 'utils/mcq-flow-utils'; import StepNode from './StepNode'; @@ -17,13 +19,21 @@ type CustomNodeProps = { const CustomNode: FC = (props) => { const { data, isConnectable, id } = props; const orientation = useServiceStore((state) => state.orientation); - const shouldOffsetHandles = data.childrenCount > 1; + const isMcq = data.stepType === StepType.MultiChoiceQuestion; + const shouldOffsetHandles = !isMcq && data.childrenCount > 1; + + const edges = useStore((state) => state.edges); + const nodes = useStore((state) => state.nodes); + + const mcqCanConnect = useMemo(() => !isMcq || mcqHasEmptyBranches(id, nodes, edges), [edges, id, isMcq, nodes]); + + const canConnect = isConnectable && mcqCanConnect; const updateNodeInternals = useUpdateNodeInternals(); useEffect(() => { updateNodeInternals(id); - }, [data.childrenCount, id, updateNodeInternals, orientation]); + }, [data.childrenCount, id, isMcq, mcqCanConnect, updateNodeInternals, orientation]); const isFinishingStep = () => { return data.type === 'finishing-step'; @@ -38,6 +48,18 @@ const CustomNode: FC = (props) => { }; const bottomHandles = (): React.JSX.Element => { + if (isMcq) { + return ( +