From dacc7fc740a4c5a9db906b634604eec9ee104930 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Wed, 29 Apr 2026 17:15:32 +0800 Subject: [PATCH] fix(web): style of conditions --- .../evaluation/__tests__/index.spec.tsx | 4 +- .../add-condition-select.tsx | 93 +++++----- .../conditions-section/condition-group.tsx | 169 +++++++++--------- .../components/metric-selector/index.tsx | 19 +- .../components/metric-selector/types.ts | 1 - web/app/components/evaluation/types.ts | 2 + web/app/components/evaluation/utils.ts | 3 + 7 files changed, 151 insertions(+), 140 deletions(-) diff --git a/web/app/components/evaluation/__tests__/index.spec.tsx b/web/app/components/evaluation/__tests__/index.spec.tsx index b451223390..6b3546b614 100644 --- a/web/app/components/evaluation/__tests__/index.spec.tsx +++ b/web/app/components/evaluation/__tests__/index.spec.tsx @@ -366,7 +366,7 @@ describe('Evaluation', () => { render() - fireEvent.click(screen.getByRole('combobox', { name: 'evaluation.conditions.addCondition' })) + fireEvent.click(screen.getByRole('button', { name: 'evaluation.conditions.addCondition' })) expect(screen.getByText('Faithfulness')).toBeInTheDocument() expect(screen.getByText('Review Workflow')).toBeInTheDocument() @@ -375,7 +375,7 @@ describe('Evaluation', () => { expect(screen.getByText('evaluation.conditions.valueTypes.number')).toBeInTheDocument() expect(screen.getByText('evaluation.conditions.valueTypes.string')).toBeInTheDocument() - fireEvent.click(screen.getByRole('option', { name: /reason/i })) + fireEvent.click(screen.getByRole('menuitem', { name: /reason/i })) const condition = useEvaluationStore.getState().resources['apps:app-conditions-dropdown'].judgmentConfig.conditions[0] diff --git a/web/app/components/evaluation/components/conditions-section/add-condition-select.tsx b/web/app/components/evaluation/components/conditions-section/add-condition-select.tsx index 2a17fadf83..c9bdb8125e 100644 --- a/web/app/components/evaluation/components/conditions-section/add-condition-select.tsx +++ b/web/app/components/evaluation/components/conditions-section/add-condition-select.tsx @@ -1,15 +1,12 @@ 'use client' import type { ConditionMetricOptionGroup, EvaluationResourceProps } from '../../types' -import { cn } from '@langgenius/dify-ui/cn' +import { Button } from '@langgenius/dify-ui/button' import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, -} from '@langgenius/dify-ui/select' + Popover, + PopoverContent, + PopoverTrigger, +} from '@langgenius/dify-ui/popover' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useEvaluationStore } from '../../store' @@ -28,47 +25,59 @@ const AddConditionSelect = ({ }: AddConditionSelectProps) => { const { t } = useTranslation('evaluation') const addCondition = useEvaluationStore(state => state.addCondition) - const [selectKey, setSelectKey] = useState(0) + const [open, setOpen] = useState(false) + + const handleOpenChange = (nextOpen: boolean) => { + if (disabled) + return + + setOpen(nextOpen) + } return ( - + + ))} + + ))} + + + ) } diff --git a/web/app/components/evaluation/components/conditions-section/condition-group.tsx b/web/app/components/evaluation/components/conditions-section/condition-group.tsx index 15b9246834..078b94ef80 100644 --- a/web/app/components/evaluation/components/conditions-section/condition-group.tsx +++ b/web/app/components/evaluation/components/conditions-section/condition-group.tsx @@ -6,7 +6,6 @@ import type { EvaluationResourceProps, JudgmentConditionItem, } from '../../types' -import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { Select, @@ -19,7 +18,9 @@ import { } from '@langgenius/dify-ui/select' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' +import ActionButton from '@/app/components/base/action-button' import Input from '@/app/components/base/input' +import BlockIcon from '@/app/components/workflow/block-icon' import { getAllowedOperators, requiresConditionValue, useEvaluationResource, useEvaluationStore } from '../../store' import { buildConditionMetricOptions, @@ -29,6 +30,7 @@ import { isSelectorEqual, serializeVariableSelector, } from '../../utils' +import { getEvaluationNodeBlockType } from '../metric-selector/utils' type ConditionMetricLabelProps = { metric?: ConditionMetricOption @@ -56,14 +58,8 @@ type ConditionValueInputProps = { type ConditionGroupProps = EvaluationResourceProps -const getMetricValueTypeIconClassName = (valueType: ConditionMetricOption['valueType']) => { - if (valueType === 'number') - return 'i-ri-hashtag' - - if (valueType === 'boolean') - return 'i-ri-checkbox-circle-line' - - return 'i-ri-bar-chart-box-line' +const getMetricVariableLabel = (variableName: string) => { + return variableName.replaceAll('-', '_') } const ConditionMetricLabel = ({ @@ -73,13 +69,28 @@ const ConditionMetricLabel = ({ if (!metric) return {placeholder} - return ( -
-
- - {metric.itemLabel} + if (metric.kind === 'builtin' && metric.nodeInfo) { + return ( +
+
+ {getMetricVariableLabel(metric.variableSelector[1])} + / + + + {metric.itemLabel} + + {metric.valueType} +
+
+ ) + } + + return ( +
+
+ {metric.itemLabel} + {metric.valueType}
- {metric.groupLabel}
) } @@ -114,7 +125,6 @@ const ConditionMetricSelect = ({ {group.options.map(option => (
- {option.itemLabel} {t(getConditionMetricValueTypeTranslationKey(option.valueType))} @@ -141,7 +151,7 @@ const ConditionOperatorSelect = ({ {getComparisonOperatorLabel(operator, t)} - + {operators.map(nextOperator => ( {getComparisonOperatorLabel(nextOperator, t)} @@ -212,88 +222,87 @@ const ConditionGroup = ({ const { t } = useTranslation('evaluation') const resource = useEvaluationResource(resourceType, resourceId) const metricOptions = useMemo(() => buildConditionMetricOptions(resource.metrics), [resource.metrics]) + const logicalOperator = resource.judgmentConfig.logicalOperator const logicalLabels = { and: t('conditions.logical.and'), or: t('conditions.logical.or'), } + const hasMultipleConditions = resource.judgmentConfig.conditions.length > 1 const setConditionLogicalOperator = useEvaluationStore(state => state.setConditionLogicalOperator) const removeCondition = useEvaluationStore(state => state.removeCondition) const updateConditionMetric = useEvaluationStore(state => state.updateConditionMetric) const updateConditionOperator = useEvaluationStore(state => state.updateConditionOperator) const updateConditionValue = useEvaluationStore(state => state.updateConditionValue) + const toggleLogicalOperator = () => { + setConditionLogicalOperator(resourceType, resourceId, logicalOperator === 'and' ? 'or' : 'and') + } return (
-
-
-
- {(['and', 'or'] as const).map(operator => ( - - ))} +
+ {hasMultipleConditions && ( +
+
+
+
-
-
+ )} -
- {resource.judgmentConfig.conditions.map((condition) => { - const metric = metricOptions.find(option => isSelectorEqual(option.variableSelector, condition.variableSelector)) - const allowedOperators = getAllowedOperators(resource.metrics, condition.variableSelector) - const showValue = !!metric && requiresConditionValue(condition.comparisonOperator) +
+ {resource.judgmentConfig.conditions.map((condition) => { + const metric = metricOptions.find(option => isSelectorEqual(option.variableSelector, condition.variableSelector)) + const allowedOperators = getAllowedOperators(resource.metrics, condition.variableSelector) + const showValue = !!metric && requiresConditionValue(condition.comparisonOperator) - return ( -
-
-
-
- updateConditionMetric(resourceType, resourceId, condition.id, value)} + return ( +
+
+
+
+ updateConditionMetric(resourceType, resourceId, condition.id, value)} + /> +
+
+ updateConditionOperator(resourceType, resourceId, condition.id, value)} />
-
- updateConditionOperator(resourceType, resourceId, condition.id, value)} - /> + {showValue && ( +
+ updateConditionValue(resourceType, resourceId, condition.id, value)} + /> +
+ )} +
+
+ removeCondition(resourceType, resourceId, condition.id)} + > +
- {showValue && ( -
- updateConditionValue(resourceType, resourceId, condition.id, value)} - /> -
- )}
-
- -
-
- ) - })} + ) + })} +
) diff --git a/web/app/components/evaluation/components/metric-selector/index.tsx b/web/app/components/evaluation/components/metric-selector/index.tsx index 68ad67d343..10ccc5b76a 100644 --- a/web/app/components/evaluation/components/metric-selector/index.tsx +++ b/web/app/components/evaluation/components/metric-selector/index.tsx @@ -3,7 +3,6 @@ import type { ChangeEvent } from 'react' import type { MetricSelectorProps } from './types' import { Button } from '@langgenius/dify-ui/button' -import { cn } from '@langgenius/dify-ui/cn' import { Popover, PopoverContent, @@ -22,7 +21,6 @@ const MetricSelector = ({ resourceType, resourceId, triggerClassName, - triggerStyle = 'button', }: MetricSelectorProps) => { const { t } = useTranslation('evaluation') const resource = useEvaluationResource(resourceType, resourceId) @@ -63,19 +61,10 @@ const MetricSelector = ({ -