diff --git a/packages/server/src/controllers/chatflows/index.ts b/packages/server/src/controllers/chatflows/index.ts index a6125ca57c5..14d43094970 100644 --- a/packages/server/src/controllers/chatflows/index.ts +++ b/packages/server/src/controllers/chatflows/index.ts @@ -73,12 +73,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 = req.query?.search as string | 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 9998ca57643..81c4a3add35 100644 --- a/packages/server/src/services/chatflows/index.ts +++ b/packages/server/src/services/chatflows/index.ts @@ -142,7 +142,7 @@ 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 +150,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 +161,16 @@ 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) { + queryBuilder.andWhere( + '(LOWER(chat_flow.name) LIKE LOWER(:search) OR chat_flow.id LIKE :search)', + { search: `%${search}%` } + ) + } + 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/ui-component/table/FlowListTable.jsx b/packages/ui/src/ui-component/table/FlowListTable.jsx index 2d1e0570472..88d908e8a10 100644 --- a/packages/ui/src/ui-component/table/FlowListTable.jsx +++ b/packages/ui/src/ui-component/table/FlowListTable.jsx @@ -55,7 +55,7 @@ export const FlowListTable = ({ images = {}, icons = {}, isLoading, - filterFunction, + filterFunction = () => true, updateFlowsApi, setError, isAgentCanvas, diff --git a/packages/ui/src/views/agentflows/index.jsx b/packages/ui/src/views/agentflows/index.jsx index c99bd6cf2d7..371dceb755c 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, useState, useCallback, useRef } from 'react' import { useNavigate } from 'react-router-dom' import { useSelector } from 'react-redux' @@ -28,6 +28,9 @@ import useApi from '@/hooks/useApi' import { baseURL, AGENTFLOW_ICONS } from '@/store/constant' import { useError } from '@/store/context/ErrorContext' +// utils +import { debounce } from 'lodash' + // icons import { IconPlus, IconLayoutGrid, IconList, IconX, IconAlertTriangle } from '@tabler/icons-react' @@ -54,17 +57,22 @@ const Agentflows = () => { const [pageLimit, setPageLimit] = useState(DEFAULT_ITEMS_PER_PAGE) const [total, setTotal] = useState(0) + const searchRef = useRef('') + const onChange = (page, pageLimit) => { setCurrentPage(page) setPageLimit(pageLimit) - refresh(page, pageLimit, agentflowVersion) + refresh(page, pageLimit, agentflowVersion, searchRef.current) } - const refresh = (page, limit, nextView) => { + const refresh = (page, limit, nextView, searchVal) => { const params = { page: page || currentPage, limit: limit || pageLimit } + if (searchVal) { + params.search = searchVal + } getAllAgentflows.request(nextView === 'v2' ? 'AGENTFLOW' : 'MULTIAGENT', params) } @@ -78,19 +86,22 @@ const Agentflows = () => { if (nextView === null) return localStorage.setItem('agentFlowVersion', nextView) setAgentflowVersion(nextView) - refresh(1, pageLimit, nextView) + refresh(1, pageLimit, nextView, searchRef.current) } + // eslint-disable-next-line react-hooks/exhaustive-deps + const debouncedSearch = useCallback( + debounce((value, version) => { + setCurrentPage(1) + refresh(1, pageLimit, version, value) + }, 300), + [pageLimit] + ) + const onSearchChange = (event) => { 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 - ) + searchRef.current = event.target.value + debouncedSearch(event.target.value, agentflowVersion) } const addNew = () => { @@ -304,7 +315,7 @@ const Agentflows = () => { <> {!view || view === 'card' ? ( - {getAllAgentflows.data?.data.filter(filterFlows).map((data, index) => ( + {getAllAgentflows.data?.data?.map((data, index) => ( goToCanvas(data)} @@ -322,7 +333,6 @@ const Agentflows = () => { images={images} icons={icons} isLoading={isLoading} - filterFunction={filterFlows} updateFlowsApi={getAllAgentflows} setError={setError} currentPage={currentPage} diff --git a/packages/ui/src/views/chatflows/index.jsx b/packages/ui/src/views/chatflows/index.jsx index ce213313dcb..f27cb4f08ab 100644 --- a/packages/ui/src/views/chatflows/index.jsx +++ b/packages/ui/src/views/chatflows/index.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useEffect, useState, useCallback, useRef } from 'react' import { useNavigate } from 'react-router-dom' // material-ui @@ -27,6 +27,9 @@ import useApi from '@/hooks/useApi' import { baseURL } from '@/store/constant' import { useError } from '@/store/context/ErrorContext' +// utils +import { debounce } from 'lodash' + // icons import { IconPlus, IconLayoutGrid, IconList } from '@tabler/icons-react' @@ -48,18 +51,22 @@ const Chatflows = () => { const [currentPage, setCurrentPage] = useState(1) const [pageLimit, setPageLimit] = useState(DEFAULT_ITEMS_PER_PAGE) const [total, setTotal] = useState(0) + const searchRef = useRef('') const onChange = (page, pageLimit) => { setCurrentPage(page) setPageLimit(pageLimit) - applyFilters(page, pageLimit) + applyFilters(page, pageLimit, searchRef.current) } - const applyFilters = (page, limit) => { + const applyFilters = (page, limit, searchVal) => { const params = { page: page || currentPage, limit: limit || pageLimit } + if (searchVal) { + params.search = searchVal + } getAllChatflowsApi.request(params) } @@ -69,16 +76,19 @@ const Chatflows = () => { setView(nextView) } + // eslint-disable-next-line react-hooks/exhaustive-deps + const debouncedSearch = useCallback( + debounce((value) => { + setCurrentPage(1) + applyFilters(1, pageLimit, value) + }, 300), + [pageLimit] + ) + const onSearchChange = (event) => { 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 - ) + searchRef.current = event.target.value + debouncedSearch(event.target.value) } const addNew = () => { @@ -196,7 +206,7 @@ const Chatflows = () => { <> {!view || view === 'card' ? ( - {getAllChatflowsApi.data?.data?.filter(filterFlows).map((data, index) => ( + {getAllChatflowsApi.data?.data?.map((data, index) => ( goToCanvas(data)} data={data} images={images[data.id]} /> ))} @@ -205,7 +215,6 @@ const Chatflows = () => { data={getAllChatflowsApi.data?.data} images={images} isLoading={isLoading} - filterFunction={filterFlows} updateFlowsApi={getAllChatflowsApi} setError={setError} currentPage={currentPage}