diff --git a/apps/web/components/ui/UsernameAvailability/PremiumTextfield.tsx b/apps/web/components/ui/UsernameAvailability/PremiumTextfield.tsx index f61d1e5d71..7b8639ec29 100644 --- a/apps/web/components/ui/UsernameAvailability/PremiumTextfield.tsx +++ b/apps/web/components/ui/UsernameAvailability/PremiumTextfield.tsx @@ -13,6 +13,7 @@ import { fetchUsername } from "@calcom/lib/fetchUsername"; import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata"; import { useDebounce } from "@calcom/lib/hooks/useDebounce"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import slugify from "@calcom/lib/slugify"; import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; import type { AppRouter } from "@calcom/trpc/types/server/routers/_app"; @@ -81,7 +82,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => { useEffect(() => { // Use the current username or if it's not set, use the one available from stripe - setInputUsernameValue(currentUsername || stripeCustomer?.username || ""); + setInputUsernameValue(slugify(currentUsername || stripeCustomer?.username || "", true)); }, [setInputUsernameValue, currentUsername, stripeCustomer?.username]); useEffect(() => { @@ -105,7 +106,8 @@ const PremiumTextfield = (props: ICustomUsernameProps) => { const updateUsername = trpc.viewer.me.updateProfile.useMutation({ onSuccess: async () => { onSuccessMutation && (await onSuccessMutation()); - await update({ username: inputUsernameValue }); + const sanitizedUsername = slugify(inputUsernameValue || ""); + await update({ username: sanitizedUsername }); setOpenDialogSaveUsername(false); }, onError: (error) => { @@ -171,11 +173,12 @@ const PremiumTextfield = (props: ICustomUsernameProps) => { }; const saveUsername = () => { + const sanitizedUsername = slugify(inputUsernameValue || ""); if (usernameChangeCondition !== UsernameChangeStatusEnum.UPGRADE) { updateUsername.mutate({ - username: inputUsernameValue, + username: sanitizedUsername, }); - setCurrentUsername(inputUsernameValue); + setCurrentUsername(sanitizedUsername); } }; @@ -234,7 +237,8 @@ const PremiumTextfield = (props: ICustomUsernameProps) => { if (searchParams?.toString() !== _searchParams.toString()) { router.replace(`${pathname}?${_searchParams.toString()}`); } - setInputUsernameValue(event.target.value); + const sanitized = slugify(event.target.value, true); + setInputUsernameValue(sanitized); }} data-testid="username-input" /> diff --git a/apps/web/components/ui/UsernameAvailability/UsernameTextfield.tsx b/apps/web/components/ui/UsernameAvailability/UsernameTextfield.tsx index d680a0afc0..654d551f15 100644 --- a/apps/web/components/ui/UsernameAvailability/UsernameTextfield.tsx +++ b/apps/web/components/ui/UsernameAvailability/UsernameTextfield.tsx @@ -16,6 +16,7 @@ import { DialogContent, DialogFooter, DialogClose } from "@calcom/ui/components/ import { TextField } from "@calcom/ui/components/form"; import { Icon } from "@calcom/ui/components/icon"; import { Tooltip } from "@calcom/ui/components/tooltip"; +import slugify from "@calcom/lib/slugify"; import type { TRPCClientErrorLike } from "@trpc/client"; @@ -72,10 +73,11 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial { + const sanitizedUsername = slugify(inputUsernameValue || ""); onSuccessMutation && (await onSuccessMutation()); setOpenDialogSaveUsername(false); - setCurrentUsername(inputUsernameValue); - await update({ username: inputUsernameValue }); + setCurrentUsername(sanitizedUsername); + await update({ username: sanitizedUsername }); }, onError: (error) => { onErrorMutation && onErrorMutation(error); @@ -108,8 +110,9 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial { + const sanitizedUsername = slugify(inputUsernameValue || ""); updateUsernameMutation.mutate({ - username: inputUsernameValue, + username: sanitizedUsername, }); }; @@ -134,7 +137,8 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial { event.preventDefault(); - setInputUsernameValue(event.target.value); + const sanitized = slugify(event.target.value, true); + setInputUsernameValue(sanitized); }} data-testid="username-input" {...rest} diff --git a/apps/web/components/ui/UsernameAvailability/index.tsx b/apps/web/components/ui/UsernameAvailability/index.tsx index e8d67a18ef..eeb1346412 100644 --- a/apps/web/components/ui/UsernameAvailability/index.tsx +++ b/apps/web/components/ui/UsernameAvailability/index.tsx @@ -6,6 +6,7 @@ import { Controller, useForm } from "react-hook-form"; import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import { WEBSITE_URL, IS_SELF_HOSTED } from "@calcom/lib/constants"; +import slugify from "@calcom/lib/slugify"; import { trpc } from "@calcom/trpc/react"; import type { AppRouter } from "@calcom/trpc/types/server/routers/_app"; @@ -56,7 +57,7 @@ export const UsernameAvailabilityField = ({ : { username: currentUsernameState || "", setQuery: setCurrentUsernameState }; const formMethods = useForm({ defaultValues: { - username: currentUsername, + username: slugify(currentUsername || user.username || ""), }, }); @@ -78,7 +79,11 @@ export const UsernameAvailabilityField = ({ setCurrentUsername={setCurrentUsername} inputUsernameValue={value} usernameRef={ref} - setInputUsernameValue={onChange} + setInputUsernameValue={(val) => { + const displayValue = slugify(val, true); + formMethods.setValue("username", displayValue); + onChange?.(displayValue); + }} onSuccessMutation={onSuccessMutation} onErrorMutation={onErrorMutation} disabled={!!user.organization?.id}