Repository: All-Hands-AI/OpenHands
File: frontend/src/routes/settings.tsx (TypeScript)

Code before:
  }

  if (!isSaas && SAAS_ONLY_PATHS.includes(pathname)) {
    // if in OSS mode, do not allow access to saas-only paths
    return redirect("/settings");
  }

  return null;
};

function SettingsScreen() {
  const { t } = useTranslation();
  const { data: config } = useConfig();
  const { data: subscriptionAccess } = useSubscriptionAccess();

  const isSaas = config?.APP_MODE === "saas";
  // this is used to determine which settings are available in the UI
  const navItems = [];
  if (isSaas) {
    if (subscriptionAccess) {
      navItems.push({ to: "/settings", text: "SETTINGS$NAV_LLM" });
    }
    navItems.push(...SAAS_NAV_ITEMS);
  } else {
    navItems.push(...OSS_NAV_ITEMS);
  }

  return (
    <main
      data-testid="settings-screen"
      className="bg-base-secondary border border-tertiary h-full rounded-xl flex flex-col"
    >
      <header className="px-3 py-1.5 border-b border-b-tertiary flex items-center gap-2">
        <SettingsIcon width={16} height={16} />
        <h1 className="text-sm leading-6">{t(I18nKey.SETTINGS$TITLE)}</h1>
      </header>

      <nav
        data-testid="settings-navbar"
        className="flex items-end gap-6 px-9 border-b border-tertiary"
      >
        {navItems.map(({ to, text }) => (
          <NavLink
            end
            key={to}
            to={to}
            className={({ isActive }) =>
              cn(
                "border-b-2 border-transparent py-2.5 px-4 min-w-[40px] flex items-center justify-center",
                isActive && "border-primary",
              )

Review comment (suggestion):
```suggestion
const navItems = useMemo(() => {
  const items = [];
  if (config?.APP_MODE === "saas") {
    if (subscriptionAccess) items.push({ to: "/settings", text: "SETTINGS$NAV_LLM" });
    items.push(...SAAS_NAV_ITEMS);
  } else {
    items.push(...OSS_NAV_ITEMS);
  }
  return items;
}, [config?.APP_MODE, subscriptionAccess]);
```

Right now `navItems` is recomputed on every render. For small lists it's fine, but prefer `useMemo` to avoid accidental re-computation if logic grows

Code after:
    return redirect("/settings/user");
  }

  if (!isSaas && SAAS_ONLY_PATHS.includes(pathname)) {
    // if in OSS mode, do not allow access to saas-only paths
    return redirect("/settings");
  }

  return null;
};

function SettingsScreen() {
  const { t } = useTranslation();
  const { data: config } = useConfig();
  const { data: subscriptionAccess } = useSubscriptionAccess();

  const isSaas = config?.APP_MODE === "saas";
  // this is used to determine which settings are available in the UI
  const navItems = useMemo(() => {
    const items = [];
    if (isSaas) {
      if (subscriptionAccess) {
        items.push({ to: "/settings", text: "SETTINGS$NAV_LLM" });
      }
      items.push(...SAAS_NAV_ITEMS);
    } else {
      items.push(...OSS_NAV_ITEMS);
    }
    return items;
  }, [isSaas, !!subscriptionAccess]);

  return (
    <main
      data-testid="settings-screen"
      className="bg-base-secondary border border-tertiary h-full rounded-xl flex flex-col"
    >
      <header className="px-3 py-1.5 border-b border-b-tertiary flex items-center gap-2">
        <SettingsIcon width={16} height={16} />
        <h1 className="text-sm leading-6">{t(I18nKey.SETTINGS$TITLE)}</h1>
      </header>

      <nav
        data-testid="settings-navbar"
        className="flex items-end gap-6 px-9 border-b border-tertiary"
      >
        {navItems.map(({ to, text }) => (
          <NavLink
            end
            key={to}
            to={to}
            className={({ isActive }) =>