From 3763efbc7cd7fecf324faa18c3b46b1875a1a9b7 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Thu, 30 Apr 2026 14:18:32 +0800 Subject: [PATCH] fix(web): selected workflow name fix --- .../__tests__/index.spec.tsx | 46 +++++++++++++++++++ .../__tests__/workflow-selector.spec.tsx | 20 +++++++- .../components/custom-metric-editor/index.tsx | 11 +++-- .../workflow-selector.tsx | 11 +++-- 4 files changed, 80 insertions(+), 8 deletions(-) diff --git a/web/app/components/evaluation/components/custom-metric-editor/__tests__/index.spec.tsx b/web/app/components/evaluation/components/custom-metric-editor/__tests__/index.spec.tsx index e72048dc6d..f70c8f7a91 100644 --- a/web/app/components/evaluation/components/custom-metric-editor/__tests__/index.spec.tsx +++ b/web/app/components/evaluation/components/custom-metric-editor/__tests__/index.spec.tsx @@ -12,6 +12,7 @@ import CustomMetricEditorCard from '..' import { useEvaluationStore } from '../../../store' const mockUseAppWorkflow = vi.hoisted(() => vi.fn()) +const mockUseAppDetail = vi.hoisted(() => vi.fn()) const mockUseSnippetPublishedWorkflow = vi.hoisted(() => vi.fn()) const mockUseAvailableEvaluationWorkflows = vi.hoisted(() => vi.fn()) const mockUseInfiniteScroll = vi.hoisted(() => vi.fn()) @@ -21,6 +22,10 @@ vi.mock('@/service/use-workflow', () => ({ useAppWorkflow: (...args: unknown[]) => mockUseAppWorkflow(...args), })) +vi.mock('@/service/use-apps', () => ({ + useAppDetail: (...args: unknown[]) => mockUseAppDetail(...args), +})) + vi.mock('@/service/use-snippet-workflows', () => ({ useSnippetPublishedWorkflow: (...args: unknown[]) => mockUseSnippetPublishedWorkflow(...args), })) @@ -179,6 +184,7 @@ describe('CustomMetricEditorCard', () => { vi.clearAllMocks() useEvaluationStore.setState({ resources: {} }) mockPublishedGraphVariablePicker.mockReset() + mockUseAppDetail.mockReturnValue({ data: undefined }) mockUseInfiniteScroll.mockImplementation(() => undefined) mockUseAvailableEvaluationWorkflows.mockReturnValue({ @@ -305,6 +311,46 @@ describe('CustomMetricEditorCard', () => { expect(syncOutputsSpy).not.toHaveBeenCalled() }) + it('should show the selected workflow app name from app detail when the config only has workflow id', () => { + const selectedWorkflow = { + ...createWorkflow([createStartNode()]), + marked_name: '', + } + const baseMetric = createMetric() + const metric = { + ...baseMetric, + customConfig: { + ...baseMetric.customConfig!, + workflowName: null, + }, + } + + mockUseAppDetail.mockReturnValue({ + data: { + id: 'workflow-app-1', + name: 'Review Workflow App', + }, + }) + mockUseAppWorkflow.mockImplementation((appId: string) => { + if (appId === 'workflow-app-1') + return { data: selectedWorkflow } + + return { data: undefined } + }) + + render( + , + ) + + expect(mockUseAppDetail).toHaveBeenCalledWith('workflow-app-1') + expect(screen.getByText('Review Workflow App')).toBeInTheDocument() + expect(screen.queryByText('workflow-1')).not.toBeInTheDocument() + }) + it('should pass the current app published graph and saved selector values to the picker', () => { const selectedWorkflow = createWorkflow([ createStartNode(), diff --git a/web/app/components/evaluation/components/custom-metric-editor/__tests__/workflow-selector.spec.tsx b/web/app/components/evaluation/components/custom-metric-editor/__tests__/workflow-selector.spec.tsx index 3cf8f30b99..e9386c6031 100644 --- a/web/app/components/evaluation/components/custom-metric-editor/__tests__/workflow-selector.spec.tsx +++ b/web/app/components/evaluation/components/custom-metric-editor/__tests__/workflow-selector.spec.tsx @@ -98,12 +98,22 @@ describe('WorkflowSelector', () => { setupWorkflowQueryMock({ workflows: [] }) renderWorkflowSelector({ - value: 'workflow-1', + value: 'app-1', selectedWorkflowName: 'Saved Review Workflow', }) expect(screen.getByText('Saved Review Workflow')).toBeInTheDocument() }) + + it('should resolve the selected workflow from app id', () => { + setupWorkflowQueryMock() + + renderWorkflowSelector({ + value: 'app-1', + }) + + expect(screen.getByText('Review Workflow')).toBeInTheDocument() + }) }) // Cover opening the popover and choosing one workflow option. @@ -120,6 +130,14 @@ describe('WorkflowSelector', () => { expect(onSelect).toHaveBeenCalledWith(createWorkflow()) }) + + it('should mark the option selected when its app id matches the value', async () => { + renderWorkflowSelector({ value: 'app-1' }) + + fireEvent.click(screen.getByRole('button', { name: 'evaluation.metrics.custom.workflowLabel' })) + + expect(await screen.findByRole('option', { name: 'Review Workflow', selected: true })).toBeInTheDocument() + }) }) // Cover the infinite-scroll callback used by the ScrollArea viewport. diff --git a/web/app/components/evaluation/components/custom-metric-editor/index.tsx b/web/app/components/evaluation/components/custom-metric-editor/index.tsx index b76d866a50..fcfcbf1417 100644 --- a/web/app/components/evaluation/components/custom-metric-editor/index.tsx +++ b/web/app/components/evaluation/components/custom-metric-editor/index.tsx @@ -8,6 +8,7 @@ import { useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { inputVarTypeToVarType } from '@/app/components/workflow/nodes/_base/components/variable/utils' import { BlockEnum, InputVarType } from '@/app/components/workflow/types' +import { useAppDetail } from '@/service/use-apps' import { useSnippetPublishedWorkflow } from '@/service/use-snippet-workflows' import { useAppWorkflow } from '@/service/use-workflow' import { isCustomMetricConfigured, useEvaluationStore } from '../../store' @@ -76,7 +77,9 @@ const CustomMetricEditorCard = ({ const syncCustomMetricMappings = useEvaluationStore(state => state.syncCustomMetricMappings) const syncCustomMetricOutputs = useEvaluationStore(state => state.syncCustomMetricOutputs) const updateCustomMetricMapping = useEvaluationStore(state => state.updateCustomMetricMapping) - const { data: selectedWorkflow } = useAppWorkflow(metric.customConfig?.workflowAppId ?? '') + const selectedWorkflowAppId = metric.customConfig?.workflowAppId ?? metric.customConfig?.workflowId ?? '' + const { data: selectedWorkflowApp } = useAppDetail(selectedWorkflowAppId) + const { data: selectedWorkflow } = useAppWorkflow(selectedWorkflowAppId) const { data: currentAppWorkflow } = useAppWorkflow(resourceType === 'apps' ? resourceId : '') const { data: currentSnippetWorkflow } = useSnippetPublishedWorkflow(resourceType === 'snippets' ? resourceId : '') const inputVariables = useMemo(() => { @@ -153,9 +156,9 @@ const CustomMetricEditorCard = ({
setCustomMetricWorkflow(resourceType, resourceId, metric.id, { - workflowId: workflow.id, + workflowId: workflow.app_id, workflowAppId: workflow.app_id, workflowName: getWorkflowName(workflow), })} @@ -163,7 +166,7 @@ const CustomMetricEditorCard = ({
-
{t('metrics.custom.mappingTitle')}
+
{t('metrics.custom.mappingTitle')}
{inputVariables.map((inputVariable) => { diff --git a/web/app/components/evaluation/components/custom-metric-editor/workflow-selector.tsx b/web/app/components/evaluation/components/custom-metric-editor/workflow-selector.tsx index e2b93e7f22..eb88b7ff44 100644 --- a/web/app/components/evaluation/components/custom-metric-editor/workflow-selector.tsx +++ b/web/app/components/evaluation/components/custom-metric-editor/workflow-selector.tsx @@ -34,6 +34,11 @@ const getWorkflowName = (workflow: AvailableEvaluationWorkflow) => { return workflow.marked_name || workflow.app_name || workflow.id } +const isSelectedWorkflow = ( + workflow: AvailableEvaluationWorkflow, + value: string | null, +) => workflow.app_id === value + const WorkflowSelector = ({ value, selectedWorkflowName, @@ -71,7 +76,7 @@ const WorkflowSelector = ({ if (!value) return null - const selectedWorkflow = workflows.find(workflow => workflow.id === value) + const selectedWorkflow = workflows.find(workflow => isSelectedWorkflow(workflow, value)) if (selectedWorkflow) return getWorkflowName(selectedWorkflow) @@ -171,7 +176,7 @@ const WorkflowSelector = ({ key={workflow.id} type="button" role="option" - aria-selected={workflow.id === value} + aria-selected={isSelectedWorkflow(workflow, value)} className="flex w-full items-center gap-2 rounded-lg px-2 py-1 text-left hover:bg-state-base-hover" onClick={() => { onSelect(workflow) @@ -187,7 +192,7 @@ const WorkflowSelector = ({
{getWorkflowName(workflow)}
- {workflow.id === value && ( + {isSelectedWorkflow(workflow, value) && (