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

pawarprasad123 pushed a commit to branch ATLAS-5246-v1
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit d3a75e45d3b283b866e7cb714a72d58b2dc166ff
Author: Prasad Pawar <[email protected]>
AuthorDate: Fri May 8 12:53:43 2026 +0530

    ATLAS-5287: ATLAS UI: Dashboard- search bar with dropdown (#616)
---
 .../src/components/GlobalSearch/QuickSearch.tsx    | 1185 ++++++++++++--------
 dashboard/src/utils/scopedQuickSearchUtils.ts      |  466 ++++++++
 .../src/views/SideBar/SideBarTree/SideBarTree.tsx  |   23 +-
 3 files changed, 1165 insertions(+), 509 deletions(-)

diff --git a/dashboard/src/components/GlobalSearch/QuickSearch.tsx 
b/dashboard/src/components/GlobalSearch/QuickSearch.tsx
index 272ee107d..c8eba9490 100644
--- a/dashboard/src/components/GlobalSearch/QuickSearch.tsx
+++ b/dashboard/src/components/GlobalSearch/QuickSearch.tsx
@@ -16,518 +16,715 @@
  */
 
 import {
-  Autocomplete,
-  CircularProgress,
-  InputAdornment,
-  Stack,
-  TextField,
-  Typography
+       Autocomplete,
+       CircularProgress,
+       FormControl,
+       InputAdornment,
+       MenuItem,
+       Select,
+       Stack,
+       TextField,
+       Typography,
+       type SelectChangeEvent
 } from "@mui/material";
-import { useRef, useState } from "react";
+import { useCallback, useMemo, useRef, useState } from "react";
 import { getGlobalSearchResult } from "../../api/apiMethods/searchApiMethod";
 import DisplayImage from "../EntityDisplayImage";
 import SearchIcon from "@mui/icons-material/Search";
 import { Link, useLocation, useNavigate } from "react-router-dom";
 import { entityStateReadOnly } from "../../utils/Enum";
 import {
-  extractKeyValueFromEntity,
-  isEmpty,
-  serverError
+       extractKeyValueFromEntity,
+       isEmpty,
+       serverError
 } from "../../utils/Utils";
 import parse from "autosuggest-highlight/parse";
 import match from "autosuggest-highlight/match";
 import ClickAwayListener from "@mui/material/ClickAwayListener";
 import AdvancedSearch from "./AdvancedSearch";
 import {
-  HandleValuesType,
-  QuickSearchOptionListType,
-  SuggestionDataType
+       HandleValuesType,
+       QuickSearchOptionListType,
+       SuggestionDataType
 } from "../../models/globalSearchType";
 import { AxiosResponse } from "axios";
 import { CustomButton } from "@components/muiComponents";
+import { useAppSelector } from "@hooks/reducerHook";
+import {
+       buildBusinessMetadataScopedOptions,
+       buildClassificationScopedOptions,
+       buildEntitiesTreeForQuickSearch,
+       buildGlossaryTermsTreeForQuickSearch,
+       entityTreeToScopedOptions,
+       glossaryTreeToScopedOptions,
+       type QuickSearchScope,
+       type ScopedQuickSearchOption
+} from "@utils/scopedQuickSearchUtils";
+import {
+       navigateToBasicTextQuery,
+       navigateToBusinessMetadataDetailPage,
+       navigateToClassificationDetailPage,
+       navigateToGlossaryTermDetailPage,
+       navigateToServiceTypeEntitySearch
+} from "@utils/dashboardSearchUtils";
+
+interface GlobalOptionRow {
+       title: string;
+       types: string;
+       entityObj?: any;
+       scoped?: ScopedQuickSearchOption;
+}
+
+const SCOPE_SELECT_ID = "quick-search-scope-select";
+
+const SCOPE_LABELS: Record<QuickSearchScope, string> = {
+       default: "Select All",
+       entity: "Entity",
+       classification: "Classification",
+       glossary: "Glossary / Terms",
+       businessMetadata: "Business Metadata"
+};
 
 const QuickSearch = () => {
-  const navigate = useNavigate();
-  const location = useLocation();
-  const toastId = useRef(null);
-  const searchParams = new URLSearchParams(location.search);
-  const [options, setOptions] = useState<any>([]);
-  const [open, setOpen] = useState<boolean>(false);
-  const [openAdvanceSearch, setOpenAdvanceSearch] = useState<boolean>(false);
-  const [loading, setLoading] = useState<boolean>(false);
-  const [value, setValue] = useState<string>("");
-
-  const getData = async (searchTerm: string) => {
-    let entities: QuickSearchOptionListType = [];
-    let suggestionNames: QuickSearchOptionListType = [];
-    let quickSearchResp: AxiosResponse | null = null;
-    let suggestionSearchResp: AxiosResponse | null = null;
-    try {
-      setLoading(true);
-      quickSearchResp = await getGlobalSearchResult("quick", {
-        params: { query: searchTerm, limit: 5, offset: 0 }
-      });
-      setLoading(false);
-    } catch (error) {
-      setLoading(false);
-      console.error("Error fetching quick search results:", error);
-      serverError(error, toastId);
-    }
-
-    try {
-      setLoading(true);
-      suggestionSearchResp = await getGlobalSearchResult("suggestions", {
-        params: { prefixString: searchTerm }
-      });
-      setLoading(false);
-    } catch (error) {
-      setLoading(false);
-      console.error("Error fetching suggestion search results:", error);
-      serverError(error, toastId);
-    }
-    const { searchResults = [] } = quickSearchResp?.data || {};
-    const { suggestions = [] }: SuggestionDataType =
-      suggestionSearchResp?.data || {};
-
-    entities = !isEmpty(searchResults?.entities)
-      ? searchResults?.entities?.map((entityDef: any) => {
-          const { name }: { name: string; found: boolean; key: any } =
-            extractKeyValueFromEntity(entityDef);
-          return {
-            title: `${name}`,
-            parent: entityDef.typeName,
-            types: "Entities",
-            entityObj: entityDef
-          };
-        })
-      : [{ title: "No Entities Found", types: "Entities" }];
-
-    suggestionNames = !isEmpty(suggestions)
-      ? suggestions.map((suggestion: any) => {
-          return {
-            title: `${suggestion}`,
-            types: "Suggestions"
-          };
-        })
-      : [{ title: "No Suggestions Found", types: "Suggestions" }];
-
-    setOptions([...entities, ...suggestionNames]);
-  };
-
-  const onInputChange = (_event: any, value: string) => {
-    // Sanitize input: remove any potential script tags and validate
-    const sanitizedValue = value ? value.trim() : "";
-    
-    if (sanitizedValue) {
-      setOpen(true);
-      getData(sanitizedValue);
-    } else {
-      setOptions([]);
-      setValue("");
-      setOpen(false);
-    }
-  };
-
-  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 = () => {
-    setOpen(false);
-  };
-
-  const handleCloseModal = () => {
-    setOpenAdvanceSearch(false);
-  };
-
-  return (
-    <>
-      <Stack
-        direction="row"
-        className="global-search-stack"
-        alignItems="center"
-        gap="0.5rem"
-      >
-        <ClickAwayListener onClickAway={handleClickAway}>
-          <Autocomplete
-            open={open}
-            loading={loading}
-            onKeyDown={(e) => {
-              const code = e.keyCode || e.which;
-
-              switch (code) {
-                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 === 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: // Tab key
-                case 27: // Escape key
-                  setOpen(false);
-                  break;
-                default:
-                  break;
-              }
-            }}
-            freeSolo
-            id="global-search"
-            disablePortal
-            className="global-search-autocomplete"
-            sx={{
-              "& + .MuiAutocomplete-popper .MuiAutocomplete-option": {
-                backgroundColor: "white"
-              },
-              "& + .MuiAutocomplete-popper .MuiAutocomplete-option:hover": {
-                backgroundColor: "#c7e3ff"
-              }
-            }}
-            value={value}
-            onChange={(_event: any, newValue: any) => {
-              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}
-            includeInputInList
-            noOptionsText={"No Entities"}
-            disableClearable
-            onInputChange={onInputChange}
-            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" &&
-                "entityObj" in option &&
-                "types" in option &&
-                "parent" in option
-                  ? (option as {
-                      entityObj: { status?: string; guid?: string };
-                      types: string;
-                      parent: string;
-                    })
-                  : { entityObj: null, types: "", parent: "" };
-              typeof option !== "string" &&
-              "entityObj" in option &&
-              "types" in option
-                ? option
-                : { entityObj: null, types: "", parent: "" };
-              const title =
-                typeof option !== "string" && "title" in option
-                  ? option.title
-                  : option;
-              
-              // 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" ? safeName : safeTitle,
-                inputValue || "",
-                {
-                  findAllOccurrences: true,
-                  insideWords: true
-                }
-              );
-              const parts = parse(
-                types === "Entities" ? safeName : safeTitle,
-                matches
-              );
-              return (
-                <Stack
-                  flexDirection="row"
-                  component="li"
-                  className="global-search-options"
-                  sx={{
-                    "& > span": {
-                      mr: 2,
-                      flexShrink: 0
-                    }
-                  }}
-                  {...props}
-                  onClick={() => {
-                    if (typeof option !== "string") {
-                      handleValues(option as unknown as HandleValuesType);
-                    }
-                  }}
-                >
-                  {types === "Entities" && !isEmpty(entityObj) ? (
-                    <Link
-                      className="entity-name text-decoration-none"
-                      style={{
-                        maxWidth: "100%",
-                        width: "100%",
-                        color: "black",
-                        textDecoration: "none",
-                        display: "inline-flex",
-                        alignItems: "center",
-                        flexWrap: "wrap"
-                      }}
-                      to={{
-                        pathname: href
-                      }}
-                      color={
-                        entityObj?.status &&
-                        entityStateReadOnly[entityObj.status]
-                          ? "error"
-                          : "primary"
-                      }
-                    >
-                      {" "}
-                      {types === "Entities" && !isEmpty(entityObj) && (
-                        <DisplayImage entity={entityObj} />
-                      )}{" "}
-                      {types === "Entities" && !isEmpty(entityObj)
-                        ? parts.map((part, index) => (
-                            <Stack
-                              flexDirection="row"
-                              key={index}
-                              style={{
-                                fontWeight: part.highlight ? "bold" : "regular"
-                              }}
-                            >
-                              {entityObj?.guid !== "-1" && !part.highlight ? (
-                                <Link
-                                  className="entity-name text-blue 
text-decoration-none"
-                                  style={{
-                                    color: "black",
-                                    textDecoration: "none",
-                                    maxWidth: "100%",
-                                    width: "100%"
-                                  }}
-                                  to={{
-                                    pathname: href
-                                  }}
-                                  color={
-                                    entityObj?.status &&
-                                    entityStateReadOnly[entityObj.status]
-                                      ? "error"
-                                      : "primary"
-                                  }
-                                >
-                                  {part.text}
-                                </Link>
-                              ) : (
-                                part.text
-                              )}
-                            </Stack>
-                          ))
-                        : parts.map((part, index) => (
-                            <Stack
-                              flexDirection="row"
-                              key={index}
-                              style={{
-                                fontWeight: part.highlight ? "bold" : "regular"
-                              }}
-                            >
-                              {part.text}
-                            </Stack>
-                          ))}
-                      {types === "Entities" &&
-                        !isEmpty(entityObj) &&
-                        ` (${parent})`}
-                    </Link>
-                  ) : (
-                    parts.map((part, index) => (
-                      <Typography
-                        component="p"
-                        key={index}
-                        className="global-search-options-text"
-                        sx={{
-                          fontWeight: part.highlight ? "bold" : "regular"
-                        }}
-                      >
-                        {" "}
-                        {part.text}
-                      </Typography>
-                    ))
-                  )}
-                </Stack>
-              );
-            }}
-            renderInput={(params) => (
-              <TextField
-                {...params}
-                placeholder="Search Entities..."
-                fullWidth
-                onClick={() => {
-                  setOpen(true);
-                }}
-                className="text-black-default"
-                InputProps={{
-                  style: {
-                    padding: "1px 10px",
-                    borderRadius: "4px",
-                    color: "white !important",
-                    opacity: 1
-                  },
-                  ...params.InputProps,
-                  type: "search",
-                  endAdornment: (
-                    <InputAdornment position="start">
-                      {loading ? (
-                        <CircularProgress sx={{ color: "gray" }} size={15} />
-                      ) : (
-                        <SearchIcon fontSize="small" />
-                      )}
-                    </InputAdornment>
-                  )
-                }}
-              />
-            )}
-            groupBy={(option) =>
-              typeof option !== "string" && "types" in option
-                ? String(option.types)
-                : ""
-            }
-            options={options}
-            filterOptions={(options) => options}
-          />
-        </ClickAwayListener>
-
-        <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: "#4a90e2 !important",
-              fontWeight: "600 !important",
-              fontSize: "0.875rem !important"
-            }}
-            display="inline"
-          >
-            Advanced
-          </Typography>
-        </CustomButton>
-      </Stack>
-
-      {openAdvanceSearch && (
-        <AdvancedSearch
-          openAdvanceSearch={openAdvanceSearch}
-          handleCloseModal={handleCloseModal}
-        />
-      )}
-    </>
-  );
+       const navigate = useNavigate();
+       const location = useLocation();
+       const toastId = useRef(null);
+       const searchParams = new URLSearchParams(location.search);
+       const [options, setOptions] = useState<GlobalOptionRow[]>([]);
+       const [open, setOpen] = useState<boolean>(false);
+       const [openAdvanceSearch, setOpenAdvanceSearch] = 
useState<boolean>(false);
+       const [loading, setLoading] = useState<boolean>(false);
+       const [inputText, setInputText] = useState<string>("");
+       const [scope, setScope] = useState<QuickSearchScope>("default");
+
+       const { typeHeaderData } = useAppSelector((state: any) => 
state.typeHeader);
+       const { metricsData } = useAppSelector((state: any) => state.metrics);
+       const { allEntityTypesData } = useAppSelector((state: any) => 
state.allEntityTypes);
+       const { classificationData } = useAppSelector((state: any) => 
state.classification);
+       const { glossaryData } = useAppSelector((state: any) => state.glossary);
+       const { businessMetaData } = useAppSelector((state: any) => 
state.businessMetaData);
+
+       const entityTree = useMemo(
+               () =>
+                       buildEntitiesTreeForQuickSearch(
+                               typeHeaderData,
+                               metricsData?.data?.entity,
+                               allEntityTypesData?.category
+                       ),
+               [typeHeaderData, metricsData, allEntityTypesData]
+       );
+
+       const rebuildScopedOptions = useCallback(
+               (term: string): GlobalOptionRow[] => {
+                       if (scope === "entity") {
+                               return entityTreeToScopedOptions(entityTree, 
term).map((o) => ({
+                                       title: o.title,
+                                       types: o.group,
+                                       scoped: o
+                               }));
+                       }
+                       if (scope === "classification") {
+                               return buildClassificationScopedOptions(
+                                       classificationData,
+                                       metricsData?.data?.tag?.tagEntities,
+                                       term
+                               ).map((o) => ({
+                                       title: o.title,
+                                       types: o.group,
+                                       scoped: o
+                               }));
+                       }
+                       if (scope === "glossary") {
+                               const gTree = 
buildGlossaryTermsTreeForQuickSearch(glossaryData);
+                               return glossaryTreeToScopedOptions(gTree, 
term).map((o) => ({
+                                       title: o.title,
+                                       types: o.group,
+                                       scoped: o
+                               }));
+                       }
+                       if (scope === "businessMetadata") {
+                               return buildBusinessMetadataScopedOptions(
+                                       businessMetaData?.businessMetadataDefs,
+                                       term
+                               ).map((o) => ({
+                                       title: o.title,
+                                       types: o.group,
+                                       scoped: o
+                               }));
+                       }
+                       return [];
+               },
+               [
+                       scope,
+                       entityTree,
+                       classificationData,
+                       metricsData,
+                       glossaryData,
+                       businessMetaData
+               ]
+       );
+
+       const getGlobalSearchData = async (searchTerm: string) => {
+               let entities: QuickSearchOptionListType = [];
+               let suggestionNames: QuickSearchOptionListType = [];
+               let quickSearchResp: AxiosResponse | null = null;
+               let suggestionSearchResp: AxiosResponse | null = null;
+               try {
+                       setLoading(true);
+                       quickSearchResp = await getGlobalSearchResult("quick", {
+                               params: { query: searchTerm, limit: 5, offset: 
0 }
+                       });
+                       setLoading(false);
+               } catch (error) {
+                       setLoading(false);
+                       console.error("Error fetching quick search results:", 
error);
+                       serverError(error, toastId);
+               }
+
+               try {
+                       setLoading(true);
+                       suggestionSearchResp = await 
getGlobalSearchResult("suggestions", {
+                               params: { prefixString: searchTerm }
+                       });
+                       setLoading(false);
+               } catch (error) {
+                       setLoading(false);
+                       console.error("Error fetching suggestion search 
results:", error);
+                       serverError(error, toastId);
+               }
+               const { searchResults = [] } = quickSearchResp?.data || {};
+               const { suggestions = [] }: SuggestionDataType =
+                       suggestionSearchResp?.data || {};
+
+               entities = !isEmpty(searchResults?.entities)
+                       ? searchResults?.entities?.map((entityDef: any) => {
+                                       const { name }: { name: string; found: 
boolean; key: any } =
+                                               
extractKeyValueFromEntity(entityDef);
+                                       return {
+                                               title: `${name}`,
+                                               parent: entityDef.typeName,
+                                               types: "Entities",
+                                               entityObj: entityDef
+                                       };
+                               })
+                       : [{ title: "No Entities Found", types: "Entities" }];
+
+               suggestionNames = !isEmpty(suggestions)
+                       ? suggestions.map((suggestion: any) => {
+                                       return {
+                                               title: `${suggestion}`,
+                                               types: "Suggestions"
+                                       };
+                               })
+                       : [{ title: "No Suggestions Found", types: 
"Suggestions" }];
+
+               setOptions([...entities, ...suggestionNames] as 
GlobalOptionRow[]);
+       };
+
+       const onInputChange = (_event: unknown, value: string) => {
+               const sanitizedValue = value ? value.trim() : "";
+               setInputText(value ?? "");
+               if (!sanitizedValue) {
+                       setOptions([]);
+                       setOpen(false);
+                       return;
+               }
+               setOpen(true);
+               if (scope === "default") {
+                       void getGlobalSearchData(sanitizedValue);
+               } else {
+                       setOptions(rebuildScopedOptions(sanitizedValue));
+               }
+       };
+
+       const handleScopedSelection = (opt: ScopedQuickSearchOption) => {
+               setOpen(false);
+               setOptions([]);
+               setInputText("");
+               if (opt.kind === "entity-type" && opt.entityTypeName) {
+                       navigateToBasicTextQuery(navigate, opt.entityTypeName);
+                       return;
+               }
+               if (opt.kind === "entity-service" && 
opt.serviceUnderlyingTypeNames?.length) {
+                       navigateToServiceTypeEntitySearch(
+                               navigate,
+                               opt.serviceUnderlyingTypeNames,
+                               false
+                       );
+                       return;
+               }
+               if (opt.kind === "classification" && opt.classificationName) {
+                       navigateToClassificationDetailPage(navigate, 
opt.classificationName);
+                       return;
+               }
+               if (
+                       opt.kind === "glossary-term" &&
+                       opt.termGuid &&
+                       opt.glossaryGuid &&
+                       opt.termId &&
+                       opt.termParent
+               ) {
+                       navigateToGlossaryTermDetailPage(navigate, {
+                               termGuid: opt.termGuid,
+                               termId: opt.termId,
+                               glossaryGuid: opt.glossaryGuid,
+                               parentName: opt.termParent
+                       });
+                       return;
+               }
+               if (opt.kind === "business-metadata" && opt.bmGuid) {
+                       navigateToBusinessMetadataDetailPage(navigate, 
opt.bmGuid);
+               }
+       };
+
+       const handleValues = (option: HandleValuesType | GlobalOptionRow | 
string | null) => {
+               if (typeof option === "string") {
+                       const queryValue = option.trim();
+                       if (!queryValue) return;
+                       setOpen(false);
+                       setOptions([]);
+                       setInputText("");
+                       if (scope !== "default") {
+                               return;
+                       }
+                       const sanitizedQuery = queryValue || "*";
+                       searchParams.set("query", sanitizedQuery);
+                       searchParams.set("searchType", "basic");
+                       navigate(
+                               {
+                                       pathname: `/search/searchResult`,
+                                       search: searchParams.toString()
+                               },
+                               { replace: true }
+                       );
+                       return;
+               }
+
+               if (!option || typeof option !== "object") return;
+
+               const row = option as GlobalOptionRow;
+               if (row.scoped) {
+                       handleScopedSelection(row.scoped);
+                       return;
+               }
+
+               const { entityObj, title, types } = row as HandleValuesType;
+               if (!title || title === "undefined" || typeof title !== 
"string") return;
+
+               const sanitizedTitle = title.trim();
+               if (!sanitizedTitle) return;
+
+               setOpen(false);
+               setOptions([]);
+               setInputText("");
+
+               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 handleSubmitSearch = () => {
+               const q = inputText.trim();
+               if (scope !== "default") {
+                       const activeOption = options.find((o) => o.title === q);
+                       if (activeOption?.scoped) {
+                               handleScopedSelection(activeOption.scoped);
+                       }
+                       return;
+               }
+               if (!q) {
+                       handleValues("*");
+                       return;
+               }
+               const activeOption = options.find(
+                       (o) => typeof o !== "string" && o.title === q
+               );
+               if (activeOption) {
+                       handleValues(activeOption as HandleValuesType);
+               } else {
+                       handleValues(q);
+               }
+       };
+
+       const handleScopeChange = (e: SelectChangeEvent<QuickSearchScope>) => {
+               const v = e.target.value as QuickSearchScope;
+               setScope(v);
+               setOptions([]);
+               setInputText("");
+               setOpen(false);
+       };
+
+       const handleClickAway = () => {
+               setOpen(false);
+       };
+
+       const handleCloseModal = () => {
+               setOpenAdvanceSearch(false);
+       };
+
+       const inputPlaceholder =
+               scope === "default" ? "Search Entities..." : "Contains text...";
+
+       return (
+               <>
+                       <Stack
+                               direction="row"
+                               className="global-search-stack"
+                               alignItems="center"
+                               gap="0.5rem"
+                       >
+                               <FormControl
+                                       size="small"
+                                       sx={{ minWidth: 160, backgroundColor: 
"white", borderRadius: 1 }}
+                               >
+                                       <Select<QuickSearchScope>
+                                               id={SCOPE_SELECT_ID}
+                                               value={scope}
+                                               onChange={handleScopeChange}
+                                               aria-label="Search scope"
+                                               displayEmpty
+                                               renderValue={(v) => 
SCOPE_LABELS[v as QuickSearchScope]}
+                                       >
+                                               <MenuItem 
value="default">Select All</MenuItem>
+                                               <MenuItem 
value="entity">Entity</MenuItem>
+                                               <MenuItem 
value="classification">Classification</MenuItem>
+                                               <MenuItem 
value="glossary">Glossary / Terms</MenuItem>
+                                               <MenuItem 
value="businessMetadata">Business Metadata</MenuItem>
+                                       </Select>
+                               </FormControl>
+
+                               <ClickAwayListener 
onClickAway={handleClickAway}>
+                                       <Autocomplete
+                                               open={open}
+                                               loading={loading}
+                                               inputValue={inputText}
+                                               onInputChange={onInputChange}
+                                               onKeyDown={(e) => {
+                                                       const code = e.keyCode 
|| e.which;
+                                                       switch (code) {
+                                                               case 13: {
+                                                                       
e.preventDefault();
+                                                                       
handleSubmitSearch();
+                                                                       break;
+                                                               }
+                                                               case 9:
+                                                               case 27:
+                                                                       
setOpen(false);
+                                                                       break;
+                                                               default:
+                                                                       break;
+                                                       }
+                                               }}
+                                               freeSolo
+                                               id="global-search"
+                                               disablePortal
+                                               
className="global-search-autocomplete"
+                                               sx={{
+                                                       minWidth: 280,
+                                                       flex: 1,
+                                                       "& + 
.MuiAutocomplete-popper .MuiAutocomplete-option": {
+                                                               
backgroundColor: "white"
+                                                       },
+                                                       "& + 
.MuiAutocomplete-popper .MuiAutocomplete-option:hover": {
+                                                               
backgroundColor: "#c7e3ff"
+                                                       }
+                                               }}
+                                               value={null}
+                                               onChange={(_event: unknown, 
newValue: unknown) => {
+                                                       if (newValue && typeof 
newValue === "object") {
+                                                               const o = 
newValue as GlobalOptionRow;
+                                                               if (o.title) {
+                                                                       
handleValues(o);
+                                                               }
+                                                       }
+                                               }}
+                                               clearOnBlur={false}
+                                               autoComplete
+                                               includeInputInList
+                                               noOptionsText={scope === 
"default" ? "No Entities" : "No matches"}
+                                               getOptionLabel={(option: string 
| GlobalOptionRow) => {
+                                                       if (typeof option === 
"string") return option;
+                                                       return option?.title && 
typeof option.title === "string"
+                                                               ? option.title
+                                                               : "";
+                                               }}
+                                               renderOption={(props, option, { 
inputValue }) => {
+                                                       const row = option as 
GlobalOptionRow;
+                                                       if (row.scoped) {
+                                                               const title = 
row.title;
+                                                               const matches = 
match(title, inputValue || "", {
+                                                                       
findAllOccurrences: true,
+                                                                       
insideWords: true
+                                                               });
+                                                               const parts = 
parse(title, matches);
+                                                               return (
+                                                                       <li 
{...props} key={row.scoped.id}>
+                                                                               
<Typography component="span" variant="body2">
+                                                                               
        {parts.map((part, index) => (
+                                                                               
                <span
+                                                                               
                        key={index}
+                                                                               
                        style={{
+                                                                               
                                fontWeight: part.highlight ? 700 : 400
+                                                                               
                        }}
+                                                                               
                >
+                                                                               
                        {part.text}
+                                                                               
                </span>
+                                                                               
        ))}
+                                                                               
</Typography>
+                                                                       </li>
+                                                               );
+                                                       }
+
+                                                       const { entityObj, 
types, parent } =
+                                                               typeof option 
!== "string" &&
+                                                               "entityObj" in 
option &&
+                                                               "types" in 
option &&
+                                                               "parent" in 
option
+                                                                       ? 
(option as {
+                                                                               
        entityObj: { status?: string; guid?: string };
+                                                                               
        types: string;
+                                                                               
        parent: string;
+                                                                               
})
+                                                                       : { 
entityObj: null, types: "", parent: "" };
+                                                       const title =
+                                                               typeof option 
!== "string" && "title" in option
+                                                                       ? 
option.title
+                                                                       : 
option;
+                                                       const safeTitle = 
typeof title === "string" ? title : "";
+                                                       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);
+                                                       const safeName = name 
&& typeof name === "string" ? name : "";
+                                                       const matches = match(
+                                                               types === 
"Entities" ? safeName : safeTitle,
+                                                               inputValue || 
"",
+                                                               {
+                                                                       
findAllOccurrences: true,
+                                                                       
insideWords: true
+                                                               }
+                                                       );
+                                                       const parts = parse(
+                                                               types === 
"Entities" ? safeName : safeTitle,
+                                                               matches
+                                                       );
+                                                       return (
+                                                               <Stack
+                                                                       
flexDirection="row"
+                                                                       
component="li"
+                                                                       
className="global-search-options"
+                                                                       sx={{
+                                                                               
"& > span": {
+                                                                               
        mr: 2,
+                                                                               
        flexShrink: 0
+                                                                               
}
+                                                                       }}
+                                                                       
{...props}
+                                                                       
onClick={() => {
+                                                                               
handleValues(option as GlobalOptionRow);
+                                                                       }}
+                                                               >
+                                                                       {types 
=== "Entities" && !isEmpty(entityObj) ? (
+                                                                               
<Link
+                                                                               
        className="entity-name text-decoration-none"
+                                                                               
        style={{
+                                                                               
                maxWidth: "100%",
+                                                                               
                width: "100%",
+                                                                               
                color: "black",
+                                                                               
                textDecoration: "none",
+                                                                               
                display: "inline-flex",
+                                                                               
                alignItems: "center",
+                                                                               
                flexWrap: "wrap"
+                                                                               
        }}
+                                                                               
        to={{ pathname: href }}
+                                                                               
        color={
+                                                                               
                entityObj?.status &&
+                                                                               
                entityStateReadOnly[entityObj.status]
+                                                                               
                        ? "error"
+                                                                               
                        : "primary"
+                                                                               
        }
+                                                                               
>
+                                                                               
        {types === "Entities" && !isEmpty(entityObj) && (
+                                                                               
                <DisplayImage entity={entityObj} />
+                                                                               
        )}
+                                                                               
        {types === "Entities" && !isEmpty(entityObj)
+                                                                               
                ? parts.map((part, index) => (
+                                                                               
                                <Stack
+                                                                               
                                        flexDirection="row"
+                                                                               
                                        key={index}
+                                                                               
                                        style={{
+                                                                               
                                                fontWeight: part.highlight ? 
"bold" : "regular"
+                                                                               
                                        }}
+                                                                               
                                >
+                                                                               
                                        {entityObj?.guid !== "-1" && 
!part.highlight ? (
+                                                                               
                                                <Link
+                                                                               
                                                        className="entity-name 
text-blue text-decoration-none"
+                                                                               
                                                        style={{
+                                                                               
                                                                color: "black",
+                                                                               
                                                                textDecoration: 
"none",
+                                                                               
                                                                maxWidth: 
"100%",
+                                                                               
                                                                width: "100%"
+                                                                               
                                                        }}
+                                                                               
                                                        to={{ pathname: href }}
+                                                                               
                                                        color={
+                                                                               
                                                                
entityObj?.status &&
+                                                                               
                                                                
entityStateReadOnly[entityObj.status]
+                                                                               
                                                                        ? 
"error"
+                                                                               
                                                                        : 
"primary"
+                                                                               
                                                        }
+                                                                               
                                                >
+                                                                               
                                                        {part.text}
+                                                                               
                                                </Link>
+                                                                               
                                        ) : (
+                                                                               
                                                part.text
+                                                                               
                                        )}
+                                                                               
                                </Stack>
+                                                                               
                        ))
+                                                                               
                : parts.map((part, index) => (
+                                                                               
                                <Stack
+                                                                               
                                        flexDirection="row"
+                                                                               
                                        key={index}
+                                                                               
                                        style={{
+                                                                               
                                                fontWeight: part.highlight ? 
"bold" : "regular"
+                                                                               
                                        }}
+                                                                               
                                >
+                                                                               
                                        {part.text}
+                                                                               
                                </Stack>
+                                                                               
                        ))}
+                                                                               
        {types === "Entities" &&
+                                                                               
                !isEmpty(entityObj) &&
+                                                                               
                ` (${parent})`}
+                                                                               
</Link>
+                                                                       ) : (
+                                                                               
parts.map((part, index) => (
+                                                                               
        <Typography
+                                                                               
                component="p"
+                                                                               
                key={index}
+                                                                               
                className="global-search-options-text"
+                                                                               
                sx={{
+                                                                               
                        fontWeight: part.highlight ? "bold" : "regular"
+                                                                               
                }}
+                                                                               
        >
+                                                                               
                {part.text}
+                                                                               
        </Typography>
+                                                                               
))
+                                                                       )}
+                                                               </Stack>
+                                                       );
+                                               }}
+                                               renderInput={(params) => (
+                                                       <TextField
+                                                               {...params}
+                                                               
placeholder={inputPlaceholder}
+                                                               fullWidth
+                                                               onClick={() => {
+                                                                       
setOpen(true);
+                                                               }}
+                                                               
className="text-black-default"
+                                                               InputProps={{
+                                                                       style: {
+                                                                               
padding: "1px 10px",
+                                                                               
borderRadius: "4px",
+                                                                               
color: "#1a1a1a",
+                                                                               
backgroundColor: "white"
+                                                                       },
+                                                                       
...params.InputProps,
+                                                                       type: 
"search",
+                                                                       
endAdornment: (
+                                                                               
<InputAdornment position="end">
+                                                                               
        {loading ? (
+                                                                               
                <CircularProgress sx={{ color: "gray" }} size={15} />
+                                                                               
        ) : (
+                                                                               
                <SearchIcon fontSize="small" sx={{ color: "gray" }} />
+                                                                               
        )}
+                                                                               
</InputAdornment>
+                                                                       )
+                                                               }}
+                                                               inputProps={{
+                                                                       
...params.inputProps,
+                                                                       
"aria-label": "Global search"
+                                                               }}
+                                                       />
+                                               )}
+                                               groupBy={(option) =>
+                                                       typeof option !== 
"string" && "types" in option
+                                                               ? 
String((option as GlobalOptionRow).types)
+                                                               : ""
+                                               }
+                                               options={options}
+                                               filterOptions={(x) => x}
+                                       />
+                               </ClickAwayListener>
+
+                               <CustomButton
+                                       variant="contained"
+                                       size="small"
+                                       sx={{
+                                               backgroundColor: "#4a90e2 
!important",
+                                               color: "#fff !important",
+                                               textTransform: "none",
+                                               fontWeight: 600
+                                       }}
+                                       onClick={handleSubmitSearch}
+                                       aria-label="Run search"
+                               >
+                                       Search
+                               </CustomButton>
+
+                               <CustomButton
+                                       variant="outlined"
+                                       size="small"
+                                       sx={{
+                                               backgroundColor: "white 
!important",
+                                               color: "#4a90e2 !important",
+                                               borderColor: "#dddddd 
!important",
+                                               "&:hover": {
+                                                       backgroundColor: 
"rgba(74, 144, 226, 0.08) !important",
+                                                       color: "#4a90e2 
!important"
+                                               }
+                                       }}
+                                       onClick={() => {
+                                               setOpenAdvanceSearch(true);
+                                       }}
+                               >
+                                       <Typography
+                                               sx={{
+                                                       color: "#4a90e2 
!important",
+                                                       fontWeight: "600 
!important",
+                                                       fontSize: "0.875rem 
!important"
+                                               }}
+                                               display="inline"
+                                       >
+                                               Advanced
+                                       </Typography>
+                               </CustomButton>
+                       </Stack>
+
+                       {openAdvanceSearch && (
+                               <AdvancedSearch
+                                       openAdvanceSearch={openAdvanceSearch}
+                                       handleCloseModal={handleCloseModal}
+                               />
+                       )}
+               </>
+       );
 };
 
 export default QuickSearch;
