'use client'; import type { HTMLAttributes, KeyboardEvent } from 'react'; import { useMemo, useState } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { AnimatePresence, motion } from 'framer-motion'; import { Loader } from 'lucide-react'; import { usePlausible } from 'next-plausible'; import { env } from 'next-runtime-env'; import { Controller, useForm } from 'react-hook-form'; import { z } from 'zod'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { Card, CardContent } from '@documenso/ui/primitives/card'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@documenso/ui/primitives/dialog'; import { Input } from '@documenso/ui/primitives/input'; import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { claimPlan } from '~/api/claim-plan/fetcher'; import { STEP } from '../constants'; import { FormErrorMessage } from '../form/form-error-message'; const ZWidgetFormSchema = z .object({ email: z.string().email({ message: 'Please enter a valid email address.' }), name: z.string().trim().min(3, { message: 'Please enter a valid name.' }), }) .and( z.union([ z.object({ signatureDataUrl: z.string().min(1), signatureText: z.null().or(z.string().max(0)), }), z.object({ signatureDataUrl: z.null().or(z.string().max(0)), signatureText: z.string().trim().min(1), }), ]), ); export type TWidgetFormSchema = z.infer; type StepKeys = keyof typeof STEP; type StepValues = (typeof STEP)[StepKeys]; export type WidgetProps = HTMLAttributes; export const Widget = ({ className, children, ...props }: WidgetProps) => { const { toast } = useToast(); const event = usePlausible(); const [step, setStep] = useState(STEP.EMAIL); const [showSigningDialog, setShowSigningDialog] = useState(false); const [draftSignatureDataUrl, setDraftSignatureDataUrl] = useState(null); const { control, register, handleSubmit, setValue, trigger, watch, formState: { errors, isSubmitting, isValid }, } = useForm({ mode: 'onChange', defaultValues: { email: '', name: '', signatureDataUrl: null, signatureText: '', }, resolver: zodResolver(ZWidgetFormSchema), }); const signatureDataUrl = watch('signatureDataUrl'); const signatureText = watch('signatureText'); const stepsRemaining = useMemo(() => { if (step === STEP.NAME) { return 2; } if (step === STEP.EMAIL) { return 3; } return 1; }, [step]); const onNextStepClick = () => { if (step === STEP.EMAIL) { setStep(STEP.NAME); setTimeout(() => { document.querySelector('#name')?.focus(); }, 0); } if (step === STEP.NAME) { setStep(STEP.SIGN); setTimeout(() => { document.querySelector('#signatureText')?.focus(); }, 0); } }; const onEnterPress = (callback: () => void) => { return (e: KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault(); callback(); } }; }; const onSignatureConfirmClick = () => { setValue('signatureDataUrl', draftSignatureDataUrl); setValue('signatureText', ''); void trigger('signatureDataUrl'); setShowSigningDialog(false); }; const onFormSubmit = async ({ email, name, signatureDataUrl, signatureText, }: TWidgetFormSchema) => { try { const delay = new Promise((resolve) => { setTimeout(resolve, 1000); }); const planId = env('NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID'); if (!planId) { throw new Error('No plan ID found.'); } const claimPlanInput = signatureDataUrl ? { name, email, planId, signatureDataUrl: signatureDataUrl, signatureText: null, } : { name, email, planId, signatureDataUrl: null, signatureText: signatureText ?? '', }; const [result] = await Promise.all([claimPlan(claimPlanInput), delay]); event('claim-plan-widget'); window.location.href = result; } catch (error) { event('claim-plan-failed'); toast({ title: 'Something went wrong', description: error instanceof Error ? error.message : 'Please try again later.', variant: 'destructive', }); } }; return ( <>
{children}

Sign up to Community Plan

with Timur Ercan & Lucas Smith from Documenso


(
field.value !== '' && !errors.email?.message && onEnterPress(onNextStepClick)(e) } {...field} />
)} />
{(step === STEP.NAME || step === STEP.SIGN) && ( (
field.value !== '' && !errors.name?.message && onEnterPress(onNextStepClick)(e) } {...field} />
)} />
)}

{isValid ? 'Ready for Signing' : `${stepsRemaining} step(s) until signed`}

Minimise contract

setShowSigningDialog(true)} >
{!signatureText && signatureDataUrl && ( user signature )} {signatureText && (

{signatureText}

)}
e.stopPropagation()} > { if (e.target.value !== '') { setValue('signatureDataUrl', null); } }, })} />
Add your signature By signing you signal your support of Documenso's mission in a

non-legally binding, but heartfelt way.



You also unlock the option to purchase the early supporter plan including everything we build this year for fixed price.
); };