From ba4e717b599e4a48e18815b1d3a1590e035ec235 Mon Sep 17 00:00:00 2001 From: Leo Giovanetti Date: Thu, 7 Sep 2023 21:21:04 -0300 Subject: [PATCH] chore: unify org data access (#11158) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Peer Richelsen Co-authored-by: Omar López --- apps/web/lib/app-providers.tsx | 15 +--------- .../features/auth/lib/getServerSession.ts | 2 +- .../features/auth/lib/next-auth-options.ts | 29 +++++++++++++++---- .../ee/organizations/context/provider.ts | 2 +- .../ee/organizations/pages/organization.tsx | 4 +-- .../settings/other-team-members-view.tsx | 2 +- .../ee/teams/components/AddNewTeamMembers.tsx | 2 +- .../ee/teams/pages/team-members-view.tsx | 2 +- .../settings/layouts/SettingsLayout.tsx | 4 +-- packages/features/shell/Shell.tsx | 12 ++++---- .../UserTable/ChangeUserRoleModal.tsx | 2 +- .../UserTable/DeleteMemberModal.tsx | 4 +-- .../UserTable/ImpersonationMemberModal.tsx | 2 +- .../UserTable/InviteMemberModal.tsx | 4 +-- packages/types/next-auth.d.ts | 15 +++++++++- 15 files changed, 59 insertions(+), 42 deletions(-) diff --git a/apps/web/lib/app-providers.tsx b/apps/web/lib/app-providers.tsx index 329f88070d..2c81796c72 100644 --- a/apps/web/lib/app-providers.tsx +++ b/apps/web/lib/app-providers.tsx @@ -14,7 +14,6 @@ import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/ import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic"; import { FeatureProvider } from "@calcom/features/flags/context/provider"; import { useFlags } from "@calcom/features/flags/hooks"; -import { trpc } from "@calcom/trpc/react"; import { MetaProvider } from "@calcom/ui"; import useIsBookingPage from "@lib/hooks/useIsBookingPage"; @@ -222,19 +221,7 @@ function FeatureFlagsProvider({ children }: { children: React.ReactNode }) { function useOrgBrandingValues() { const session = useSession(); - - const res = trpc.viewer.organizations.getBrand.useQuery(undefined, { - // Only fetch if we have a session to avoid flooding logs with errors - enabled: session.status === "authenticated", - }); - - if (res.status === "loading") { - return undefined; - } - - if (res.status === "error") return null; - - return res.data; + return session?.data?.user.org; } function OrgBrandProvider({ children }: { children: React.ReactNode }) { diff --git a/packages/features/auth/lib/getServerSession.ts b/packages/features/auth/lib/getServerSession.ts index a2a53e9117..029c9f5e8e 100644 --- a/packages/features/auth/lib/getServerSession.ts +++ b/packages/features/auth/lib/getServerSession.ts @@ -72,7 +72,7 @@ export async function getServerSession(options: { image: `${CAL_URL}/${user.username}/avatar.png`, impersonatedByUID: token.impersonatedByUID ?? undefined, belongsToActiveTeam: token.belongsToActiveTeam, - organizationId: token.organizationId, + org: token.org, locale: user.locale ?? undefined, }, }; diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts index 3c7ab80d80..b101ccfe74 100644 --- a/packages/features/auth/lib/next-auth-options.ts +++ b/packages/features/auth/lib/next-auth-options.ts @@ -8,6 +8,7 @@ import GoogleProvider from "next-auth/providers/google"; import checkLicense from "@calcom/features/ee/common/server/checkLicense"; import ImpersonationProvider from "@calcom/features/ee/impersonation/lib/ImpersonationProvider"; +import { getOrgFullDomain, subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains"; import { clientSecretVerifier, hostedCal, isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml"; import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError"; import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants"; @@ -402,7 +403,14 @@ export const AUTH_OPTIONS: AuthOptions = { username: true, name: true, email: true, - organizationId: true, + organization: { + select: { + id: true, + name: true, + slug: true, + metadata: true, + }, + }, role: true, locale: true, teams: { @@ -419,12 +427,23 @@ export const AUTH_OPTIONS: AuthOptions = { // Check if the existingUser has any active teams const belongsToActiveTeam = checkIfUserBelongsToActiveTeam(existingUser); - const { teams: _teams, ...existingUserWithoutTeamsField } = existingUser; + const { teams: _teams, organization, ...existingUserWithoutTeamsField } = existingUser; + + const parsedOrgMetadata = teamMetadataSchema.parse(organization?.metadata ?? {}); return { ...existingUserWithoutTeamsField, ...token, belongsToActiveTeam, + org: organization + ? { + id: organization.id, + name: organization.name, + slug: organization.slug ?? parsedOrgMetadata?.requestedSlug ?? "", + fullDomain: getOrgFullDomain(organization.slug ?? parsedOrgMetadata?.requestedSlug ?? ""), + domainSuffix: subdomainSuffix(), + } + : undefined, }; }; if (!user) { @@ -448,7 +467,7 @@ export const AUTH_OPTIONS: AuthOptions = { role: user.role, impersonatedByUID: user?.impersonatedByUID, belongsToActiveTeam: user?.belongsToActiveTeam, - organizationId: user?.organizationId, + org: user?.org, locale: user?.locale, }; } @@ -487,7 +506,7 @@ export const AUTH_OPTIONS: AuthOptions = { role: existingUser.role, impersonatedByUID: token.impersonatedByUID as number, belongsToActiveTeam: token?.belongsToActiveTeam as boolean, - organizationId: token?.organizationId, + org: token?.org, locale: existingUser.locale, }; } @@ -507,7 +526,7 @@ export const AUTH_OPTIONS: AuthOptions = { role: token.role as UserPermissionRole, impersonatedByUID: token.impersonatedByUID as number, belongsToActiveTeam: token?.belongsToActiveTeam as boolean, - organizationId: token?.organizationId, + org: token?.org, locale: token.locale, }, }; diff --git a/packages/features/ee/organizations/context/provider.ts b/packages/features/ee/organizations/context/provider.ts index bd09a395c8..2cee02224f 100644 --- a/packages/features/ee/organizations/context/provider.ts +++ b/packages/features/ee/organizations/context/provider.ts @@ -10,7 +10,7 @@ import type { teamMetadataSchema } from "@calcom/prisma/zod-utils"; */ export type OrganizationBranding = | ({ - logo?: string | null | undefined; + id: number; name?: string; slug: string; fullDomain: string; diff --git a/packages/features/ee/organizations/pages/organization.tsx b/packages/features/ee/organizations/pages/organization.tsx index 6f93a2610d..b9114d70da 100644 --- a/packages/features/ee/organizations/pages/organization.tsx +++ b/packages/features/ee/organizations/pages/organization.tsx @@ -16,7 +16,7 @@ export const getServerSideProps = async ({ req, res }: GetServerSidePropsContext // Check if logged in user has an organization assigned const session = await getServerSession({ req, res }); - if (!session?.user.organizationId) { + if (!session?.user.org?.id) { return { notFound: true, }; @@ -26,7 +26,7 @@ export const getServerSideProps = async ({ req, res }: GetServerSidePropsContext const membership = await prisma.membership.findFirst({ where: { userId: session?.user.id, - teamId: session?.user.organizationId, + teamId: session?.user.org.id, }, select: { role: true, diff --git a/packages/features/ee/organizations/pages/settings/other-team-members-view.tsx b/packages/features/ee/organizations/pages/settings/other-team-members-view.tsx index 4eb9139696..0e026e8127 100644 --- a/packages/features/ee/organizations/pages/settings/other-team-members-view.tsx +++ b/packages/features/ee/organizations/pages/settings/other-team-members-view.tsx @@ -69,7 +69,7 @@ const MembersView = () => { const [showMemberInvitationModal, setShowMemberInvitationModal] = useState(false); const [members, setMembers] = useState([]); const { data: currentOrg } = trpc.viewer.organizations.listCurrent.useQuery(undefined, { - enabled: !!session.data?.user?.organizationId, + enabled: !!session.data?.user?.org, }); const { data: team, isLoading: isTeamLoading } = trpc.viewer.organizations.getOtherTeam.useQuery( { teamId }, diff --git a/packages/features/ee/teams/components/AddNewTeamMembers.tsx b/packages/features/ee/teams/components/AddNewTeamMembers.tsx index 917d06e537..c1c7902fa5 100644 --- a/packages/features/ee/teams/components/AddNewTeamMembers.tsx +++ b/packages/features/ee/teams/components/AddNewTeamMembers.tsx @@ -215,7 +215,7 @@ const PendingMemberItem = (props: { member: TeamMember; index: number; teamId: n const session = useSession(); const bookerUrl = useBookerUrl(); const { data: currentOrg } = trpc.viewer.organizations.listCurrent.useQuery(undefined, { - enabled: !!session.data?.user?.organizationId, + enabled: !!session.data?.user?.org, }); const removeMemberMutation = trpc.viewer.teams.removeMember.useMutation({ async onSuccess() { diff --git a/packages/features/ee/teams/pages/team-members-view.tsx b/packages/features/ee/teams/pages/team-members-view.tsx index 12390b7443..d2871ea882 100644 --- a/packages/features/ee/teams/pages/team-members-view.tsx +++ b/packages/features/ee/teams/pages/team-members-view.tsx @@ -80,7 +80,7 @@ const MembersView = () => { const [showMemberInvitationModal, setShowMemberInvitationModal] = useState(showDialog); const [showInviteLinkSettingsModal, setInviteLinkSettingsModal] = useState(false); const { data: currentOrg } = trpc.viewer.organizations.listCurrent.useQuery(undefined, { - enabled: !!session.data?.user?.organizationId, + enabled: !!session.data?.user?.org, }); const { data: orgMembersNotInThisTeam, isLoading: isOrgListLoading } = diff --git a/packages/features/settings/layouts/SettingsLayout.tsx b/packages/features/settings/layouts/SettingsLayout.tsx index 1971d4e1f7..d032021772 100644 --- a/packages/features/settings/layouts/SettingsLayout.tsx +++ b/packages/features/settings/layouts/SettingsLayout.tsx @@ -159,7 +159,7 @@ const useTabs = () => { // check if name is in adminRequiredKeys return tabs.filter((tab) => { - if (organizationRequiredKeys.includes(tab.name)) return !!session.data?.user?.organizationId; + if (organizationRequiredKeys.includes(tab.name)) return !!session.data?.user?.org; if (isAdmin) return true; return !adminRequiredKeys.includes(tab.name); @@ -205,7 +205,7 @@ const SettingsSidebarContainer = ({ const { data: teams } = trpc.viewer.teams.list.useQuery(); const session = useSession(); const { data: currentOrg } = trpc.viewer.organizations.listCurrent.useQuery(undefined, { - enabled: !!session.data?.user?.organizationId, + enabled: !!session.data?.user?.org, }); const { data: otherTeams } = trpc.viewer.organizations.listOtherTeams.useQuery(); diff --git a/packages/features/shell/Shell.tsx b/packages/features/shell/Shell.tsx index 6a6b353749..004801f69e 100644 --- a/packages/features/shell/Shell.tsx +++ b/packages/features/shell/Shell.tsx @@ -22,7 +22,6 @@ import AdminPasswordBanner from "@calcom/features/users/components/AdminPassword import VerifyEmailBanner from "@calcom/features/users/components/VerifyEmailBanner"; import classNames from "@calcom/lib/classNames"; import { APP_NAME, DESKTOP_APP_LINK, JOIN_DISCORD, ROADMAP, WEBAPP_URL } from "@calcom/lib/constants"; -import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import getBrandColours from "@calcom/lib/getBrandColours"; import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl"; import { useIsomorphicLayoutEffect } from "@calcom/lib/hooks/useIsomorphicLayoutEffect"; @@ -792,13 +791,12 @@ function SideBarContainer({ bannersHeight }: SideBarContainerProps) { function SideBar({ bannersHeight, user }: SideBarProps) { const { t, isLocaleReady } = useLocale(); const orgBranding = useOrgBranding(); - const isOrgBrandingDataFetched = orgBranding !== undefined; const publicPageUrl = useMemo(() => { - if (!user?.organizationId) return `${process.env.NEXT_PUBLIC_WEBSITE_URL}/${user?.username}`; + if (!user?.org?.id) return `${process.env.NEXT_PUBLIC_WEBSITE_URL}/${user?.username}`; const publicPageUrl = orgBranding?.slug ? getOrgFullDomain(orgBranding.slug) : ""; return publicPageUrl; - }, [orgBranding?.slug, user?.organizationId, user?.username]); + }, [orgBranding?.slug, user?.username, user?.org?.id]); const bottomNavItems: NavigationItemType[] = [ { @@ -819,7 +817,7 @@ function SideBar({ bannersHeight, user }: SideBarProps) { }, { name: "settings", - href: user?.organizationId ? `/settings/organizations/profile` : "/settings/my-account/profile", + href: user?.org ? `/settings/organizations/profile` : "/settings/my-account/profile", icon: Settings, }, ]; @@ -830,12 +828,12 @@ function SideBar({ bannersHeight, user }: SideBarProps) { className="desktop-transparent bg-muted border-muted fixed left-0 hidden h-full max-h-screen w-14 flex-col overflow-y-auto overflow-x-hidden border-r dark:bg-gradient-to-tr dark:from-[#2a2a2a] dark:to-[#1c1c1c] md:sticky md:flex lg:w-56 lg:px-3">
- {!isOrgBrandingDataFetched ? null : orgBranding ? ( + {orgBranding ? (

diff --git a/packages/features/users/components/UserTable/ChangeUserRoleModal.tsx b/packages/features/users/components/UserTable/ChangeUserRoleModal.tsx index d96b5e8094..1f5e51a12e 100644 --- a/packages/features/users/components/UserTable/ChangeUserRoleModal.tsx +++ b/packages/features/users/components/UserTable/ChangeUserRoleModal.tsx @@ -7,7 +7,7 @@ import type { Action, State } from "./UserListTable"; export function ChangeUserRoleModal(props: { state: State; dispatch: Dispatch }) { const { data: session } = useSession(); - const orgId = session?.user.organizationId; + const orgId = session?.user.org?.id; if (!orgId || !props.state.changeMemberRole.user) return null; return ( diff --git a/packages/features/users/components/UserTable/DeleteMemberModal.tsx b/packages/features/users/components/UserTable/DeleteMemberModal.tsx index 2651f90ced..e88bae7ac8 100644 --- a/packages/features/users/components/UserTable/DeleteMemberModal.tsx +++ b/packages/features/users/components/UserTable/DeleteMemberModal.tsx @@ -40,10 +40,10 @@ export function DeleteMemberModal({ state, dispatch }: { state: State; dispatch: confirmBtnText={t("confirm_remove_member")} onConfirm={() => { // Shouldnt ever happen just for type safety - if (!session?.user.organizationId || !state?.deleteMember?.user?.id) return; + if (!session?.user.org?.id || !state?.deleteMember?.user?.id) return; removeMemberMutation.mutate({ - teamId: session?.user.organizationId, + teamId: session?.user.org.id, memberId: state?.deleteMember?.user.id, isOrg: true, }); diff --git a/packages/features/users/components/UserTable/ImpersonationMemberModal.tsx b/packages/features/users/components/UserTable/ImpersonationMemberModal.tsx index e17ae810d8..c5cf7b90d9 100644 --- a/packages/features/users/components/UserTable/ImpersonationMemberModal.tsx +++ b/packages/features/users/components/UserTable/ImpersonationMemberModal.tsx @@ -9,7 +9,7 @@ import type { Action, State } from "./UserListTable"; export function ImpersonationMemberModal(props: { state: State; dispatch: Dispatch }) { const { t } = useLocale(); const { data: session } = useSession(); - const teamId = session?.user.organizationId; + const teamId = session?.user.org?.id; const user = props.state.impersonateMember.user; if (!user || !teamId) return null; diff --git a/packages/features/users/components/UserTable/InviteMemberModal.tsx b/packages/features/users/components/UserTable/InviteMemberModal.tsx index cead1d6775..9f8f16b880 100644 --- a/packages/features/users/components/UserTable/InviteMemberModal.tsx +++ b/packages/features/users/components/UserTable/InviteMemberModal.tsx @@ -46,9 +46,9 @@ export function InviteMemberModal(props: Props) { }, }); - if (!session?.user.organizationId) return null; + if (!session?.user.org?.id) return null; - const orgId = session.user.organizationId; + const orgId = session.user.org.id; return (