From 2350b7fb23ab66b92a81ec0617f89b04bf6dac51 Mon Sep 17 00:00:00 2001 From: Zecheng Zhang Date: Thu, 30 Oct 2025 14:25:39 -0700 Subject: [PATCH] feat: service name contain and modify selected search --- ui/src/components/explore/SearchBar.tsx | 134 ++++++++++++++++++++---- 1 file changed, 112 insertions(+), 22 deletions(-) diff --git a/ui/src/components/explore/SearchBar.tsx b/ui/src/components/explore/SearchBar.tsx index 43585832..10f8552f 100644 --- a/ui/src/components/explore/SearchBar.tsx +++ b/ui/src/components/explore/SearchBar.tsx @@ -73,6 +73,9 @@ const SearchBar: React.FC = ({ const [metadataCategoryValue, setMetadataCategoryValue] = useState(""); const [metadataValue, setMetadataValue] = useState(""); const [logSearchValue, setLogSearchValue] = useState(""); + const [editingCriterionId, setEditingCriterionId] = useState( + null, + ); const searchBarRef = useRef(null); // Memoize extracted search terms to prevent unnecessary re-renders @@ -106,11 +109,20 @@ const SearchBar: React.FC = ({ // Click-outside detection to collapse search useEffect(() => { const handleClickOutside = (event: MouseEvent) => { - if ( - searchBarRef.current && - !searchBarRef.current.contains(event.target as Node) && - isSearchExpanded - ) { + const target = event.target as Node; + + // Check if click is inside the search bar + if (searchBarRef.current && searchBarRef.current.contains(target)) { + return; + } + + // Check if click is inside a dropdown menu portal + // Radix UI portals have data-radix-popper-content-wrapper attribute + const isInsideDropdown = (target as Element).closest?.( + '[role="menu"], [data-radix-popper-content-wrapper]', + ); + + if (isSearchExpanded && !isInsideDropdown) { setIsSearchExpanded(false); } }; @@ -140,6 +152,40 @@ const SearchBar: React.FC = ({ } }, [metadataSearchTerms, onMetadataSearchTermsChange]); + const handleEditCriterion = (criterion: SearchCriterion) => { + if (disabled) return; + + // Load the criterion into the input fields + setEditingCriterionId(criterion.id); + setCurrentCriterion({ + category: criterion.category, + operation: criterion.operation, + }); + + // Set the appropriate input value based on category + if ( + criterion.category !== "metadata" && + criterion.category !== "log" && + criterion.category !== "service_name" && + criterion.category !== "service_environment" + ) { + // For metadata categories, split category and value + setMetadataCategoryValue(criterion.category); + setMetadataValue(criterion.value); + setCurrentCriterion({ + category: "metadata", + operation: criterion.operation, + }); + } else if (criterion.category === "log") { + setLogSearchValue(criterion.value); + } else { + setInputValue(criterion.value); + } + + // Ensure search bar is expanded + setIsSearchExpanded(true); + }; + const handleAddCriterion = () => { if (disabled) return; @@ -154,24 +200,42 @@ const SearchBar: React.FC = ({ } if (categoryValue && currentCriterion.operation && searchValue) { - const newCriterion: SearchCriterion = { - id: Date.now().toString(), - category: categoryValue, - operation: currentCriterion.operation, - value: searchValue, - logicalOperator: criteria.length > 0 ? "AND" : undefined, - }; - - const newCriteria = [...criteria, newCriterion]; + let newCriteria: SearchCriterion[]; + const operation = currentCriterion.operation; // Guaranteed to be string due to the if condition + + if (editingCriterionId) { + // Update existing criterion + newCriteria = criteria.map((c) => + c.id === editingCriterionId + ? { + ...c, + category: categoryValue, + operation: operation, + value: searchValue, + } + : c, + ); + setEditingCriterionId(null); + } else { + // Add new criterion + const newCriterion: SearchCriterion = { + id: Date.now().toString(), + category: categoryValue, + operation: operation, + value: searchValue, + logicalOperator: criteria.length > 0 ? "AND" : undefined, + }; + newCriteria = [...criteria, newCriterion]; + } + setCriteria(newCriteria); setCurrentCriterion({ category: "log", operation: "contains" }); setInputValue(""); setMetadataCategoryValue(""); setMetadataValue(""); - // Clear logSearchValue when adding a criterion setLogSearchValue(""); onSearch(newCriteria); - // Collapse search after adding criterion + // Collapse search after adding/updating criterion setIsSearchExpanded(false); } }; @@ -181,6 +245,17 @@ const SearchBar: React.FC = ({ const newCriteria = criteria.filter((c) => c.id !== id); setCriteria(newCriteria); + + // If we're editing the criterion being removed, clear the edit state + if (editingCriterionId === id) { + setEditingCriterionId(null); + setCurrentCriterion({ category: "log", operation: "contains" }); + setInputValue(""); + setMetadataCategoryValue(""); + setMetadataValue(""); + setLogSearchValue(""); + } + // If no criteria left, default back to log category if (newCriteria.length === 0) { setCurrentCriterion({ category: "log", operation: "contains" }); @@ -237,7 +312,10 @@ const SearchBar: React.FC = ({ {criterion.logicalOperator} )} -
+
handleEditCriterion(criterion)} + > {getCategoryLabel(criterion.category)} @@ -250,7 +328,10 @@ const SearchBar: React.FC = ({