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

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


The following commit(s) were added to refs/heads/master by this push:
     new eedc894a9a3 Fix duplicate Zookeeper Browser nav entry (#17500)
eedc894a9a3 is described below

commit eedc894a9a33a7a1c3637f9123cdba66677c64ec
Author: Xiang Fu <[email protected]>
AuthorDate: Tue Jan 13 22:26:11 2026 +0800

    Fix duplicate Zookeeper Browser nav entry (#17500)
    
    Layout previously mutated a module-level navigationItems array when 
authWorkflow=NONE, causing duplicates on re-render. Compute nav items immutably 
per render.
---
 .../src/main/resources/app/components/Layout.tsx   | 37 +++++++++++++---------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/pinot-controller/src/main/resources/app/components/Layout.tsx 
b/pinot-controller/src/main/resources/app/components/Layout.tsx
index 6f8ea0b3556..534ae0e7d43 100644
--- a/pinot-controller/src/main/resources/app/components/Layout.tsx
+++ b/pinot-controller/src/main/resources/app/components/Layout.tsx
@@ -28,7 +28,7 @@ import ZookeeperIcon from './SvgIcons/ZookeeperIcon';
 import app_state from '../app_state';
 import AccountCircleOutlinedIcon from 
'@material-ui/icons/AccountCircleOutlined';
 
-let navigationItems = [
+const BASE_NAVIGATION_ITEMS = [
   { id: 1, name: 'Cluster Manager', link: '/', icon: <ClusterManagerIcon /> },
   { id: 2, name: 'Query Console', link: '/query', icon: <QueryConsoleIcon /> },
   { id: 4, name: 'Swagger REST API', link: 'help', target: '_blank', icon: 
<SwaggerIcon /> }
@@ -36,21 +36,28 @@ let navigationItems = [
 
 const Layout = (props) => {
   const role = props.role;
-  if (props.authWorkflow === 'NONE') {
-    navigationItems.push(
-        {id: 3, name: 'Zookeeper Browser', link: '/zookeeper', icon: 
<ZookeeperIcon /> }
-    );
-  } else if(role === 'ADMIN'){
-    if(navigationItems.length <5){
-      navigationItems = [
-        ...navigationItems,
-        {id: 3, name: 'Zookeeper Browser', link: '/zookeeper', icon: 
<ZookeeperIcon /> },
-        {id: 5, name: "User Console", link: '/user', icon: 
<AccountCircleOutlinedIcon style={{ width: 24, height: 24, verticalAlign: 'sub' 
}}/>}
-      ]
+  const navigationItems = React.useMemo(() => {
+    // IMPORTANT: do not mutate a module-level array here. Layout can 
re-render many times,
+    // and mutation would duplicate entries in the sidebar.
+    if (props.authWorkflow === 'NONE') {
+      return [
+        ...BASE_NAVIGATION_ITEMS,
+        { id: 3, name: 'Zookeeper Browser', link: '/zookeeper', icon: 
<ZookeeperIcon /> }
+      ];
     }
-  }
+    if (role === 'ADMIN') {
+      return [
+        ...BASE_NAVIGATION_ITEMS,
+        { id: 3, name: 'Zookeeper Browser', link: '/zookeeper', icon: 
<ZookeeperIcon /> },
+        { id: 5, name: 'User Console', link: '/user',
+          icon: <AccountCircleOutlinedIcon style={{ width: 24, height: 24, 
verticalAlign: 'sub' }} /> }
+      ];
+    }
+    return BASE_NAVIGATION_ITEMS;
+  }, [props.authWorkflow, role]);
+
   const hash = `/${window.location.hash.split('/')[1]}`;
-  const routeObj = navigationItems.find((obj)=>{ return obj.link === hash;});
+  const routeObj = navigationItems.find((obj) => obj.link === hash);
 
   const [selectedId, setSelectedId] = React.useState(routeObj?.id || 1);
   const sidebarOpenState = !(localStorage.getItem('pinot_ui:sidebarState') === 
'false');
@@ -62,7 +69,7 @@ const Layout = (props) => {
     }
     if (app_state.hideQueryConsoleTab) {
       return navigationItems.filter((navItem) => navItem.link !== '/query');
-  }
+    }
 
     return navigationItems;
   }, [navigationItems, app_state.queryConsoleOnlyView, 
app_state.hideQueryConsoleTab]);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to