diff --git a/dashboard/src/utils/scopedQuickSearchUtils.ts 
b/dashboard/src/utils/scopedQuickSearchUtils.ts
new file mode 100644
index 000000000..81a97c44e
--- /dev/null
+++ b/dashboard/src/utils/scopedQuickSearchUtils.ts
@@ -0,0 +1,466 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { TreeNode } from "@models/treeStructureType";
+import type {
+       ChildrenInterface,
+       ServiceTypeInterface,
+       TypeHeaderInterface
+} from "@models/entityTreeType";
+import { addOnEntities } from "./Enum";
+import { customSortBy, customSortByObjectKeys, isEmpty } from "./Utils";
+
+export type QuickSearchScope =
+       | "default"
+       | "entity"
+       | "classification"
+       | "glossary"
+       | "businessMetadata";
+
+export type ScopedOptionKind =
+       | "entity-service"
+       | "entity-type"
+       | "classification"
+       | "glossary-term"
+       | "business-metadata";
+
+export interface ScopedQuickSearchOption {
+       id: string;
+       title: string;
+       group: string;
+       kind: ScopedOptionKind;
+       entityTypeName?: string;
+       serviceUnderlyingTypeNames?: string[];
+       classificationName?: string;
+       termGuid?: string;
+       glossaryGuid?: string;
+       termId?: string;
+       termParent?: string;
+       bmGuid?: string;
+}
+
+interface MetricsEntitySnapshot {
+       entityActive?: Record<string, number>;
+       entityDeleted?: Record<string, number>;
+}
+
+interface GlossaryTermCategoryRow {
+       parentCategoryGuid?: string;
+       categoryGuid?: string;
+       displayText?: string;
+       termGuid?: string;
+}
+
+interface GlossaryApiRow {
+       name: string;
+       guid: string;
+       categories?: GlossaryTermCategoryRow[];
+       terms?: GlossaryTermCategoryRow[];
+}
+
+const DEFAULT_SERVICE = "other_types";
+
+const generateServiceTypeArr = (
+       entityCountArr: ServiceTypeInterface[],
+       serviceType: string,
+       children: ChildrenInterface,
+       entityCount: number
+) => {
+       const existing = entityCountArr.find(
+               (obj): obj is ServiceTypeInterface =>
+                       typeof obj === "object" && obj !== null && serviceType 
in obj
+       );
+       if (existing) {
+               const bucket = (existing as Record<string, {
+                       children: ChildrenInterface[];
+                       totalCount: number;
+               }>)[serviceType];
+               if (bucket) {
+                       bucket.children.push(children);
+                       bucket.totalCount += entityCount;
+               }
+       } else {
+               entityCountArr.push({
+                       [serviceType]: {
+                               children: [children],
+                               name: serviceType,
+                               totalCount: entityCount
+                       }
+               } as ServiceTypeInterface);
+       }
+};
+
+const pushRootEntityToTree = (
+       entities: ServiceTypeInterface[],
+       allEntityCategory: string | undefined
+) => {
+       const rootEntityChildren: ChildrenInterface = {
+               gType: "Entity",
+               guid: addOnEntities[0],
+               id: addOnEntities[0],
+               name: addOnEntities[0],
+               type: allEntityCategory,
+               text: addOnEntities[0]
+       };
+       const hasOther = entities.some((obj) => obj["other_types"] !== 
undefined);
+       if (hasOther) {
+               const idx = entities.findIndex((obj) => "other_types" in obj);
+               (entities[idx] as 
ServiceTypeInterface)["other_types"].children.push(
+                       rootEntityChildren
+               );
+       } else {
+               entities.push({
+                       other_types: {
+                               name: "other_types",
+                               children: [rootEntityChildren],
+                               totalCount: 0
+                       }
+               } as ServiceTypeInterface);
+       }
+       return entities;
+};
+
+/**
+ * Mirrors {@link EntitiesTree} group view (service type → entity typedefs).
+ */
+export const buildEntitiesTreeForQuickSearch = (
+       typeHeaderData: TypeHeaderInterface[] | null | undefined,
+       metricsEntity: MetricsEntitySnapshot | null | undefined,
+       allEntityCategory: string | undefined
+): TreeNode[] => {
+       if (!Array.isArray(typeHeaderData) || !typeHeaderData.length || 
!metricsEntity) {
+               return [];
+       }
+       const active = metricsEntity.entityActive || {};
+       const deleted = metricsEntity.entityDeleted || {};
+       const newArr: ServiceTypeInterface[] = [];
+
+       typeHeaderData.forEach((entity) => {
+               let { serviceType = DEFAULT_SERVICE, category, name, guid } = 
entity;
+               if (category !== "ENTITY") return;
+               const entityCount =
+                       Number(active[name] ?? 0) + Number(deleted[name] ?? 0);
+               const modelName = entityCount ? `${name} (${entityCount})` : 
name;
+               const children: ChildrenInterface = {
+                       text: modelName,
+                       name,
+                       type: category,
+                       gType: "Entity",
+                       guid,
+                       id: guid
+               };
+               generateServiceTypeArr(newArr, serviceType, children, 
entityCount);
+       });
+
+       pushRootEntityToTree(newArr, allEntityCategory);
+
+       const child = (childs: ChildrenInterface[]) => {
+               if (!childs?.length) return [];
+               return customSortBy(
+                       childs.map((obj) => ({
+                               id: obj.name as string,
+                               label: obj.text as string,
+                               types: "child"
+                       })),
+                       ["label"]
+               );
+       };
+
+       const sorted = customSortByObjectKeys(newArr);
+       return sorted.map((entity: ServiceTypeInterface) => {
+               const key = Object.keys(entity)[0];
+               const entityData = entity[key];
+               return {
+                       id: entityData.name,
+                       label:
+                               entityData.totalCount === 0
+                                       ? entityData.name
+                                       : `${entityData.name} 
(${entityData.totalCount})`,
+                       children: child(entityData.children),
+                       types: "parent"
+               };
+       });
+};
+
+const filterTreeNodes = (treeData: TreeNode[], searchTerm: string): TreeNode[] 
=> {
+       const q = searchTerm.trim().toLowerCase();
+       if (!q) return treeData;
+       return treeData
+               .filter(
+                       (node) =>
+                               node.label?.toLowerCase().includes(q) ||
+                               node.children?.some((child) => 
child.label?.toLowerCase().includes(q))
+               )
+               .map((node) => {
+                       const parentMatches = 
node.label?.toLowerCase().includes(q) ?? false;
+                       const kids = node.children?.length
+                               ? parentMatches
+                                       ? node.children
+                                       : node.children.filter((c) => 
c.label?.toLowerCase().includes(q))
+                               : undefined;
+                       return { ...node, children: kids };
+               });
+};
+
+export const entityTreeToScopedOptions = (
+       tree: TreeNode[],
+       searchTerm: string
+): ScopedQuickSearchOption[] => {
+       const filtered = filterTreeNodes(tree, searchTerm);
+       const out: ScopedQuickSearchOption[] = [];
+       for (const parent of filtered) {
+               const underlying =
+                       parent.children?.map((c) => c.id).filter(Boolean) ?? [];
+               out.push({
+                       id: `svc:${parent.id}`,
+                       title: parent.label,
+                       group: "Entity",
+                       kind: "entity-service",
+                       serviceUnderlyingTypeNames: underlying
+               });
+               for (const child of parent.children ?? []) {
+                       out.push({
+                               id: `type:${child.id}`,
+                               title: child.label,
+                               group: "Entity",
+                               kind: "entity-type",
+                               entityTypeName: child.id
+                       });
+               }
+       }
+       return out;
+};
+
+interface ClassificationDefRow {
+       name: string;
+       subTypes: string[];
+       guid: string;
+       superTypes: string[];
+}
+
+/** Depth-first classification list (same traversal as sidebar tree). */
+export const buildClassificationScopedOptions = (
+       classificationData: { classificationDefs: ClassificationDefRow[] } | 
null,
+       tagEntities: Record<string, number> | undefined,
+       searchTerm: string
+): ScopedQuickSearchOption[] => {
+       if (!classificationData?.classificationDefs?.length) return [];
+       const defs = classificationData.classificationDefs;
+       const byName = new Map(defs.map((d) => [d.name, d]));
+       const q = searchTerm.trim().toLowerCase();
+       const out: ScopedQuickSearchOption[] = [];
+       const seen = new Set<string>();
+
+       const visit = (typeName: string) => {
+               if (seen.has(typeName)) return;
+               seen.add(typeName);
+               const def = byName.get(typeName);
+               if (!def) return;
+               const count = tagEntities?.[typeName];
+               const label =
+                       count !== undefined ? `${typeName} (${count})` : 
typeName;
+               if (!q || label.toLowerCase().includes(q) || 
typeName.toLowerCase().includes(q)) {
+                       out.push({
+                               id: `cls:${def.guid}:${typeName}`,
+                               title: label,
+                               group: "Classification",
+                               kind: "classification",
+                               classificationName: typeName
+                       });
+               }
+               (def.subTypes ?? []).forEach((st) => visit(st));
+       };
+
+       defs
+               .filter((d) => isEmpty(d.superTypes))
+               .forEach((d) => visit(d.name));
+
+       return customSortBy(out, ["title"]);
+};
+
+/** Build glossary tree for “terms” mode (see {@link GlossaryTree} 
glossaryType true). */
+export const buildGlossaryTermsTreeForQuickSearch = (
+       glossaryData: GlossaryApiRow[] | null | undefined
+): TreeNode[] => {
+       if (!glossaryData?.length) return [];
+       const glossaryType = true;
+
+       const toServiceRows: Record<string, {
+               name: string;
+               children: ChildrenInterface[];
+               id: string;
+               types: string;
+               parent: string;
+               guid: string;
+       }>[] = glossaryData.map((glossary) => {
+               const categoryRelation =
+                       glossary.categories?.filter((o) => o.parentCategoryGuid 
!== undefined) ??
+                       [];
+
+               const getChildren = (glossaries: {
+                       children: NonNullable<GlossaryApiRow["terms"]>;
+                       parent: string;
+               }) => {
+                       if (isEmpty(glossaries.children)) return [];
+                       return glossaries.children
+                               .map((glossariesType) => {
+                                       const getChild = () =>
+                                               categoryRelation
+                                                       .map((obj) => {
+                                                               if 
(obj.parentCategoryGuid === glossariesType.categoryGuid) {
+                                                                       return {
+                                                                               
name: obj.displayText,
+                                                                               
id: obj.displayText,
+                                                                               
children: [] as ChildrenInterface[],
+                                                                               
types: "child",
+                                                                               
parent: glossaries.parent,
+                                                                               
cGuid: glossaryType ? obj.termGuid : obj.categoryGuid,
+                                                                               
guid: glossary.guid
+                                                                       };
+                                                               }
+                                                               return 
undefined;
+                                                       })
+                                                       .filter(Boolean) as 
ChildrenInterface[];
+                                       if (glossariesType.parentCategoryGuid 
=== undefined) {
+                                               return {
+                                                       name: 
glossariesType.displayText,
+                                                       id: 
glossariesType.displayText,
+                                                       children: getChild(),
+                                                       types: "child",
+                                                       parent: 
glossaries.parent,
+                                                       cGuid: glossaryType
+                                                               ? 
glossariesType.termGuid
+                                                               : 
glossariesType.categoryGuid,
+                                                       guid: glossary.guid
+                                               } as ChildrenInterface;
+                                       }
+                                       return undefined;
+                               })
+                               .filter(Boolean) as ChildrenInterface[];
+               };
+
+               const children = getChildren({
+                       children: glossary?.terms ?? [],
+                       parent: glossary.name
+               });
+
+               return {
+                       [glossary.name]: {
+                               name: glossary.name,
+                               children: children || [],
+                               id: glossary.guid,
+                               types: "parent",
+                               parent: glossary.name,
+                               guid: glossary.guid
+                       }
+               };
+       });
+
+       const child = (childs: ChildrenInterface[]): TreeNode[] => {
+               if (!childs?.length) return [];
+               return customSortBy(
+                       childs.map((obj) => ({
+                               id: obj?.name as string,
+                               label: obj?.name as string,
+                               children:
+                                       obj?.children !== undefined
+                                               ? 
child(obj.children.filter(Boolean) as ChildrenInterface[])
+                                               : [],
+                               types: obj?.types,
+                               parent: obj?.parent,
+                               guid: obj?.guid,
+                               cGuid: obj?.cGuid
+                       })),
+                       ["label"]
+               );
+       };
+
+       const sorted = customSortByObjectKeys(toServiceRows as 
ServiceTypeInterface[]);
+       return sorted.map((entity: Record<string, {
+               name: string;
+               children: ChildrenInterface[];
+               types: string;
+               parent: string;
+               guid: string;
+       }>) => {
+               const g = entity[Object.keys(entity)[0]];
+               return {
+                       id: g.name,
+                       label: g.name,
+                       children: child((g.children ?? []) as 
ChildrenInterface[]),
+                       types: g.types,
+                       parent: g.parent,
+                       guid: g.guid
+               };
+       });
+};
+
+const collectGlossaryTermOptions = (
+       nodes: TreeNode[],
+       glossaryLabel: string,
+       out: ScopedQuickSearchOption[]
+) => {
+       for (const n of nodes) {
+               if (n.cGuid && n.types === "child" && n.id) {
+                       out.push({
+                               id: `term:${n.cGuid}`,
+                               title: `${n.label} (${glossaryLabel})`,
+                               group: "Glossary / Terms",
+                               kind: "glossary-term",
+                               termGuid: n.cGuid as string,
+                               glossaryGuid: (n.guid as string) ?? "",
+                               termId: n.id,
+                               termParent: (n.parent as string) ?? 
glossaryLabel
+                       });
+               }
+               if (n.children?.length) {
+                       collectGlossaryTermOptions(n.children, glossaryLabel, 
out);
+               }
+       }
+};
+
+export const glossaryTreeToScopedOptions = (
+       tree: TreeNode[],
+       searchTerm: string
+): ScopedQuickSearchOption[] => {
+       const filtered = filterTreeNodes(tree, searchTerm);
+       const out: ScopedQuickSearchOption[] = [];
+       for (const parent of filtered) {
+               collectGlossaryTermOptions(parent.children ?? [], parent.label, 
out);
+       }
+       return customSortBy(out, ["title"]);
+};
+
+export const buildBusinessMetadataScopedOptions = (
+       businessMetadataDefs: Array<{ name: string; guid: string }> | undefined,
+       searchTerm: string
+): ScopedQuickSearchOption[] => {
+       if (!businessMetadataDefs?.length) return [];
+       const q = searchTerm.trim().toLowerCase();
+       return customSortBy(
+               businessMetadataDefs
+                       .filter((d) => !q || d.name.toLowerCase().includes(q))
+                       .map((d) => ({
+                               id: `bm:${d.guid}`,
+                               title: d.name,
+                               group: "Business Metadata",
+                               kind: "business-metadata" as const,
+                               bmGuid: d.guid
+                       })),
+               ["title"]
+       );
+};
diff --git a/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx 
b/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx
index 02f5e27b6..bc9e444c2 100644
--- a/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx
+++ b/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx
@@ -64,7 +64,7 @@ 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 { downloadGlossaryImportTemplate } from "@utils/glossaryImportFlow";
 import { toast } from "react-toastify";
 import { EnumTypeDefData, TreeNode } from "@models/treeStructureType";
 import ImportDialog from "@components/ImportDialog";
