import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import InviteLinkSettingsModal from "@calcom/features/ee/teams/components/InviteLinkSettingsModal"; import MemberInvitationModal from "@calcom/features/ee/teams/components/MemberInvitationModal"; import { classNames } from "@calcom/lib"; import { APP_NAME } from "@calcom/lib/constants"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { MembershipRole } from "@calcom/prisma/enums"; import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; import { Badge, Button, showToast, SkeletonButton, SkeletonContainer, SkeletonText, UserAvatar, } from "@calcom/ui"; type TeamMember = RouterOutputs["viewer"]["teams"]["get"]["members"][number]; type FormValues = { members: TeamMember[]; }; const AddNewTeamMembers = ({ isOrg = false }: { isOrg?: boolean }) => { const searchParams = useCompatSearchParams(); const session = useSession(); const telemetry = useTelemetry(); const teamId = searchParams?.get("id") ? Number(searchParams.get("id")) : -1; const teamQuery = trpc.viewer.teams.get.useQuery( { teamId, isOrg }, { enabled: session.status === "authenticated" } ); useEffect(() => { const event = searchParams?.get("event"); if (event === "team_created") { telemetry.event(telemetryEventTypes.team_created); } }, []); if (session.status === "loading" || !teamQuery.data) return ; return ( ); }; export const AddNewTeamMembersForm = ({ defaultValues, teamId, isOrg, }: { defaultValues: FormValues; teamId: number; isOrg?: boolean; }) => { const searchParams = useCompatSearchParams(); const { t, i18n } = useLocale(); const router = useRouter(); const utils = trpc.useUtils(); const orgBranding = useOrgBranding(); const showDialog = searchParams?.get("inviteModal") === "true"; const [memberInviteModal, setMemberInviteModal] = useState(showDialog); const [inviteLinkSettingsModal, setInviteLinkSettingsModal] = useState(false); const { data: team, isPending } = trpc.viewer.teams.get.useQuery({ teamId, isOrg }, { enabled: !!teamId }); const { data: orgMembersNotInThisTeam } = trpc.viewer.organizations.getMembers.useQuery( { teamIdToExclude: teamId, distinctUser: true, }, { enabled: orgBranding !== null, } ); const inviteMemberMutation = trpc.viewer.teams.inviteMember.useMutation(); const publishTeamMutation = trpc.viewer.teams.publish.useMutation({ onSuccess(data) { router.push(data.url); }, onError: (error) => { showToast(error.message, "error"); }, }); return ( <>
{defaultValues.members.length > 0 && (
    {defaultValues.members.map((member, index) => ( ))}
)}
{isPending ? ( ) : ( <> setMemberInviteModal(false)} onSubmit={(values, resetFields) => { inviteMemberMutation.mutate( { teamId, language: i18n.language, role: values.role, usernameOrEmail: values.emailOrUsername, }, { onSuccess: async (data) => { await utils.viewer.teams.get.invalidate(); setMemberInviteModal(false); resetFields(); if (Array.isArray(data.usernameOrEmail)) { showToast( t("email_invite_team_bulk", { userCount: data.numUsersInvited, }), "success" ); } else { showToast( t("email_invite_team", { email: data.usernameOrEmail, }), "success" ); } }, onError: (error) => { showToast(error.message, "error"); }, } ); }} onSettingsOpen={() => { setMemberInviteModal(false); setInviteLinkSettingsModal(true); }} members={defaultValues.members} /> {team?.inviteToken && ( { setInviteLinkSettingsModal(false); setMemberInviteModal(true); }} /> )} )}
); }; export default AddNewTeamMembers; const AddNewTeamMemberSkeleton = () => { return (

); }; const PendingMemberItem = (props: { member: TeamMember; index: number; teamId: number; isOrg?: boolean }) => { const { member, index, teamId } = props; const { t } = useLocale(); const utils = trpc.useUtils(); const session = useSession(); const orgRole = session?.data?.user.org?.role; const bookerUrl = member.bookerUrl; const removeMemberMutation = trpc.viewer.teams.removeMember.useMutation({ async onSuccess() { await utils.viewer.teams.get.invalidate(); await utils.viewer.eventTypes.invalidate(); showToast(t("member_removed"), "success"); }, async onError(err) { showToast(err.message, "error"); }, }); const isOrgAdminOrOwner = orgRole === MembershipRole.OWNER || orgRole === MembershipRole.ADMIN; return (
  • {member.name || member.email || t("team_member")}

    {/* Assume that the first member of the team is the creator */} {member.id === session.data?.user.id && {t("you")}} {!member.accepted && {t("pending")}} {member.role === MembershipRole.MEMBER && {t("member")}} {member.role === MembershipRole.ADMIN && {t("admin")}} {member.role === MembershipRole.OWNER && {t("owner")}}
    {member.username ? (

    {`${bookerUrl}/${member.username}`}

    ) : (

    {t("not_on_cal", { appName: APP_NAME })}

    )}
    {(member.role !== "OWNER" || isOrgAdminOrOwner) && member.id !== session.data?.user.id && (
  • ); };