diff --git a/apps/web/components/getting-started/steps-views/UserSettings.tsx b/apps/web/components/getting-started/steps-views/UserSettings.tsx index 6a0ab4a5ef..53d50d14a4 100644 --- a/apps/web/components/getting-started/steps-views/UserSettings.tsx +++ b/apps/web/components/getting-started/steps-views/UserSettings.tsx @@ -1,8 +1,11 @@ import { ArrowRightIcon } from "@heroicons/react/outline"; +import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; +import { z } from "zod"; import dayjs from "@calcom/dayjs"; +import { FULL_NAME_LENGTH_MAX_LIMIT } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { trpc } from "@calcom/trpc/react"; @@ -22,23 +25,30 @@ const UserSettings = (props: IUserSettingsProps) => { const { t } = useLocale(); const [selectedTimeZone, setSelectedTimeZone] = useState(dayjs.tz.guess()); const telemetry = useTelemetry(); + const userSettingsSchema = z.object({ + name: z + .string() + .min(1) + .max(FULL_NAME_LENGTH_MAX_LIMIT, { + message: t("max_limit_allowed_hint", { limit: FULL_NAME_LENGTH_MAX_LIMIT }), + }), + }); const { register, handleSubmit, formState: { errors }, - } = useForm({ + } = useForm>({ defaultValues: { name: user?.name || "", }, reValidateMode: "onChange", + resolver: zodResolver(userSettingsSchema), }); useEffect(() => { telemetry.event(telemetryEventTypes.onboardingStarted); }, [telemetry]); - const defaultOptions = { required: true, maxLength: 255 }; - const utils = trpc.useContext(); const onSuccess = async () => { await utils.viewer.me.invalidate(); @@ -67,7 +77,9 @@ const UserSettings = (props: IUserSettingsProps) => { {t("full_name")} { /> {errors.name && (

- {t("required")} + {errors.name.message}

)} diff --git a/apps/web/pages/settings/my-account/profile.tsx b/apps/web/pages/settings/my-account/profile.tsx index 9b580dc316..09015c0d0a 100644 --- a/apps/web/pages/settings/my-account/profile.tsx +++ b/apps/web/pages/settings/my-account/profile.tsx @@ -1,12 +1,15 @@ +import { zodResolver } from "@hookform/resolvers/zod"; import { IdentityProvider } from "@prisma/client"; import crypto from "crypto"; import { signOut } from "next-auth/react"; import type { BaseSyntheticEvent } from "react"; import { useRef, useState } from "react"; import { Controller, useForm } from "react-hook-form"; +import { z } from "zod"; import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode"; import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; +import { FULL_NAME_LENGTH_MAX_LIMIT } from "@calcom/lib/constants"; import { APP_NAME } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { md } from "@calcom/lib/markdownIt"; @@ -315,6 +318,20 @@ const ProfileForm = ({ extraField?: React.ReactNode; }) => { const { t } = useLocale(); + + const profileFormSchema = z.object({ + username: z.string(), + avatar: z.string(), + name: z + .string() + .min(1) + .max(FULL_NAME_LENGTH_MAX_LIMIT, { + message: t("max_limit_allowed_hint", { limit: FULL_NAME_LENGTH_MAX_LIMIT }), + }), + email: z.string().email(), + bio: z.string(), + }); + const emailMd5 = crypto .createHash("md5") .update(defaultValues.email || "example@example.com") @@ -322,6 +339,7 @@ const ProfileForm = ({ const formMethods = useForm({ defaultValues, + resolver: zodResolver(profileFormSchema), }); const { diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index fe91bdb923..402fe71765 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -432,6 +432,7 @@ "password_hint_min": "Minimum 7 characters long", "password_hint_admin_min": "Minimum 15 characters long", "password_hint_num": "Contain at least 1 number", + "max_limit_allowed_hint":"Must be {{limit}} or fewer characters long", "invalid_password_hint": "The password must be a minimum of {{passwordLength}} characters long containing at least one number and have a mixture of uppercase and lowercase letters", "incorrect_password": "Password is incorrect.", "incorrect_username_password": "Username or password is incorrect.", diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts index 7c4aa7545c..85c5311481 100644 --- a/packages/lib/constants.ts +++ b/packages/lib/constants.ts @@ -62,3 +62,4 @@ export const IS_STRIPE_ENABLED = !!( ); /** Self hosted shouldn't checkout when creating teams unless required */ export const IS_TEAM_BILLING_ENABLED = IS_STRIPE_ENABLED && (!IS_SELF_HOSTED || HOSTED_CAL_FEATURES); +export const FULL_NAME_LENGTH_MAX_LIMIT = 50; diff --git a/packages/trpc/server/routers/viewer.tsx b/packages/trpc/server/routers/viewer.tsx index d7f99aab12..eecdee8282 100644 --- a/packages/trpc/server/routers/viewer.tsx +++ b/packages/trpc/server/routers/viewer.tsx @@ -26,6 +26,7 @@ import { samlTenantProduct } from "@calcom/features/ee/sso/lib/saml"; import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib"; import getEnabledApps from "@calcom/lib/apps/getEnabledApps"; import { IS_SELF_HOSTED, WEBAPP_URL } from "@calcom/lib/constants"; +import { FULL_NAME_LENGTH_MAX_LIMIT } from "@calcom/lib/constants"; import { symmetricDecrypt } from "@calcom/lib/crypto"; import getPaymentAppData from "@calcom/lib/getPaymentAppData"; import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata"; @@ -595,7 +596,7 @@ const loggedInViewerRouter = router({ .input( z.object({ username: z.string().optional(), - name: z.string().optional(), + name: z.string().max(FULL_NAME_LENGTH_MAX_LIMIT).optional(), email: z.string().optional(), bio: z.string().optional(), avatar: z.string().optional(),