@@ -969,33 +969,26 @@ const BarTreeView: FC<{
 
   const downloadFile = async () => {
     try {
-      let apiResp: any = {};
-      if (treeName == "Entities") {
-        apiResp = await getBusinessMetadataImportTmpl({});
-      } else if (treeName == "Glossary") {
-        apiResp = await getGlossaryImportTmpl({});
-      }
-      let text: string = "";
-      if (apiResp) {
-        text = apiResp.data;
+      if (treeName == "Glossary") {
+        await downloadGlossaryImportTemplate();
+        return;
       }
+      const apiResp: any = await getBusinessMetadataImportTmpl({});
+      const text: string = apiResp ? apiResp.data : "";
       const blob = new Blob([text], { type: "text/plain" });
 
       const url = window.URL.createObjectURL(blob);
 
       const link = document.createElement("a");
       link.href = url;
-      if (treeName == "Entities") {
-        link.setAttribute("download", "template_business_metadata");
-      } else if (treeName == "Glossary") {
-        link.setAttribute("download", "template");
-      }
+      link.setAttribute("download", "template_business_metadata");
 
       document.body.appendChild(link);
 
       link.click();
 
       document.body.removeChild(link);
+      window.URL.revokeObjectURL(url);
     } catch {
       /* ignore download error */
     }

Reply via email to