- {t('metrics.custom.mappingWarning')}
+
+
+
+
+
+
{t('metrics.custom.mappingTitle')}
+
+
+ {metric.customConfig.mappings.map(mapping => (
+ updateCustomMetricMapping(resourceType, resourceId, metric.id, mapping.id, patch)}
+ onRemove={() => removeCustomMetricMapping(resourceType, resourceId, metric.id, mapping.id)}
+ />
+ ))}
+
+ {!isConfigured && (
+
+ {t('metrics.custom.mappingWarning')}
+
+ )}
)
diff --git a/web/app/components/evaluation/components/metric-section/__tests__/index.spec.tsx b/web/app/components/evaluation/components/metric-section/__tests__/index.spec.tsx
index b112d76f47..b7cb0780d8 100644
--- a/web/app/components/evaluation/components/metric-section/__tests__/index.spec.tsx
+++ b/web/app/components/evaluation/components/metric-section/__tests__/index.spec.tsx
@@ -86,8 +86,8 @@ describe('MetricSection', () => {
renderMetricSection()
expect(screen.getByText('Custom Evaluator')).toBeInTheDocument()
- expect(screen.getByText('evaluation.metrics.custom.title')).toBeInTheDocument()
expect(screen.getByText('evaluation.metrics.custom.warningBadge')).toBeInTheDocument()
+ expect(screen.getByText('evaluation.metrics.custom.workflowPlaceholder')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'evaluation.metrics.custom.addMapping' })).toBeInTheDocument()
})
})
diff --git a/web/app/components/evaluation/components/metric-section/index.tsx b/web/app/components/evaluation/components/metric-section/index.tsx
index 582fcbfa30..c244e27a8a 100644
--- a/web/app/components/evaluation/components/metric-section/index.tsx
+++ b/web/app/components/evaluation/components/metric-section/index.tsx
@@ -22,7 +22,7 @@ const MetricSection = ({
title={t('metrics.title')}
tooltip={t('metrics.description')}
/>
-
+
{!hasMetrics && }
{resource.metrics.map(metric => (
))}
diff --git a/web/app/components/evaluation/components/metric-section/metric-card.tsx b/web/app/components/evaluation/components/metric-section/metric-card.tsx
index 70af26a2dc..a1ddc55c32 100644
--- a/web/app/components/evaluation/components/metric-section/metric-card.tsx
+++ b/web/app/components/evaluation/components/metric-section/metric-card.tsx
@@ -3,63 +3,125 @@
import type { EvaluationMetric, EvaluationResourceProps } from '../../types'
import Badge from '@/app/components/base/badge'
import Button from '@/app/components/base/button'
-import { useEvaluationStore } from '../../store'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
+import { cn } from '@/utils/classnames'
+import { isCustomMetricConfigured, useEvaluationStore } from '../../store'
import CustomMetricEditorCard from '../custom-metric-editor-card'
+import { getMetricVisual, getNodeVisual, getToneClasses } from '../metric-selector/utils'
type MetricCardProps = EvaluationResourceProps & {
metric: EvaluationMetric
- nodesLabel: string
nodesAllLabel: string
removeLabel: string
+ customWarningLabel: string
}
const MetricCard = ({
resourceType,
resourceId,
metric,
- nodesLabel,
nodesAllLabel,
removeLabel,
+ customWarningLabel,
}: MetricCardProps) => {
+ const updateBuiltinMetric = useEvaluationStore(state => state.addBuiltinMetric)
const removeMetric = useEvaluationStore(state => state.removeMetric)
+ const metricVisual = metric.kind === 'custom-workflow'
+ ? { icon: 'i-ri-equalizer-2-line', tone: 'indigo' as const }
+ : getMetricVisual(metric.optionId)
+ const metricToneClasses = getToneClasses(metricVisual.tone)
+ const isCustomMetricInvalid = metric.kind === 'custom-workflow' && !isCustomMetricConfigured(metric)
+ const hasSelectedNodes = metric.kind === 'builtin' && !!metric.nodeInfoList?.length
return (
-
-
-
-
{metric.label}
-
{metric.description}
-
- {metric.badges.map(badge => (
-
{badge}
- ))}
+
+
+
+
+
+
+
+
{metric.label}
+ {metric.description && (
+
+
+
+
+ )}
+ />
+
+ {metric.description}
+
+
+ )}
- {metric.kind === 'builtin' && (
-
-
{nodesLabel}
-
- {metric.nodeInfoList?.length
- ? metric.nodeInfoList.map(nodeInfo => (
-
- {nodeInfo.title}
-
- ))
- : (
- {nodesAllLabel}
- )}
-
-
- )}
-
+
+ {isCustomMetricInvalid && (
+
+ {customWarningLabel}
+
+ )}
+
+
+
+ {metric.kind === 'builtin' && (
+
+ {metric.nodeInfoList?.length
+ ? metric.nodeInfoList.map((nodeInfo) => {
+ const nodeVisual = getNodeVisual(nodeInfo)
+ const nodeToneClasses = getToneClasses(nodeVisual.tone)
+
+ return (
+
+
+
+
+
{nodeInfo.title}
+
+
+ )
+ })
+ : (
+
{nodesAllLabel}
+ )}
+
+ )}
+
{metric.kind === 'custom-workflow' && (
{
return (
-
-
-
-
-
- {description}
-
+
+ {description}
)
}