/ frontend / src / app / navigations.js
navigations.js
  1  import { authRoles } from "app/auth/authRoles";
  2  import { usePlatformCapabilities } from "app/contexts/PlatformContext";
  3  import { useTranslation } from "react-i18next";
  4  
  5  // Default navigations without conditional items
  6  // Internal: the static nav tree. `nameKey`/`labelKey` are i18n keys; the
  7  // `name`/`label` fallbacks are used when i18n is not yet initialized or
  8  // a key is missing. `useNavigations()` below resolves the keys.
  9  export const defaultNavigations = [
 10    { name: "Home", nameKey: "nav.home", path: "/home", icon: "dashboard" },
 11    {
 12      name: "Library", nameKey: "nav.library",
 13      icon: "library_books",
 14      path: "/projects/library",
 15    },
 16    { label: "AIaaS", type: "label" },
 17    {
 18      name: "Projects", nameKey: "nav.projects",
 19      icon: "assignment",
 20      path: "/projects",
 21    },
 22    {
 23      name: "Teams", nameKey: "nav.teams",
 24      icon: "groups",
 25      path: "/teams",
 26    },
 27    {
 28      name: "Users", nameKey: "nav.users",
 29      icon: "person",
 30      path: "/users",
 31      auth: authRoles.admin,
 32    },
 33    { label: "AI", type: "label" },
 34    {
 35      name: "LLMs", nameKey: "nav.llms",
 36      icon: "psychology",
 37      path: "/llms",
 38    },
 39    {
 40      name: "Embeddings", nameKey: "nav.embeddings",
 41      icon: "psychology",
 42      path: "/embeddings",
 43    },
 44    {
 45      name: "Tools", nameKey: "nav.tools",
 46      icon: "build",
 47      path: "/projects/tools",
 48    },
 49    {
 50      name: "Classifiers", nameKey: "nav.classifiers",
 51      icon: "category",
 52      path: "/classifier",
 53    },
 54    {
 55      name: "Direct Access", nameKey: "nav.directAccess",
 56      icon: "api",
 57      path: "/direct",
 58    },
 59    {
 60      name: "Generators", nameKey: "nav.generators",
 61      icon: "memory",
 62      children: [
 63        { name: "Image Generators", nameKey: "nav.imageGenerators", iconText: "IG", path: "/generators/image" },
 64        { name: "Speech-to-Text",   nameKey: "nav.speechToText",    iconText: "ST", path: "/generators/speech2text" },
 65        { name: "GPU", nameKey: "nav.gpu", iconText: "GP", path: "/gpu", auth: authRoles.admin },
 66      ]
 67    },
 68    { label: "Admin", labelKey: "nav.adminLabel", type: "label", auth: authRoles.teamAdmin },
 69    {
 70      name: "Settings", nameKey: "nav.settings",
 71      icon: "settings",
 72      path: "/settings",
 73      auth: authRoles.admin,
 74    },
 75    {
 76      name: "Audit Log", nameKey: "nav.auditLog",
 77      icon: "history",
 78      path: "/audit",
 79      auth: authRoles.admin,
 80    },
 81    {
 82      name: "Cron Logs", nameKey: "nav.cronLogs",
 83      icon: "schedule",
 84      path: "/admin/cron-logs",
 85      auth: authRoles.admin,
 86    },
 87    {
 88      name: "Permissions", nameKey: "nav.permissionMatrix",
 89      icon: "lock",
 90      path: "/admin/permissions",
 91      auth: authRoles.teamAdmin,
 92    },
 93    { label: "Docs", labelKey: "nav.docsLabel", type: "label" },
 94    {
 95      name: "Swagger", nameKey: "nav.swagger",
 96      icon: "launch",
 97      type: "extLink",
 98      path: "/docs/"
 99    }
100  ];
101  
102  // Pure function to build navigation structure based on gpu capability
103  // This doesn't use any hooks
104  export const buildNavigations = (hasGpu, hasProxy) => {
105    const navigations = [...defaultNavigations.map(item => {
106      // Deep clone the Generators item so we can modify its children
107      if (item.name === "Generators") {
108        return { ...item, children: [...item.children] };
109      }
110      return item;
111    })];
112  
113    // The Image Gen and Audio Gen playgrounds are reachable from the
114    // Image Generators / Speech-to-Text pages themselves (Playground
115    // button), so they don't need dedicated nav entries.
116  
117    // Show AI Proxy after Tools when proxy is enabled
118    if (hasProxy) {
119      const toolsIndex = navigations.findIndex(item => item.path === "/projects/tools");
120      navigations.splice(toolsIndex + 1, 0, {
121        name: "AI Proxy", nameKey: "nav.aiProxy",
122        icon: "route",
123        path: "/proxy/keys",
124        auth: authRoles.admin,
125      });
126    }
127  
128    return navigations;
129  };
130  
131  // Walks the nav tree and swaps each `name`/`label` with its translated
132  // counterpart. Keeps other fields untouched so downstream renderers
133  // that read `item.icon`, `item.path`, etc. still work.
134  const translateNav = (items, t) =>
135    items.map((item) => {
136      const out = { ...item };
137      if (item.nameKey)  out.name  = t(item.nameKey,  item.name);
138      if (item.labelKey) out.label = t(item.labelKey, item.label);
139      if (item.children) out.children = translateNav(item.children, t);
140      return out;
141    });
142  
143  // Custom React hook that follows the rules of hooks
144  export const useNavigations = () => {
145    const { platformCapabilities } = usePlatformCapabilities();
146    const { t } = useTranslation();
147    const hasGpu = platformCapabilities?.gpu ?? false;
148    const hasProxy = platformCapabilities?.proxy ?? false;
149    return translateNav(buildNavigations(hasGpu, hasProxy), t);
150  };
151  
152  // For compatibility with direct imports (non-component files)
153  // This will not have dynamic GPU features but prevents errors
154  export const navigations = defaultNavigations;