Skip to content

Commit 0d80195

Browse files
committed
feat: setting variables
1 parent 95e3d4c commit 0d80195

36 files changed

+1260
-581
lines changed

client/package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"i18next-chained-backend": "^4.6.2",
6565
"i18next-http-backend": "^2.4.3",
6666
"i18next-localstorage-backend": "^4.2.0",
67+
"lodash": "^4.17.21",
6768
"lucide-react": "^0.335.0",
6869
"next-themes": "^0.2.1",
6970
"react": "^18.2.0",
@@ -88,6 +89,7 @@
8889
"@tailwindcss/typography": "^0.5.10",
8990
"@tanstack/router-vite-plugin": "^1.16.5",
9091
"@types/crypto-js": "^4.2.2",
92+
"@types/lodash": "^4.17.0",
9193
"@types/mdx": "^2.0.11",
9294
"@types/node": "^20.11.24",
9395
"@types/react": "^18.2.55",

client/src/components/forms/channel.tsx

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1+
import { useDidUpdate } from '@/hooks/use-did-update'
2+
import { useErrorsLngChange } from '@/hooks/use-errors-lng-change'
3+
import { queryChannelTypesOption } from '@/lib/query-options/channel'
4+
import { queryFlowsForSelectOption } from '@/lib/query-options/flow'
5+
import { TChannelInput, useChannelSchema } from '@/lib/schema/channel'
6+
import { ChannelType } from '@/types/channel'
7+
import { zodResolver } from '@hookform/resolvers/zod'
8+
import { useQuery } from '@tanstack/react-query'
9+
import { useMemo } from 'react'
110
import { useForm } from 'react-hook-form'
11+
import { useTranslation } from 'react-i18next'
212
import {
313
Form,
414
FormControl,
@@ -15,16 +25,6 @@ import {
1525
SelectValue,
1626
Switch,
1727
} from '../ui'
18-
import { TChannelInput, useChannelSchema } from '@/lib/schema/channel'
19-
import { useErrorsLngChange } from '@/hooks/use-errors-lng-change'
20-
import { useTranslation } from 'react-i18next'
21-
import { useQuery } from '@tanstack/react-query'
22-
import { queryChannelTypesOption } from '@/lib/query-options/channel'
23-
import { zodResolver } from '@hookform/resolvers/zod'
24-
import { ChannelType } from '@/types/channel'
25-
import { useMemo } from 'react'
26-
import { useDidUpdate } from '@/hooks/use-did-update'
27-
import { queryFlowsForSelectOption } from '@/lib/query-options/flow'
2828

2929
type Props = {
3030
id?: string
@@ -138,13 +138,12 @@ const ChannelForm = ({
138138
render={({ field }) => (
139139
<FormItem>
140140
<FormLabel>
141-
<Label>{t('active.label')}</Label>
141+
<Label>{t('flow.label')}</Label>
142142
</FormLabel>
143-
144143
<Select onValueChange={field.onChange} defaultValue={field.value}>
145144
<FormControl>
146145
<SelectTrigger>
147-
<SelectValue placeholder='Select a verified email to display' />
146+
<SelectValue placeholder={t('flow.placeholder')} />
148147
</SelectTrigger>
149148
</FormControl>
150149
<SelectContent>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
export * from './change-pass'
22
export * from './forgot-pass'
3+
export * from './general-setting'
34
export * from './login'
45
export * from './register'
56
export * from './set-pass'
7+
export * from './setting-mail'
8+
export * from './variables-setting'
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { useErrorsLngChange } from '@/hooks/use-errors-lng-change'
2+
import { TFlowInput, useFlowInputSchema } from '@/lib/schema/flow-input'
3+
import { isStringBoolean, isStringNumber, toBoolean } from '@/utils'
4+
import { zodResolver } from '@hookform/resolvers/zod'
5+
import { toNumber } from 'lodash'
6+
import { X } from 'lucide-react'
7+
import { useCallback } from 'react'
8+
import { useFieldArray, useForm } from 'react-hook-form'
9+
import { useTranslation } from 'react-i18next'
10+
import {
11+
Button,
12+
Form,
13+
FormControl,
14+
FormField,
15+
FormItem,
16+
FormMessage,
17+
Input,
18+
Label,
19+
Select,
20+
SelectContent,
21+
SelectItem,
22+
SelectTrigger,
23+
SelectValue,
24+
} from '../ui'
25+
26+
type Props = {
27+
id?: string
28+
onSubmit?: (data: TFlowInput) => void
29+
defaultValues?: TFlowInput
30+
}
31+
32+
const parseVariableValue = (value: string) => {
33+
if (isStringNumber(value)) return toNumber(value)
34+
if (isStringBoolean(value)) return toBoolean(value)
35+
return value
36+
}
37+
38+
export const VariablesSettingForm = ({
39+
id = 'variables-setting-form',
40+
onSubmit,
41+
defaultValues,
42+
}: Props) => {
43+
const { t } = useTranslation(['forms', 'common'])
44+
const schema = useFlowInputSchema()
45+
const form = useForm<TFlowInput>({
46+
resolver: zodResolver(schema),
47+
mode: 'onChange',
48+
defaultValues: {
49+
...defaultValues,
50+
},
51+
})
52+
const {
53+
fields: variables,
54+
append,
55+
prepend,
56+
remove,
57+
swap,
58+
move,
59+
insert,
60+
} = useFieldArray({
61+
control: form.control,
62+
name: 'variables', // unique name for your Field Array
63+
})
64+
65+
useErrorsLngChange(form)
66+
67+
const handleSubmit = (data: TFlowInput) => {
68+
if (onSubmit) {
69+
onSubmit({
70+
...data,
71+
variables: data.variables?.map((variable) => ({
72+
...variable,
73+
value: parseVariableValue(variable.value),
74+
})),
75+
})
76+
}
77+
}
78+
79+
const handleAddVariable = useCallback(() => {
80+
form.trigger()
81+
82+
append({ name: '', value: '', type: 'string' })
83+
}, [append, form])
84+
85+
return (
86+
<div className='space-y-3'>
87+
<div className='space-y-2 max-h-[25rem] overflow-y-auto pb-1 overflow-x-auto hidden-scroll pl-[2px]'>
88+
<Label>
89+
<span>{t('common:variables')}</span>
90+
</Label>
91+
<Form {...form}>
92+
<form
93+
className='space-y-3 '
94+
id={id}
95+
onSubmit={form.handleSubmit(handleSubmit)}
96+
>
97+
{variables.length > 0 ? (
98+
variables.map((variable, index) => {
99+
return (
100+
<div key={variable.id} className='flex gap-3 w-full'>
101+
<FormField
102+
name={`variables.${index}.name`}
103+
control={form.control}
104+
render={({ field }) => {
105+
return (
106+
<FormItem className='w-full'>
107+
<FormControl>
108+
<Input
109+
{...field}
110+
autoComplete='off'
111+
placeholder={t('variable_name.placeholder')}
112+
/>
113+
</FormControl>
114+
<FormMessage />
115+
</FormItem>
116+
)
117+
}}
118+
/>
119+
<FormField
120+
name={`variables.${index}.value`}
121+
control={form.control}
122+
render={({ field }) => {
123+
return (
124+
<FormItem className='w-full'>
125+
<FormControl>
126+
<Input
127+
{...field}
128+
autoComplete='off'
129+
placeholder={t('variable_value.placeholder')}
130+
/>
131+
</FormControl>
132+
<FormMessage />
133+
</FormItem>
134+
)
135+
}}
136+
/>
137+
<FormField
138+
name={`variables.${index}.type`}
139+
control={form.control}
140+
render={({ field }) => {
141+
return (
142+
<FormItem className='w-24 flex-shrink-0'>
143+
<Select
144+
onValueChange={field.onChange}
145+
defaultValue={field.value}
146+
>
147+
<FormControl>
148+
<SelectTrigger>
149+
<SelectValue
150+
placeholder={t('variable_type.placeholder')}
151+
/>
152+
</SelectTrigger>
153+
</FormControl>
154+
<SelectContent>
155+
{['string', 'number', 'boolean'].map((type) => (
156+
<SelectItem key={type} value={type}>
157+
{type}
158+
</SelectItem>
159+
))}
160+
</SelectContent>
161+
</Select>
162+
<FormMessage />
163+
</FormItem>
164+
)
165+
}}
166+
/>
167+
<Button
168+
className='p-0 w-9 h-9 flex-shrink-0'
169+
variant='destructive'
170+
type='button'
171+
onClick={() => remove(index)}
172+
>
173+
<X />
174+
</Button>
175+
</div>
176+
)
177+
})
178+
) : (
179+
<span className='text-sm text-muted-foreground block text-center py-2'>
180+
Not have any variable, click button below to add new variable.
181+
</span>
182+
)}
183+
</form>
184+
</Form>
185+
</div>
186+
<Button onClick={handleAddVariable} className='w-full'>
187+
Add new variable
188+
</Button>
189+
</div>
190+
)
191+
}
192+
193+
export default VariablesSettingForm

client/src/components/pages/channels/row-action.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const RowActions = ({ row }: Props) => {
4343
<MoreHorizontal className='w-4 h-4' />
4444
</Button>
4545
</DropdownMenuTrigger>
46-
<DropdownMenuContent align='start'>
46+
<DropdownMenuContent align='start' className='max-h-svh scroll-auto'>
4747
<Sheet
4848
open={open}
4949
onOpenChange={(value) => {
@@ -53,6 +53,7 @@ const RowActions = ({ row }: Props) => {
5353
setOpenDropdown(false)
5454
}
5555
}}
56+
modal
5657
>
5758
<SheetTrigger asChild>
5859
<DropdownMenuItem
@@ -84,7 +85,7 @@ const RowActions = ({ row }: Props) => {
8485
: undefined,
8586
active: row.original.active,
8687
contactName: row.original.contactName,
87-
flowId: row.original.flowId,
88+
flowId: row.original.flowId || undefined,
8889
}}
8990
onSubmit={async (data) => {
9091
await updateChannelMutation.mutateAsync({

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

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ import { cn } from '@/lib/utils'
33
import { EActionTypes } from '@/types/flow'
44
import { X } from 'lucide-react'
55
import { DragEvent, useCallback, useLayoutEffect, useRef } from 'react'
6+
import { useTranslation } from 'react-i18next'
67
import { useFlowCtx } from '.'
7-
import { ACTIONS, MAP_ACTION_TO_LABEL } from './constant'
8+
import { ACTIONS, useMapActionToLabel } from './constant'
89

910
export const Actions = () => {
11+
const { t } = useTranslation('flowDetail')
1012
const { openActions, toggleActions } = useFlowCtx()
1113
const actionsRef = useRef<HTMLDivElement>(null)
14+
const actionToLabel = useMapActionToLabel()
1215

1316
const handleDragStart = useCallback(
1417
(e: DragEvent<HTMLDivElement>, type: EActionTypes) => {
15-
console.log('drag start')
1618
e.dataTransfer.setData('application/reactflow', type)
1719
e.dataTransfer.effectAllowed = 'move'
1820
},
@@ -42,7 +44,7 @@ export const Actions = () => {
4244
<div>
4345
<div className='flex items-center justify-between'>
4446
<span className='text-lg font-semibold leading-none tracking-tight'>
45-
Actions
47+
{t('actions.title')}
4648
</span>
4749
<Button
4850
className='w-4 h-4 p-0 hover:bg-transparent'
@@ -52,16 +54,9 @@ export const Actions = () => {
5254
<X className='w-4 h-4' />
5355
</Button>
5456
</div>
55-
<p className='text-sm text-muted-foreground'>
56-
Drag and drop actions to create your node
57-
</p>
57+
<p className='text-sm text-muted-foreground'>{t('actions.desc')}</p>
5858
</div>
5959
<div className='grid gap-2'>
60-
<div
61-
onDragStart={(e) => handleDragStart(e, EActionTypes.CHECK_VARIABLES)}
62-
>
63-
hi
64-
</div>
6560
{ACTIONS.map((action) => {
6661
return (
6762
<div
@@ -71,7 +66,7 @@ export const Actions = () => {
7166
draggable
7267
>
7368
{action.Icon()}
74-
<span>{MAP_ACTION_TO_LABEL[action.type]}</span>
69+
<span>{actionToLabel[action.type]}</span>
7570
</div>
7671
)
7772
})}

0 commit comments

Comments
 (0)