From a4aced9a88d0dfdbdf365407505f02f788144ac3 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Sun, 8 Feb 2026 23:49:34 +0530 Subject: [PATCH] feat: refactor organization membership logic and simplify admin role checks --- frontend/src/app/agents/[agentId]/page.tsx | 19 ++--------- frontend/src/app/agents/new/page.tsx | 19 ++--------- frontend/src/app/agents/page.tsx | 19 ++--------- .../src/app/boards/[boardId]/edit/page.tsx | 19 ++--------- frontend/src/app/boards/new/page.tsx | 19 ++--------- frontend/src/app/boards/page.tsx | 19 ++--------- .../app/gateways/[gatewayId]/edit/page.tsx | 19 ++--------- .../src/app/gateways/[gatewayId]/page.tsx | 19 ++--------- frontend/src/app/gateways/new/page.tsx | 19 ++--------- frontend/src/app/gateways/page.tsx | 19 ++--------- .../components/organisms/DashboardSidebar.tsx | 19 ++--------- .../src/lib/use-organization-membership.ts | 33 +++++++++++++++++++ 12 files changed, 55 insertions(+), 187 deletions(-) create mode 100644 frontend/src/lib/use-organization-membership.ts diff --git a/frontend/src/app/agents/[agentId]/page.tsx b/frontend/src/app/agents/[agentId]/page.tsx index ca9dfe81..8ddfe119 100644 --- a/frontend/src/app/agents/[agentId]/page.tsx +++ b/frontend/src/app/agents/[agentId]/page.tsx @@ -22,10 +22,7 @@ import { type listBoardsApiV1BoardsGetResponse, useListBoardsApiV1BoardsGet, } from "@/api/generated/boards/boards"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import type { ActivityEventRead, AgentRead, @@ -84,19 +81,7 @@ export default function AgentDetailPage() { const agentIdParam = params?.agentId; const agentId = Array.isArray(agentIdParam) ? agentIdParam[0] : agentIdParam; - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const [deleteOpen, setDeleteOpen] = useState(false); const [deleteError, setDeleteError] = useState(null); diff --git a/frontend/src/app/agents/new/page.tsx b/frontend/src/app/agents/new/page.tsx index ddfd3fb8..65375dd9 100644 --- a/frontend/src/app/agents/new/page.tsx +++ b/frontend/src/app/agents/new/page.tsx @@ -13,10 +13,7 @@ import { useListBoardsApiV1BoardsGet, } from "@/api/generated/boards/boards"; import { useCreateAgentApiV1AgentsPost } from "@/api/generated/agents/agents"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import type { BoardRead } from "@/api/generated/model"; import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice"; import { SignedOutPanel } from "@/components/auth/SignedOutPanel"; @@ -86,19 +83,7 @@ export default function NewAgentPage() { const router = useRouter(); const { isSignedIn } = useAuth(); - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const [name, setName] = useState(""); const [boardId, setBoardId] = useState(""); diff --git a/frontend/src/app/agents/page.tsx b/frontend/src/app/agents/page.tsx index bd7ada32..f54363bf 100644 --- a/frontend/src/app/agents/page.tsx +++ b/frontend/src/app/agents/page.tsx @@ -42,10 +42,7 @@ import { getListBoardsApiV1BoardsGetQueryKey, useListBoardsApiV1BoardsGet, } from "@/api/generated/boards/boards"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import type { AgentRead } from "@/api/generated/model"; import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice"; import { SignedOutPanel } from "@/components/auth/SignedOutPanel"; @@ -94,19 +91,7 @@ export default function AgentsPage() { const queryClient = useQueryClient(); const router = useRouter(); - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const [sorting, setSorting] = useState([ { id: "name", desc: false }, diff --git a/frontend/src/app/boards/[boardId]/edit/page.tsx b/frontend/src/app/boards/[boardId]/edit/page.tsx index 7468f42f..ad87618a 100644 --- a/frontend/src/app/boards/[boardId]/edit/page.tsx +++ b/frontend/src/app/boards/[boardId]/edit/page.tsx @@ -22,10 +22,7 @@ import { type listGatewaysApiV1GatewaysGetResponse, useListGatewaysApiV1GatewaysGet, } from "@/api/generated/gateways/gateways"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import type { BoardGroupRead, BoardRead, @@ -65,19 +62,7 @@ export default function EditBoardPage() { const boardIdParam = params?.boardId; const boardId = Array.isArray(boardIdParam) ? boardIdParam[0] : boardIdParam; - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const mainRef = useRef(null); diff --git a/frontend/src/app/boards/new/page.tsx b/frontend/src/app/boards/new/page.tsx index 41e74831..d7fbfd8f 100644 --- a/frontend/src/app/boards/new/page.tsx +++ b/frontend/src/app/boards/new/page.tsx @@ -18,10 +18,7 @@ import { type listGatewaysApiV1GatewaysGetResponse, useListGatewaysApiV1GatewaysGet, } from "@/api/generated/gateways/gateways"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import type { BoardGroupRead } from "@/api/generated/model"; import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice"; import { SignedOutPanel } from "@/components/auth/SignedOutPanel"; @@ -42,19 +39,7 @@ export default function NewBoardPage() { const router = useRouter(); const { isSignedIn } = useAuth(); - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const [name, setName] = useState(""); const [gatewayId, setGatewayId] = useState(""); diff --git a/frontend/src/app/boards/page.tsx b/frontend/src/app/boards/page.tsx index 011a2c7a..b8cf9373 100644 --- a/frontend/src/app/boards/page.tsx +++ b/frontend/src/app/boards/page.tsx @@ -25,10 +25,7 @@ import { type listBoardGroupsApiV1BoardGroupsGetResponse, useListBoardGroupsApiV1BoardGroupsGet, } from "@/api/generated/board-groups/board-groups"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import type { BoardGroupRead, BoardRead } from "@/api/generated/model"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardShell } from "@/components/templates/DashboardShell"; @@ -62,19 +59,7 @@ export default function BoardsPage() { const { isSignedIn } = useAuth(); const queryClient = useQueryClient(); - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const [deleteTarget, setDeleteTarget] = useState(null); const boardsKey = getListBoardsApiV1BoardsGetQueryKey(); diff --git a/frontend/src/app/gateways/[gatewayId]/edit/page.tsx b/frontend/src/app/gateways/[gatewayId]/edit/page.tsx index bb51515c..850a42fe 100644 --- a/frontend/src/app/gateways/[gatewayId]/edit/page.tsx +++ b/frontend/src/app/gateways/[gatewayId]/edit/page.tsx @@ -13,10 +13,7 @@ import { useGetGatewayApiV1GatewaysGatewayIdGet, useUpdateGatewayApiV1GatewaysGatewayIdPatch, } from "@/api/generated/gateways/gateways"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import type { GatewayUpdate } from "@/api/generated/model"; import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice"; import { SignedOutPanel } from "@/components/auth/SignedOutPanel"; @@ -40,19 +37,7 @@ export default function EditGatewayPage() { ? gatewayIdParam[0] : gatewayIdParam; - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const [name, setName] = useState(undefined); const [gatewayUrl, setGatewayUrl] = useState(undefined); diff --git a/frontend/src/app/gateways/[gatewayId]/page.tsx b/frontend/src/app/gateways/[gatewayId]/page.tsx index e702ab65..c74e23e9 100644 --- a/frontend/src/app/gateways/[gatewayId]/page.tsx +++ b/frontend/src/app/gateways/[gatewayId]/page.tsx @@ -18,10 +18,7 @@ import { type listAgentsApiV1AgentsGetResponse, useListAgentsApiV1AgentsGet, } from "@/api/generated/agents/agents"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice"; import { SignedOutPanel } from "@/components/auth/SignedOutPanel"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; @@ -55,19 +52,7 @@ export default function GatewayDetailPage() { ? gatewayIdParam[0] : gatewayIdParam; - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const gatewayQuery = useGetGatewayApiV1GatewaysGatewayIdGet< getGatewayApiV1GatewaysGatewayIdGetResponse, diff --git a/frontend/src/app/gateways/new/page.tsx b/frontend/src/app/gateways/new/page.tsx index c1326488..e264d537 100644 --- a/frontend/src/app/gateways/new/page.tsx +++ b/frontend/src/app/gateways/new/page.tsx @@ -9,10 +9,7 @@ import { SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { ApiError } from "@/api/mutator"; import { useCreateGatewayApiV1GatewaysPost } from "@/api/generated/gateways/gateways"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice"; import { SignedOutPanel } from "@/components/auth/SignedOutPanel"; import { GatewayForm } from "@/components/gateways/GatewayForm"; @@ -30,19 +27,7 @@ export default function NewGatewayPage() { const { isSignedIn } = useAuth(); const router = useRouter(); - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const [name, setName] = useState(""); const [gatewayUrl, setGatewayUrl] = useState(""); diff --git a/frontend/src/app/gateways/page.tsx b/frontend/src/app/gateways/page.tsx index 6ea11c6a..0c340f9b 100644 --- a/frontend/src/app/gateways/page.tsx +++ b/frontend/src/app/gateways/page.tsx @@ -35,10 +35,7 @@ import { useDeleteGatewayApiV1GatewaysGatewayIdDelete, useListGatewaysApiV1GatewaysGet, } from "@/api/generated/gateways/gateways"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import type { GatewayRead } from "@/api/generated/model"; import { AdminOnlyNotice } from "@/components/auth/AdminOnlyNotice"; import { SignedOutPanel } from "@/components/auth/SignedOutPanel"; @@ -65,19 +62,7 @@ export default function GatewaysPage() { const { isSignedIn } = useAuth(); const queryClient = useQueryClient(); - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const [sorting, setSorting] = useState([ { id: "name", desc: false }, ]); diff --git a/frontend/src/components/organisms/DashboardSidebar.tsx b/frontend/src/components/organisms/DashboardSidebar.tsx index 428980ea..d56b54ab 100644 --- a/frontend/src/components/organisms/DashboardSidebar.tsx +++ b/frontend/src/components/organisms/DashboardSidebar.tsx @@ -15,10 +15,7 @@ import { import { useAuth } from "@/auth/clerk"; import { ApiError } from "@/api/mutator"; -import { - type getMyMembershipApiV1OrganizationsMeMemberGetResponse, - useGetMyMembershipApiV1OrganizationsMeMemberGet, -} from "@/api/generated/organizations/organizations"; +import { useOrganizationMembership } from "@/lib/use-organization-membership"; import { type healthzHealthzGetResponse, useHealthzHealthzGet, @@ -28,19 +25,7 @@ import { cn } from "@/lib/utils"; export function DashboardSidebar() { const pathname = usePathname(); const { isSignedIn } = useAuth(); - const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< - getMyMembershipApiV1OrganizationsMeMemberGetResponse, - ApiError - >({ - query: { - enabled: Boolean(isSignedIn), - refetchOnMount: "always", - retry: false, - }, - }); - const member = - membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; - const isAdmin = member ? ["owner", "admin"].includes(member.role) : false; + const { isAdmin } = useOrganizationMembership(isSignedIn); const healthQuery = useHealthzHealthzGet( { query: { diff --git a/frontend/src/lib/use-organization-membership.ts b/frontend/src/lib/use-organization-membership.ts new file mode 100644 index 00000000..fe4d2d64 --- /dev/null +++ b/frontend/src/lib/use-organization-membership.ts @@ -0,0 +1,33 @@ +import { ApiError } from "@/api/mutator"; +import { + type getMyMembershipApiV1OrganizationsMeMemberGetResponse, + useGetMyMembershipApiV1OrganizationsMeMemberGet, +} from "@/api/generated/organizations/organizations"; + +export const isOrganizationAdminRole = ( + role: string | null | undefined, +): boolean => role === "owner" || role === "admin"; + +export function useOrganizationMembership( + isSignedIn: boolean | null | undefined, +) { + const membershipQuery = useGetMyMembershipApiV1OrganizationsMeMemberGet< + getMyMembershipApiV1OrganizationsMeMemberGetResponse, + ApiError + >({ + query: { + enabled: Boolean(isSignedIn), + refetchOnMount: "always", + retry: false, + }, + }); + + const member = + membershipQuery.data?.status === 200 ? membershipQuery.data.data : null; + + return { + membershipQuery, + member, + isAdmin: isOrganizationAdminRole(member?.role), + }; +}