import classNames from "classnames"; import { debounce } from "lodash"; import { MutableRefObject, useCallback, useEffect, useState } from "react"; import { fetchUsername } from "@calcom/lib/fetchUsername"; import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { User } from "@calcom/prisma/client"; import { TRPCClientErrorLike } from "@calcom/trpc/client"; import { trpc } from "@calcom/trpc/react"; import type { AppRouter } from "@calcom/trpc/server/routers/_app"; import Button from "@calcom/ui/Button"; import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog"; import { Icon } from "@calcom/ui/Icon"; import { Input, Label } from "@calcom/ui/form/fields"; export enum UsernameChangeStatusEnum { NORMAL = "NORMAL", UPGRADE = "UPGRADE", DOWNGRADE = "DOWNGRADE", } interface ICustomUsernameProps { currentUsername: string | undefined; setCurrentUsername: (value: string | undefined) => void; inputUsernameValue: string | undefined; usernameRef: MutableRefObject; setInputUsernameValue: (value: string) => void; onSuccessMutation?: () => void; onErrorMutation?: (error: TRPCClientErrorLike) => void; user: Pick< User, | "username" | "name" | "email" | "bio" | "avatar" | "timeZone" | "weekStart" | "hideBranding" | "theme" | "plan" | "brandColor" | "darkBrandColor" | "metadata" | "timeFormat" | "allowDynamicBooking" >; } const PremiumTextfield = (props: ICustomUsernameProps) => { const { t } = useLocale(); const { currentUsername, setCurrentUsername, inputUsernameValue, setInputUsernameValue, usernameRef, onSuccessMutation, onErrorMutation, user, } = props; const [usernameIsAvailable, setUsernameIsAvailable] = useState(false); const [markAsError, setMarkAsError] = useState(false); const [openDialogSaveUsername, setOpenDialogSaveUsername] = useState(false); const [usernameChangeCondition, setUsernameChangeCondition] = useState( null ); const userIsPremium = user && user.metadata && hasKeyInMetadata(user, "isPremium") ? !!user.metadata.isPremium : false; const [premiumUsername, setPremiumUsername] = useState(false); const debouncedApiCall = useCallback( debounce(async (username) => { const { data } = await fetchUsername(username); setMarkAsError(!data.available); setPremiumUsername(data.premium); setUsernameIsAvailable(data.available); }, 150), [] ); useEffect(() => { if (currentUsername !== inputUsernameValue) { debouncedApiCall(inputUsernameValue); } else if (inputUsernameValue === "") { setMarkAsError(false); setPremiumUsername(false); setUsernameIsAvailable(false); } else { setPremiumUsername(userIsPremium); setUsernameIsAvailable(false); } }, [inputUsernameValue]); useEffect(() => { if (usernameIsAvailable || premiumUsername) { const condition = obtainNewUsernameChangeCondition({ userIsPremium, isNewUsernamePremium: premiumUsername, }); setUsernameChangeCondition(condition); } }, [usernameIsAvailable, premiumUsername]); const obtainNewUsernameChangeCondition = ({ userIsPremium, isNewUsernamePremium, }: { userIsPremium: boolean; isNewUsernamePremium: boolean; }) => { let resultCondition: UsernameChangeStatusEnum; if (!userIsPremium && isNewUsernamePremium) { resultCondition = UsernameChangeStatusEnum.UPGRADE; } else if (userIsPremium && !isNewUsernamePremium) { resultCondition = UsernameChangeStatusEnum.DOWNGRADE; } else { resultCondition = UsernameChangeStatusEnum.NORMAL; } return resultCondition; }; const utils = trpc.useContext(); const updateUsername = trpc.useMutation("viewer.updateProfile", { onSuccess: async () => { onSuccessMutation && (await onSuccessMutation()); setCurrentUsername(inputUsernameValue); setOpenDialogSaveUsername(false); }, onError: (error) => { onErrorMutation && onErrorMutation(error); }, async onSettled() { await utils.invalidateQueries(["viewer.public.i18n"]); }, }); const ActionButtons = (props: { index: string }) => { const { index } = props; return (usernameIsAvailable || premiumUsername) && currentUsername !== inputUsernameValue ? (
) : ( <> ); }; const saveUsername = () => { if (usernameChangeCondition === UsernameChangeStatusEnum.NORMAL) { updateUsername.mutate({ username: inputUsernameValue, }); } }; return ( <>
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
{ event.preventDefault(); setInputUsernameValue(event.target.value); }} data-testid="username-input" /> {currentUsername !== inputUsernameValue && (
{premiumUsername ? : <>} {!premiumUsername && usernameIsAvailable ? : <>}
)}
{markAsError &&

Username is already taken

} {usernameIsAvailable && (

{usernameChangeCondition === UsernameChangeStatusEnum.DOWNGRADE && ( <>{t("standard_to_premium_username_description")} )}

)} {(usernameIsAvailable || premiumUsername) && currentUsername !== inputUsernameValue && (
)}
{usernameChangeCondition && usernameChangeCondition !== UsernameChangeStatusEnum.NORMAL && (

{usernameChangeCondition === UsernameChangeStatusEnum.UPGRADE && t("change_username_standard_to_premium")} {usernameChangeCondition === UsernameChangeStatusEnum.DOWNGRADE && t("change_username_premium_to_standard")}

)}

{t("current_username")}

{currentUsername}

{t("new_username")}

{inputUsernameValue}

{/* redirect to checkout */} {(usernameChangeCondition === UsernameChangeStatusEnum.UPGRADE || usernameChangeCondition === UsernameChangeStatusEnum.DOWNGRADE) && ( )} {/* Normal save */} {usernameChangeCondition === UsernameChangeStatusEnum.NORMAL && ( )}
); }; export { PremiumTextfield };