feat: remove OAuth clients link from settings navigation (#25206)

* feat: remove OAuth clients link from settings navigation

Co-Authored-By: peer@cal.com <peer@cal.com>

* fix: resolve infinite loop in sidebar close effect

Use functional state update to avoid reading sideContainerOpen in dependency array, which was causing infinite re-renders when navigating between settings pages.

Co-Authored-By: peer@cal.com <peer@cal.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2025-11-19 11:09:07 +00:00
1 parent 26e8582302
commit 075d209b6f
1 file changed
+22 -18
@@ -2,6 +2,7 @@
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
import { useSession } from "next-auth/react";
import Image from "next/image";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import type { ComponentProps } from "react";
@@ -101,8 +102,6 @@ const getTabs = (orgBranding: OrganizationBranding | null) => {
name: "privacy_and_security",
href: "/settings/organizations/privacy",
},
{ name: "OAuth Clients", href: "/settings/organizations/platform/oauth-clients" },
{
name: "SSO",
href: "/settings/organizations/sso",
@@ -173,7 +172,6 @@ const organizationRequiredKeys = ["organization"];
const organizationAdminKeys = [
"privacy",
"privacy_and_security",
"OAuth Clients",
"SSO",
"directory_sync",
"delegation_credential",
@@ -358,11 +356,12 @@ const TeamListCollapsible = ({ teamFeatures }: { teamFeatures?: Record<number, T
const [teamMenuState, setTeamMenuState] =
useState<{ teamId: number | undefined; teamMenuOpen: boolean }[]>();
const searchParams = useCompatSearchParams();
const searchParamsId = searchParams?.get("id");
useEffect(() => {
if (teams) {
const teamStates = teams?.map((team) => ({
teamId: team.id,
teamMenuOpen: String(team.id) === searchParams?.get("id"),
teamMenuOpen: String(team.id) === searchParamsId,
}));
setTeamMenuState(teamStates);
setTimeout(() => {
@@ -372,7 +371,7 @@ const TeamListCollapsible = ({ teamFeatures }: { teamFeatures?: Record<number, T
tabMembers?.scrollIntoView({ behavior: "smooth" });
}, 100);
}
}, [searchParams?.get("id"), teams]);
}, [searchParamsId, teams]);
return (
<>
@@ -431,9 +430,11 @@ const TeamListCollapsible = ({ teamFeatures }: { teamFeatures?: Record<number, T
)}
</div>
{!team.parentId && (
<img
<Image
src={getPlaceholderAvatar(team.logoUrl, team.name)}
className="h-[16px] w-[16px] self-start rounded-full stroke-[2px] ltr:mr-2 rtl:ml-2 md:mt-0"
width={16}
height={16}
className="self-start rounded-full stroke-[2px] ltr:mr-2 rtl:ml-2 md:mt-0"
alt={team.name || "Team logo"}
/>
)}
@@ -554,12 +555,13 @@ const SettingsSidebarContainer = ({
enabled: !!session.data?.user?.org,
});
const searchParamsId = searchParams?.get("id");
// Same as above but for otherTeams
useEffect(() => {
if (otherTeams) {
const otherTeamStates = otherTeams?.map((team) => ({
teamId: team.id,
teamMenuOpen: String(team.id) === searchParams?.get("id"),
teamMenuOpen: String(team.id) === searchParamsId,
}));
setOtherTeamMenuState(otherTeamStates);
setTimeout(() => {
@@ -570,7 +572,7 @@ const SettingsSidebarContainer = ({
tabMembers?.scrollIntoView({ behavior: "smooth" });
}, 100);
}
}, [searchParams?.get("id"), otherTeams]);
}, [searchParamsId, otherTeams]);
return (
<nav
@@ -599,8 +601,10 @@ const SettingsSidebarContainer = ({
/>
)}
{!tab.icon && tab?.avatar && (
<img
className="h-4 w-4 rounded-full ltr:mr-3 rtl:ml-3"
<Image
width={16}
height={16}
className="rounded-full ltr:mr-3 rtl:ml-3"
src={tab?.avatar}
alt="Organization Logo"
/>
@@ -750,9 +754,11 @@ const SettingsSidebarContainer = ({
)}
</div>
{!otherTeam.parentId && (
<img
<Image
src={getPlaceholderAvatar(otherTeam.logoUrl, otherTeam.name)}
className="h-[16px] w-[16px] self-start rounded-full stroke-[2px] ltr:mr-2 rtl:ml-2 md:mt-0"
width={16}
height={16}
className="self-start rounded-full stroke-[2px] ltr:mr-2 rtl:ml-2 md:mt-0"
alt={otherTeam.name || "Team logo"}
/>
)}
@@ -852,13 +858,11 @@ export default function SettingsLayoutAppDirClient({
return () => {
window.removeEventListener("resize", closeSideContainer);
};
}, []);
}, [setSideContainerOpen]);
useEffect(() => {
if (sideContainerOpen) {
setSideContainerOpen(!sideContainerOpen);
}
}, [pathname]);
setSideContainerOpen((prev) => (prev ? false : prev));
}, [pathname, setSideContainerOpen]);
return (
<Shell