From 9fdaa14c8de9adef47de3df8b27c02bc99218778 Mon Sep 17 00:00:00 2001 From: twwu Date: Wed, 10 Sep 2025 10:51:48 +0800 Subject: [PATCH] fix: Handle missing dataset avatars and improve routing --- .../datasets/common/credential-icon.tsx | 10 ++- .../components/header/dataset-nav/index.tsx | 66 +++++++++++++------ web/app/components/header/nav/index.tsx | 1 - web/models/datasets.ts | 2 +- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/web/app/components/datasets/common/credential-icon.tsx b/web/app/components/datasets/common/credential-icon.tsx index d701ed25fa..bc891529ab 100644 --- a/web/app/components/datasets/common/credential-icon.tsx +++ b/web/app/components/datasets/common/credential-icon.tsx @@ -1,5 +1,5 @@ import cn from '@/utils/classnames' -import React, { useMemo } from 'react' +import React, { useCallback, useMemo, useState } from 'react' type CredentialIconProps = { avatar_url?: string @@ -21,10 +21,15 @@ export const CredentialIcon: React.FC = ({ size = 20, className = '', }) => { + const [showAvatar, setShowAvatar] = useState(!!avatar_url) const firstLetter = useMemo(() => name.charAt(0).toUpperCase(), [name]) const bgColor = useMemo(() => ICON_BG_COLORS[firstLetter.charCodeAt(0) % ICON_BG_COLORS.length], [firstLetter]) - if (avatar_url && avatar_url !== 'default') { + const onImgLoadError = useCallback(() => { + setShowAvatar(false) + }, []) + + if (avatar_url && avatar_url !== 'default' && showAvatar) { return (
= ({ width={size} height={size} className={cn('shrink-0 object-contain', className)} + onError={onImgLoadError} />
) diff --git a/web/app/components/header/dataset-nav/index.tsx b/web/app/components/header/dataset-nav/index.tsx index 6165128e82..60fd1b3f68 100644 --- a/web/app/components/header/dataset-nav/index.tsx +++ b/web/app/components/header/dataset-nav/index.tsx @@ -1,6 +1,6 @@ 'use client' -import { useCallback } from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useParams, useRouter } from 'next/navigation' import { @@ -28,6 +28,49 @@ const DatasetNav = () => { }) const datasetItems = flatten(datasetList?.pages.map(datasetData => datasetData.data)) + const curNav = useMemo(() => { + if (!currentDataset) return + return { + id: currentDataset.id, + name: currentDataset.name, + icon: currentDataset.icon_info.icon, + icon_type: currentDataset.icon_info.icon_type, + icon_background: currentDataset.icon_info.icon_background, + icon_url: currentDataset.icon_info.icon_url, + } as Omit + }, [currentDataset?.id, currentDataset?.name, currentDataset?.icon_info]) + + const navigationItems = useMemo(() => { + return datasetItems.map((dataset) => { + const isPipelineUnpublished = dataset.runtime_mode === 'rag_pipeline' && !dataset.is_published + const internalLink = isPipelineUnpublished + ? `/datasets/${dataset.id}/pipeline` + : `/datasets/${dataset.id}/documents` + const link = dataset.provider === 'external' + ? `/datasets/${dataset.id}/hitTesting` + : internalLink + return { + id: dataset.id, + name: dataset.name, + link, + icon: dataset.icon_info.icon, + icon_type: dataset.icon_info.icon_type, + icon_background: dataset.icon_info.icon_background, + icon_url: dataset.icon_info.icon_url, + } + }) as NavItem[] + }, [datasetItems]) + + const createRouter = useMemo(() => { + const runtimeMode = currentDataset?.runtime_mode + if (runtimeMode === 'rag_pipeline') + return `${basePath}/datasets/create-from-pipeline` + else if (runtimeMode === 'general') + return `${basePath}/datasets/create` + else + return `${basePath}/datasets/create` + }, [currentDataset?.runtime_mode]) + const handleLoadMore = useCallback(() => { if (hasNextPage) fetchNextPage() @@ -41,25 +84,10 @@ const DatasetNav = () => { text={t('common.menus.datasets')} activeSegment='datasets' link='/datasets' - curNav={currentDataset && { - id: currentDataset.id, - name: currentDataset.name, - icon: currentDataset.icon_info.icon, - icon_type: currentDataset.icon_info.icon_type, - icon_background: currentDataset.icon_info.icon_background, - icon_url: currentDataset.icon_info.icon_url, - } as Omit} - navigationItems={datasetItems.map(dataset => ({ - id: dataset.id, - name: dataset.name, - link: dataset.provider === 'external' ? `/datasets/${dataset.id}/hitTesting` : `/datasets/${dataset.id}/documents`, - icon: dataset.icon_info.icon, - icon_type: dataset.icon_info.icon_type, - icon_background: dataset.icon_info.icon_background, - icon_url: dataset.icon_info.icon_url, - })) as NavItem[]} + curNav={curNav} + navigationItems={navigationItems} createText={t('common.menus.newDataset')} - onCreate={() => router.push(`${basePath}/datasets/create`)} + onCreate={() => router.push(createRouter)} onLoadMore={handleLoadMore} /> ) diff --git a/web/app/components/header/nav/index.tsx b/web/app/components/header/nav/index.tsx index 8c09a724de..3dfb77ca6a 100644 --- a/web/app/components/header/nav/index.tsx +++ b/web/app/components/header/nav/index.tsx @@ -42,7 +42,6 @@ const Nav = ({ useEffect(() => { if (pathname === link) setLinkLastSearchParams(searchParams.toString()) - // eslint-disable-next-line react-hooks/exhaustive-deps }, [pathname, searchParams]) return ( diff --git a/web/models/datasets.ts b/web/models/datasets.ts index 92506aa733..c71a46e0a5 100644 --- a/web/models/datasets.ts +++ b/web/models/datasets.ts @@ -403,7 +403,7 @@ export type SimpleDocumentDetail = InitialDocumentDetail & { } } doc_metadata?: MetadataItemWithValue[] - created_from: string + created_from: 'rag-pipeline' | 'web' } export type DocumentListResponse = {