diff --git a/packages/server/src/controllers/chatflows/index.ts b/packages/server/src/controllers/chatflows/index.ts index c77f293e4f4..0c4bf9f26c7 100644 --- a/packages/server/src/controllers/chatflows/index.ts +++ b/packages/server/src/controllers/chatflows/index.ts @@ -74,12 +74,14 @@ const deleteChatflow = async (req: Request, res: Response, next: NextFunction) = const getAllChatflows = async (req: Request, res: Response, next: NextFunction) => { try { const { page, limit } = getPageAndLimitParams(req) + const search = typeof req.query?.search === 'string' ? req.query.search : undefined const apiResponse = await chatflowsService.getAllChatflows( req.query?.type as ChatflowType, req.user?.activeWorkspaceId, page, - limit + limit, + search ) return res.json(apiResponse) } catch (error) { diff --git a/packages/server/src/services/chatflows/index.ts b/packages/server/src/services/chatflows/index.ts index 697b757d6c7..b3d578c1a56 100644 --- a/packages/server/src/services/chatflows/index.ts +++ b/packages/server/src/services/chatflows/index.ts @@ -142,7 +142,13 @@ const deleteChatflow = async (chatflowId: string, orgId: string, workspaceId: st } } -const getAllChatflows = async (type?: ChatflowType, workspaceId?: string, page: number = -1, limit: number = -1) => { +const getAllChatflows = async ( + type?: ChatflowType, + workspaceId?: string, + page: number = -1, + limit: number = -1, + search?: string +) => { try { const appServer = getRunningExpressApp() @@ -150,10 +156,6 @@ const getAllChatflows = async (type?: ChatflowType, workspaceId?: string, page: .createQueryBuilder('chat_flow') .orderBy('chat_flow.updatedDate', 'DESC') - if (page > 0 && limit > 0) { - queryBuilder.skip((page - 1) * limit) - queryBuilder.take(limit) - } if (type === 'MULTIAGENT') { queryBuilder.andWhere('chat_flow.type = :type', { type: 'MULTIAGENT' }) } else if (type === 'AGENTFLOW') { @@ -165,6 +167,20 @@ const getAllChatflows = async (type?: ChatflowType, workspaceId?: string, page: queryBuilder.andWhere('chat_flow.type = :type', { type: 'CHATFLOW' }) } if (workspaceId) queryBuilder.andWhere('chat_flow.workspaceId = :workspaceId', { workspaceId }) + if (search?.trim()) { + const searchTerm = `%${search.trim().toLowerCase()}%` + queryBuilder.andWhere( + new Brackets((qb) => { + qb.where('LOWER(chat_flow.name) LIKE :search', { search: searchTerm }) + .orWhere("LOWER(COALESCE(chat_flow.category, '')) LIKE :search", { search: searchTerm }) + .orWhere('CAST(chat_flow.id AS TEXT) LIKE :search', { search: searchTerm }) + }) + ) + } + if (page > 0 && limit > 0) { + queryBuilder.skip((page - 1) * limit) + queryBuilder.take(limit) + } const [data, total] = await queryBuilder.getManyAndCount() if (page > 0 && limit > 0) { diff --git a/packages/ui/src/views/agentflows/index.jsx b/packages/ui/src/views/agentflows/index.jsx index 9ee745c16fc..f8b10feb828 100644 --- a/packages/ui/src/views/agentflows/index.jsx +++ b/packages/ui/src/views/agentflows/index.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { useSelector } from 'react-redux' import { useNavigate } from 'react-router-dom' @@ -42,12 +42,14 @@ const Agentflows = () => { const [images, setImages] = useState({}) const [icons, setIcons] = useState({}) const [search, setSearch] = useState('') + const [searchQuery, setSearchQuery] = useState('') const { error, setError } = useError() const getAllAgentflows = useApi(chatflowsApi.getAllAgentflows) const [view, setView] = useState(localStorage.getItem('agentFlowDisplayStyle') || 'card') const [agentflowVersion, setAgentflowVersion] = useState(localStorage.getItem('agentFlowVersion') || 'v2') const [showDeprecationNotice, setShowDeprecationNotice] = useState(true) + const latestAgentflowVersionRef = useRef(agentflowVersion) /* Table Pagination */ const [currentPage, setCurrentPage] = useState(1) @@ -61,11 +63,12 @@ const Agentflows = () => { refresh(page, pageLimit, agentflowVersion) } - const refresh = (page, limit, nextView) => { + const refresh = (page, limit, nextView, nextSearch = searchQuery) => { const params = { page: page || currentPage, limit: limit || pageLimit } + if (nextSearch) params.search = nextSearch getAllAgentflows.request(nextView === 'v2' ? 'AGENTFLOW' : 'MULTIAGENT', params) } @@ -79,6 +82,7 @@ const Agentflows = () => { if (nextView === null) return localStorage.setItem('agentFlowVersion', nextView) setAgentflowVersion(nextView) + setCurrentPage(1) refresh(1, pageLimit, nextView) } @@ -86,14 +90,6 @@ const Agentflows = () => { setSearch(event.target.value) } - function filterFlows(data) { - return ( - data.name.toLowerCase().indexOf(search.toLowerCase()) > -1 || - (data.category && data.category.toLowerCase().indexOf(search.toLowerCase()) > -1) || - data.id.toLowerCase().indexOf(search.toLowerCase()) > -1 - ) - } - const addNew = () => { if (agentflowVersion === 'v2') { navigate('/v2/agentcanvas') @@ -120,6 +116,25 @@ const Agentflows = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []) + useEffect(() => { + latestAgentflowVersionRef.current = agentflowVersion + }, [agentflowVersion]) + + useEffect(() => { + const normalizedSearch = search.trim() + if (normalizedSearch === searchQuery) return + + const timeoutId = setTimeout(() => { + setSearchQuery(normalizedSearch) + setCurrentPage(1) + refresh(1, pageLimit, latestAgentflowVersionRef.current, normalizedSearch) + }, 300) + + return () => clearTimeout(timeoutId) + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [search]) + useEffect(() => { if (getAllAgentflows.error) { setError(getAllAgentflows.error) @@ -178,7 +193,7 @@ const Agentflows = () => { @@ -305,7 +320,7 @@ const Agentflows = () => { <> {!view || view === 'card' ? ( - {getAllAgentflows.data?.data.filter(filterFlows).map((data, index) => ( + {getAllAgentflows.data?.data.map((data, index) => ( goToCanvas(data)} @@ -323,7 +338,7 @@ const Agentflows = () => { images={images} icons={icons} isLoading={isLoading} - filterFunction={filterFlows} + filterFunction={() => true} updateFlowsApi={getAllAgentflows} setError={setError} currentPage={currentPage}