From f0caa24d91a9a8dc8b59333c6589a05a4e2df133 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 23 Apr 2026 08:24:58 +0200 Subject: [PATCH] fix(ServerHealthSection): add debounce to linesCount input to prevent rapid API calls - Introduce linesCountRaw state to capture raw input values - Add handleLinesCountChange callback with 300ms debounce delay - Reuse existing filterDebounceRef pattern with linesCountDebounceRef - Guard against zero/negative values by enforcing minimum of 100 lines - Update Select component to use debounced value and new handler - Add comprehensive test coverage for debounce behavior and input validation Fixes TASK-BUG-09: Typing '500' in the Lines field now fires single API request instead of three (one per keystroke). This mirrors the existing debounce pattern used for the filter input. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../components/config/ServerHealthSection.tsx | 21 +++- .../__tests__/ServerHealthSection.test.tsx | 110 +++++++++++++++++- 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/config/ServerHealthSection.tsx b/frontend/src/components/config/ServerHealthSection.tsx index 4c0ed83..15f8355 100644 --- a/frontend/src/components/config/ServerHealthSection.tsx +++ b/frontend/src/components/config/ServerHealthSection.tsx @@ -54,6 +54,9 @@ const AUTO_REFRESH_INTERVALS: { label: string; value: number }[] = [ /** Debounce delay for the filter input in milliseconds. */ const FILTER_DEBOUNCE_MS = 300; +/** Debounce delay for the lines count input in milliseconds. */ +const LINES_COUNT_DEBOUNCE_MS = 300; + /** Log targets that are not file paths — file-based viewing is unavailable. */ const NON_FILE_TARGETS = new Set(["STDOUT", "STDERR", "SYSLOG", "SYSTEMD-JOURNAL"]); @@ -179,6 +182,7 @@ export function ServerHealthSection(): React.JSX.Element { const [isRefreshing, setIsRefreshing] = useState(false); // ---- toolbar state ------------------------------------------------------- + const [linesCountRaw, setLinesCountRaw] = useState("200"); const [linesCount, setLinesCount] = useState(200); const [filterRaw, setFilterRaw] = useState(""); const [filterValue, setFilterValue] = useState(""); @@ -189,6 +193,7 @@ export function ServerHealthSection(): React.JSX.Element { // ---- refs ---------------------------------------------------------------- const logContainerRef = useRef(null); const filterDebounceRef = useRef | null>(null); + const linesCountDebounceRef = useRef | null>(null); const autoRefreshTimerRef = useRef | null>(null); // ---- scroll helper ------------------------------------------------------- @@ -259,6 +264,18 @@ export function ServerHealthSection(): React.JSX.Element { }, FILTER_DEBOUNCE_MS); }, []); + // ---- lines count debounce ------------------------------------------------ + const handleLinesCountChange = useCallback((value: string): void => { + setLinesCountRaw(value); + if (linesCountDebounceRef.current) clearTimeout(linesCountDebounceRef.current); + linesCountDebounceRef.current = setTimeout(() => { + const parsed = Number(value); + // Guard against zero or negative values; use minimum of 100 + const validated = Math.max(parsed || 100, 100); + setLinesCount(validated); + }, LINES_COUNT_DEBOUNCE_MS); + }, []); + // ---- render helpers ------------------------------------------------------ const renderLogLine = (line: string, idx: number): React.JSX.Element => { const severity = detectSeverity(line); @@ -406,8 +423,8 @@ export function ServerHealthSection(): React.JSX.Element { {/* Lines count selector */}