Skip to content

Commit 0149d04

Browse files
chandrasekharan-zipstackclaudecoderabbitai[bot]
authored
[FEAT] Add search functionality to LLMs, VectorDBs, Embeddings, and Connectors pages (#1694)
* UN-2454 [FIX] Avoid polling infinitely for in-progress execution - Update DetailedLogs and ExecutionLogs components to properly handle polling state - Update index.js dependencies - Update workers dependencies in pyproject.toml and uv.lock 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * Revert accidental changes to index.js and workers dependencies Reverted files that were not part of the intended fix: - frontend/src/index.js - workers/pyproject.toml - workers/uv.lock 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * UN-2454 [FIX] Fix date handling and polling logic in logging components - Fixed date calculation bug: use raw ISO timestamps instead of formatted display strings - Added createdAtRaw and modified_atRaw fields to preserve parseable date values - Implement stale interval re-check: stop polling when execution is >1 hour old via executionDetailsRef - Replace forEach loops with for...of for improved performance (4 instances) Fixes CodeRabbit comments about infinite polling and ensures logging components correctly handle timestamp calculations and respect staleness thresholds. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * Update frontend/src/components/logging/detailed-logs/DetailedLogs.jsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Chandrasekharan M <117059509+chandrasekharan-zipstack@users.noreply.github.com> * UN-2454 [FIX] Use isFinite() for robust date validation in logging components Replace isNaN() with isFinite() for date validation in ExecutionLogs and DetailedLogs to prevent infinite polling when invalid timestamps occur. - DetailedLogs.jsx: Changed isNaN() to isFinite() check - ExecutionLogs.jsx: Added missing isFinite() date validation 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * UN-2454 [FIX] Resolve stale closure bug in polling state management Use ref instead of state for pollingIds to prevent stale closure issues in polling logic: - Added pollingIdsRef to track actively polling execution IDs - Updated all polling operations to directly mutate ref (no re-renders needed) - Removed redundant pollingIds state to eliminate unnecessary re-renders - Prevents duplicate polling loops and potential memory leaks 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * minor: Addressed a code smell * wip: Refresh and data filter above table * UN-2966 [FEAT] Auto-refresh toggle and controls for execution logs - Add reusable LogsRefreshControls component (toggle + refresh button) - Move date picker from nav bar to content area in ExecutionLogs - Update DetailedLogs to use shared LogsRefreshControls component - Fix table height to fill available space using flexbox layout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [FEAT] Enhanced execution logs UI with improved layout and controls - Added sticky table header for better scrolling experience - Improved pagination positioning and styling with reduced padding - Enhanced table layout with scrollbar isolated to table body - Fixed layout spacing in DetailedLogs header with refresh controls - Updated CSS for better visual consistency across logs components 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [REFACTOR] Clean up DetailedLogs layout and improve UI - Moved "View Logs" button from header to cards row for better layout - Kept LogsRefreshControls in header top-right - Changed "View Logs" from link to button with FileTextOutlined icon - Removed unnecessary .detailed-logs-header-controls wrapper div - Updated header padding and button margins for consistency - Fixed pagination label in LogsTable (items → executions) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [FEAT] Table layout, column visibility controls and pagination fixes for execution/detailed logs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [REFACTOR] Improve DetailedLogs table column sizing with percentage-based widths - Implement responsive column widths using fixed px for predictable content - Status Message column gets 30% (most important variable content) - File Name gets 12%, File Path gets 20% for variable content - Reduced fixed columns: Executed At (140px), Status (110px), File Size (70px) - Execution Time (90px), Action (60px) for better space distribution - Added explanatory comment for width allocation strategy * UN-2966 [FEATURE] Add sorting and execution ID filter to logs - Add file_size and execution_time sorting to DetailedLogs and LogsTable - Add execution ID filter search to ExecutionLogs page - Implement field mapping for proper backend sort ordering - Update API filter backend with id field for search capability 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [FIX] Resolve CodeRabbit review comments Fixed RangePicker null guard to prevent runtime errors on partial date selection. Removed unused createdAtRaw field from ExecutionLogs and DetailedLogs components. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [FIX] Use UUIDFilter for execution id field Changed `id` filter from `CharFilter(lookup_expr="icontains")` to `UUIDFilter()` in ExecutionFilter. UUIDs don't support icontains lookup, so this fixes a potential FieldError. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [FIX] Disable refresh controls when execution reaches terminal state - Added terminal state detection for COMPLETED/ERROR/STOPPED statuses - Auto-refresh automatically disables when execution completes - Refresh button and toggle are disabled with visual feedback - Tooltip shows execution completion message when disabled 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [FIX] Add PropTypes validation for StatusMessageCell Added PropTypes import and validation for StatusMessageCell component's 'text' prop to resolve ESLint error and prevent potential runtime issues. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [FIX] Address SonarQube code smells in logging components - ExecutionLogs.jsx: Use optional chaining (value?.[0]) for cleaner null checks - LogsTable.jsx: Extract inline components (SearchFilterDropdown, SearchFilterIcon) with proper PropTypes for reusability and maintainability 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [FIX] Prevent Action column from being hidden in visibility menu Excluded "action" column from the visibility menu to prevent users from losing access to the column visibility dropdown. This ensures the Action column remains always visible, maintaining user ability to interact with the table controls. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [FIX] Add error handling for clipboard operations * UN-2966 [FIX] Mark StatusMessageCell text prop as required * UN-2966 [FIX] Add missing id dependency to useEffect * UN-2966 [REFACTOR] Move SearchFilterIcon inline style to CSS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [REFACTOR] Extract inline component definitions to module level - DetailedLogs.jsx: Extracted ActionColumnHeader component to module level - LogsTable.jsx: Removed unnecessary arrow function wrappers for filterDropdown and filterIcon - Resolves SonarQube code smell for inline component definitions 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * UN-2966 [REFACTOR] Remove unnecessary arrow function wrapper from title prop Simplified the title prop by removing redundant arrow function wrapper. The title prop accepts JSX elements directly, so the wrapper was unnecessary. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Add search functionality to LLMs, VectorDBs, Embeddings, and Connectors pages - Create reusable useListSearch hook for client-side search - Add search to ToolSettings (LLMs, VectorDBs, Embeddings pages) - Add search to ConnectorsPage - Search is case-insensitive, debounced (600ms), filters by name 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Preserve search filter when updating master list in useListSearch hook Previously, CRUD operations (add/edit/delete) would clear the active search filter because updateMasterList directly set displayList to the full updated list. Now a searchTextRef stores the current search text and re-applies the filter when the master list is updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Refactor handleDelete to reduce nesting depth in ToolSettings Extract handleDeleteSuccess function to avoid nesting callbacks more than 4 levels deep, fixing SonarQube code smell. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Signed-off-by: Chandrasekharan M <117059509+chandrasekharan-zipstack@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 48e4a01 commit 0149d04

3 files changed

Lines changed: 101 additions & 34 deletions

File tree

frontend/src/components/tool-settings/tool-settings/ToolSettings.jsx

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ToolNavBar } from "../../navigations/tool-nav-bar/ToolNavBar";
1515
import { ViewTools } from "../../custom-tools/view-tools/ViewTools";
1616
import { SharePermission } from "../../widgets/share-permission/SharePermission";
1717
import usePostHogEvents from "../../../hooks/usePostHogEvents";
18+
import { useListSearch } from "../../../hooks/useListSearch";
1819

