Skip to content

Commit 57a17a5

Browse files
committed
feat: check variable dialog
1 parent 35b3fa4 commit 57a17a5

File tree

9 files changed

+154
-48
lines changed

9 files changed

+154
-48
lines changed

client/src/components/forms/variables-setting.tsx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import {
99
toBoolean,
1010
} from '@/utils'
1111
import { zodResolver } from '@hookform/resolvers/zod'
12-
import { toNumber } from 'lodash'
12+
import _, { toNumber } from 'lodash'
1313
import { X } from 'lucide-react'
1414
import { useCallback } from 'react'
15-
import { useFieldArray, useForm } from 'react-hook-form'
15+
import { useFieldArray, useForm, useWatch } from 'react-hook-form'
1616
import { useTranslation } from 'react-i18next'
1717
import {
1818
Button,
@@ -73,8 +73,6 @@ export const VariablesSettingForm = ({
7373
},
7474
})
7575

76-
const variablesWatch = form.watch('variables')
77-
7876
const {
7977
fields: variables,
8078
append,
@@ -98,11 +96,22 @@ export const VariablesSettingForm = ({
9896
}
9997
}
10098

99+
const variablesWatch = useWatch({
100+
control: form.control,
101+
name: 'variables',
102+
})
103+
101104
const handleAddVariable = useCallback(() => {
102105
form.trigger()
103106

107+
if (
108+
!_.isEmpty(form.formState.errors.variables) ||
109+
variablesWatch?.some((field) => !field.name || !field.value)
110+
)
111+
return
112+
104113
append({ name: '', value: '', type: 'string' })
105-
}, [append, form])
114+
}, [append, form, variablesWatch])
106115

