Feat: Add Explore page (#13043)

### What problem does this PR solve?

Feat: Add Explore page

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2026-02-09 19:53:51 +08:00
committed by GitHub
parent 8ad7339448
commit db37804f10
21 changed files with 1105 additions and 31 deletions

View File

@@ -95,6 +95,13 @@ export const useNavigatePage = () => {
[navigate],
);
const navigateToAgentExplore = useCallback(
(id: string) => () => {
navigate(`${Routes.Agent}/${id}/explore`);
},
[navigate],
);
const navigateToAgentLogs = useCallback(
(id: string) => () => {
navigate(`${Routes.AgentLogPage}/${id}`);
@@ -176,7 +183,7 @@ export const useNavigatePage = () => {
const navigateToDataflowResult = useCallback(
(props: NavigateToDataflowResultProps) => () => {
let params: string[] = [];
const params: string[] = [];
Object.keys(props).forEach((key) => {
if (props[key as keyof typeof props]) {
params.push(`${key}=${props[key as keyof typeof props]}`);
@@ -203,6 +210,7 @@ export const useNavigatePage = () => {
navigateToChunk,
navigateToAgents,
navigateToAgent,
navigateToAgentExplore,
navigateToAgentLogs,
navigateToAgentTemplates,
navigateToSearchList,

View File

@@ -1,8 +1,14 @@
import { FileUploadProps } from '@/components/file-upload';
import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit';
import message from '@/components/ui/message';
import { AgentGlobals, initialBeginValues } from '@/constants/agent';
import {
AgentCategory,
AgentGlobals,
initialBeginValues,
} from '@/constants/agent';
import { useFetchTenantInfo } from '@/hooks/use-user-setting-request';
import {
IAgentLogResponse,
IAgentLogsRequest,
IAgentLogsResponse,
IFlow,
@@ -20,7 +26,10 @@ import { BeginId } from '@/pages/agent/constant';
import { IInputs } from '@/pages/agent/interface';
import { useGetSharedChatSearchParams } from '@/pages/next-chats/hooks/use-send-shared-message';
import agentService, {
createAgentSession,
deleteAgentSession,
fetchAgentLogsByCanvasId,
fetchAgentLogsById,
fetchPipeLineList,
fetchTrace,
fetchWebhookTrace,
@@ -39,6 +48,7 @@ import {
export const enum AgentApiAction {
FetchAgentListByPage = 'fetchAgentListByPage',
FetchAllAgentList = 'fetchAllAgentList',
FetchAgentList = 'fetchAgentList',
UpdateAgentSetting = 'updateAgentSetting',
DeleteAgent = 'deleteAgent',
@@ -61,6 +71,11 @@ export const enum AgentApiAction {
CancelDataflow = 'cancelDataflow',
CancelCanvas = 'cancelCanvas',
FetchWebhookTrace = 'fetchWebhookTrace',
FetchSessionsByCanvasId = 'fetchSessionsByCanvasId',
FetchSessionById = 'fetchSessionById',
CreateAgentSession = 'createAgentSession',
DeleteAgentSession = 'deleteAgentSession',
FetchSessionByIdManually = 'fetchSessionByIdManually',
}
export const EmptyDsl = {
@@ -194,6 +209,28 @@ export const useFetchAgentListByPage = () => {
};
};
export function useFetchAllAgentList() {
const { data, isFetching: loading } = useQuery<IFlow[]>({
queryKey: [AgentApiAction.FetchAllAgentList],
queryFn: async () => {
const { data } = await agentService.listCanvas(
{
params: {
page: 1,
page_size: 100000,
canvas_category: AgentCategory.AgentCanvas,
},
},
true,
);
return data?.data?.canvas;
},
});
return { data, loading };
}
export const useUpdateAgentSetting = () => {
const queryClient = useQueryClient();
@@ -627,6 +664,37 @@ export const useFetchAgentLog = (searchParams: IAgentLogsRequest) => {
return { data, loading };
};
export const useFetchSessionsByCanvasId = () => {
const { id: canvasId } = useParams();
const { data: tenantInfo } = useFetchTenantInfo();
const { data, isFetching: loading } = useQuery<IAgentLogsResponse>({
queryKey: [AgentApiAction.FetchSessionsByCanvasId, canvasId],
initialData: { total: 0, sessions: [] } as IAgentLogsResponse,
gcTime: 0,
enabled: !!canvasId && !isEmpty(tenantInfo),
queryFn: async () => {
if (!canvasId) {
return { total: 0, sessions: [] };
}
const { data } = await fetchAgentLogsByCanvasId(canvasId, {
page: 1,
page_size: 100000,
exp_user_id: tenantInfo.tenant_id,
});
return data?.data ?? { total: 0, sessions: [] };
},
});
return {
data: data?.sessions ?? [],
loading,
total: data?.total ?? 0,
};
};
export const useFetchExternalAgentInputs = () => {
const { sharedId } = useGetSharedChatSearchParams();
@@ -873,3 +941,82 @@ export const useFetchWebhookTrace = (autoStart: boolean = true) => {
currentNextSinceTs,
};
};
export function useCreateAgentSession() {
const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [AgentApiAction.CreateAgentSession],
mutationFn: async (payload: { id: string; name: string }) => {
const { data } = await createAgentSession(payload);
if (data.code === 0) {
queryClient.invalidateQueries({
queryKey: [AgentApiAction.FetchSessionsByCanvasId],
});
}
return data?.data ?? {};
},
});
return { data, loading, createAgentSession: mutateAsync };
}
export function useDeleteAgentSession() {
const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [AgentApiAction.DeleteAgentSession],
mutationFn: async ({
canvasId,
sessionId,
}: {
canvasId: string;
sessionId: string;
}) => {
const { data } = await deleteAgentSession(canvasId, sessionId);
if (data.code === 0) {
queryClient.invalidateQueries({
queryKey: [AgentApiAction.FetchSessionsByCanvasId],
});
}
return data?.code ?? -1;
},
});
return { data, loading, deleteAgentSession: mutateAsync };
}
export function useFetchSessionManually() {
const { id: canvasId } = useParams();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation<IAgentLogResponse, unknown, string>({
mutationKey: [AgentApiAction.FetchSessionByIdManually, canvasId],
mutationFn: async (sessionId) => {
if (!canvasId || !sessionId) {
return null;
}
const { data } = await fetchAgentLogsById(canvasId, sessionId);
return data?.data;
},
});
return { data, loading, fetchSessionManually: mutateAsync };
}

View File

@@ -0,0 +1,61 @@
import { useDebounce } from 'ahooks';
import { useCallback, useMemo, useState } from 'react';
export interface SearchFilterOptions<T> {
data: T[];
searchFields: Array<keyof T | ((item: T) => string)>;
debounceMs?: number;
}
export function useClientSearch<T>({
data,
searchFields,
debounceMs = 300,
}: SearchFilterOptions<T>) {
const [searchKeyword, setSearchKeyword] = useState('');
const debouncedSearchKeyword = useDebounce(searchKeyword, {
wait: debounceMs,
});
const handleSearchChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setSearchKeyword(e.target.value);
},
[],
);
const clearSearch = useCallback(() => {
setSearchKeyword('');
}, []);
const filteredData = useMemo(() => {
if (!debouncedSearchKeyword.trim()) {
return data;
}
const keyword = debouncedSearchKeyword.toLowerCase().trim();
return data.filter((item) => {
return searchFields.some((field) => {
let value: string;
if (typeof field === 'function') {
value = field(item);
} else {
value = String(item[field] ?? '');
}
return value?.toLowerCase().includes(keyword);
});
});
}, [data, debouncedSearchKeyword, searchFields]);
return {
filteredData,
searchKeyword,
handleSearchChange,
clearSearch,
isSearching: debouncedSearchKeyword !== searchKeyword,
};
}