### What problem does this PR solve? Feat: Allows users to delete a condition of a conditional operator #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.20.0
| @@ -83,7 +83,7 @@ export const SelectWithSearch = forwardRef< | |||
| aria-expanded={open} | |||
| ref={ref} | |||
| className={cn( | |||
| 'bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]', | |||
| 'bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]', | |||
| triggerClassName, | |||
| )} | |||
| > | |||
| @@ -209,7 +209,7 @@ export const MultiSelect = React.forwardRef< | |||
| {selectedValues.length > 0 ? ( | |||
| <div className="flex justify-between items-center w-full"> | |||
| <div className="flex flex-wrap items-center"> | |||
| {selectedValues.slice(0, maxCount).map((value) => { | |||
| {selectedValues?.slice(0, maxCount)?.map((value) => { | |||
| const option = options.find((o) => o.value === value); | |||
| const IconComponent = option?.icon; | |||
| return ( | |||
| @@ -78,14 +78,17 @@ function InnerSwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) { | |||
| {positions.map((position, idx) => { | |||
| return ( | |||
| <div key={idx}> | |||
| <section className="flex flex-col"> | |||
| <div className="flex justify-between"> | |||
| <span className="text-text-sub-title text-xs translate-y-2"> | |||
| {idx < positions.length - 1 && | |||
| position.condition?.logical_operator?.toUpperCase()} | |||
| </span> | |||
| <section className="flex flex-col text-xs"> | |||
| <div className="text-right"> | |||
| <span>{getConditionKey(idx, positions.length)}</span> | |||
| <div className="text-text-sub-title"> | |||
| {idx < positions.length - 1 && position.text} | |||
| </div> | |||
| </div> | |||
| <span className="text-background-checked"> | |||
| {idx < positions.length - 1 && | |||
| position.condition?.logical_operator?.toUpperCase()} | |||
| </span> | |||
| {position.condition && ( | |||
| <ConditionBlock | |||
| condition={position.condition} | |||
| @@ -18,10 +18,6 @@ export const useBuildSwitchHandlePositions = ({ | |||
| return get(data, 'form.conditions', []); | |||
| }, [data]); | |||
| useEffect(() => { | |||
| console.info('xxx0000'); | |||
| }, [conditions]); | |||
| const positions = useMemo(() => { | |||
| const list: Array<{ | |||
| text: string; | |||
| @@ -31,7 +27,7 @@ export const useBuildSwitchHandlePositions = ({ | |||
| }> = []; | |||
| [...conditions, ''].forEach((x, idx) => { | |||
| let top = idx === 0 ? 53 : list[idx - 1].top + 10 + 14; // case number (Case 1) height + flex gap | |||
| let top = idx === 0 ? 53 : list[idx - 1].top + 10 + 14 + 16 + 16; // case number (Case 1) height + flex gap | |||
| if (idx >= 1) { | |||
| const previousItems = conditions[idx - 1]?.items ?? []; | |||
| if (previousItems.length > 0) { | |||
| @@ -30,7 +30,7 @@ import QWeatherForm from '../form/qweather-form'; | |||
| import RelevantForm from '../form/relevant-form'; | |||
| import RetrievalForm from '../form/retrieval-form/next'; | |||
| import RewriteQuestionForm from '../form/rewrite-question-form'; | |||
| import { StringTransformForm } from '../form/string-transform-form'; | |||
| import StringTransformForm from '../form/string-transform-form'; | |||
| import SwitchForm from '../form/switch-form'; | |||
| import TavilyExtractForm from '../form/tavily-extract-form'; | |||
| import TavilyForm from '../form/tavily-form'; | |||
| @@ -11,7 +11,7 @@ import { MultiSelect } from '@/components/ui/multi-select'; | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { buildOptions } from '@/utils/form'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| import { memo, useCallback, useMemo } from 'react'; | |||
| import { useForm, useWatch } from 'react-hook-form'; | |||
| import { z } from 'zod'; | |||
| import { | |||
| @@ -30,14 +30,14 @@ const DelimiterOptions = Object.entries(StringTransformDelimiter).map( | |||
| ([key, val]) => ({ label: key, value: val }), | |||
| ); | |||
| export const StringTransformForm = ({ node }: INextOperatorForm) => { | |||
| function StringTransformForm({ node }: INextOperatorForm) { | |||
| const values = useValues(node); | |||
| const FormSchema = z.object({ | |||
| method: z.string(), | |||
| split_ref: z.string().optional(), | |||
| script: z.string().optional(), | |||
| delimiters: z.array(z.string()), | |||
| delimiters: z.array(z.string()).or(z.string()), | |||
| outputs: z.object({ result: z.object({ type: z.string() }) }).optional(), | |||
| }); | |||
| @@ -56,14 +56,18 @@ export const StringTransformForm = ({ node }: INextOperatorForm) => { | |||
| const handleMethodChange = useCallback( | |||
| (value: StringTransformMethod) => { | |||
| const isMerge = value === StringTransformMethod.Merge; | |||
| const outputs = { | |||
| ...initialStringTransformValues.outputs, | |||
| result: { | |||
| type: | |||
| value === StringTransformMethod.Merge ? 'string' : 'Array<string>', | |||
| type: isMerge ? 'string' : 'Array<string>', | |||
| }, | |||
| }; | |||
| form.setValue('outputs', outputs); | |||
| form.setValue( | |||
| 'delimiters', | |||
| isMerge ? StringTransformDelimiter.Comma : [], | |||
| ); | |||
| }, | |||
| [form], | |||
| ); | |||
| @@ -132,8 +136,9 @@ export const StringTransformForm = ({ node }: INextOperatorForm) => { | |||
| <MultiSelect | |||
| options={DelimiterOptions} | |||
| onValueChange={field.onChange} | |||
| defaultValue={field.value as string[]} | |||
| variant="inverted" | |||
| {...field} | |||
| // {...field} | |||
| /> | |||
| ) : ( | |||
| <RAGFlowSelect | |||
| @@ -158,4 +163,6 @@ export const StringTransformForm = ({ node }: INextOperatorForm) => { | |||
| </div> | |||
| </Form> | |||
| ); | |||
| }; | |||
| } | |||
| export default memo(StringTransformForm); | |||
| @@ -17,7 +17,7 @@ import { cn } from '@/lib/utils'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { toLower } from 'lodash'; | |||
| import { X } from 'lucide-react'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| import { memo, useCallback, useMemo } from 'react'; | |||
| import { useFieldArray, useForm, useFormContext } from 'react-hook-form'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { z } from 'zod'; | |||
| @@ -118,13 +118,13 @@ function ConditionCards({ | |||
| ); | |||
| return ( | |||
| <section className="flex-1 space-y-2.5"> | |||
| <section className="flex-1 space-y-2.5 min-w-0"> | |||
| {fields.map((field, index) => { | |||
| return ( | |||
| <div key={field.id} className="flex"> | |||
| <Card | |||
| className={cn( | |||
| 'relative bg-transparent border-input-border border flex-1 ', | |||
| 'relative bg-transparent border-input-border border flex-1 min-w-0', | |||
| { | |||
| 'before:w-10 before:absolute before:h-[1px] before:bg-input-border before:top-1/2 before:-left-10': | |||
| index === 0 || index === fields.length - 1, | |||
| @@ -136,12 +136,12 @@ function ConditionCards({ | |||
| control={form.control} | |||
| name={`${name}.${index}.cpn_id`} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormItem className="flex-1 min-w-0"> | |||
| <FormControl> | |||
| <SelectWithSearch | |||
| {...field} | |||
| options={finalOptions} | |||
| triggerClassName="w-30 text-background-checked bg-transparent border-none text-ellipsis" | |||
| triggerClassName="text-background-checked bg-transparent border-none truncate" | |||
| ></SelectWithSearch> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| @@ -202,7 +202,7 @@ function ConditionCards({ | |||
| ); | |||
| } | |||
| const SwitchForm = ({ node }: IOperatorForm) => { | |||
| function SwitchForm({ node }: IOperatorForm) { | |||
| const { t } = useTranslation(); | |||
| const values = useValues(node); | |||
| const switchOperatorOptions = useBuildSwitchOperatorOptions(); | |||
| @@ -257,9 +257,23 @@ const SwitchForm = ({ node }: IOperatorForm) => { | |||
| {fields.map((field, index) => { | |||
| return ( | |||
| <FormContainer key={field.id} className=""> | |||
| <div>{index === 0 ? 'IF' : 'ELSEIF'}</div> | |||
| <div className="flex justify-between items-center"> | |||
| <section> | |||
| <span>{index === 0 ? 'IF' : 'ELSEIF'}</span> | |||
| <div className="text-text-sub-title">Case {index + 1}</div> | |||
| </section> | |||
| {index !== 0 && ( | |||
| <Button | |||
| variant={'secondary'} | |||
| className="-translate-y-1" | |||
| onClick={() => remove(index)} | |||
| > | |||
| Remove <X /> | |||
| </Button> | |||
| )} | |||
| </div> | |||
| <section className="flex gap-2 !mt-2 relative"> | |||
| <section className="flex flex-col"> | |||
| <section className="flex flex-col w-[72px]"> | |||
| <div className="relative w-1 flex-1 before:absolute before:w-[1px] before:bg-input-border before:top-20 before:bottom-0 before:left-10"></div> | |||
| <FormField | |||
| control={form.control} | |||
| @@ -270,7 +284,6 @@ const SwitchForm = ({ node }: IOperatorForm) => { | |||
| <RAGFlowSelect | |||
| {...field} | |||
| options={switchLogicOperatorOptions} | |||
| triggerClassName="w-18" | |||
| /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| @@ -307,6 +320,6 @@ const SwitchForm = ({ node }: IOperatorForm) => { | |||
| </form> | |||
| </Form> | |||
| ); | |||
| }; | |||
| } | |||
| export default SwitchForm; | |||
| export default memo(SwitchForm); | |||
| @@ -1,5 +1,5 @@ | |||
| import { MoreButton } from '@/components/more-button'; | |||
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | |||
| import { RAGFlowAvatar } from '@/components/ragflow-avatar'; | |||
| import { Card, CardContent } from '@/components/ui/card'; | |||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | |||
| import { IFlow } from '@/interfaces/database/flow'; | |||
| @@ -19,10 +19,11 @@ export function AgentCard({ data, showAgentRenameModal }: DatasetCardProps) { | |||
| <CardContent className="p-2.5 pt-2 group"> | |||
| <section className="flex justify-between mb-2"> | |||
| <div className="flex gap-2 items-center"> | |||
| <Avatar className="size-6 rounded-lg"> | |||
| <AvatarImage src={data.avatar} /> | |||
| <AvatarFallback className="rounded-lg ">CN</AvatarFallback> | |||
| </Avatar> | |||
| <RAGFlowAvatar | |||
| className="size-6 rounded-lg" | |||
| avatar={data.avatar} | |||
| name={data.title || 'CN'} | |||
| ></RAGFlowAvatar> | |||
| </div> | |||
| <AgentDropdown | |||
| showAgentRenameModal={showAgentRenameModal} | |||