1920
const titles = {
2021
llm: "LLMs",
@@ -33,7 +34,6 @@ const btnText = {
3334
};
3435

3536
function ToolSettings({ type }) {
36-
const [tableRows, setTableRows] = useState([]);
3737
const [isLoading, setIsLoading] = useState(false);
3838
const [isShareLoading, setIsShareLoading] = useState(false);
3939
const [adapterDetails, setAdapterDetails] = useState(null);
@@ -48,9 +48,16 @@ function ToolSettings({ type }) {
4848
const axiosPrivate = useAxiosPrivate();
4949
const handleException = useExceptionHandler();
5050
const { posthogEventText, setPostHogCustomEvent } = usePostHogEvents();
51+
const {
52+
displayList,
53+
setDisplayList,
54+
setMasterList,
55+
updateMasterList,
56+
onSearch,
57+
} = useListSearch("adapter_name");
5158

5259
useEffect(() => {
53-
setTableRows([]);
60+
setMasterList([]);
5461
if (!type) {
5562
return;
5663
}
@@ -67,7 +74,7 @@ function ToolSettings({ type }) {
6774
setIsLoading(true);
6875
axiosPrivate(requestOptions)
6976
.then((res) => {
70-
setTableRows(res?.data);
77+
setMasterList(res?.data || []);
7178
})
7279
.catch((err) => {
7380
setAlertDetails(handleException(err));
@@ -79,21 +86,29 @@ function ToolSettings({ type }) {
7986

8087
const addNewItem = (row, isEdit) => {
8188
if (isEdit) {
82-
const rowsModified = [...tableRows].map((tableRow) => {
83-
if (tableRow?.id !== row?.id) {
84-
return tableRow;
85-
}
86-
tableRow["adapter_name"] = row?.adapter_name;
87-
return tableRow;
88-
});
89-
setTableRows(rowsModified);
89+
updateMasterList((currentList) =>
90+
currentList.map((tableRow) => {
91+
if (tableRow?.id !== row?.id) {
92+
return tableRow;
93+
}
94+
return { ...tableRow, adapter_name: row?.adapter_name };
95+
})
96+
);
9097
} else {
91-
const rowsModified = [...tableRows];
92-
rowsModified.push(row);
93-
setTableRows(rowsModified);
98+
updateMasterList((currentList) => [...currentList, row]);
9499
}
95100
};
96101

102+
const handleDeleteSuccess = (adapterId) => {
103+
updateMasterList((currentList) =>
104+
currentList.filter((row) => row?.id !== adapterId)
105+
);
106+
setAlertDetails({
107+
type: "success",
108+
content: "Successfully deleted",
109+
});
110+
};
111+
97112
const handleDelete = (_event, adapter) => {
98113
const requestOptions = {
99114
method: "DELETE",
@@ -105,20 +120,9 @@ function ToolSettings({ type }) {
105120

106121
setIsLoading(true);
107122
axiosPrivate(requestOptions)
108-
.then((res) => {
109-
const filteredList = tableRows.filter((row) => row?.id !== adapter?.id);
110-
setTableRows(filteredList);
111-
setAlertDetails({
112-
type: "success",
113-
content: "Successfully deleted",
114-
});
115-
})
116-
.catch((err) => {
117-
setAlertDetails(handleException(err));
118-
})
119-
.finally(() => {
120-
setIsLoading(false);
121-
});
123+
.then(() => handleDeleteSuccess(adapter?.id))
124+
.catch((err) => setAlertDetails(handleException(err)))
125+
.finally(() => setIsLoading(false));
122126
};
123127

124128
const handleShare = (_event, adapter, isEdit) => {
@@ -221,6 +225,9 @@ function ToolSettings({ type }) {
221225
<div className="plt-tool-settings-layout">
222226
<ToolNavBar
223227
title={titles[type]}
228+
enableSearch
229+
setSearchList={setDisplayList}
230+
onSearch={onSearch}
224231
CustomButtons={() => {
225232
return (
226233
<CustomButton
@@ -237,7 +244,7 @@ function ToolSettings({ type }) {
237244
<div className="plt-tool-settings-layout-2">
238245
<div className="plt-tool-settings-body">
239246
<ViewTools
240-
listOfTools={tableRows}
247+
listOfTools={displayList}
241248
isLoading={isLoading}
242249
handleDelete={handleDelete}
243250
setOpenAddTool={setOpenAddSourcesModal}
@@ -257,7 +264,7 @@ function ToolSettings({ type }) {
257264
titleProp="adapter_name"
258265
descriptionProp="description"
259266
iconProp="icon"
260-
isEmpty={!tableRows?.length}
267+
isEmpty={!displayList?.length}
261268
centered
262269
isClickable={false}
263270
handleShare={handleShare}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { useRef, useState, useCallback } from "react";
2+
3+
function useListSearch(searchField) {
4+
const listRef = useRef([]);
5+
const searchTextRef = useRef("");
6+
const [displayList, setDisplayList] = useState([]);
7+
8+
const filterList = useCallback(
9+
(list, searchText) => {
10+
if (!searchText.trim()) {
11+
return list;
12+
}
13+
return list.filter((item) =>
14+
item[searchField]?.toLowerCase().includes(searchText.toLowerCase())
15+
);
16+
},
17+
[searchField]
18+
);
19+
20+
const setMasterList = useCallback(
21+
(list) => {
22+
listRef.current = list;
23+
setDisplayList(filterList(list, searchTextRef.current));
24+
},
25+
[filterList]
26+
);
27+
28+
const onSearch = useCallback(
29+
(searchText, setSearchList) => {
30+
searchTextRef.current = searchText;
31+
setSearchList(filterList(listRef.current, searchText));
32+
},
33+
[filterList]
34+
);
35+
36+
const updateMasterList = useCallback(
37+
(updateFn) => {
38+
const updatedList = updateFn(listRef.current);
39+
listRef.current = updatedList;
40+
setDisplayList(filterList(updatedList, searchTextRef.current));
41+
},
42+
[filterList]
43+
);
44+
45+
return {
46+
listRef,
47+
displayList,
48+
setDisplayList,
49+
setMasterList,
50+
updateMasterList,
51+
onSearch,
52+
};
53+
}
54+
55+
export { useListSearch };

frontend/src/pages/ConnectorsPage.jsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import { useSessionStore } from "../store/session-store";
77
import { useAlertStore } from "../store/alert-store";
88
import { useExceptionHandler } from "../hooks/useExceptionHandler";
99
import useRequestUrl from "../hooks/useRequestUrl";
10+
import { useListSearch } from "../hooks/useListSearch";
1011
import "./ConnectorsPage.css";
1112
import { ToolNavBar } from "../components/navigations/tool-nav-bar/ToolNavBar";
1213
import { ViewTools } from "../components/custom-tools/view-tools/ViewTools";
1314
import { SharePermission } from "../components/widgets/share-permission/SharePermission";
1415
import { AddSourceModal } from "../components/input-output/add-source-modal/AddSourceModal";
1516

1617
function ConnectorsPage() {
17-
const [connectors, setConnectors] = useState([]);
1818
const [loading, setLoading] = useState(false);
1919
const [modalVisible, setModalVisible] = useState(false);
2020
const [editingConnector, setEditingConnector] = useState(null);
@@ -29,6 +29,8 @@ function ConnectorsPage() {
2929
const { setAlertDetails } = useAlertStore();
3030
const handleException = useExceptionHandler();
3131
const { getUrl } = useRequestUrl();
32+
const { displayList, setDisplayList, setMasterList, onSearch } =
33+
useListSearch("connector_name");
3234

3335
useEffect(() => {
3436
fetchConnectors();
@@ -39,7 +41,7 @@ function ConnectorsPage() {
3941
setLoading(true);
4042
try {
4143
const response = await axiosPrivate.get(getUrl("connector/"));
42-
setConnectors(response.data || []);
44+
setMasterList(response.data || []);
4345
} catch (error) {
4446
setAlertDetails(handleException(error, "Failed to load connectors"));
4547
} finally {
@@ -155,12 +157,15 @@ function ConnectorsPage() {
155157
<div className="connectors-layout">
156158
<ToolNavBar
157159
title="Connectors"
160+
enableSearch
161+
setSearchList={setDisplayList}
162+
onSearch={onSearch}
158163
CustomButtons={renderCreateConnectorButtons}
159164
/>
160165
<div className="connectors-pg-layout">
161166
<div className="connectors-pg-body">
162167
<ViewTools
163-
listOfTools={connectors}
168+
listOfTools={displayList}
164169
isLoading={loading}
165170
handleDelete={handleDeleteConnector}
166171
handleEdit={handleEditConnector}
@@ -172,7 +177,7 @@ function ConnectorsPage() {
172177
iconProp="icon"
173178
showOwner={true}
174179
type="Connector"
175-
isEmpty={!connectors.length}
180+
isEmpty={!displayList.length}
176181
centered
177182
isClickable={false}
178183
/>

0 commit comments

Comments
 (0)