From 4b2f89d6e867eb104136dd9a928cc593f5486a90 Mon Sep 17 00:00:00 2001
From: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Date: Thu, 31 Jul 2025 00:56:25 +0000
Subject: [PATCH] feat: enable PBAC checking on organization settings page
(#22467)
---
.../(org-admin-only)/attributes/page.tsx | 40 ++-
.../(org-admin-only)/dsync/page.tsx | 27 +-
.../(org-admin-only)/privacy/page.tsx | 35 ++-
.../(org-admin-only)/sso/page.tsx | 27 +-
.../organizations/general/page.tsx | 31 +-
.../organizations/profile/page.tsx | 44 ++-
apps/web/public/static/locales/en/common.json | 11 +
.../ee/dsync/page/team-dsync-view.tsx | 6 +-
...DisablePhoneOnlySMSNotificationsSwitch.tsx | 5 +-
.../pages/components/LockEventTypeSwitch.tsx | 11 +-
.../components/NoSlotsNotificationSwitch.tsx | 5 +-
.../attributes/attributes-list-view.tsx | 109 ++++---
.../organizations/pages/settings/general.tsx | 40 ++-
.../organizations/pages/settings/privacy.tsx | 6 +-
.../organizations/pages/settings/profile.tsx | 15 +-
.../features/ee/sso/page/orgs-sso-view.tsx | 16 +-
.../pbac/domain/mappers/PermissionMapper.ts | 6 +-
.../pbac/domain/types/permission-registry.ts | 30 ++
.../__tests__/resource-permissions.test.ts | 278 ++++++++++++++++++
.../features/pbac/lib/resource-permissions.ts | 86 ++++++
.../migration.sql | 22 ++
.../viewer/attributes/create.handler.ts | 40 ++-
.../viewer/attributes/delete.handler.ts | 39 +++
.../routers/viewer/attributes/edit.handler.ts | 39 +++
.../viewer/attributes/toggleActive.handler.ts | 39 +++
.../viewer/organizations/update.handler.ts | 34 ++-
26 files changed, 936 insertions(+), 105 deletions(-)
create mode 100644 packages/features/pbac/lib/__tests__/resource-permissions.test.ts
create mode 100644 packages/features/pbac/lib/resource-permissions.ts
create mode 100644 packages/prisma/migrations/20250714075355_add_attributes_pbac_permissions/migration.sql
diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/attributes/page.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/attributes/page.tsx
index 55cbeac844..cfa0900043 100644
--- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/attributes/page.tsx
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/attributes/page.tsx
@@ -1,7 +1,15 @@
import { _generateMetadata, getTranslate } from "app/_utils";
+import { headers, cookies } from "next/headers";
+import { redirect } from "next/navigation";
import OrgSettingsAttributesPage from "@calcom/ee/organizations/pages/settings/attributes/attributes-list-view";
+import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
+import { Resource } from "@calcom/features/pbac/domain/types/permission-registry";
+import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
+import { MembershipRole } from "@calcom/prisma/enums";
+
+import { buildLegacyRequest } from "@lib/buildLegacyCtx";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -14,10 +22,40 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
+ const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
+
+ if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
+ return redirect("/settings/profile");
+ }
+
+ const { canRead, canEdit, canDelete, canCreate } = await getResourcePermissions({
+ userId: session.user.id,
+ teamId: session.user.profile.organizationId,
+ resource: Resource.Attributes,
+ userRole: session.user.org.role,
+ fallbackRoles: {
+ read: {
+ roles: [MembershipRole.MEMBER, MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ update: {
+ roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ delete: {
+ roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ create: {
+ roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ },
+ });
+
+ if (!canRead) {
+ return redirect("/settings/profile");
+ }
return (
-
+
);
};
diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/dsync/page.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/dsync/page.tsx
index a53e48fd1e..161ce65275 100644
--- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/dsync/page.tsx
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/dsync/page.tsx
@@ -1,7 +1,15 @@
import { _generateMetadata, getTranslate } from "app/_utils";
+import { headers, cookies } from "next/headers";
+import { redirect } from "next/navigation";
+import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import DirectorySyncTeamView from "@calcom/features/ee/dsync/page/team-dsync-view";
+import { Resource } from "@calcom/features/pbac/domain/types/permission-registry";
+import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
+import { MembershipRole } from "@calcom/prisma/enums";
+
+import { buildLegacyRequest } from "@lib/buildLegacyCtx";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -14,10 +22,27 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
+ const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
+
+ if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
+ return redirect("/settings/organizations/general");
+ }
+
+ const { canEdit } = await getResourcePermissions({
+ userId: session.user.id,
+ teamId: session.user.profile.organizationId,
+ resource: Resource.Organization,
+ userRole: session.user.org.role,
+ fallbackRoles: {
+ update: {
+ roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ },
+ });
return (
-
+
);
};
diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/privacy/page.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/privacy/page.tsx
index 8900c77c70..7a4e75a2f4 100644
--- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/privacy/page.tsx
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/privacy/page.tsx
@@ -1,7 +1,15 @@
import { _generateMetadata, getTranslate } from "app/_utils";
+import { headers, cookies } from "next/headers";
+import { redirect } from "next/navigation";
+import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import PrivacyView from "@calcom/features/ee/organizations/pages/settings/privacy";
+import { Resource } from "@calcom/features/pbac/domain/types/permission-registry";
+import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
+import { MembershipRole } from "@calcom/prisma/enums";
+
+import { buildLegacyRequest } from "@lib/buildLegacyCtx";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -15,9 +23,34 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
+ const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
+
+ if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
+ return redirect("/settings/profile");
+ }
+
+ const { canRead, canEdit } = await getResourcePermissions({
+ userId: session.user.id,
+ teamId: session.user.profile.organizationId,
+ resource: Resource.Organization,
+ userRole: session.user.org.role,
+ fallbackRoles: {
+ read: {
+ roles: [MembershipRole.MEMBER, MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ update: {
+ roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ },
+ });
+
+ if (!canRead) {
+ return redirect("/settings/profile");
+ }
+
return (
-
+
);
};
diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/sso/page.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/sso/page.tsx
index 93750ac2fd..a3fcf936b1 100644
--- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/sso/page.tsx
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/sso/page.tsx
@@ -1,7 +1,15 @@
import { _generateMetadata, getTranslate } from "app/_utils";
+import { headers, cookies } from "next/headers";
+import { redirect } from "next/navigation";
+import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import OrgSSOView from "@calcom/features/ee/sso/page/orgs-sso-view";
+import { Resource } from "@calcom/features/pbac/domain/types/permission-registry";
+import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
+import { MembershipRole } from "@calcom/prisma/enums";
+
+import { buildLegacyRequest } from "@lib/buildLegacyCtx";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -14,10 +22,27 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
+ const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
+
+ if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
+ return redirect("/settings/organizations/general");
+ }
+
+ const { canEdit } = await getResourcePermissions({
+ userId: session.user.id,
+ teamId: session.user.profile.organizationId,
+ resource: Resource.Organization,
+ userRole: session.user.org.role,
+ fallbackRoles: {
+ update: {
+ roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ },
+ });
return (
-
+
);
};
diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/general/page.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/general/page.tsx
index 75aeaf2f6e..5ce47fad0b 100644
--- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/general/page.tsx
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/general/page.tsx
@@ -1,7 +1,15 @@
import { _generateMetadata, getTranslate } from "app/_utils";
+import { headers, cookies } from "next/headers";
+import { redirect } from "next/navigation";
+import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import LegacyPage from "@calcom/features/ee/organizations/pages/settings/general";
+import { Resource } from "@calcom/features/pbac/domain/types/permission-registry";
+import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
+import { MembershipRole } from "@calcom/prisma/enums";
+
+import { buildLegacyRequest } from "@lib/buildLegacyCtx";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -15,9 +23,30 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
+ const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
+
+ if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
+ return redirect("/settings/profile");
+ }
+
+ const { canRead, canEdit } = await getResourcePermissions({
+ userId: session.user.id,
+ teamId: session.user.profile.organizationId,
+ resource: Resource.Organization,
+ userRole: session.user.org.role,
+ fallbackRoles: {
+ read: {
+ roles: [MembershipRole.MEMBER, MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ update: {
+ roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ },
+ });
+
return (
-
+
);
};
diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/profile/page.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/profile/page.tsx
index 3849a83305..4b58650b45 100644
--- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/profile/page.tsx
+++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/profile/page.tsx
@@ -1,7 +1,16 @@
import { _generateMetadata, getTranslate } from "app/_utils";
+import { cookies, headers } from "next/headers";
+import { redirect } from "next/navigation";
+import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import LegacyPage from "@calcom/features/ee/organizations/pages/settings/profile";
+import { Resource } from "@calcom/features/pbac/domain/types/permission-registry";
+import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
+import type { Membership } from "@calcom/prisma/client";
+import { MembershipRole } from "@calcom/prisma/enums";
+
+import { buildLegacyRequest } from "@lib/buildLegacyCtx";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -13,14 +22,47 @@ export const generateMetadata = async () =>
);
const Page = async () => {
+ const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
const t = await getTranslate();
+ const orgRole = session?.user.profile?.organization.members?.find(
+ (member: Membership) => member.userId === session?.user.id
+ )?.role;
+
+ if (!session?.user.id || !session?.user.profile?.organizationId || !orgRole) {
+ return redirect("/settings/profile");
+ }
+
+ const { canRead, canEdit, canDelete } = await getResourcePermissions({
+ userId: session.user.id,
+ teamId: session?.user.profile?.organizationId,
+ resource: Resource.Organization,
+ userRole: orgRole,
+ fallbackRoles: {
+ read: {
+ roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ update: {
+ roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
+ },
+ delete: {
+ roles: [MembershipRole.OWNER],
+ },
+ },
+ });
+
return (
-
+
);
};
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index 11a55d364c..fe8f9ffdd5 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -871,6 +871,10 @@
"create_team": "Create Team",
"name": "Name",
"nameless_team": "Nameless Team",
+ "oauth_clients": "OAuth Clients",
+ "oauth_clients_description": "Manage OAuth clients for your organization",
+ "create_oauth_client": "Create OAuth Client",
+ "create_oauth_client_description": "Create a new OAuth client for third-party integrations",
"oauth_client_deletion_message": "OAuth client deleted successfully",
"create_new_team_description": "Create a new team to collaborate with users.",
"create_new_team": "Create a new team",
@@ -1836,6 +1840,8 @@
"edit_event_type": "Edit event type",
"only_admin_can_see_members_of_org": "This Organization is private, and only the organization's admin or owner can view its members.",
"only_admin_can_manage_sso_org": "Only the organization's admin or owner can manage SSO settings",
+ "only_admin_can_manage_directory_sync": "Only the organization's admin or owner can manage directory sync settings",
+ "only_admin_can_manage_oauth_clients": "Only the organization's admin or owner can manage OAuth clients",
"collective_scheduling": "Collective Scheduling",
"make_it_easy_to_book": "Make it easy to book your team when everyone is available.",
"find_the_best_person": "Find the best person available and cycle through your team.",
@@ -3302,6 +3308,11 @@
"error_creating_role": "Error creating role",
"error_updating_role": "Error updating role",
"pbac_desc_create_roles": "Create roles",
+ "pbac_resource_attributes": "Attributes",
+ "pbac_desc_view_organization_attributes": "View organization attributes",
+ "pbac_desc_update_organization_attributes": "Update organization attributes",
+ "pbac_desc_delete_organization_attributes": "Delete organization attributes",
+ "pbac_desc_create_organization_attributes": "Create organization attributes",
"pbac_desc_view_roles": "View roles",
"pbac_desc_update_roles": "Update roles",
"pbac_desc_delete_roles": "Delete roles",
diff --git a/packages/features/ee/dsync/page/team-dsync-view.tsx b/packages/features/ee/dsync/page/team-dsync-view.tsx
index c8a453ba2e..bf8202f3af 100644
--- a/packages/features/ee/dsync/page/team-dsync-view.tsx
+++ b/packages/features/ee/dsync/page/team-dsync-view.tsx
@@ -12,7 +12,7 @@ import { showToast } from "@calcom/ui/components/toast";
import ConfigureDirectorySync from "../components/ConfigureDirectorySync";
// For Hosted Cal - Team view
-const DirectorySync = () => {
+const DirectorySync = ({ permissions }: { permissions?: { canEdit: boolean } }) => {
const { t } = useLocale();
const router = useRouter();
@@ -36,6 +36,10 @@ const DirectorySync = () => {
showToast(error.message, "error");
}
+ if (!permissions?.canEdit) {
+ router.push("/404");
+ }
+
return (
{HOSTED_CAL_FEATURES && }
diff --git a/packages/features/ee/organizations/pages/components/DisablePhoneOnlySMSNotificationsSwitch.tsx b/packages/features/ee/organizations/pages/components/DisablePhoneOnlySMSNotificationsSwitch.tsx
index 9e499828e8..b1852f8685 100644
--- a/packages/features/ee/organizations/pages/components/DisablePhoneOnlySMSNotificationsSwitch.tsx
+++ b/packages/features/ee/organizations/pages/components/DisablePhoneOnlySMSNotificationsSwitch.tsx
@@ -10,10 +10,9 @@ import { showToast } from "@calcom/ui/components/toast";
interface GeneralViewProps {
currentOrg: RouterOutputs["viewer"]["organizations"]["listCurrent"];
- isAdminOrOwner: boolean;
}
-export const DisablePhoneOnlySMSNotificationsSwitch = ({ currentOrg, isAdminOrOwner }: GeneralViewProps) => {
+export const DisablePhoneOnlySMSNotificationsSwitch = ({ currentOrg }: GeneralViewProps) => {
const { t } = useLocale();
const utils = trpc.useUtils();
const [disablePhoneOnlySMSNotificationsActive, setDisablePhoneOnlySMSNotificationsActive] = useState(
@@ -32,8 +31,6 @@ export const DisablePhoneOnlySMSNotificationsSwitch = ({ currentOrg, isAdminOrOw
},
});
- if (!isAdminOrOwner) return null;
-
return (
<>
{
+export const LockEventTypeSwitch = ({ currentOrg }: GeneralViewProps) => {
const [lockEventTypeCreationForUsers, setLockEventTypeCreationForUsers] = useState(
!!currentOrg.organizationSettings.lockEventTypeCreationForUsers
);
@@ -50,8 +49,6 @@ export const LockEventTypeSwitch = ({ currentOrg, isAdminOrOwner }: GeneralViewP
},
});
- if (!isAdminOrOwner) return null;
-
const currentLockedOption = formMethods.watch("currentEventTypeOptions");
const { reset, getValues } = formMethods;
@@ -69,7 +66,7 @@ export const LockEventTypeSwitch = ({ currentOrg, isAdminOrOwner }: GeneralViewP
{
@@ -124,9 +121,7 @@ export const LockEventTypeSwitch = ({ currentOrg, isAdminOrOwner }: GeneralViewP
-
+
diff --git a/packages/features/ee/organizations/pages/components/NoSlotsNotificationSwitch.tsx b/packages/features/ee/organizations/pages/components/NoSlotsNotificationSwitch.tsx
index 4494251490..4d714409df 100644
--- a/packages/features/ee/organizations/pages/components/NoSlotsNotificationSwitch.tsx
+++ b/packages/features/ee/organizations/pages/components/NoSlotsNotificationSwitch.tsx
@@ -8,10 +8,9 @@ import { showToast } from "@calcom/ui/components/toast";
interface GeneralViewProps {
currentOrg: RouterOutputs["viewer"]["organizations"]["listCurrent"];
- isAdminOrOwner: boolean;
}
-export const NoSlotsNotificationSwitch = ({ currentOrg, isAdminOrOwner }: GeneralViewProps) => {
+export const NoSlotsNotificationSwitch = ({ currentOrg }: GeneralViewProps) => {
const { t } = useLocale();
const utils = trpc.useUtils();
const [notificationActive, setNotificationActive] = useState(
@@ -30,8 +29,6 @@ export const NoSlotsNotificationSwitch = ({ currentOrg, isAdminOrOwner }: Genera
},
});
- if (!isAdminOrOwner) return null;
-
return (
<>
>;
+ permissions: { canEdit: boolean; canDelete: boolean };
}) {
const { t } = useLocale();
const [isEnabled, setIsEnabled] = useState(attribute.enabled);
@@ -87,44 +89,54 @@ function AttributeItem({
-
-
-
-
-
-
-
- }
+ {(permissions.canEdit || permissions.canDelete) && (
+
+
+
-
- setAttributeToDelete(attribute)}>
- {t("delete")}
-
-
-
-
+ variant="icon"
+ color="secondary"
+ StartIcon="ellipsis"
+ className="ltr:radix-state-open:rounded-r-md rtl:radix-state-open:rounded-l-md"
+ />
+
+
+ {permissions.canEdit && (
+
+
+ {t("edit")}
+
+
+ )}
+ {permissions.canDelete && (
+
+ setAttributeToDelete(attribute)}>
+ {t("delete")}
+
+
+ )}
+
+
+ )}
);
}
-function OrganizationAttributesPage() {
+function OrganizationAttributesPage({
+ permissions,
+}: {
+ permissions: { canEdit: boolean; canDelete: boolean; canCreate: boolean };
+}) {
const { t } = useLocale();
const { data, isLoading } = trpc.viewer.attributes.list.useQuery();
const [attributeToDelete, setAttributeToDelete] = useState();
@@ -149,17 +161,20 @@ function OrganizationAttributesPage() {
))}
-
+ {permissions.canCreate && (
+
+ )}
>
) : (
@@ -173,13 +188,15 @@ function OrganizationAttributesPage() {
{t("add_attributes_description")}
-
+ {permissions.canCreate && (
+
+ )}
)}
diff --git a/packages/features/ee/organizations/pages/settings/general.tsx b/packages/features/ee/organizations/pages/settings/general.tsx
index b4862548c6..98141bab96 100644
--- a/packages/features/ee/organizations/pages/settings/general.tsx
+++ b/packages/features/ee/organizations/pages/settings/general.tsx
@@ -5,7 +5,6 @@ import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
-import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
import { TimezoneSelect } from "@calcom/features/components/timezone-select";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import SectionBottomActions from "@calcom/features/settings/SectionBottomActions";
@@ -42,22 +41,31 @@ const SkeletonLoader = () => {
interface GeneralViewProps {
currentOrg: RouterOutputs["viewer"]["organizations"]["listCurrent"];
- isAdminOrOwner: boolean;
localeProp: string;
+
+ permissions: {
+ canRead: boolean;
+ canEdit: boolean;
+ };
}
-const OrgGeneralView = () => {
+const OrgGeneralView = ({
+ permissions,
+}: {
+ permissions: {
+ canRead: boolean;
+ canEdit: boolean;
+ };
+}) => {
const { t } = useLocale();
const router = useRouter();
const session = useSession();
- const isAdminOrOwner = checkAdminOrOwner(session.data?.user?.org?.role);
const {
data: currentOrg,
isPending,
error,
} = trpc.viewer.organizations.listCurrent.useQuery(undefined, {});
- const { data: user } = trpc.viewer.me.get.useQuery();
useEffect(
function refactorMeWithoutEffect() {
@@ -77,18 +85,22 @@ const OrgGeneralView = () => {
-
-
-
+ {permissions.canEdit && (
+ <>
+
+
+
+ >
+ )}
);
};
-const GeneralView = ({ currentOrg, isAdminOrOwner, localeProp }: GeneralViewProps) => {
+const GeneralView = ({ currentOrg, permissions, localeProp }: GeneralViewProps) => {
const { t } = useLocale();
const mutation = trpc.viewer.organizations.update.useMutation({
@@ -136,7 +148,7 @@ const GeneralView = ({ currentOrg, isAdminOrOwner, localeProp }: GeneralViewProp
reset,
getValues,
} = formMethods;
- const isDisabled = isSubmitting || !isDirty || !isAdminOrOwner;
+ const isDisabled = isSubmitting || !isDirty || !permissions.canEdit;
return (