'use client'; import { useState } from 'react'; import Image from 'next/image'; import Link from 'next/link'; import { useRouter, useSearchParams } from 'next/navigation'; import { zodResolver } from '@hookform/resolvers/zod'; import { AnimatePresence, motion } from 'framer-motion'; import { signIn } from 'next-auth/react'; import { useForm } from 'react-hook-form'; import { FcGoogle } from 'react-icons/fc'; import { z } from 'zod'; import communityCardsImage from '@documenso/assets/images/community-cards.png'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { TRPCClientError } from '@documenso/trpc/client'; import { trpc } from '@documenso/trpc/react'; import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@documenso/ui/primitives/form/form'; import { Input } from '@documenso/ui/primitives/input'; import { PasswordInput } from '@documenso/ui/primitives/password-input'; import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { UserProfileSkeleton } from '~/components/ui/user-profile-skeleton'; import { UserProfileTimur } from '~/components/ui/user-profile-timur'; const SIGN_UP_REDIRECT_PATH = '/documents'; type SignUpStep = 'BASIC_DETAILS' | 'CLAIM_USERNAME'; export const ZSignUpFormV2Schema = z .object({ name: z.string().trim().min(1, { message: 'Please enter a valid name.' }), email: z.string().email().min(1), password: ZPasswordSchema, signature: z.string().min(1, { message: 'We need your signature to sign documents' }), url: z .string() .trim() .toLowerCase() .min(1, { message: 'We need a username to create your profile' }) .regex(/^[a-z0-9-]+$/, { message: 'Username can only container alphanumeric characters and dashes.', }), }) .refine( (data) => { const { name, email, password } = data; return !password.includes(name) && !password.includes(email.split('@')[0]); }, { message: 'Password should not be common or based on personal information', }, ); export type TSignUpFormV2Schema = z.infer; export type SignUpFormV2Props = { className?: string; initialEmail?: string; isGoogleSSOEnabled?: boolean; }; export const SignUpFormV2 = ({ className, initialEmail, isGoogleSSOEnabled, }: SignUpFormV2Props) => { const { toast } = useToast(); const analytics = useAnalytics(); const router = useRouter(); const searchParams = useSearchParams(); const [step, setStep] = useState('BASIC_DETAILS'); const utmSrc = searchParams?.get('utm_source') ?? null; const baseUrl = new URL(NEXT_PUBLIC_WEBAPP_URL() ?? 'http://localhost:3000'); const form = useForm({ values: { name: '', email: initialEmail ?? '', password: '', signature: '', url: '', }, mode: 'onBlur', resolver: zodResolver(ZSignUpFormV2Schema), }); const isSubmitting = form.formState.isSubmitting; const name = form.watch('name'); const url = form.watch('url'); // To continue we need to make sure name, email, password and signature are valid const canContinue = form.formState.dirtyFields.name && form.formState.errors.name === undefined && form.formState.dirtyFields.email && form.formState.errors.email === undefined && form.formState.dirtyFields.password && form.formState.errors.password === undefined && form.formState.dirtyFields.signature && form.formState.errors.signature === undefined; const { mutateAsync: signup } = trpc.auth.signup.useMutation(); const onFormSubmit = async ({ name, email, password, signature, url }: TSignUpFormV2Schema) => { try { await signup({ name, email, password, signature, url }); router.push(`/unverified-account`); toast({ title: 'Registration Successful', description: 'You have successfully registered. Please verify your account by clicking on the link you received in the email.', duration: 5000, }); analytics.capture('App: User Sign Up', { email, timestamp: new Date().toISOString(), custom_campaign_params: { src: utmSrc }, }); } catch (err) { const error = AppError.parseError(err); if (error.code === AppErrorCode.PROFILE_URL_TAKEN) { form.setError('url', { type: 'manual', message: 'This username has already been taken', }); } else if (error.code === AppErrorCode.PREMIUM_PROFILE_URL) { form.setError('url', { type: 'manual', message: error.message, }); } else if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { toast({ title: 'An error occurred', description: err.message, variant: 'destructive', }); } else { toast({ title: 'An unknown error occurred', description: 'We encountered an unknown error while attempting to sign you up. Please try again later.', variant: 'destructive', }); } } }; const onSignUpWithGoogleClick = async () => { try { await signIn('google', { callbackUrl: SIGN_UP_REDIRECT_PATH }); } catch (err) { toast({ title: 'An unknown error occurred', description: 'We encountered an unknown error while attempting to sign you Up. Please try again later.', variant: 'destructive', }); } }; return (
community-cards
User profiles are coming soon!
{step === 'BASIC_DETAILS' ? ( ) : ( )}
{step === 'BASIC_DETAILS' && (

Create a new account

Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp.

)} {step === 'CLAIM_USERNAME' && (

Claim your username now

You will get notified & be able to set up your documenso public profile when we launch the feature.

)}
{step === 'BASIC_DETAILS' && (
( Full Name )} /> ( Email Address )} /> ( Password )} /> ( Sign Here onChange(v ?? '')} /> )} /> {isGoogleSSOEnabled && ( <>
Or
)}

Already have an account?{' '} Sign in instead

)} {step === 'CLAIM_USERNAME' && (
( Public profile username
{baseUrl.host}/u/{field.value || ''}
)} />
)}
{step === 'BASIC_DETAILS' && (

Basic details 1/2

)} {step === 'CLAIM_USERNAME' && (

Claim username 2/2

)}
{/* Go back button, disabled if step is basic details */} {/* Continue button */} {step === 'BASIC_DETAILS' && ( )} {/* Sign up button */} {step === 'CLAIM_USERNAME' && ( )}
); };