mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Add variable assignment node #10427 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -76,7 +76,6 @@ export enum Operator {
|
||||
Relevant = 'Relevant',
|
||||
RewriteQuestion = 'RewriteQuestion',
|
||||
KeywordExtract = 'KeywordExtract',
|
||||
Baidu = 'Baidu',
|
||||
DuckDuckGo = 'DuckDuckGo',
|
||||
Wikipedia = 'Wikipedia',
|
||||
PubMed = 'PubMed',
|
||||
@ -85,7 +84,6 @@ export enum Operator {
|
||||
Bing = 'Bing',
|
||||
GoogleScholar = 'GoogleScholar',
|
||||
GitHub = 'GitHub',
|
||||
BaiduFanyi = 'BaiduFanyi',
|
||||
QWeather = 'QWeather',
|
||||
ExeSQL = 'ExeSQL',
|
||||
Switch = 'Switch',
|
||||
@ -111,6 +109,7 @@ export enum Operator {
|
||||
SearXNG = 'SearXNG',
|
||||
Placeholder = 'Placeholder',
|
||||
DataOperations = 'DataOperations',
|
||||
VariableAssigner = 'VariableAssigner',
|
||||
File = 'File', // pipeline
|
||||
Parser = 'Parser',
|
||||
Tokenizer = 'Tokenizer',
|
||||
|
||||
@ -27,9 +27,12 @@ export function useBuildSwitchOperatorOptions(
|
||||
const { t } = useTranslation();
|
||||
|
||||
const switchOperatorOptions = useMemo(() => {
|
||||
return SwitchOperatorOptions.filter((x) =>
|
||||
subset.some((y) => y === x.value),
|
||||
).map((x) => ({
|
||||
const filteredOptions =
|
||||
subset.length > 0
|
||||
? SwitchOperatorOptions.filter((x) => subset.some((y) => y === x.value))
|
||||
: SwitchOperatorOptions;
|
||||
|
||||
return filteredOptions.map((x) => ({
|
||||
value: x.value,
|
||||
icon: (
|
||||
<LogicalOperatorIcon
|
||||
@ -39,7 +42,7 @@ export function useBuildSwitchOperatorOptions(
|
||||
),
|
||||
label: t(`flow.switchOperatorOptions.${x.label}`),
|
||||
}));
|
||||
}, [t]);
|
||||
}, [subset, t]);
|
||||
|
||||
return switchOperatorOptions;
|
||||
}
|
||||
|
||||
@ -1547,6 +1547,9 @@ This delimiter is used to split the input text into several text pieces echo of
|
||||
codeDescription: 'It allows developers to write custom Python logic.',
|
||||
dataOperations: 'Data operations',
|
||||
dataOperationsDescription: 'Perform various operations on a Data object.',
|
||||
variableAssigner: 'Variable assigner',
|
||||
variableAssignerDescription:
|
||||
'This component performs operations on Data objects, including extracting, filtering, and editing keys and values in the Data.',
|
||||
inputVariables: 'Input variables',
|
||||
runningHintText: 'is running...🕞',
|
||||
openingSwitch: 'Opening switch',
|
||||
|
||||
@ -1468,6 +1468,9 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
codeDescription: '它允许开发人员编写自定义 Python 逻辑。',
|
||||
dataOperations: '数据操作',
|
||||
dataOperationsDescription: '对数据对象执行各种操作。',
|
||||
variableAssigner: '变量赋值器',
|
||||
variableAssignerDescription:
|
||||
'此组件对数据对象执行操作,包括提取、筛选和编辑数据中的键和值。',
|
||||
inputVariables: '输入变量',
|
||||
addVariable: '新增变量',
|
||||
runningHintText: '正在运行中...🕞',
|
||||
|
||||
@ -72,6 +72,7 @@ import { SwitchNode } from './node/switch-node';
|
||||
import { TemplateNode } from './node/template-node';
|
||||
import TokenizerNode from './node/tokenizer-node';
|
||||
import { ToolNode } from './node/tool-node';
|
||||
import { VariableAssignerNode } from './node/variable-assigner-node';
|
||||
|
||||
export const nodeTypes: NodeTypes = {
|
||||
ragNode: RagNode,
|
||||
@ -98,6 +99,7 @@ export const nodeTypes: NodeTypes = {
|
||||
splitterNode: SplitterNode,
|
||||
contextNode: ExtractorNode,
|
||||
dataOperationsNode: DataOperationsNode,
|
||||
variableAssignerNode: VariableAssignerNode,
|
||||
};
|
||||
|
||||
const edgeTypes = {
|
||||
|
||||
@ -79,6 +79,7 @@ export function AccordionOperators({
|
||||
Operator.Code,
|
||||
Operator.StringTransform,
|
||||
Operator.DataOperations,
|
||||
Operator.VariableAssigner,
|
||||
]}
|
||||
isCustomDropdown={isCustomDropdown}
|
||||
mousePosition={mousePosition}
|
||||
|
||||
11
web/src/pages/agent/canvas/node/variable-assigner-node.tsx
Normal file
11
web/src/pages/agent/canvas/node/variable-assigner-node.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { IRagNode } from '@/interfaces/database/agent';
|
||||
import { NodeProps } from '@xyflow/react';
|
||||
import { RagNode } from '.';
|
||||
|
||||
export function VariableAssignerNode({ ...props }: NodeProps<IRagNode>) {
|
||||
return (
|
||||
<RagNode {...props}>
|
||||
<section>select</section>
|
||||
</RagNode>
|
||||
);
|
||||
}
|
||||
@ -66,106 +66,6 @@ export const AgentOperatorList = [
|
||||
Operator.Agent,
|
||||
];
|
||||
|
||||
export const componentMenuList = [
|
||||
{
|
||||
name: Operator.Retrieval,
|
||||
},
|
||||
{
|
||||
name: Operator.Categorize,
|
||||
},
|
||||
{
|
||||
name: Operator.Message,
|
||||
},
|
||||
|
||||
{
|
||||
name: Operator.RewriteQuestion,
|
||||
},
|
||||
{
|
||||
name: Operator.KeywordExtract,
|
||||
},
|
||||
{
|
||||
name: Operator.Switch,
|
||||
},
|
||||
{
|
||||
name: Operator.Iteration,
|
||||
},
|
||||
{
|
||||
name: Operator.Code,
|
||||
},
|
||||
{
|
||||
name: Operator.WaitingDialogue,
|
||||
},
|
||||
{
|
||||
name: Operator.Agent,
|
||||
},
|
||||
{
|
||||
name: Operator.Note,
|
||||
},
|
||||
{
|
||||
name: Operator.DuckDuckGo,
|
||||
},
|
||||
{
|
||||
name: Operator.Baidu,
|
||||
},
|
||||
{
|
||||
name: Operator.Wikipedia,
|
||||
},
|
||||
{
|
||||
name: Operator.PubMed,
|
||||
},
|
||||
{
|
||||
name: Operator.ArXiv,
|
||||
},
|
||||
{
|
||||
name: Operator.Google,
|
||||
},
|
||||
{
|
||||
name: Operator.Bing,
|
||||
},
|
||||
{
|
||||
name: Operator.GoogleScholar,
|
||||
},
|
||||
{
|
||||
name: Operator.GitHub,
|
||||
},
|
||||
{
|
||||
name: Operator.BaiduFanyi,
|
||||
},
|
||||
{
|
||||
name: Operator.QWeather,
|
||||
},
|
||||
{
|
||||
name: Operator.ExeSQL,
|
||||
},
|
||||
{
|
||||
name: Operator.WenCai,
|
||||
},
|
||||
{
|
||||
name: Operator.AkShare,
|
||||
},
|
||||
{
|
||||
name: Operator.YahooFinance,
|
||||
},
|
||||
{
|
||||
name: Operator.Jin10,
|
||||
},
|
||||
{
|
||||
name: Operator.TuShare,
|
||||
},
|
||||
{
|
||||
name: Operator.Crawler,
|
||||
},
|
||||
{
|
||||
name: Operator.Invoke,
|
||||
},
|
||||
{
|
||||
name: Operator.Email,
|
||||
},
|
||||
{
|
||||
name: Operator.SearXNG,
|
||||
},
|
||||
];
|
||||
|
||||
export const DataOperationsOperatorOptions = [
|
||||
ComparisonOperator.Equal,
|
||||
ComparisonOperator.NotEqual,
|
||||
@ -281,11 +181,6 @@ export const initialSearXNGValues = {
|
||||
},
|
||||
};
|
||||
|
||||
export const initialBaiduValues = {
|
||||
top_n: 10,
|
||||
...initialQueryBaseValues,
|
||||
};
|
||||
|
||||
export const initialWikipediaValues = {
|
||||
top_n: 10,
|
||||
language: 'en',
|
||||
@ -385,13 +280,6 @@ export const initialGithubValues = {
|
||||
},
|
||||
};
|
||||
|
||||
export const initialBaiduFanyiValues = {
|
||||
appid: 'xxx',
|
||||
secret_key: 'xxx',
|
||||
trans_type: 'translate',
|
||||
...initialQueryBaseValues,
|
||||
};
|
||||
|
||||
export const initialQWeatherValues = {
|
||||
web_apikey: 'xxx',
|
||||
type: 'weather',
|
||||
@ -717,6 +605,8 @@ export const initialDataOperationsValues = {
|
||||
},
|
||||
};
|
||||
|
||||
export const initialVariableAssignerValues = {};
|
||||
|
||||
export const CategorizeAnchorPointPositions = [
|
||||
{ top: 1, right: 34 },
|
||||
{ top: 8, right: 18 },
|
||||
@ -757,7 +647,6 @@ export const RestrictedUpstreamMap = {
|
||||
Operator.Message,
|
||||
Operator.Relevant,
|
||||
],
|
||||
[Operator.Baidu]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.DuckDuckGo]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.Wikipedia]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.PubMed]: [Operator.Begin, Operator.Retrieval],
|
||||
@ -766,7 +655,6 @@ export const RestrictedUpstreamMap = {
|
||||
[Operator.Bing]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.GoogleScholar]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.GitHub]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.BaiduFanyi]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.QWeather]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.SearXNG]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.ExeSQL]: [Operator.Begin],
|
||||
@ -798,6 +686,7 @@ export const RestrictedUpstreamMap = {
|
||||
[Operator.Tokenizer]: [Operator.Begin],
|
||||
[Operator.Extractor]: [Operator.Begin],
|
||||
[Operator.File]: [Operator.Begin],
|
||||
[Operator.VariableAssigner]: [Operator.Begin],
|
||||
};
|
||||
|
||||
export const NodeMap = {
|
||||
@ -809,7 +698,6 @@ export const NodeMap = {
|
||||
[Operator.RewriteQuestion]: 'rewriteNode',
|
||||
[Operator.KeywordExtract]: 'keywordNode',
|
||||
[Operator.DuckDuckGo]: 'ragNode',
|
||||
[Operator.Baidu]: 'ragNode',
|
||||
[Operator.Wikipedia]: 'ragNode',
|
||||
[Operator.PubMed]: 'ragNode',
|
||||
[Operator.ArXiv]: 'ragNode',
|
||||
@ -817,7 +705,6 @@ export const NodeMap = {
|
||||
[Operator.Bing]: 'ragNode',
|
||||
[Operator.GoogleScholar]: 'ragNode',
|
||||
[Operator.GitHub]: 'ragNode',
|
||||
[Operator.BaiduFanyi]: 'ragNode',
|
||||
[Operator.QWeather]: 'ragNode',
|
||||
[Operator.SearXNG]: 'ragNode',
|
||||
[Operator.ExeSQL]: 'ragNode',
|
||||
@ -849,6 +736,7 @@ export const NodeMap = {
|
||||
[Operator.HierarchicalMerger]: 'splitterNode',
|
||||
[Operator.Extractor]: 'contextNode',
|
||||
[Operator.DataOperations]: 'dataOperationsNode',
|
||||
[Operator.VariableAssigner]: 'variableAssignerNode',
|
||||
};
|
||||
|
||||
export enum BeginQueryType {
|
||||
|
||||
@ -2,8 +2,6 @@ import { Operator } from '../constant';
|
||||
import AgentForm from '../form/agent-form';
|
||||
import AkShareForm from '../form/akshare-form';
|
||||
import ArXivForm from '../form/arxiv-form';
|
||||
import BaiduFanyiForm from '../form/baidu-fanyi-form';
|
||||
import BaiduForm from '../form/baidu-form';
|
||||
import BeginForm from '../form/begin-form';
|
||||
import BingForm from '../form/bing-form';
|
||||
import CategorizeForm from '../form/categorize-form';
|
||||
@ -72,9 +70,6 @@ export const FormConfigMap = {
|
||||
[Operator.Agent]: {
|
||||
component: AgentForm,
|
||||
},
|
||||
[Operator.Baidu]: {
|
||||
component: BaiduForm,
|
||||
},
|
||||
[Operator.DuckDuckGo]: {
|
||||
component: DuckDuckGoForm,
|
||||
},
|
||||
@ -102,9 +97,6 @@ export const FormConfigMap = {
|
||||
[Operator.GitHub]: {
|
||||
component: GithubForm,
|
||||
},
|
||||
[Operator.BaiduFanyi]: {
|
||||
component: BaiduFanyiForm,
|
||||
},
|
||||
[Operator.QWeather]: {
|
||||
component: QWeatherForm,
|
||||
},
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { Form, Input, Select } from 'antd';
|
||||
import { useMemo } from 'react';
|
||||
import { IOperatorForm } from '../../interface';
|
||||
import {
|
||||
BaiduFanyiDomainOptions,
|
||||
BaiduFanyiSourceLangOptions,
|
||||
} from '../../options';
|
||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
||||
|
||||
const BaiduFanyiForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
||||
const { t } = useTranslate('flow');
|
||||
const options = useMemo(() => {
|
||||
return ['translate', 'fieldtranslate'].map((x) => ({
|
||||
value: x,
|
||||
label: t(`baiduSecretKeyOptions.${x}`),
|
||||
}));
|
||||
}, [t]);
|
||||
|
||||
const baiduFanyiOptions = useMemo(() => {
|
||||
return BaiduFanyiDomainOptions.map((x) => ({
|
||||
value: x,
|
||||
label: t(`baiduDomainOptions.${x}`),
|
||||
}));
|
||||
}, [t]);
|
||||
|
||||
const baiduFanyiSourceLangOptions = useMemo(() => {
|
||||
return BaiduFanyiSourceLangOptions.map((x) => ({
|
||||
value: x,
|
||||
label: t(`baiduSourceLangOptions.${x}`),
|
||||
}));
|
||||
}, [t]);
|
||||
|
||||
return (
|
||||
<Form
|
||||
name="basic"
|
||||
autoComplete="off"
|
||||
form={form}
|
||||
onValuesChange={onValuesChange}
|
||||
layout={'vertical'}
|
||||
>
|
||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
||||
<Form.Item label={t('appid')} name={'appid'}>
|
||||
<Input></Input>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('secretKey')} name={'secret_key'}>
|
||||
<Input></Input>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('transType')} name={'trans_type'}>
|
||||
<Select options={options}></Select>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle dependencies={['model_type']}>
|
||||
{({ getFieldValue }) =>
|
||||
getFieldValue('trans_type') === 'fieldtranslate' && (
|
||||
<Form.Item label={t('domain')} name={'domain'}>
|
||||
<Select options={baiduFanyiOptions}></Select>
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
</Form.Item>
|
||||
<Form.Item label={t('sourceLang')} name={'source_lang'}>
|
||||
<Select options={baiduFanyiSourceLangOptions}></Select>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('targetLang')} name={'target_lang'}>
|
||||
<Select options={baiduFanyiSourceLangOptions}></Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaiduFanyiForm;
|
||||
@ -1,22 +0,0 @@
|
||||
import { TopNFormField } from '@/components/top-n-item';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
||||
|
||||
const BaiduForm = ({ form, node }: INextOperatorForm) => {
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="space-y-6"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
||||
<TopNFormField></TopNFormField>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default BaiduForm;
|
||||
@ -12,8 +12,6 @@ import {
|
||||
initialAgentValues,
|
||||
initialAkShareValues,
|
||||
initialArXivValues,
|
||||
initialBaiduFanyiValues,
|
||||
initialBaiduValues,
|
||||
initialBeginValues,
|
||||
initialBingValues,
|
||||
initialCategorizeValues,
|
||||
@ -50,6 +48,7 @@ import {
|
||||
initialTokenizerValues,
|
||||
initialTuShareValues,
|
||||
initialUserFillUpValues,
|
||||
initialVariableAssignerValues,
|
||||
initialWaitingDialogueValues,
|
||||
initialWenCaiValues,
|
||||
initialWikipediaValues,
|
||||
@ -86,7 +85,6 @@ export const useInitializeOperatorParams = () => {
|
||||
llm_id: llmId,
|
||||
},
|
||||
[Operator.DuckDuckGo]: initialDuckValues,
|
||||
[Operator.Baidu]: initialBaiduValues,
|
||||
[Operator.Wikipedia]: initialWikipediaValues,
|
||||
[Operator.PubMed]: initialPubMedValues,
|
||||
[Operator.ArXiv]: initialArXivValues,
|
||||
@ -95,7 +93,6 @@ export const useInitializeOperatorParams = () => {
|
||||
[Operator.GoogleScholar]: initialGoogleScholarValues,
|
||||
[Operator.SearXNG]: initialSearXNGValues,
|
||||
[Operator.GitHub]: initialGithubValues,
|
||||
[Operator.BaiduFanyi]: initialBaiduFanyiValues,
|
||||
[Operator.QWeather]: initialQWeatherValues,
|
||||
[Operator.ExeSQL]: initialExeSqlValues,
|
||||
[Operator.Switch]: initialSwitchValues,
|
||||
@ -131,6 +128,7 @@ export const useInitializeOperatorParams = () => {
|
||||
prompts: t('flow.prompts.user.summary'),
|
||||
},
|
||||
[Operator.DataOperations]: initialDataOperationsValues,
|
||||
[Operator.VariableAssigner]: initialVariableAssignerValues,
|
||||
};
|
||||
}, [llmId]);
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import { ReactComponent as YahooFinanceIcon } from '@/assets/svg/yahoo-finance.s
|
||||
|
||||
import { IconFont } from '@/components/icon-font';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { FileCode, HousePlus } from 'lucide-react';
|
||||
import { Equal, FileCode, HousePlus } from 'lucide-react';
|
||||
import { Operator } from './constant';
|
||||
|
||||
interface IProps {
|
||||
@ -58,6 +58,7 @@ export const SVGIconMap = {
|
||||
|
||||
export const LucideIconMap = {
|
||||
[Operator.DataOperations]: FileCode,
|
||||
[Operator.VariableAssigner]: Equal,
|
||||
};
|
||||
|
||||
const Empty = () => {
|
||||
|
||||
@ -1,129 +0,0 @@
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { SwitchOperatorOptions } from '@/constants/agent';
|
||||
import { useBuildSwitchOperatorOptions } from '@/hooks/logic-hooks/use-build-operator-options';
|
||||
import { useFetchKnowledgeMetadata } from '@/hooks/use-knowledge-request';
|
||||
import { Plus, X } from 'lucide-react';
|
||||
import { useCallback } from 'react';
|
||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function MetadataFilterConditions({ kbIds }: { kbIds: string[] }) {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
const name = 'meta_data_filter.manual';
|
||||
const metadata = useFetchKnowledgeMetadata(kbIds);
|
||||
|
||||
const switchOperatorOptions = useBuildSwitchOperatorOptions();
|
||||
|
||||
const { fields, remove, append } = useFieldArray({
|
||||
name,
|
||||
control: form.control,
|
||||
});
|
||||
|
||||
const add = useCallback(
|
||||
(key: string) => () => {
|
||||
append({
|
||||
key,
|
||||
value: '',
|
||||
op: SwitchOperatorOptions[0].value,
|
||||
});
|
||||
},
|
||||
[append],
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<FormLabel>{t('chat.conditions')}</FormLabel>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Button variant={'ghost'} type="button">
|
||||
<Plus />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{Object.keys(metadata.data).map((key, idx) => {
|
||||
return (
|
||||
<DropdownMenuItem key={idx} onClick={add(key)}>
|
||||
{key}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="space-y-5">
|
||||
{fields.map((field, index) => {
|
||||
const typeField = `${name}.${index}.key`;
|
||||
return (
|
||||
<div key={field.id} className="flex w-full items-center gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={typeField}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1 overflow-hidden">
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={t('common.pleaseInput')}
|
||||
></Input>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Separator className="w-3 text-text-secondary" />
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`${name}.${index}.op`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1 overflow-hidden">
|
||||
<FormControl>
|
||||
<SelectWithSearch
|
||||
{...field}
|
||||
options={switchOperatorOptions}
|
||||
></SelectWithSearch>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Separator className="w-3 text-text-secondary" />
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`${name}.${index}.value`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1 overflow-hidden">
|
||||
<FormControl>
|
||||
<Input placeholder={t('common.pleaseInput')} {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button variant={'ghost'} onClick={() => remove(index)}>
|
||||
<X className="text-text-sub-title-invert " />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user