Brijesh619 commented on code in PR #688:
URL: https://github.com/apache/atlas/pull/688#discussion_r3844650799


##########
dashboard/src/views/Layout/About.tsx:
##########
@@ -24,31 +24,9 @@ import {
   Stack,
   Typography
 } from "@mui/material";
-import { serverError } from "@utils/Utils";
-import { useEffect, useRef, useState } from "react";
 
 const About = () => {
-  const [versionData, setVersionData] = useState<any>({});
-  const [loader, setLoader] = useState(false);
-  const toastId = useRef(null);
-
-  useEffect(() => {
-    fetchVersionDetails();
-  }, []);
-
-  const fetchVersionDetails = async () => {
-    setLoader(true);
-    try {
-      const versionResp = await getVersion();
-      const { data = {} } = versionResp || {};
-      setVersionData(data);
-      setLoader(false);
-    } catch (error) {
-      setLoader(false);
-      console.error(`Error occur while fetching version details`, error);
-      serverError(error, toastId);
-    }
-  };
+  const { data: versionData, loading: loader, error } = useAppSelector((state: 
any) => state.session.versionData);

Review Comment:
   Fixed! Removed (state: any) and switched to (state) so that TypeScript 
automatically infers the correct RootState type from our pre-typed 
useAppSelector hook, keeping our strict typing consistent.



##########
dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx:
##########
@@ -287,1027 +322,1070 @@ const BarTreeView: FC<{
   sideBarOpen,
   searchTerm,
   loader,
+  isPopover,
 }) => {
-  const dispatch = useAppDispatch();
-  const { savedSearchData }: any = useAppSelector(
-    (state: any) => state.savedSearch
-  );
-  const { bmguid } = useParams();
-  const location = useLocation();
-  const navigate = useNavigate();
-  const searchParams = new URLSearchParams(location.search);
-  const [expand, setExpand] = useState<null | HTMLElement>(null);
-  const [selectedNode, setSelectedNode] = useState<{
-    type: string | null;
-    tag: string | null;
-    relationship: string | null;
-    businessMetadata: string | null;
-  }>({
-    type: null,
-    tag: null,
-    relationship: null,
-    businessMetadata: null,
-  });
-
-  const [openModal, setOpenModal] = useState<boolean>(false);
-  const toastId: any = useRef(null);
-  const open = Boolean(expand);
-  const [expandedItems, setExpandedItems] = useState<string[]>([]);
-  const [tagModal, setTagModal] = useState<boolean>(false);
-  const [glossaryModal, setGlossaryModal] = useState<boolean>(false);
-  const { businessMetaData }: any = useAppSelector(
-    (state: any) => state.businessMetaData
-  );
-
-  const filteredData = useMemo(() => {
-    return treeData.filter((node) => {
-      return (
-        node.label?.toLowerCase().includes(searchTerm.toLowerCase()) ||
-        (node.children &&
-          node.children.some((child) =>
-            child.label?.toLowerCase().includes(searchTerm.toLowerCase())
-          ))
-      );
+    const { savedSearchData }: any = useAppSelector(
+      (state: any) => state.savedSearch
+    );
+    const { bmguid } = useParams();
+    const dispatch = useAppDispatch();
+    const location = useLocation();
+    const navigate = useNavigate();
+    const searchParams = new URLSearchParams(location.search);
+    const [expand, setExpand] = useState<null | HTMLElement>(null);
+    const [selectedNode, setSelectedNode] = useState<SelectedNode>({
+      type: null,
+      tag: null,
+      relationship: null,
+      businessMetadata: null,
+      term: null,
+      customFilter: null,
     });
-  }, [treeData, searchTerm]);
 
-  const displayTreeName = useMemo(() => {
-    return treeName === "CustomFilters" ? "Custom Filters" : treeName
-  }, [treeName]);
+    const [openModal, setOpenModal] = useState<boolean>(false);
+    const toastId: any = useRef(null);
+    const open = Boolean(expand);
+    const [expandedItems, setExpandedItems] = useState<string[]>([]);
+    const [tagModal, setTagModal] = useState<boolean>(false);
+    const [glossaryModal, setGlossaryModal] = useState<boolean>(false);
+    const { businessMetaData }: any = useAppSelector(
+      (state: any) => state.businessMetaData
+    );
 
-  const highlightText = useMemo(() => {
-    return (text: string) => {
-      if (!searchTerm) return text;
+    const filteredData = useMemo(() => {
+      if (!searchTerm) return treeData;
+      const lowerSearch = searchTerm.toLowerCase();
+      return treeData.reduce((acc: any[], node: any) => {
+        const nodeMatches = node.label?.toLowerCase().includes(lowerSearch);
+        let filteredChildren = node.children;
+        if (!nodeMatches && node.children) {
+          filteredChildren = node.children.filter((child: any) =>
+            child.label?.toLowerCase().includes(lowerSearch)
+          );
+        }
+        if (nodeMatches || (filteredChildren && filteredChildren.length > 0)) {
+          acc.push({ ...node, children: filteredChildren });
+        }
+        return acc;
+      }, []);
+    }, [treeData, searchTerm]);
 
-      const parts = text.split(new RegExp(`(${searchTerm})`, "gi"));
-      return parts.map((part, index) =>
-        part.toLowerCase() === searchTerm.toLowerCase() ? (
-          <span key={index} style={{ color: "#D3D3D3", fontWeight: "600" }}>
-            {part}
-          </span>
-        ) : (
-          part
-        )
-      );
-    };
-  }, [searchTerm]);
-
-  const expandedItemsMemo = useMemo(() => {
-    const allNodeIds = filteredData.flatMap((node) => {
-      return [
-        node.id,
-        ...(node.children ? node.children.map((child) => child.id) : []),
-      ];
-    });
-    return [...allNodeIds, ...[treeName]];
-  }, [filteredData, treeName]);
+    const displayTreeName = useMemo(() => {
+      return treeName === "CustomFilters" ? "Custom Filters" : treeName
+    }, [treeName]);
 
-  useEffect(() => {
-    setExpandedItems(expandedItemsMemo);
-  }, [expandedItemsMemo]);
+    const highlightText = useMemo(() => {
+      return (text: string) => {
+        if (!searchTerm) return text;
 
-  useEffect(() => {
-    const searchParams = new URLSearchParams(location.search);
-    const nodeIdFromParamsType = searchParams.get("type");
-    const nodeIdFromParamsTag = searchParams.get("tag");
-    const nodeIdFromParamsRelationshipName =
-      searchParams.get("relationshipName");
-    const nodeIdFromBMName = location.pathname.includes(
-      "/administrator/businessMetadata"
-    );
+        const escapeRegExp = (string: string) => {
+          return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means 
the whole matched string
+        };
 
-    const bmObj = !isEmpty(businessMetaData?.businessMetadataDefs)
-      ? businessMetaData?.businessMetadataDefs?.find((obj: EnumTypeDefData) => 
{
+        const escapedSearchTerm = escapeRegExp(searchTerm);
+        const parts = text.split(new RegExp(`(${escapedSearchTerm})`, "gi"));
+        return parts.map((part, index) =>
+          part.toLowerCase() === searchTerm.toLowerCase() ? (
+            <span key={index} className="sidebar-tree-highlight">
+              {part}
+            </span>
+          ) : (
+            part
+          )
+        );
+      };
+    }, [searchTerm]);
+
+    const expandedItemsMemo = useMemo(() => {
+      if (!searchTerm) {
+        return [treeName];
+      }
+      const parentNodeIds = filteredData.map((node) => node.id);
+      return [...parentNodeIds, treeName];
+    }, [filteredData, treeName, searchTerm]);
+
+    useEffect(() => {
+      setExpandedItems(expandedItemsMemo);
+    }, [expandedItemsMemo]);
+
+    const getNodeId = (node: TreeNode) => {
+      if (treeName == "Classifications" && node.types == "parent") {
+        return node.label;
+      } else if (treeName == "Classifications" && node.types == "child") {
+        return `${node.id}@${node.label}`;
+      }
+      return !isEmpty(node?.parent) ? `${node.id}@${node?.parent}` : node.id;
+    };
+
+    useEffect(() => {
+      const searchParams = new URLSearchParams(location.search);
+      const nodeIdFromParamsType = searchParams.get("type");
+      const nodeIdFromParamsTag = searchParams.get("tag");
+      const nodeIdFromParamsRelationshipName =
+        searchParams.get("relationshipName");
+      const nodeIdFromBMName = location.pathname.includes(
+        "/administrator/businessMetadata"
+      );
+      const nodeIdFromParamsTerm = searchParams.get("term") || 
searchParams.get("category") || searchParams.get("gtype") || 
location.pathname.split("/glossary/")[1];
+      const nodeIdFromCustomFilter = searchParams.get("customFilter");
+
+      const bmObj = !isEmpty(businessMetaData?.businessMetadataDefs)
+        ? businessMetaData?.businessMetadataDefs?.find((obj: EnumTypeDefData) 
=> {
           if (bmguid == obj.guid) {
             return obj;
           }
         })
-      : {};
-    const { name = "" } = bmObj || {};
-
-    setSelectedNode({
-      type: nodeIdFromParamsType,
-      tag: nodeIdFromParamsTag,
-      relationship: nodeIdFromParamsRelationshipName,
-      businessMetadata: nodeIdFromBMName ? name : null,
-    });
+        : {};
+      const { name = "" } = bmObj || {};
 
-    if (
-      !nodeIdFromParamsType &&
-      !nodeIdFromParamsTag &&
-      !nodeIdFromParamsRelationshipName &&
-      !nodeIdFromBMName
-    ) {
       setSelectedNode({
-        type: null,
-        tag: null,
-        relationship: null,
-        businessMetadata: null,
+        type: nodeIdFromParamsType,
+        tag: nodeIdFromParamsTag,
+        relationship: nodeIdFromParamsRelationshipName,
+        businessMetadata: nodeIdFromBMName ? name : null,
+        term: nodeIdFromParamsTerm || null,
+        customFilter: nodeIdFromCustomFilter || null,
       });
-    }
-  }, [location.search]);
 
-  const getEmptyTypesTitle = () => {
-    switch (treeName) {
-      case "Entities":
-        return `${isEmptyServicetype ? "Hide" : "Show"} empty service types`;
+      if (nodeIdFromParamsTerm && typeof nodeIdFromParamsTerm === "string" && 
nodeIdFromParamsTerm.includes("@") && treeName === "Glossary") {
+        const glossaryName = nodeIdFromParamsTerm.split("@")[1];
+        if (glossaryName) {
+          const parentNode = treeData.find((n) => n.label === glossaryName);
+          if (parentNode) {
+            const nodeIdToExpand = getNodeId(parentNode);
+            setExpandedItems((prev) => {
+              if (!prev.includes(nodeIdToExpand)) {
+                return [...prev, nodeIdToExpand];
+              }
+              return prev;
+            });
+          }
+        }
+      }
 
-      case "Classifications":
-        return `${isEmptyServicetype ? "Show" : "Hide"} unused 
classifications`;
+      if (
+        !nodeIdFromParamsType &&
+        !nodeIdFromParamsTag &&
+        !nodeIdFromParamsRelationshipName &&
+        !nodeIdFromBMName &&
+        !nodeIdFromParamsTerm &&
+        !nodeIdFromCustomFilter
+      ) {
+        setSelectedNode({
+          type: null,
+          tag: null,
+          relationship: null,
+          businessMetadata: null,
+          term: null,
+          customFilter: null,
+        });
+      }
+    }, [location.search, treeData, treeName, businessMetaData, bmguid]);

Review Comment:
   Fixed! Wrapped the getNodeId helper in a useCallback to keep its reference 
stable and added both it and location.pathname to the useEffect dependency 
array. This resolves the ESLint warning and prevents any stale selection state 
edge cases when routing changes.



##########
dashboard/src/views/SideBar/SideBarBody.tsx:
##########
@@ -101,61 +101,116 @@ const DrawerHeader = styled("div")(({ theme }) => ({
   marginBottom: "1rem",
 }));
 
+
 const SideBarBody = (props: {
-  loading: boolean;
-  handleOpenModal: any;
-  handleOpenAboutModal: any;
+  handleOpenModal: () => void;
+  handleOpenAboutModal: () => void;
 }) => {
   const location = useLocation();
   const routes = useRoutes(AppRoutes as RouteObject[]);
   const history = useHistory();
   const dispatch = useAppDispatch();
-  const { loading: loader, handleOpenModal, handleOpenAboutModal } = props;
+  const { handleOpenModal, handleOpenAboutModal } = props;
   const navigate = useNavigate();
-  const { loading } = useSelector((state: TypeHeaderState) => 
state.typeHeader);
-  const { relationshipSearch = {} } = globalSessionData || {};
+  const { relationshipSearch = false } = globalSessionData || {};
   const [open, setOpen] = useState(true);
   const [searchTerm, setSearchTerm] = useState<string>("");
+  const { data: versionData, loading: isVersionLoading, error: versionError } 
= useAppSelector((state) => state.session?.versionData || {});
+  const searchParams = new URLSearchParams(location.search);
+
+  const activeModule = useMemo(() => {
+    if (searchParams.get("isCF") === "true") return "customFilters";
+    if (location.pathname.includes("/glossary") || !!searchParams.get("gtype") 
|| !!searchParams.get("term") || !!searchParams.get("category")) return 
"glossary";
+    if (location.pathname.includes("/administrator/businessMetadata")) return 
"businessMetadata";
+    if (!!searchParams.get("tag") || 
location.pathname.includes("/tag/tagAttribute")) return "classification";
+    if (!!searchParams.get("relationshipName") || 
location.pathname.includes("/relationshipDetailPage")) return "relationships";
+    if (!!searchParams.get("type") || 
location.pathname.includes("/detailPage")) return "entities";
+    return null;
+  }, [location.pathname, location.search]);

Review Comment:
   Moved the searchParams initialization inside the useMemo block so it derives 
directly from location.search without triggering unnecessary re-renders or 
missing dependency warnings.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to