refactor: replace checkAdminOrOwner with team.create permission in teams page (#24116)

* refactor: replace checkAdminOrOwner with team.create permission in teams page

- Replace role-based access control with PBAC using team.create permission
- Follow PBAC refactoring guide pattern for server-side permission checking
- Use permission-specific variable naming (canCreateTeam)
- Maintain existing TeamsListing component interface

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: implement permissions object pattern for TeamsListing

- Replace isOrgAdmin prop with permissions object containing canCreateTeam
- Follow PBAC refactoring guide pattern for UI components
- Update TeamsListing component to use permissions.canCreateTeam
- Maintain existing functionality while improving code structure
- All type checking and linting verification passed

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* use different method

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
This commit is contained in:
Eunjae Lee
2025-09-29 14:11:42 +05:30
committed by GitHub
co-authored by Devin AI sean-brydon
parent f6b061f787
commit 752f7fcde0
2 changed files with 22 additions and 11 deletions
@@ -2,11 +2,12 @@ import type { SearchParams } from "app/_types";
import type { Session } from "next-auth";
import { unstable_cache } from "next/cache";
import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
import { TeamsListing } from "@calcom/features/ee/teams/components/TeamsListing";
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
import { TeamRepository } from "@calcom/lib/server/repository/team";
import { TeamService } from "@calcom/lib/server/service/teamService";
import prisma from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/enums";
import { TRPCError } from "@trpc/server";
@@ -49,21 +50,29 @@ export const ServerTeamsListing = async ({
const teams = await getCachedTeams(userId);
const userProfile = session?.user?.profile;
const orgId = userProfile?.organizationId ?? session?.user.org?.id;
const orgRole =
session?.user?.org?.role ??
userProfile?.organization?.members.find((m: { userId: number }) => m.userId === userId)?.role;
const isOrgAdminOrOwner = checkAdminOrOwner(orgRole);
const permissionCheckService = new PermissionCheckService();
const canCreateTeam = orgId
? await permissionCheckService.checkPermission({
userId: session.user.id,
teamId: orgId,
permission: "team.create",
fallbackRoles: [MembershipRole.ADMIN, MembershipRole.OWNER],
})
: false;
return {
Main: (
<TeamsListing
teams={teams}
orgId={orgId ?? null}
isOrgAdmin={isOrgAdminOrOwner}
permissions={{
canCreateTeam: canCreateTeam,
}}
teamNameFromInvite={teamNameFromInvite ?? null}
errorMsgFromInvite={errorMsgFromInvite}
/>
),
CTA: !orgId || isOrgAdminOrOwner ? <TeamsCTA /> : null,
CTA: !orgId || canCreateTeam ? <TeamsCTA /> : null,
};
};
@@ -19,7 +19,9 @@ import TeamList from "./TeamList";
type TeamsListingProps = {
orgId: number | null;
isOrgAdmin: boolean;
permissions: {
canCreateTeam: boolean;
};
teams: RouterOutputs["viewer"]["teams"]["list"];
teamNameFromInvite: string | null;
errorMsgFromInvite: string | null;
@@ -27,7 +29,7 @@ type TeamsListingProps = {
export function TeamsListing({
orgId,
isOrgAdmin,
permissions,
teams: data,
teamNameFromInvite,
errorMsgFromInvite,
@@ -51,7 +53,7 @@ export function TeamsListing({
}
);
const isCreateTeamButtonDisabled = !!(orgId && !isOrgAdmin);
const isCreateTeamButtonDisabled = !!(orgId && !permissions.canCreateTeam);
const features = [
{
@@ -128,7 +130,7 @@ export function TeamsListing({
features={features}
background="/tips/teams"
buttons={
!orgId || isOrgAdmin ? (
!orgId || permissions.canCreateTeam ? (
<div className="space-y-2 rtl:space-x-reverse sm:space-x-2">
<ButtonGroup>
<Button color="primary" href={`${WEBAPP_URL}/settings/teams/new`}>