Fix: Added styles for empty states on the page. #10703 (#11588)

### What problem does this PR solve?

Fix: Added styles for empty states on the page.
### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-11-28 14:03:20 +08:00
committed by GitHub
parent dbdda0fbab
commit 7d05d4ced7
29 changed files with 866 additions and 419 deletions

View File

@ -1,4 +1,6 @@
import { CardContainer } from '@/components/card-container';
import { EmptyCardType } from '@/components/empty/constant';
import { EmptyAppCard } from '@/components/empty/empty';
import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog';
import { Button } from '@/components/ui/button';
@ -14,7 +16,8 @@ import { useFetchAgentListByPage } from '@/hooks/use-agent-request';
import { t } from 'i18next';
import { pick } from 'lodash';
import { Clipboard, ClipboardPlus, FileInput, Plus } from 'lucide-react';
import { useCallback } from 'react';
import { useCallback, useEffect } from 'react';
import { useSearchParams } from 'umi';
import { AgentCard } from './agent-card';
import { CreateAgentDialog } from './create-agent-dialog';
import { useCreateAgentOrPipeline } from './hooks/use-create-agent';
@ -67,95 +70,120 @@ export default function Agents() {
},
[setPagination],
);
const [searchUrl, setSearchUrl] = useSearchParams();
const isCreate = searchUrl.get('isCreate') === 'true';
useEffect(() => {
if (isCreate) {
showCreatingModal();
searchUrl.delete('isCreate');
setSearchUrl(searchUrl);
}
}, [isCreate, showCreatingModal, searchUrl, setSearchUrl]);
return (
<section className="flex flex-col w-full flex-1">
<div className="px-8 pt-8 ">
<ListFilterBar
title={t('flow.agents')}
searchString={searchString}
onSearchChange={handleInputChange}
icon="agents"
filters={filters}
onChange={handleFilterSubmit}
value={filterValue}
>
<DropdownMenu>
<DropdownMenuTrigger>
<Button>
<Plus className="mr-2 h-4 w-4" />
{t('flow.createGraph')}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
justifyBetween={false}
onClick={showCreatingModal}
>
<Clipboard />
{t('flow.createFromBlank')}
</DropdownMenuItem>
<DropdownMenuItem
justifyBetween={false}
onClick={navigateToAgentTemplates}
>
<ClipboardPlus />
{t('flow.createFromTemplate')}
</DropdownMenuItem>
<DropdownMenuItem
justifyBetween={false}
onClick={handleImportJson}
>
<FileInput />
{t('flow.importJsonFile')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</ListFilterBar>
</div>
<div className="flex-1 overflow-auto">
<CardContainer className="max-h-[calc(100dvh-280px)] overflow-auto px-8">
{data.map((x) => {
return (
<AgentCard
key={x.id}
data={x}
showAgentRenameModal={showAgentRenameModal}
></AgentCard>
);
})}
</CardContainer>
</div>
<div className="mt-8 px-8 pb-8">
<RAGFlowPagination
{...pick(pagination, 'current', 'pageSize')}
total={pagination.total}
onChange={handlePageChange}
></RAGFlowPagination>
</div>
{agentRenameVisible && (
<RenameDialog
hideModal={hideAgentRenameModal}
onOk={onAgentRenameOk}
initialName={initialAgentName}
loading={agentRenameLoading}
></RenameDialog>
<>
{(!data?.length || data?.length <= 0) && (
<div className="flex w-full items-center justify-center h-[calc(100vh-164px)]">
<EmptyAppCard
showIcon
size="large"
className="w-[480px] p-14"
type={EmptyCardType.Agent}
onClick={() => showCreatingModal()}
/>
</div>
)}
{creatingVisible && (
<CreateAgentDialog
loading={loading}
visible={creatingVisible}
hideModal={hideCreatingModal}
shouldChooseAgent
onOk={handleCreateAgentOrPipeline}
></CreateAgentDialog>
)}
{fileUploadVisible && (
<UploadAgentDialog
hideModal={hideFileUploadModal}
onOk={onFileUploadOk}
></UploadAgentDialog>
)}
</section>
<section className="flex flex-col w-full flex-1">
{!!data?.length && (
<>
<div className="px-8 pt-8 ">
<ListFilterBar
title={t('flow.agents')}
searchString={searchString}
onSearchChange={handleInputChange}
icon="agents"
filters={filters}
onChange={handleFilterSubmit}
value={filterValue}
>
<DropdownMenu>
<DropdownMenuTrigger>
<Button>
<Plus className="mr-2 h-4 w-4" />
{t('flow.createGraph')}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
justifyBetween={false}
onClick={showCreatingModal}
>
<Clipboard />
{t('flow.createFromBlank')}
</DropdownMenuItem>
<DropdownMenuItem
justifyBetween={false}
onClick={navigateToAgentTemplates}
>
<ClipboardPlus />
{t('flow.createFromTemplate')}
</DropdownMenuItem>
<DropdownMenuItem
justifyBetween={false}
onClick={handleImportJson}
>
<FileInput />
{t('flow.importJsonFile')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</ListFilterBar>
</div>
<div className="flex-1 overflow-auto">
<CardContainer className="max-h-[calc(100dvh-280px)] overflow-auto px-8">
{data.map((x) => {
return (
<AgentCard
key={x.id}
data={x}
showAgentRenameModal={showAgentRenameModal}
></AgentCard>
);
})}
</CardContainer>
</div>
<div className="mt-8 px-8 pb-8">
<RAGFlowPagination
{...pick(pagination, 'current', 'pageSize')}
total={pagination.total}
onChange={handlePageChange}
></RAGFlowPagination>
</div>
</>
)}
{agentRenameVisible && (
<RenameDialog
hideModal={hideAgentRenameModal}
onOk={onAgentRenameOk}
initialName={initialAgentName}
loading={agentRenameLoading}
></RenameDialog>
)}
{creatingVisible && (
<CreateAgentDialog
loading={loading}
visible={creatingVisible}
hideModal={hideCreatingModal}
shouldChooseAgent
onOk={handleCreateAgentOrPipeline}
></CreateAgentDialog>
)}
{fileUploadVisible && (
<UploadAgentDialog
hideModal={hideFileUploadModal}
onOk={onFileUploadOk}
></UploadAgentDialog>
)}
</section>
</>
);
}

View File

@ -1,3 +1,5 @@
import { EmptyType } from '@/components/empty/constant';
import Empty from '@/components/empty/empty';
import FileStatusBadge from '@/components/file-status-badge';
import { FileIcon, IconFontFill } from '@/components/icon-font';
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
@ -344,6 +346,7 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [rowSelection, setRowSelection] = useState({});
const { t } = useTranslate('knowledgeDetails');
const { t: tDatasetOverview } = useTranslate('datasetOverview');
const [isModalVisible, setIsModalVisible] = useState(false);
const { navigateToDataflowResult } = useNavigatePage();
const [logInfo, setLogInfo] = useState<IFileLogItem>();
@ -445,7 +448,10 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
<Empty
type={EmptyType.Data}
text={tDatasetOverview('noData')}
/>
</TableCell>
</TableRow>
)}

View File

@ -14,6 +14,8 @@ import {
import * as React from 'react';
import { ChunkMethodDialog } from '@/components/chunk-method-dialog';
import { EmptyType } from '@/components/empty/constant';
import Empty from '@/components/empty/empty';
import { RenameDialog } from '@/components/rename-dialog';
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import {
@ -164,7 +166,7 @@ export function DatasetTable({
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
<Empty type={EmptyType.Data} />
</TableCell>
</TableRow>
)}

View File

@ -136,7 +136,7 @@ export function useDatasetTableColumns({
{
DataSourceInfo[
row.original.source_type as keyof typeof DataSourceInfo
].icon
]?.icon
}
</div>
)}

View File

@ -1,3 +1,4 @@
import { EmptyType } from '@/components/empty/constant';
import Empty from '@/components/empty/empty';
import { FormContainer } from '@/components/form-container';
import { FilterButton } from '@/components/list-filter-bar';
@ -101,7 +102,7 @@ export function TestingResult({
{!data.chunks?.length && !loading && (
<div className="flex justify-center items-center w-full h-[calc(100vh-241px)]">
<div>
<Empty>
<Empty type={EmptyType.SearchData}>
{data.isRuned && (
<div className="text-text-secondary">
{t('knowledgeDetails.noTestResultsForRuned')}

View File

@ -1,13 +1,17 @@
import { CardContainer } from '@/components/card-container';
import { EmptyCardType } from '@/components/empty/constant';
import { EmptyAppCard } from '@/components/empty/empty';
import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog';
import { Button } from '@/components/ui/button';
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import { useFetchNextKnowledgeListByPage } from '@/hooks/use-knowledge-request';
import { useQueryClient } from '@tanstack/react-query';
import { pick } from 'lodash';
import { Plus } from 'lucide-react';
import { useCallback } from 'react';
import { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useSearchParams } from 'umi';
import { DatasetCard } from './dataset-card';
import { DatasetCreatingDialog } from './dataset-creating-dialog';
import { useSaveKnowledge } from './hooks';
@ -52,59 +56,86 @@ export default function Datasets() {
},
[setPagination],
);
const [searchUrl, setSearchUrl] = useSearchParams();
const isCreate = searchUrl.get('isCreate') === 'true';
const queryClient = useQueryClient();
useEffect(() => {
if (isCreate) {
queryClient.invalidateQueries({ queryKey: ['tenantInfo'] });
showModal();
searchUrl.delete('isCreate');
setSearchUrl(searchUrl);
}
}, [isCreate, showModal, searchUrl, setSearchUrl]);
return (
<section className="py-4 flex-1 flex flex-col">
<ListFilterBar
title={t('header.dataset')}
searchString={searchString}
onSearchChange={handleInputChange}
value={filterValue}
filters={owners}
onChange={handleFilterSubmit}
className="px-8"
icon={'datasets'}
>
<Button onClick={showModal}>
<Plus className=" size-2.5" />
{t('knowledgeList.createKnowledgeBase')}
</Button>
</ListFilterBar>
<div className="flex-1">
<CardContainer className="max-h-[calc(100dvh-280px)] overflow-auto px-8">
{kbs.map((dataset) => {
return (
<DatasetCard
dataset={dataset}
key={dataset.id}
showDatasetRenameModal={showDatasetRenameModal}
></DatasetCard>
);
})}
</CardContainer>
</div>
<div className="mt-8 px-8">
<RAGFlowPagination
{...pick(pagination, 'current', 'pageSize')}
total={total}
onChange={handlePageChange}
></RAGFlowPagination>
</div>
{visible && (
<DatasetCreatingDialog
hideModal={hideModal}
onOk={onCreateOk}
loading={creatingLoading}
></DatasetCreatingDialog>
)}
{datasetRenameVisible && (
<RenameDialog
hideModal={hideDatasetRenameModal}
onOk={onDatasetRenameOk}
initialName={initialDatasetName}
loading={datasetRenameLoading}
></RenameDialog>
)}
</section>
<>
<section className="py-4 flex-1 flex flex-col">
{(!kbs?.length || kbs?.length <= 0) && (
<div className="flex w-full items-center justify-center h-[calc(100vh-164px)]">
<EmptyAppCard
showIcon
size="large"
className="w-[480px] p-14"
type={EmptyCardType.Dataset}
onClick={() => showModal()}
/>
</div>
)}
{!!kbs?.length && (
<>
<ListFilterBar
title={t('header.dataset')}
searchString={searchString}
onSearchChange={handleInputChange}
value={filterValue}
filters={owners}
onChange={handleFilterSubmit}
className="px-8"
icon={'datasets'}
>
<Button onClick={showModal}>
<Plus className=" size-2.5" />
{t('knowledgeList.createKnowledgeBase')}
</Button>
</ListFilterBar>
<div className="flex-1">
<CardContainer className="max-h-[calc(100dvh-280px)] overflow-auto px-8">
{kbs.map((dataset) => {
return (
<DatasetCard
dataset={dataset}
key={dataset.id}
showDatasetRenameModal={showDatasetRenameModal}
></DatasetCard>
);
})}
</CardContainer>
</div>
<div className="mt-8 px-8">
<RAGFlowPagination
{...pick(pagination, 'current', 'pageSize')}
total={total}
onChange={handlePageChange}
></RAGFlowPagination>
</div>
</>
)}
{visible && (
<DatasetCreatingDialog
hideModal={hideModal}
onOk={onCreateOk}
loading={creatingLoading}
></DatasetCreatingDialog>
)}
{datasetRenameVisible && (
<RenameDialog
hideModal={hideDatasetRenameModal}
onOk={onDatasetRenameOk}
initialName={initialDatasetName}
loading={datasetRenameLoading}
></RenameDialog>
)}
</section>
</>
);
}

View File

@ -3,11 +3,18 @@ import { MoreButton } from '@/components/more-button';
import { RenameDialog } from '@/components/rename-dialog';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchAgentListByPage } from '@/hooks/use-agent-request';
import { useEffect } from 'react';
import { AgentDropdown } from '../agents/agent-dropdown';
import { useRenameAgent } from '../agents/use-rename-agent';
export function Agents() {
const { data } = useFetchAgentListByPage();
export function Agents({
setListLength,
setLoading,
}: {
setListLength: (length: number) => void;
setLoading?: (loading: boolean) => void;
}) {
const { data, loading } = useFetchAgentListByPage();
const { navigateToAgent } = useNavigatePage();
const {
agentRenameLoading,
@ -18,6 +25,11 @@ export function Agents() {
showAgentRenameModal,
} = useRenameAgent();
useEffect(() => {
setListLength(data?.length || 0);
setLoading?.(loading || false);
}, [data, setListLength, loading, setLoading]);
return (
<>
{data.slice(0, 10).map((x) => (

View File

@ -1,4 +1,6 @@
import { CardSineLineContainer } from '@/components/card-singleline-container';
import { EmptyCardType } from '@/components/empty/constant';
import { EmptyAppCard } from '@/components/empty/empty';
import { HomeIcon } from '@/components/svg-icon';
import { Segmented, SegmentedValue } from '@/components/ui/segmented';
import { Routes } from '@/routes';
@ -16,14 +18,29 @@ const IconMap = {
[Routes.Agents]: 'agents',
};
const EmptyTypeMap = {
[Routes.Chats]: EmptyCardType.Chat,
[Routes.Searches]: EmptyCardType.Search,
[Routes.Agents]: EmptyCardType.Agent,
};
export function Applications() {
const [val, setVal] = useState(Routes.Chats);
const { t } = useTranslation();
const navigate = useNavigate();
const [listLength, setListLength] = useState(0);
const [loading, setLoading] = useState(false);
const handleNavigate = useCallback(() => {
navigate(val);
}, [navigate, val]);
const handleNavigate = useCallback(
({ isCreate }: { isCreate?: boolean }) => {
if (isCreate) {
navigate(val + '?isCreate=true');
} else {
navigate(val);
}
},
[navigate, val],
);
const options = useMemo(
() => [
@ -36,16 +53,14 @@ export function Applications() {
const handleChange = (path: SegmentedValue) => {
setVal(path as Routes);
setListLength(0);
setLoading(true);
};
return (
<section className="mt-12">
<div className="flex justify-between items-center mb-5">
<h2 className="text-2xl font-semibold flex gap-2.5">
{/* <IconFont
name={IconMap[val as keyof typeof IconMap]}
className="size-8"
></IconFont> */}
<HomeIcon
name={`${IconMap[val as keyof typeof IconMap]}`}
width={'32'}
@ -63,11 +78,36 @@ export function Applications() {
</div>
{/* <div className="flex flex-wrap gap-4"> */}
<CardSineLineContainer>
{val === Routes.Agents && <Agents></Agents>}
{val === Routes.Chats && <ChatList></ChatList>}
{val === Routes.Searches && <SearchList></SearchList>}
{<SeeAllAppCard click={handleNavigate}></SeeAllAppCard>}
{val === Routes.Agents && (
<Agents
setListLength={(length: number) => setListLength(length)}
setLoading={(loading: boolean) => setLoading(loading)}
></Agents>
)}
{val === Routes.Chats && (
<ChatList
setListLength={(length: number) => setListLength(length)}
setLoading={(loading: boolean) => setLoading(loading)}
></ChatList>
)}
{val === Routes.Searches && (
<SearchList
setListLength={(length: number) => setListLength(length)}
setLoading={(loading: boolean) => setLoading(loading)}
></SearchList>
)}
{listLength > 0 && (
<SeeAllAppCard
click={() => handleNavigate({ isCreate: false })}
></SeeAllAppCard>
)}
</CardSineLineContainer>
{listLength <= 0 && !loading && (
<EmptyAppCard
type={EmptyTypeMap[val as keyof typeof EmptyTypeMap]}
onClick={() => handleNavigate({ isCreate: true })}
/>
)}
{/* </div> */}
</section>
);

View File

@ -3,13 +3,20 @@ import { MoreButton } from '@/components/more-button';
import { RenameDialog } from '@/components/rename-dialog';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchDialogList } from '@/hooks/use-chat-request';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { ChatDropdown } from '../next-chats/chat-dropdown';
import { useRenameChat } from '../next-chats/hooks/use-rename-chat';
export function ChatList() {
export function ChatList({
setListLength,
setLoading,
}: {
setListLength: (length: number) => void;
setLoading?: (loading: boolean) => void;
}) {
const { t } = useTranslation();
const { data } = useFetchDialogList();
const { data, loading } = useFetchDialogList();
const { navigateToChat } = useNavigatePage();
const {
@ -20,7 +27,10 @@ export function ChatList() {
onChatRenameOk,
chatRenameLoading,
} = useRenameChat();
useEffect(() => {
setListLength(data?.dialogs?.length || 0);
setLoading?.(loading || false);
}, [data, setListLength, loading, setLoading]);
return (
<>
{data.dialogs.slice(0, 10).map((x) => (

View File

@ -1,4 +1,6 @@
import { CardSineLineContainer } from '@/components/card-singleline-container';
import { EmptyCardType } from '@/components/empty/constant';
import { EmptyAppCard } from '@/components/empty/empty';
import { RenameDialog } from '@/components/rename-dialog';
import { HomeIcon } from '@/components/svg-icon';
import { CardSkeleton } from '@/components/ui/skeleton';
@ -35,18 +37,32 @@ export function Datasets() {
<CardSkeleton />
</div>
) : (
<CardSineLineContainer>
{kbs
?.slice(0, 6)
.map((dataset) => (
<DatasetCard
key={dataset.id}
dataset={dataset}
showDatasetRenameModal={showDatasetRenameModal}
></DatasetCard>
))}
{<SeeAllAppCard click={navigateToDatasetList}></SeeAllAppCard>}
</CardSineLineContainer>
<>
{kbs?.length > 0 && (
<CardSineLineContainer>
{kbs
?.slice(0, 6)
.map((dataset) => (
<DatasetCard
key={dataset.id}
dataset={dataset}
showDatasetRenameModal={showDatasetRenameModal}
></DatasetCard>
))}
{
<SeeAllAppCard
click={() => navigateToDatasetList({ isCreate: false })}
></SeeAllAppCard>
}
</CardSineLineContainer>
)}
{kbs?.length <= 0 && (
<EmptyAppCard
type={EmptyCardType.Dataset}
onClick={() => navigateToDatasetList({ isCreate: true })}
/>
)}
</>
// </div>
)}
</div>

View File

@ -3,11 +3,18 @@ import { IconFont } from '@/components/icon-font';
import { MoreButton } from '@/components/more-button';
import { RenameDialog } from '@/components/rename-dialog';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useEffect } from 'react';
import { useFetchSearchList, useRenameSearch } from '../next-searches/hooks';
import { SearchDropdown } from '../next-searches/search-dropdown';
export function SearchList() {
const { data, refetch: refetchList } = useFetchSearchList();
export function SearchList({
setListLength,
setLoading,
}: {
setListLength: (length: number) => void;
setLoading?: (loading: boolean) => void;
}) {
const { data, refetch: refetchList, isLoading } = useFetchSearchList();
const { navigateToSearch } = useNavigatePage();
const {
openCreateModal,
@ -22,6 +29,11 @@ export function SearchList() {
refetchList();
});
};
useEffect(() => {
setListLength(data?.data?.search_apps?.length || 0);
setLoading?.(isLoading || false);
}, [data, setListLength, isLoading, setLoading]);
return (
<>
{data?.data.search_apps.slice(0, 10).map((x) => (

View File

@ -1,4 +1,6 @@
import { CardContainer } from '@/components/card-container';
import { EmptyCardType } from '@/components/empty/constant';
import { EmptyAppCard } from '@/components/empty/empty';
import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog';
import { Button } from '@/components/ui/button';
@ -6,8 +8,9 @@ import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import { useFetchDialogList } from '@/hooks/use-chat-request';
import { pick } from 'lodash';
import { Plus } from 'lucide-react';
import { useCallback } from 'react';
import { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useSearchParams } from 'umi';
import { ChatCard } from './chat-card';
import { useRenameChat } from './hooks/use-rename-chat';
@ -35,41 +38,66 @@ export default function ChatList() {
showChatRenameModal();
}, [showChatRenameModal]);
const [searchParams, setSearchParams] = useSearchParams();
const isCreate = searchParams.get('isCreate') === 'true';
useEffect(() => {
if (isCreate) {
handleShowCreateModal();
searchParams.delete('isCreate');
setSearchParams(searchParams);
}
}, [isCreate, handleShowCreateModal, searchParams, setSearchParams]);
return (
<section className="flex flex-col w-full flex-1">
<div className="px-8 pt-8">
<ListFilterBar
title={t('chat.chatApps')}
icon="chats"
onSearchChange={handleInputChange}
searchString={searchString}
>
<Button onClick={handleShowCreateModal}>
<Plus className="size-2.5" />
{t('chat.createChat')}
</Button>
</ListFilterBar>
</div>
<div className="flex-1 overflow-auto">
<CardContainer className="max-h-[calc(100dvh-280px)] overflow-auto px-8">
{data.dialogs.map((x) => {
return (
<ChatCard
key={x.id}
data={x}
showChatRenameModal={showChatRenameModal}
></ChatCard>
);
})}
</CardContainer>
</div>
<div className="mt-8 px-8 pb-8">
<RAGFlowPagination
{...pick(pagination, 'current', 'pageSize')}
total={pagination.total}
onChange={handlePageChange}
></RAGFlowPagination>
</div>
{data.dialogs?.length <= 0 && (
<div className="flex w-full items-center justify-center h-[calc(100vh-164px)]">
<EmptyAppCard
showIcon
size="large"
className="w-[480px] p-14"
type={EmptyCardType.Chat}
onClick={() => handleShowCreateModal()}
/>
</div>
)}
{data.dialogs?.length > 0 && (
<>
<div className="px-8 pt-8">
<ListFilterBar
title={t('chat.chatApps')}
icon="chats"
onSearchChange={handleInputChange}
searchString={searchString}
>
<Button onClick={handleShowCreateModal}>
<Plus className="size-2.5" />
{t('chat.createChat')}
</Button>
</ListFilterBar>
</div>
<div className="flex-1 overflow-auto">
<CardContainer className="max-h-[calc(100dvh-280px)] overflow-auto px-8">
{data.dialogs.map((x) => {
return (
<ChatCard
key={x.id}
data={x}
showChatRenameModal={showChatRenameModal}
></ChatCard>
);
})}
</CardContainer>
</div>
<div className="mt-8 px-8 pb-8">
<RAGFlowPagination
{...pick(pagination, 'current', 'pageSize')}
total={pagination.total}
onChange={handlePageChange}
></RAGFlowPagination>
</div>
</>
)}
{chatRenameVisible && (
<RenameDialog
hideModal={hideChatRenameModal}

View File

@ -17,26 +17,39 @@ import {
import { Separator } from '@/components/ui/separator';
import {
useAllTestingResult,
useChunkIsTesting,
useSelectTestingResult,
} from '@/hooks/knowledge-hooks';
import { cn } from '@/lib/utils';
import { CheckIcon, ChevronDown, Files, XIcon } from 'lucide-react';
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
interface IProps {
onTesting(documentIds: string[]): void;
setSelectedDocumentIds(documentIds: string[]): void;
selectedDocumentIds: string[];
setLoading?: (loading: boolean) => void;
}
const RetrievalDocuments = ({
onTesting,
selectedDocumentIds,
setSelectedDocumentIds,
setLoading,
}: IProps) => {
const { documents: documentsAll } = useAllTestingResult();
const { documents } = useSelectTestingResult();
const isTesting = useChunkIsTesting();
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
useEffect(() => {
if (isTesting) {
setLoading?.(true);
} else {
setLoading?.(false);
}
}, [isTesting, setLoading]);
const { documents: useDocuments } = {
documents:
documentsAll?.length > documents?.length ? documentsAll : documents,
@ -45,6 +58,9 @@ const RetrievalDocuments = ({
useState<string[]>(selectedDocumentIds);
const multiOptions = useMemo(() => {
if (!useDocuments || !useDocuments.length) {
return [];
}
return useDocuments?.map((item) => {
return {
label: item.doc_name,
@ -97,36 +113,38 @@ const RetrievalDocuments = ({
return (
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
<PopoverTrigger asChild>
<Button
onClick={handleTogglePopover}
className={cn(
'flex w-full p-1 rounded-md text-base text-text-primary border min-h-10 h-auto items-center justify-between bg-inherit hover:bg-inherit [&_svg]:pointer-events-auto',
)}
>
<div className="flex justify-between items-center w-full">
<div className="flex flex-wrap items-center gap-2">
<Files />
<span>
{selectedDocumentIds?.length ?? 0}/{useDocuments?.length ?? 0}
</span>
Files
{useDocuments?.length && (
<Button
onClick={handleTogglePopover}
className={cn(
'flex w-full p-1 rounded-md text-base text-text-primary border min-h-10 h-auto items-center justify-between bg-inherit hover:bg-inherit [&_svg]:pointer-events-auto',
)}
>
<div className="flex justify-between items-center w-full">
<div className="flex flex-wrap items-center gap-2">
<Files />
<span>
{selectedDocumentIds?.length ?? 0}/{useDocuments?.length ?? 0}
</span>
Files
</div>
<div className="flex items-center justify-between">
<XIcon
className="h-4 mx-2 cursor-pointer text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
handleClear();
}}
/>
<Separator
orientation="vertical"
className="flex min-h-6 h-full"
/>
<ChevronDown className="h-4 mx-2 cursor-pointer text-muted-foreground" />
</div>
</div>
<div className="flex items-center justify-between">
<XIcon
className="h-4 mx-2 cursor-pointer text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
handleClear();
}}
/>
<Separator
orientation="vertical"
className="flex min-h-6 h-full"
/>
<ChevronDown className="h-4 mx-2 cursor-pointer text-muted-foreground" />
</div>
</div>
</Button>
</Button>
)}
</PopoverTrigger>
<PopoverContent
className="w-auto p-0"

View File

@ -1,3 +1,5 @@
import { EmptyType } from '@/components/empty/constant';
import Empty from '@/components/empty/empty';
import { FileIcon } from '@/components/icon-font';
import { ImageWithPopover } from '@/components/image';
import { Input } from '@/components/originui/input';
@ -65,6 +67,7 @@ export default function SearchingView({
// changeLanguage();
// }, [i18n]);
const [searchtext, setSearchtext] = useState<string>('');
const [retrievalLoading, setRetrievalLoading] = useState(false);
useEffect(() => {
setSearchtext(searchStr);
@ -182,6 +185,9 @@ export default function SearchingView({
selectedDocumentIds={selectedDocumentIds}
setSelectedDocumentIds={setSelectedDocumentIds}
onTesting={handleTestChunk}
setLoading={(loading: boolean) => {
setRetrievalLoading(loading);
}}
></RetrievalDocuments>
</div>
{/* <div className="w-full border-b border-border-default/80 my-6"></div> */}
@ -264,6 +270,17 @@ export default function SearchingView({
</>
)}
</div>
{!isSearchStrEmpty &&
!retrievalLoading &&
!answer.answer &&
!sendingLoading &&
total <= 0 &&
chunks?.length <= 0 &&
relatedQuestions?.length <= 0 && (
<div className="h-2/5 flex items-center justify-center">
<Empty type={EmptyType.SearchData} iconWidth={80} />
</div>
)}
</div>
{total > 0 && (

View File

@ -1,4 +1,6 @@
import { CardContainer } from '@/components/card-container';
import { EmptyCardType } from '@/components/empty/constant';
import { EmptyAppCard } from '@/components/empty/empty';
import { IconFont } from '@/components/icon-font';
import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog';
@ -6,6 +8,8 @@ import { Button } from '@/components/ui/button';
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import { useTranslate } from '@/hooks/common-hooks';
import { Plus } from 'lucide-react';
import { useCallback, useEffect } from 'react';
import { useSearchParams } from 'umi';
import { useFetchSearchList, useRenameSearch } from './hooks';
import { SearchCard } from './search-card';
@ -27,6 +31,7 @@ export default function SearchList() {
onSearchRenameOk,
initialSearchName,
} = useRenameSearch();
const handleSearchChange = (value: string) => {
console.log(value);
};
@ -35,61 +40,86 @@ export default function SearchList() {
refetchList();
});
};
const openCreateModalFun = () => {
const openCreateModalFun = useCallback(() => {
// setIsEdit(false);
showSearchRenameModal();
};
}, [showSearchRenameModal]);
const handlePageChange = (page: number, pageSize: number) => {
// setIsEdit(false);
setSearchListParams({ ...searchParams, page, page_size: pageSize });
};
const [searchUrl, setSearchUrl] = useSearchParams();
const isCreate = searchUrl.get('isCreate') === 'true';
useEffect(() => {
if (isCreate) {
openCreateModalFun();
searchUrl.delete('isCreate');
setSearchUrl(searchUrl);
}
}, [isCreate, openCreateModalFun, searchUrl, setSearchUrl]);
return (
<section className="w-full h-full flex flex-col">
<div className="px-8 pt-8">
<ListFilterBar
icon="searches"
title={t('searchApps')}
showFilter={false}
onSearchChange={(e) => handleSearchChange(e.target.value)}
>
<Button
variant={'default'}
onClick={() => {
openCreateModalFun();
}}
>
<Plus className="mr-2 h-4 w-4" />
{t('createSearch')}
</Button>
</ListFilterBar>
</div>
<div className="flex-1">
<CardContainer className="max-h-[calc(100dvh-280px)] overflow-auto px-8">
{list?.data.search_apps.map((x) => {
return (
<SearchCard
key={x.id}
data={x}
showSearchRenameModal={() => {
showSearchRenameModal(x);
}}
></SearchCard>
);
})}
</CardContainer>
</div>
{list?.data.total && list?.data.total > 0 && (
<div className="px-8 mb-4">
<RAGFlowPagination
current={searchParams.page}
pageSize={searchParams.page_size}
total={list?.data.total}
onChange={handlePageChange}
{(!list?.data?.search_apps?.length ||
list?.data?.search_apps?.length <= 0) && (
<div className="flex w-full items-center justify-center h-[calc(100vh-164px)]">
<EmptyAppCard
showIcon
size="large"
className="w-[480px] p-14"
type={EmptyCardType.Search}
onClick={() => openCreateModalFun()}
/>
</div>
)}
{!!list?.data?.search_apps?.length && (
<>
<div className="px-8 pt-8">
<ListFilterBar
icon="searches"
title={t('searchApps')}
showFilter={false}
onSearchChange={(e) => handleSearchChange(e.target.value)}
>
<Button
variant={'default'}
onClick={() => {
openCreateModalFun();
}}
>
<Plus className="mr-2 h-4 w-4" />
{t('createSearch')}
</Button>
</ListFilterBar>
</div>
<div className="flex-1">
<CardContainer className="max-h-[calc(100dvh-280px)] overflow-auto px-8">
{list?.data.search_apps.map((x) => {
return (
<SearchCard
key={x.id}
data={x}
showSearchRenameModal={() => {
showSearchRenameModal(x);
}}
></SearchCard>
);
})}
</CardContainer>
</div>
{list?.data.total && list?.data.total > 0 && (
<div className="px-8 mb-4">
<RAGFlowPagination
current={searchParams.page}
pageSize={searchParams.page_size}
total={list?.data.total}
onChange={handlePageChange}
/>
</div>
)}
</>
)}
{openCreateModal && (
<RenameDialog
hideModal={hideSearchRenameModal}

View File

@ -96,62 +96,77 @@ export default function McpServer() {
</>
}
>
{isSelectionMode && (
<section className="pb-5 flex items-center">
<Checkbox id="all" onCheckedChange={handleSelectAll} />
<Label
className="pl-2 text-text-primary cursor-pointer"
htmlFor="all"
>
{t('common.selectAll')}
</Label>
<span className="text-text-secondary pr-10 pl-5">
{t('mcp.selected')} {selectedList.length}
</span>
<div className="flex gap-10 items-center">
<Button variant={'secondary'} onClick={handleExportMcp}>
<Upload className="size-3.5"></Upload>
{t('mcp.export')}
</Button>
<ConfirmDeleteDialog
onOk={handleDelete}
title={t('common.delete') + ' ' + t('mcp.mcpServers')}
content={{
title: t('common.deleteThem'),
node: (
<ConfirmDeleteDialogNode
name={`${t('mcp.selected')} ${selectedList.length} ${t('mcp.mcpServers')}`}
></ConfirmDeleteDialogNode>
),
}}
>
<Button variant={'danger'}>
<Trash2 className="size-3.5 cursor-pointer" />
{t('common.delete')}
</Button>
</ConfirmDeleteDialog>
</div>
</section>
{!data.mcp_servers?.length && (
<div
className="flex items-center justify-between border border-dashed border-border-button rounded-md p-10 cursor-pointer w-[590px]"
onClick={showEditModal('')}
>
<div className="text-text-secondary text-sm">{t('empty.noMCP')}</div>
<Button variant={'ghost'} className="border border-border-button">
<Plus className="size-3.5 font-medium" /> {t('empty.addNow')}
</Button>
</div>
)}
{!!data.mcp_servers?.length && (
<>
{isSelectionMode && (
<section className="pb-5 flex items-center">
<Checkbox id="all" onCheckedChange={handleSelectAll} />
<Label
className="pl-2 text-text-primary cursor-pointer"
htmlFor="all"
>
{t('common.selectAll')}
</Label>
<span className="text-text-secondary pr-10 pl-5">
{t('mcp.selected')} {selectedList.length}
</span>
<div className="flex gap-10 items-center">
<Button variant={'secondary'} onClick={handleExportMcp}>
<Upload className="size-3.5"></Upload>
{t('mcp.export')}
</Button>
<ConfirmDeleteDialog
onOk={handleDelete}
title={t('common.delete') + ' ' + t('mcp.mcpServers')}
content={{
title: t('common.deleteThem'),
node: (
<ConfirmDeleteDialogNode
name={`${t('mcp.selected')} ${selectedList.length} ${t('mcp.mcpServers')}`}
></ConfirmDeleteDialogNode>
),
}}
>
<Button variant={'danger'}>
<Trash2 className="size-3.5 cursor-pointer" />
{t('common.delete')}
</Button>
</ConfirmDeleteDialog>
</div>
</section>
)}
<CardContainer>
{data.mcp_servers.map((item) => (
<McpCard
key={item.id}
data={item}
selectedList={selectedList}
handleSelectChange={handleSelectChange}
showEditModal={showEditModal}
isSelectionMode={isSelectionMode}
></McpCard>
))}
</CardContainer>
<div className="mt-8">
<RAGFlowPagination
{...pick(pagination, 'current', 'pageSize')}
total={pagination.total || 0}
onChange={handlePageChange}
></RAGFlowPagination>
</div>
</>
)}
<CardContainer>
{data.mcp_servers.map((item) => (
<McpCard
key={item.id}
data={item}
selectedList={selectedList}
handleSelectChange={handleSelectChange}
showEditModal={showEditModal}
isSelectionMode={isSelectionMode}
></McpCard>
))}
</CardContainer>
<div className="mt-8">
<RAGFlowPagination
{...pick(pagination, 'current', 'pageSize')}
total={pagination.total || 0}
onChange={handlePageChange}
></RAGFlowPagination>
</div>
{editVisible && (
<EditMcpDialog
hideModal={hideEditModal}

View File

@ -75,7 +75,9 @@ export const ModelProviderCard: FC<IModelCardProps> = ({
<div className="flex items-center space-x-3">
<LlmIcon name={item.name} />
<div>
<div className="font-medium text-xl">{item.name}</div>
<div className="font-medium text-xl text-text-primary">
{item.name}
</div>
</div>
</div>