107116
const handleJsonChange = (
108117
e: React.ChangeEvent<HTMLTextAreaElement>,
@@ -146,12 +155,19 @@ export const VariablesSettingForm = ({
146155
}
147156
}
148157

158+
const variablesError = form.formState.errors.variables
159+
149160
return (
150161
<div className='space-y-3'>
151162
<div className='space-y-2 max-h-[25rem] overflow-y-auto pb-1 overflow-x-auto hidden-scroll pl-[2px]'>
152163
<Label>
153164
<span>{t('common:variables')}</span>
154165
</Label>
166+
{variablesError?.root?.message && (
167+
<div className='text-center p-1 rounded-md border border-destructive text-sm text-destructive bg-red-50'>
168+
{variablesError?.root?.message}
169+
</div>
170+
)}
155171
<Form {...form}>
156172
<form
157173
className='space-y-3 '

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
Workflow,
1212
} from 'lucide-react'
1313
import { useTranslation } from 'react-i18next'
14-
import { MessageDialogContent } from './node-dialog'
14+
import { CheckVariablesContent, MessageDialogContent } from './node-dialog'
1515

1616
export const MAP_ACTION_TO_LABEL: Record<EActionTypes, string> = {
1717
[EActionTypes.MESSAGE]: i18n.t('flowDetail:actions.items.message') as string,
@@ -70,12 +70,12 @@ export const MAP_ACTION: Record<
7070
[EActionTypes.PROMPT_AND_COLLECT]: {
7171
icon: () => <HelpCircle className='w-4 h-4' />,
7272
label: 'Prompt and collect',
73-
dialogContent: () => <div>Prompt and collect</div>,
73+
dialogContent: () => <MessageDialogContent />,
7474
},
7575
[EActionTypes.CHECK_VARIABLES]: {
7676
icon: () => <Variable className='w-4 h-4' />,
7777
label: 'Check variables',
78-
dialogContent: () => <div>Check variables</div>,
78+
dialogContent: () => <CheckVariablesContent />,
7979
},
8080
[EActionTypes.HTTP_REQUEST]: {
8181
icon: () => <GitPullRequest className='w-4 h-4' />,
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {
2+
Label,
3+
Select,
4+
SelectContent,
5+
SelectItem,
6+
SelectTrigger,
7+
SelectValue,
8+
} from '@/components/ui'
9+
import _ from 'lodash'
10+
import { useTranslation } from 'react-i18next'
11+
import { useFlowCtx } from '..'
12+
13+
export const CheckVariablesContent = () => {
14+
const { flow, selectedNode, handleChangeSelectedNode } = useFlowCtx()
15+
const { t } = useTranslation(['flowDetail', 'forms'])
16+
17+
return (
18+
<div className='space-y-2'>
19+
<Label>{t('forms:variable.label')}</Label>
20+
<Select
21+
value={selectedNode?.data?.variable}
22+
onValueChange={(value) => {
23+
if (!selectedNode) return
24+
25+
const cloneSelectedNode = _.cloneDeep(selectedNode)
26+
27+
cloneSelectedNode.data.variable = value
28+
29+
handleChangeSelectedNode(cloneSelectedNode)
30+
}}
31+
>
32+
<SelectTrigger>
33+
<SelectValue placeholder={t('forms:variable.placeholder')} />
34+
</SelectTrigger>
35+
<SelectContent>
36+
{flow.variables?.map((variable, index) => {
37+
return (
38+
<SelectItem
39+
key={`${variable.name}-${index}`}
40+
value={variable.name}
41+
>
42+
{variable.name}
43+
</SelectItem>
44+
)
45+
})}
46+
</SelectContent>
47+
</Select>
48+
</div>
49+
)
50+
}
51+
52+
export default CheckVariablesContent
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export * from './check-variables'
12
export * from './message'
23
export * from './node-dialog'

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

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const NodeDialog = () => {
6767
<DialogTrigger asChild>
6868
<div className='!hidden'></div>
6969
</DialogTrigger>
70-
<DialogContent className='max-w-3xl'>
70+
<DialogContent className='max-w-xl'>
7171
<DialogHeader>
7272
<DialogTitle>{selectedNode?.data.label}</DialogTitle>
7373
<DialogDescription>{t('node_dialog.desc')}</DialogDescription>
@@ -83,28 +83,30 @@ export const NodeDialog = () => {
8383
placeholder={t('forms:name.placeholder')}
8484
/>
8585
</div>
86-
<div className='space-y-2'>
87-
<Label>{t('forms:bot_lang.label')}</Label>
88-
<Select
89-
onValueChange={(value) => {
90-
handleChangeLang(value)
91-
}}
92-
value={currentLang}
93-
>
94-
<SelectTrigger>
95-
<SelectValue placeholder={t('forms:bot_lang.placeholder')} />
96-
</SelectTrigger>
97-
<SelectContent>
98-
{Object.keys(LANGS).map((lang) => {
99-
return (
100-
<SelectItem key={lang} value={lang}>
101-
{LANGS[lang]}
102-
</SelectItem>
103-
)
104-
})}
105-
</SelectContent>
106-
</Select>
107-
</div>
86+
{selectedNode.data.action !== EActionTypes.CHECK_VARIABLES && (
87+
<div className='space-y-2'>
88+
<Label>{t('forms:bot_lang.label')}</Label>
89+
<Select
90+
onValueChange={(value) => {
91+
handleChangeLang(value)
92+
}}
93+
value={currentLang}
94+
>
95+
<SelectTrigger>
96+
<SelectValue placeholder={t('forms:bot_lang.placeholder')} />
97+
</SelectTrigger>
98+
<SelectContent>
99+
{Object.keys(LANGS).map((lang) => {
100+
return (
101+
<SelectItem key={lang} value={lang}>
102+
{LANGS[lang]}
103+
</SelectItem>
104+
)
105+
})}
106+
</SelectContent>
107+
</Select>
108+
</div>
109+
)}
108110
{selectedNode &&
109111
MAP_ACTION[
110112
selectedNode.data.action as unknown as EActionTypes

client/src/lib/schema/flow-input.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,35 @@ export const useFlowInputSchema = () => {
77

88
const variableSchema = useVariableInputSchema()
99

10-
return z.object({
11-
name: z
12-
.string({
13-
required_error: t('name.errors.required'),
14-
})
15-
.min(1, {
16-
message: t('name.errors.required'),
17-
}),
18-
edges: z.array(z.record(z.any())).optional(),
19-
nodes: z.array(z.record(z.any())).optional(),
20-
settings: z.array(z.record(z.any())).optional(),
21-
variables: z.array(variableSchema).optional(),
22-
flows: z.array(z.record(z.any())).optional(),
23-
channelIds: z.array(z.string()).optional(),
24-
})
10+
return z
11+
.object({
12+
name: z
13+
.string({
14+
required_error: t('name.errors.required'),
15+
})
16+
.min(1, {
17+
message: t('name.errors.required'),
18+
}),
19+
edges: z.array(z.record(z.any())).optional(),
20+
nodes: z.array(z.record(z.any())).optional(),
21+
settings: z.array(z.record(z.any())).optional(),
22+
variables: z.array(variableSchema).optional(),
23+
flows: z.array(z.record(z.any())).optional(),
24+
channelIds: z.array(z.string()).optional(),
25+
})
26+
.superRefine(({ variables }, ctx) => {
27+
if (!variables) return
28+
29+
const names = variables.map((variable) => variable.name)
30+
31+
if (new Set(names).size !== names.length) {
32+
return ctx.addIssue({
33+
code: z.ZodIssueCode.custom,
34+
message: t('variable_name.errors.unique'),
35+
path: ['variables'],
36+
})
37+
}
38+
})
2539
}
2640

2741
export type TFlowInput = z.infer<ReturnType<typeof useFlowInputSchema>>

client/src/lib/schema/variable-input.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export const useVariableInputSchema = () => {
1818
})
1919
.min(1, {
2020
message: t('variable_name.errors.required'),
21+
})
22+
.regex(/^[a-zA-Z][a-zA-Z\d_]*$/, {
23+
message: t('variable_name.errors.invalid'),
2124
}),
2225
value: z.any().optional(),
2326
type: z

client/src/locales/en/forms.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@
112112
"label": "Variable {{index}}",
113113
"placeholder": "Enter variable name",
114114
"errors": {
115-
"required": "Please enter variable name."
115+
"required": "Please enter variable name.",
116+
"invalid": "Please enter a valid variable name.",
117+
"unique": "Variable name must be unique."
116118
}
117119
},
118120
"variable_value": {
@@ -176,5 +178,12 @@
176178
"errors": {
177179
"required": "Please enter image URL."
178180
}
181+
},
182+
"variable": {
183+
"label": "Variable",
184+
"placeholder": "Select variable",
185+
"errors": {
186+
"required": "Please select variable."
187+
}
179188
}
180189
}

client/src/locales/vi/forms.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@
112112
"label": "Biến {{index}}",
113113
"placeholder": "Nhập tên biến",
114114
"errors": {
115-
"required": "Vui lòng nhập tên biến."
115+
"required": "Vui lòng nhập tên biến.",
116+
"invalid": "Tên biến không hợp lệ.",
117+
"unique": "Tên biến đã tồn tại."
116118
}
117119
},
118120
"variable_value": {
@@ -169,5 +171,12 @@
169171
"errors": {
170172
"required": "Vui lòng nhập phụ đề."
171173
}
174+
},
175+
"variable": {
176+
"label": "Biến",
177+
"placeholder": "Chọn biến",
178+
"errors": {
179+
"required": "Vui lòng chọn biến."
180+
}
172181
}
173182
}

0 commit comments

Comments
 (0)