This is an automated email from the ASF dual-hosted git repository.

ppawar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git


The following commit(s) were added to refs/heads/master by this push:
     new b3175a301 ATLAS-5166: React UI: Save search, Quick search, Alignment 
issue (#485)
b3175a301 is described below

commit b3175a301b04d8bd2bef12cb60160bae8671d603
Author: Prasad Pawar <[email protected]>
AuthorDate: Wed Dec 10 16:41:20 2025 +0530

    ATLAS-5166: React UI: Save search, Quick search, Alignment issue (#485)
---
 dashboard/src/components/FilterQuery.tsx           |  32 +++-
 .../src/components/GlobalSearch/QuickSearch.tsx    | 172 ++++++++++++++++-----
 dashboard/src/styles/classificationForm.scss       |  13 ++
 .../BusinessMetadata/BusinessMetadataForm.tsx      |   2 +-
 .../views/Classification/ClassificationForm.tsx    |   1 -
 .../PropertiesTab/BMAttributesFields.tsx           |  34 ++--
 .../RelationshipPropertiesTab.tsx                  |  38 +++--
 dashboard/src/views/Glossary/GlossaryForm.tsx      |   2 +-
 .../src/views/SideBar/SideBarTree/SideBarTree.tsx  | 147 ++++++++++++++++--
 9 files changed, 361 insertions(+), 80 deletions(-)

diff --git a/dashboard/src/components/FilterQuery.tsx 
b/dashboard/src/components/FilterQuery.tsx
index 644d8e67e..fb14e0288 100644
--- a/dashboard/src/components/FilterQuery.tsx
+++ b/dashboard/src/components/FilterQuery.tsx
@@ -108,7 +108,32 @@ export const FilterQuery = ({ value }: any) => {
 
     searchParams.delete(currentType);
 
-    if ([...searchParams]?.length == 1) {
+    // Check if there are any meaningful filters left after removal
+    const meaningfulFilterParams = [
+      "type",
+      "tag",
+      "query",
+      "term",
+      "relationshipName",
+      "entityFilters",
+      "tagFilters",
+      "relationshipFilters",
+      "excludeST",
+      "excludeSC",
+      "includeDE"
+    ];
+
+    const hasMeaningfulFilters = meaningfulFilterParams.some((param) =>
+      searchParams.has(param)
+    );
+
+    // If no meaningful filters remain, navigate to clear all (like Clear 
button)
+    if (!hasMeaningfulFilters) {
+      navigate({
+        pathname: "/search"
+      });
+    } else if ([...searchParams]?.length <= 1) {
+      // Only searchType or other system params remain
       navigate({
         pathname: "/search"
       });
@@ -440,6 +465,11 @@ export const FilterQuery = ({ value }: any) => {
     queryArray.push(<div className="group">{queryIncludeDE}</div>);
   }
 
+  // If no filters to display, return null (don't show empty parentheses)
+  if (queryArray.length === 0) {
+    return null;
+  }
+
   return (
     <>
       {queryArray.length === 1 ? (
diff --git a/dashboard/src/components/GlobalSearch/QuickSearch.tsx 
b/dashboard/src/components/GlobalSearch/QuickSearch.tsx
index 5f6fa12b2..1a8700755 100644
--- a/dashboard/src/components/GlobalSearch/QuickSearch.tsx
+++ b/dashboard/src/components/GlobalSearch/QuickSearch.tsx
@@ -115,9 +115,12 @@ const QuickSearch = () => {
   };
 
   const onInputChange = (_event: any, value: string) => {
-    if (value) {
+    // Sanitize input: remove any potential script tags and validate
+    const sanitizedValue = value ? value.trim() : "";
+    
+    if (sanitizedValue) {
       setOpen(true);
-      getData(value);
+      getData(sanitizedValue);
     } else {
       setOptions([]);
       setValue("");
@@ -125,27 +128,69 @@ const QuickSearch = () => {
     }
   };
 
-  const handleValues = (option: HandleValuesType) => {
-    const { entityObj, title, types } = option;
-    setOpen(false);
-    setOptions([]);
-    searchParams.set("query", title);
-    searchParams.set("searchType", "basic");
-
-    types == "Entities"
-      ? navigate(
-          {
-            pathname: `/detailPage/${entityObj.guid}`
-          },
-          { replace: true }
-        )
-      : navigate(
+  const handleValues = (option: HandleValuesType | string | null) => {
+    // Handle case when option is a string (direct search query)
+    if (typeof option === "string") {
+      const queryValue = option.trim();
+      if (queryValue) {
+        setOpen(false);
+        setOptions([]);
+        // URLSearchParams automatically encodes the value, preventing XSS
+        // Additional validation: ensure query is not empty after trim
+        const sanitizedQuery = queryValue || "*";
+        searchParams.set("query", sanitizedQuery);
+        searchParams.set("searchType", "basic");
+        navigate(
           {
             pathname: `/search/searchResult`,
             search: searchParams.toString()
           },
           { replace: true }
         );
+      }
+      return;
+    }
+
+    // Handle case when option is null or undefined
+    if (!option || typeof option !== "object") {
+      return;
+    }
+
+    const { entityObj, title, types } = option;
+    
+    // Validate that title exists and is not undefined
+    if (!title || title === "undefined" || typeof title !== "string") {
+      return;
+    }
+
+    // Sanitize title: trim and validate
+    const sanitizedTitle = title.trim();
+    if (!sanitizedTitle) {
+      return;
+    }
+
+    setOpen(false);
+    setOptions([]);
+    // URLSearchParams automatically encodes the value, preventing XSS
+    searchParams.set("query", sanitizedTitle);
+    searchParams.set("searchType", "basic");
+
+    if (types === "Entities" && entityObj && entityObj.guid) {
+      navigate(
+        {
+          pathname: `/detailPage/${entityObj.guid}`
+        },
+        { replace: true }
+      );
+    } else {
+      navigate(
+        {
+          pathname: `/search/searchResult`,
+          search: searchParams.toString()
+        },
+        { replace: true }
+      );
+    }
   };
 
   const handleClickAway = () => {
@@ -172,18 +217,30 @@ const QuickSearch = () => {
               const code = e.keyCode || e.which;
 
               switch (code) {
-                case 13:
+                case 13: // Enter key
+                  e.preventDefault();
+                  const inputValue = (e.target as 
HTMLInputElement).value.trim();
+                  
+                  // If input is empty, use "*" for wildcard search (matching 
classic UI behavior)
+                  const searchQuery = inputValue === "" ? "*" : inputValue;
+                  
+                  // Try to find exact match in options
                   const activeOption = options.find(
                     (option: { title: any }) =>
                       typeof option !== "string" &&
-                      option.title === (e.target as HTMLInputElement).value
+                      option.title === searchQuery
                   );
+                  
                   if (activeOption) {
+                    // If exact match found, use that option
                     handleValues(activeOption as HandleValuesType);
+                  } else {
+                    // If no match found, trigger basic search with typed 
value (matching classic UI behavior)
+                    handleValues(searchQuery);
                   }
                   break;
-                case 9:
-                case 27:
+                case 9: // Tab key
+                case 27: // Escape key
                   setOpen(false);
                   break;
                 default:
@@ -204,9 +261,19 @@ const QuickSearch = () => {
             }}
             value={value}
             onChange={(_event: any, newValue: any) => {
-              setOptions(newValue ? [newValue, ...options] : options);
-              setValue(newValue);
-              handleValues(newValue);
+              if (newValue) {
+                // Only update options if newValue is a valid option object
+                if (typeof newValue === "object" && newValue.title) {
+                  setOptions([newValue, ...options]);
+                }
+                setValue(newValue);
+                // Only call handleValues if newValue is a valid option
+                if (typeof newValue === "object" && newValue.title && 
newValue.title !== "undefined") {
+                  handleValues(newValue);
+                }
+              } else {
+                setValue("");
+              }
             }}
             clearOnBlur={false}
             autoComplete={true}
@@ -214,9 +281,14 @@ const QuickSearch = () => {
             noOptionsText={"No Entities"}
             disableClearable
             onInputChange={onInputChange}
-            getOptionLabel={(option: string | QuickSearchOptionListType) =>
-              typeof option === "string" ? option : (option as any).title
-            }
+            getOptionLabel={(option: string | QuickSearchOptionListType) => {
+              if (typeof option === "string") {
+                return option;
+              }
+              // Safely extract title, defaulting to empty string if undefined
+              const title = (option as any)?.title;
+              return title && typeof title === "string" ? title : "";
+            }}
             renderOption={(props, option, { inputValue }) => {
               const { entityObj, types, parent } =
                 typeof option !== "string" &&
@@ -238,21 +310,31 @@ const QuickSearch = () => {
                 typeof option !== "string" && "title" in option
                   ? option.title
                   : option;
-              const href = `/detailPage/${
-                (entityObj as { guid: string })?.guid
-              }`;
+              
+              // Validate and sanitize title to prevent XSS
+              const safeTitle = typeof title === "string" ? title : "";
+              
+              // Validate guid to prevent XSS in URL
+              const guid = (entityObj as { guid?: string })?.guid;
+              const safeGuid = guid && typeof guid === "string" ? guid : "";
+              const href = safeGuid ? `/detailPage/${safeGuid}` : "#";
+              
               const { name }: { name: string; found: boolean; key: any } =
                 extractKeyValueFromEntity(entityObj);
+              
+              // Safely handle name extraction
+              const safeName = name && typeof name === "string" ? name : "";
+              
               const matches = match(
-                types == "Entities" ? (name as string) : (title as string),
-                inputValue,
+                types === "Entities" ? safeName : safeTitle,
+                inputValue || "",
                 {
                   findAllOccurrences: true,
                   insideWords: true
                 }
               );
               const parts = parse(
-                types == "Entities" ? (name as string) : (title as string),
+                types === "Entities" ? safeName : safeTitle,
                 matches
               );
               return (
@@ -273,7 +355,7 @@ const QuickSearch = () => {
                     }
                   }}
                 >
-                  {types == "Entities" && !isEmpty(entityObj) ? (
+                  {types === "Entities" && !isEmpty(entityObj) ? (
                     <Link
                       className="entity-name text-decoration-none"
                       style={{
@@ -296,10 +378,10 @@ const QuickSearch = () => {
                       }
                     >
                       {" "}
-                      {types == "Entities" && !isEmpty(entityObj) && (
+                      {types === "Entities" && !isEmpty(entityObj) && (
                         <DisplayImage entity={entityObj} />
                       )}{" "}
-                      {types == "Entities" && !isEmpty(entityObj)
+                      {types === "Entities" && !isEmpty(entityObj)
                         ? parts.map((part, index) => (
                             <Stack
                               flexDirection="row"
@@ -308,7 +390,7 @@ const QuickSearch = () => {
                                 fontWeight: part.highlight ? "bold" : "regular"
                               }}
                             >
-                              {entityObj?.guid != "-1" && !part.highlight ? (
+                              {entityObj?.guid !== "-1" && !part.highlight ? (
                                 <Link
                                   className="entity-name text-blue 
text-decoration-none"
                                   style={{
@@ -345,7 +427,7 @@ const QuickSearch = () => {
                               {part.text}
                             </Stack>
                           ))}
-                      {types == "Entities" &&
+                      {types === "Entities" &&
                         !isEmpty(entityObj) &&
                         ` (${parent})`}
                     </Link>
@@ -410,13 +492,25 @@ const QuickSearch = () => {
         <CustomButton
           variant="outlined"
           size="small"
+          sx={{
+            backgroundColor: "white !important",
+            color: "#4a90e2 !important",
+            borderColor: "#dddddd !important",
+            "&:hover": {
+              backgroundColor: "rgba(74, 144, 226, 0.08) !important",
+              // borderColor: "#4a90e2 !important",
+              color: "#4a90e2 !important"
+            }
+          }}
           onClick={() => {
             setOpenAdvanceSearch(true);
           }}
         >
           <Typography
             sx={{
-              color: location?.pathname == "/search" ? "blue" : "eeeeee"
+              color: "#4a90e2 !important",
+              fontWeight: "600 !important",
+              fontSize: "0.875rem !important"
             }}
             display="inline"
           >
diff --git a/dashboard/src/styles/classificationForm.scss 
b/dashboard/src/styles/classificationForm.scss
index eb81c6810..612ff3665 100644
--- a/dashboard/src/styles/classificationForm.scss
+++ b/dashboard/src/styles/classificationForm.scss
@@ -22,3 +22,16 @@
 .classification-form-editor .ql-container {
   min-height: 90px !important;
 }
+
+/* Prevent duplicate toolbars in ReactQuill */
+.classification-form-editor .ql-toolbar:not(:first-of-type) {
+  display: none !important;
+}
+
+.classification-form-editor .ql-toolbar {
+  border-top: 1px solid #ccc;
+  border-bottom: 1px solid #ccc;
+  box-sizing: border-box;
+  font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
+  padding: 8px;
+}
diff --git a/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx 
b/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
index 2f6af49dc..5fcbb22ed 100644
--- a/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
+++ b/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
@@ -448,7 +448,6 @@ const BusinessMetaDataForm = ({
                               {alignment == "formatted" ? (
                                 <div style={{ position: "relative" }}>
                                   <ReactQuill
-                                    {...field}
                                     theme="snow"
                                     placeholder={"Description required"}
                                     onChange={(text) => {
@@ -456,6 +455,7 @@ const BusinessMetaDataForm = ({
                                       setValue("description", text);
                                     }}
                                     className="classification-form-editor"
+                                    value={field.value || ""}
                                   />
                                 </div>
                               ) : (
diff --git a/dashboard/src/views/Classification/ClassificationForm.tsx 
b/dashboard/src/views/Classification/ClassificationForm.tsx
index 61bd4582d..170f9f16c 100644
--- a/dashboard/src/views/Classification/ClassificationForm.tsx
+++ b/dashboard/src/views/Classification/ClassificationForm.tsx
@@ -314,7 +314,6 @@ const ClassificationForm = ({
                       {alignment == "formatted" ? (
                         <div style={{ position: "relative" }}>
                           <ReactQuill
-                            {...field}
                             theme="snow"
                             placeholder={"Description required"}
                             value={descriptionValue}
diff --git 
a/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributesFields.tsx
 
b/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributesFields.tsx
index 2cd05d688..76f618e8d 100644
--- 
a/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributesFields.tsx
+++ 
b/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributesFields.tsx
@@ -138,21 +138,22 @@ const BMAttributesFields = ({ obj, control, index }: any) 
=> {
           required: true
         }}
         defaultValue={""}
-        render={({ field }) => (
-          <Stack gap="0.5rem">
-            <div style={{ position: "relative", flexBasis: "100%" }}>
-              {typeName == "string" ? (
-                <ReactQuill
-                  {...field}
-                  theme="snow"
-                  placeholder={"Enter String"}
-                  onChange={(text) => {
-                    field.onChange(text);
-                  }}
-                  className="classification-form-editor"
-                  value={typeof field.value === "string" ? field.value : ""}
-                />
-              ) : (
+        render={({ field }) => {
+          return (
+            <Stack gap="0.5rem">
+              <div style={{ position: "relative", flexBasis: "100%" }}>
+                {typeName == "string" ? (
+                  <ReactQuill
+                    key={`quill-${index}-${name}`}
+                    theme="snow"
+                    placeholder={"Enter String"}
+                    onChange={(text) => {
+                      field.onChange(text);
+                    }}
+                    className="classification-form-editor"
+                    value={typeof field.value === "string" ? field.value : ""}
+                  />
+                ) : (
                 <TextField
                   margin="none"
                   fullWidth
@@ -176,7 +177,8 @@ const BMAttributesFields = ({ obj, control, index }: any) 
=> {
               )}
             </div>
           </Stack>
-        )}
+          );
+        }}
       />
     );
   } else if (typeName === "boolean") {
diff --git 
a/dashboard/src/views/DetailPage/RelationshipDetails/RelationshipPropertiesTab.tsx
 
b/dashboard/src/views/DetailPage/RelationshipDetails/RelationshipPropertiesTab.tsx
index 9d3b5f68a..d0e99794c 100644
--- 
a/dashboard/src/views/DetailPage/RelationshipDetails/RelationshipPropertiesTab.tsx
+++ 
b/dashboard/src/views/DetailPage/RelationshipDetails/RelationshipPropertiesTab.tsx
@@ -190,13 +190,20 @@ const RelationshipPropertiesTab = (props: {
                             spacing={4}
                             marginBottom={1}
                             marginTop={1}
+                            sx={{
+                              flexWrap: "nowrap",
+                              alignItems: "flex-start"
+                            }}
                           >
                             <div
                               style={{
-                                flex: 1,
-                                wordBreak: "break-all",
+                                flexBasis: "30%",
+                                flexShrink: 0,
+                                minWidth: "120px",
+                                wordBreak: "break-word",
                                 textAlign: "left",
-                                fontWeight: "600"
+                                fontWeight: "600",
+                                paddingRight: "16px"
                               }}
                             >
                               {`${keys} ${
@@ -205,8 +212,9 @@ const RelationshipPropertiesTab = (props: {
                             </div>
                             <div
                               style={{
-                                // flex: 1,
-                                wordBreak: "break-all",
+                                flex: 1,
+                                minWidth: 0,
+                                wordBreak: "break-word",
                                 textAlign: "left"
                               }}
                             >
@@ -242,7 +250,7 @@ const RelationshipPropertiesTab = (props: {
             </AccordionSummary>
             <AccordionDetails>
               {" "}
-              {!isEmpty(end1)
+              {!isEmpty(end2)
                 ? Object.entries(end2)
                     .sort()
                     .map(([keys, value]: [string, any]) => {
@@ -253,13 +261,20 @@ const RelationshipPropertiesTab = (props: {
                             spacing={4}
                             marginBottom={1}
                             marginTop={1}
+                            sx={{
+                              flexWrap: "nowrap",
+                              alignItems: "flex-start"
+                            }}
                           >
                             <div
                               style={{
-                                flex: 1,
-                                wordBreak: "break-all",
+                                flexBasis: "30%",
+                                flexShrink: 0,
+                                minWidth: "120px",
+                                wordBreak: "break-word",
                                 textAlign: "left",
-                                fontWeight: "600"
+                                fontWeight: "600",
+                                paddingRight: "16px"
                               }}
                             >
                               {`${keys} ${
@@ -268,8 +283,9 @@ const RelationshipPropertiesTab = (props: {
                             </div>
                             <div
                               style={{
-                                // flex: 1,
-                                wordBreak: "break-all",
+                                flex: 1,
+                                minWidth: 0,
+                                wordBreak: "break-word",
                                 textAlign: "left"
                               }}
                             >
diff --git a/dashboard/src/views/Glossary/GlossaryForm.tsx 
b/dashboard/src/views/Glossary/GlossaryForm.tsx
index bdbae34c8..3ab9a82fd 100644
--- a/dashboard/src/views/Glossary/GlossaryForm.tsx
+++ b/dashboard/src/views/Glossary/GlossaryForm.tsx
@@ -142,7 +142,6 @@ const GlossaryForm = (props: {
                   {alignment == "formatted" ? (
                     <div style={{ position: "relative" }}>
                       <ReactQuill
-                        {...field}
                         theme="snow"
                         placeholder={"Description required"}
                         onChange={(text) => {
@@ -150,6 +149,7 @@ const GlossaryForm = (props: {
                           setValue("description", text);
                         }}
                         className="classification-form-editor"
+                        value={field.value || ""}
                       />
                     </div>
                   ) : (
diff --git a/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx 
b/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx
index 2d75117cd..fd4bf92f9 100644
--- a/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx
+++ b/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx
@@ -61,6 +61,8 @@ import {
 
 import Stack from "@mui/material/Stack";
 import { globalSearchFilterInitialQuery, isEmpty } from "@utils/Utils";
+import { attributeFilter } from "@utils/CommonViewFunction";
+import { cloneDeep } from "@utils/Helper";
 import LaunchOutlinedIcon from "@mui/icons-material/LaunchOutlined";
 import { getGlossaryImportTmpl } from "@api/apiMethods/glossaryApiMethod";
 import { toast } from "react-toastify";
@@ -593,12 +595,13 @@ const BarTreeView: FC<{
         break;
     }
 
-    searchParams.delete("attributes");
-    searchParams.delete("entityFilters");
-    searchParams.delete("tagFilters");
-    searchParams.delete("relationshipFilters");
-    // Always reset pagination defaults on tree navigation
+    // Note: Don't delete filters here for CustomFilters - they will be set by 
setCustomFiltersSearchParams
     if (treeName !== "CustomFilters") {
+      searchParams.delete("attributes");
+      searchParams.delete("entityFilters");
+      searchParams.delete("tagFilters");
+      searchParams.delete("relationshipFilters");
+      // Always reset pagination defaults on tree navigation
       searchParams.set("pageLimit", "25");
       searchParams.set("pageOffset", "0");
     }
@@ -633,6 +636,7 @@ const BarTreeView: FC<{
     searchParams: URLSearchParams,
     savedSearchData: any[]
   ) => {
+    // Clear all existing params except searchType
     const keys = Array.from(searchParams.keys());
     for (let i = 0; i < keys.length; i++) {
       if (keys[i] !== "searchType") {
@@ -640,18 +644,137 @@ const BarTreeView: FC<{
       }
     }
 
+    // Clear globalSearchFilterInitialQuery when applying new saved search
+    globalSearchFilterInitialQuery.setQuery({});
+
     if (treeName === "CustomFilters") {
       const params = savedSearchData.find((obj) => obj.name === node.id);
-      for (const key in params?.searchParameters) {
-        if (shouldSetCustomFilterParam(node, key)) {
-          setCustomFilterParam(searchParams, key, 
params.searchParameters[key]);
+      if (params) {
+        const searchParamsObj = params?.searchParameters || {};
+        
+        // Step 1: Set searchType based on saved search type
+        if (params.searchType) {
+          const searchTypeValue = params.searchType.toLowerCase() === 
"advanced" ? "advanced" : "basic";
+          searchParams.set("searchType", searchTypeValue);
+        }
+        
+        // Step 2: Apply basic search parameters (excluding filters which are 
handled separately)
+        for (const key in searchParamsObj) {
+          if (shouldSetCustomFilterParam(node, key) && 
+              !["entityFilters", "tagFilters", 
"relationshipFilters"].includes(key)) {
+            setCustomFilterParam(searchParams, key, searchParamsObj[key]);
+          }
+        }
+        
+        // Step 3: Convert and apply entityFilters from API format to URL 
string format
+        if (searchParamsObj.entityFilters && 
!isEmpty(searchParamsObj.entityFilters)) {
+          const clonedFilter = cloneDeep(searchParamsObj.entityFilters);
+          const ruleUrl = attributeFilter.generateUrl({
+            value: clonedFilter,
+            formatedDateToLong: true
+          });
+          
+          if (ruleUrl && !isEmpty(ruleUrl) && typeof ruleUrl === "string") {
+            searchParams.set("entityFilters", ruleUrl);
+            
+            // Convert API format to query builder format for Filters 
component UI
+            const qbFilter = 
convertApiToQueryBuilder(searchParamsObj.entityFilters);
+            if (qbFilter && (qbFilter.rules || qbFilter.combinator)) {
+              globalSearchFilterInitialQuery.setQuery({ 
+                entityFilters: qbFilter 
+              });
+            }
+          }
+        }
+        
+        // Step 4: Convert and apply tagFilters from API format to URL string 
format
+        if (searchParamsObj.tagFilters && 
!isEmpty(searchParamsObj.tagFilters)) {
+          const clonedFilter = cloneDeep(searchParamsObj.tagFilters);
+          const ruleUrl = attributeFilter.generateUrl({
+            value: clonedFilter,
+            formatedDateToLong: true
+          });
+          
+          if (ruleUrl && !isEmpty(ruleUrl) && typeof ruleUrl === "string") {
+            searchParams.set("tagFilters", ruleUrl);
+            
+            // Convert API format to query builder format for Filters 
component UI
+            const qbFilter = 
convertApiToQueryBuilder(searchParamsObj.tagFilters);
+            if (qbFilter && (qbFilter.rules || qbFilter.combinator)) {
+              globalSearchFilterInitialQuery.setQuery({ 
+                tagFilters: qbFilter 
+              });
+            }
+          }
+        }
+        
+        // Step 5: Convert and apply relationshipFilters from API format to 
URL string format
+        if (searchParamsObj.relationshipFilters && 
!isEmpty(searchParamsObj.relationshipFilters)) {
+          const clonedFilter = cloneDeep(searchParamsObj.relationshipFilters);
+          const ruleUrl = attributeFilter.generateUrl({
+            value: clonedFilter,
+            formatedDateToLong: true
+          });
+          
+          if (ruleUrl && !isEmpty(ruleUrl) && typeof ruleUrl === "string") {
+            searchParams.set("relationshipFilters", ruleUrl);
+            
+            // Convert API format to query builder format for Filters 
component UI
+            const qbFilter = 
convertApiToQueryBuilder(searchParamsObj.relationshipFilters);
+            if (qbFilter && (qbFilter.rules || qbFilter.combinator)) {
+              globalSearchFilterInitialQuery.setQuery({ 
+                relationshipFilters: qbFilter 
+              });
+            }
+          }
         }
+        
+        searchParams.set("isCF", "true");
       }
-      searchParams.set("isCF", "true");
     } else {
       searchParams.set("relationshipName", node.id);
     }
   };
+  
+  // Helper function to convert API format filter (criterion/condition) to 
query builder format (rules/combinator)
+  const convertApiToQueryBuilder = (apiFilter: any): any => {
+    if (!apiFilter || typeof apiFilter !== "object") {
+      return null;
+    }
+    
+    const result: any = {};
+    
+    // Convert condition to combinator
+    if (apiFilter.condition) {
+      result.combinator = apiFilter.condition.toLowerCase();
+    } else {
+      result.combinator = "and"; // default
+    }
+    
+    // Convert criterion to rules
+    if (apiFilter.criterion && Array.isArray(apiFilter.criterion)) {
+      result.rules = apiFilter.criterion.map((rule: any) => {
+        // If nested condition, recurse
+        if (rule.condition || rule.criterion) {
+          return convertApiToQueryBuilder(rule);
+        }
+        // Convert API rule format to query builder format
+        return {
+          field: rule.attributeName || rule.id,
+          operator: rule.operator,
+          value: rule.attributeValue || rule.value,
+          type: rule.type || rule.attributeType
+        };
+      });
+    } else if (apiFilter.rules && Array.isArray(apiFilter.rules)) {
+      // Already in query builder format
+      result.rules = apiFilter.rules.map((rule: any) => 
+        rule.condition || rule.criterion ? convertApiToQueryBuilder(rule) : 
rule
+      );
+    }
+    
+    return Object.keys(result).length > 0 ? result : null;
+  };
 
   const shouldSetCustomFilterParam = (node: TreeNode, key: string) => {
     return (
@@ -673,7 +796,11 @@ const BarTreeView: FC<{
       searchParams.set("pageOffset", value);
     } else if (key === "typeName") {
       searchParams.set("type", value);
-    } else {
+    } else if (key === "classification") {
+      // Map classification to tag parameter for URL (matching classic UI)
+      searchParams.set("tag", value);
+    } else if (value !== null && value !== undefined && value !== "") {
+      // Only set parameter if value is not null, undefined, or empty string
       searchParams.set(key, value);
     }
   };

Reply via email to