Skip to content

Commit ef22b2f

Browse files
authored
Merge pull request #47 from Dialogue-Bot/DIAL-37-bug-not-remove-action-when-node-remove
Dial 37 bug not remove action when node remove
2 parents 84f8281 + cef60e8 commit ef22b2f

File tree

15 files changed

+292
-103
lines changed

15 files changed

+292
-103
lines changed

client/src/components/forms/condition.tsx

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import {
33
TCompareValue,
44
useCompareValueSchema,
55
} from '@/lib/schema/compare-value'
6+
import { isStringBoolean } from '@/utils'
67
import { zodResolver } from '@hookform/resolvers/zod'
8+
import { useEffect } from 'react'
79
import { useForm } from 'react-hook-form'
810
import { useTranslation } from 'react-i18next'
911
import {
@@ -16,6 +18,7 @@ import {
1618
Input,
1719
Select,
1820
SelectContent,
21+
SelectGroup,
1922
SelectItem,
2023
SelectTrigger,
2124
SelectValue,
@@ -34,15 +37,31 @@ const ConditionForm = ({
3437
}: Props) => {
3538
const schema = useCompareValueSchema()
3639
const form = useForm<TCompareValue>({
37-
defaultValues,
40+
defaultValues: {
41+
operator: defaultValues?.operator || '',
42+
value: defaultValues?.value
43+
? JSON.stringify(defaultValues.value).replace(/"/g, '')
44+
: '',
45+
},
3846
resolver: zodResolver(schema),
3947
})
4048
const { t } = useTranslation(['forms', 'flowDetail'])
4149

50+
const watchOperator = form.watch('operator')
51+
4252
const handleSubmit = form.handleSubmit((data) => {
43-
onSubmit?.(data)
53+
onSubmit?.({
54+
...data,
55+
value: isStringBoolean(data.value) ? JSON.parse(data.value) : data.value,
56+
})
4457
})
4558

59+
useEffect(() => {
60+
if (watchOperator === 'exist' && !form.getValues('value')) {
61+
form.reset({ value: 'true' })
62+
}
63+
}, [watchOperator, form])
64+
4665
return (
4766
<Form {...form}>
4867
<form className='space-y-3' onSubmit={handleSubmit} id={id}>
@@ -56,44 +75,79 @@ const ConditionForm = ({
5675
<Select
5776
onValueChange={field.onChange}
5877
defaultValue={field.value}
78+
value={field.value}
5979
>
6080
<FormControl>
6181
<SelectTrigger>
6282
<SelectValue placeholder={t('operator.placeholder')} />
6383
</SelectTrigger>
6484
</FormControl>
65-
<SelectContent>
66-
{CONDITIONAL_OPERATOR.map((c) => {
67-
return (
68-
<SelectItem key={c} value={c}>
69-
{
70-
// @ts-ignore
71-
t(`flowDetail:compare_types.${c}`)
72-
}
73-
</SelectItem>
74-
)
75-
})}
85+
<SelectContent className='max-h-72'>
86+
<SelectGroup>
87+
{CONDITIONAL_OPERATOR.map((c) => {
88+
return (
89+
<SelectItem key={c} value={c}>
90+
{
91+
// @ts-ignore
92+
t(`flowDetail:compare_types.${c}`)
93+
}
94+
</SelectItem>
95+
)
96+
})}
97+
</SelectGroup>
7698
</SelectContent>
7799
</Select>
78100
<FormMessage />
79101
</FormItem>
80102
)}
81103
/>
82-
<FormField
83-
name='value'
84-
control={form.control}
85-
render={({ field }) => {
86-
return (
104+
{watchOperator === 'exist' ? (
105+
<FormField
106+
name='value'
107+
control={form.control}
108+
render={({ field }) => (
87109
<FormItem className='w-full'>
88110
<FormLabel>{t('value.label')}</FormLabel>
89-
<FormControl>
90-
<Input {...field} placeholder={t('value.placeholder')} />
91-
</FormControl>
111+
<Select
112+
onValueChange={field.onChange}
113+
defaultValue={field.value}
114+
>
115+
<FormControl>
116+
<SelectTrigger className='capitalize'>
117+
<SelectValue placeholder={t('value.placeholder')} />
118+
</SelectTrigger>
119+
</FormControl>
120+
<SelectContent>
121+
{['true', 'false'].map((c) => {
122+
return (
123+
<SelectItem key={c} value={c} className='capitalize'>
124+
{c}
125+
</SelectItem>
126+
)
127+
})}
128+
</SelectContent>
129+
</Select>
92130
<FormMessage />
93131
</FormItem>
94-
)
95-
}}
96-
/>
132+
)}
133+
/>
134+
) : (
135+
<FormField
136+
name='value'
137+
control={form.control}
138+
render={({ field }) => {
139+
return (
140+
<FormItem className='w-full'>
141+
<FormLabel>{t('value.label')}</FormLabel>
142+
<FormControl>
143+
<Input {...field} placeholder={t('value.placeholder')} />
144+
</FormControl>
145+
<FormMessage />
146+
</FormItem>
147+
)
148+
}}
149+
/>
150+
)}
97151
</div>
98152
</form>
99153
</Form>

client/src/components/pages/flow-detail/flow-inside.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const FlowInside = () => {
3434
handleDoubleClickNode,
3535
handleDoubleClickEdge,
3636
flow,
37+
handleNodesDelete,
3738
} = useFlowCtx()
3839
const { t } = useTranslation('common')
3940

@@ -89,6 +90,7 @@ export const FlowInside = () => {
8990
onInit={handleInit}
9091
onNodeDoubleClick={handleDoubleClickNode}
9192
onEdgeDoubleClick={handleDoubleClickEdge}
93+
onNodesDelete={handleNodesDelete}
9294
>
9395
<Background
9496
gap={24}

client/src/components/pages/flow-detail/flow-provider.tsx

Lines changed: 95 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
NodeChange,
1919
NodeMouseHandler,
2020
OnConnect,
21+
OnNodesDelete,
2122
ReactFlowInstance,
2223
addEdge,
2324
useEdgesState,
@@ -59,6 +60,7 @@ type FlowCtx = {
5960
setNodes: React.Dispatch<React.SetStateAction<Node<any>[]>>
6061
setEdges: React.Dispatch<React.SetStateAction<Edge<any>[]>>
6162
handleDeleteEdgeById: (id: string) => void
63+
handleNodesDelete: OnNodesDelete
6264
}
6365

6466
const FlowContext = createContext<FlowCtx | undefined>(undefined)
@@ -135,6 +137,8 @@ export const FlowProvider = ({ children, flow }: Props) => {
135137
selectedEdge,
136138
selectedNode,
137139
flows: nodes.map((node) => node.data),
140+
nodes,
141+
edges,
138142
})
139143

140144
const [reactFlowInstance, setReactFlowInstance] = useState<ReactFlowInstance<
@@ -499,6 +503,8 @@ export const FlowProvider = ({ children, flow }: Props) => {
499503
(edge) => edge.target === connection.target,
500504
).length
501505

506+
const targetNode = nodes.find((node) => node.id === connection.target)
507+
502508
if (connection.source === connection.target) return false
503509

504510
if (
@@ -526,6 +532,14 @@ export const FlowProvider = ({ children, flow }: Props) => {
526532
})
527533
}
528534

535+
if (
536+
(targetNode?.data.action === EActionTypes.CHECK_VARIABLES ||
537+
targetNode?.data.action === EActionTypes.PROMPT_AND_COLLECT) &&
538+
sourcesFromHandleInState < 1
539+
) {
540+
return true
541+
}
542+
529543
if (targetFromHandleInState === 1) return false
530544
if (sourcesFromHandleInState < 1) return true
531545

@@ -584,6 +598,33 @@ export const FlowProvider = ({ children, flow }: Props) => {
584598
})
585599
}, [])
586600

601+
/**
602+
* Finds a node in the given array of nodes that has the specified node as its next action.
603+
* @param nodes - The array of nodes to search in.
604+
* @param node - The node to find the next action for.
605+
* @returns The node that has the specified node as its next action, or undefined if not found.
606+
*/
607+
const findNodeIsNextAction = useCallback((nodes: Node[], node: Node<any>) => {
608+
return nodes.find((nd) => {
609+
return (
610+
nd.data?.nextAction === node.id ||
611+
nd.data?.nextActions?.some((na: any) => na.id === node.id)
612+
)
613+
})
614+
}, [])
615+
616+
const findNodeContainNextAction = useCallback(
617+
(nodes: Node[], node: Node<any>) => {
618+
return nodes.find((nd) => {
619+
return (
620+
nd.data?.nextAction === node.id ||
621+
nd.data?.nextActions?.some((na: any) => na.id === node.id)
622+
)
623+
})
624+
},
625+
[],
626+
)
627+
587628
/**
588629
* Retrieves the complete flows from the given nodes.
589630
* A complete flow is a sequence of nodes where each node has a next action or is a final node.
@@ -594,20 +635,6 @@ export const FlowProvider = ({ children, flow }: Props) => {
594635
const getCompleteFlows = useCallback(() => {
595636
let clonedNodes = _.cloneDeep(nodes)
596637

597-
/**
598-
* Checks if a given node is the next action of any of the cloned nodes.
599-
* @param node - The node to check.
600-
* @returns True if the node is the next action of any cloned node, false otherwise.
601-
*/
602-
const nodeIsNextAction = (node: Node<any>) => {
603-
return clonedNodes.some((nd) => {
604-
return (
605-
nd.data?.nextAction === node.id ||
606-
nd.data?.nextActions?.some((na: any) => na.id === node.id)
607-
)
608-
})
609-
}
610-
611638
/**
612639
* Checks if a node is to be removed.
613640
* @param node - The node to check.
@@ -622,7 +649,7 @@ export const FlowProvider = ({ children, flow }: Props) => {
622649
}
623650

624651
if (
625-
!nodeIsNextAction(node) &&
652+
!findNodeIsNextAction(clonedNodes, node) &&
626653
(!node.data?.nextAction || !node.data?.nextActions)
627654
) {
628655
return true
@@ -643,8 +670,59 @@ export const FlowProvider = ({ children, flow }: Props) => {
643670
}
644671

645672
return clonedNodes.map((node) => node.data)
646-
}, [nodes])
673+
}, [findNodeIsNextAction, nodes])
674+
675+
/**
676+
* Handles the deletion of nodes.
677+
* @param nodeDels - The nodes to be deleted.
678+
*/
679+
const handleNodesDelete: OnNodesDelete = useCallback(
680+
(nodeDels) => {
681+
const nodeToDelete = nodeDels[0]
647682

683+
// get node is have next action to remove this next action
684+
const nodeIsHaveNextAction = findNodeIsNextAction(nodes, nodeToDelete)
685+
686+
if (!nodeIsHaveNextAction) {
687+
return
688+
}
689+
690+
// remove next action from node is have next action
691+
if (nodeIsHaveNextAction?.data.nextAction) {
692+
delete nodeIsHaveNextAction.data.nextAction
693+
}
694+
695+
// remove next actions from node is have next action
696+
if (nodeIsHaveNextAction?.data.nextActions) {
697+
// remove next action from next actions
698+
nodeIsHaveNextAction.data.nextActions =
699+
nodeIsHaveNextAction.data.nextActions.filter(
700+
(na: any) => na.id !== nodeToDelete.id,
701+
)
702+
703+
// remove next actions if it empty
704+
if (nodeIsHaveNextAction.data.nextActions.length === 0) {
705+
delete nodeIsHaveNextAction.data.nextActions
706+
}
707+
}
708+
709+
setNodes((nodes) => {
710+
return nodes.map((node) => {
711+
if (node.id === nodeIsHaveNextAction.id) {
712+
return nodeIsHaveNextAction
713+
}
714+
715+
return node
716+
})
717+
})
718+
},
719+
[findNodeIsNextAction, nodes, setNodes],
720+
)
721+
722+
/**
723+
* Deletes an edge by its ID.
724+
* @param id - The ID of the edge to delete.
725+
*/
648726
const handleDeleteEdgeById = useCallback(
649727
(id: string) => {
650728
const edge = getEdge(id)
@@ -727,6 +805,7 @@ export const FlowProvider = ({ children, flow }: Props) => {
727805
setNodes,
728806
setEdges,
729807
handleDeleteEdgeById,
808+
handleNodesDelete,
730809
}}
731810
>
732811
{children}

client/src/components/pages/flow-detail/node-dialog/message.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const MessageDialogContent = () => {
8080
return (
8181
<div className='space-y-3'>
8282
<div className='space-y-2'>
83-
<Label>{t('message_dialog.responses')}</Label>
83+
<Label required>{t('message_dialog.responses')}</Label>
8484
<Select value={messageType} onValueChange={handleSelectChange}>
8585
<SelectTrigger>
8686
<SelectValue placeholder='Select a fruit' />
@@ -98,7 +98,7 @@ export const MessageDialogContent = () => {
9898
</div>
9999
{messageType !== EMessageTypes.LIST_BUTTON && (
100100
<div className='space-y-2'>
101-
<Label>{t('message_dialog.forms.bot_response.label')}</Label>
101+
<Label required>{t('message_dialog.forms.bot_response.label')}</Label>
102102
{messageType === EMessageTypes.TEXT && (
103103
<Input
104104
placeholder={t('message_dialog.forms.bot_response.placeholder')}

0 commit comments

Comments
 (0)