diff --git a/apps/web/src/app/(dashboard)/dashboard/page.tsx b/apps/web/src/app/(dashboard)/dashboard/page.tsx
index a83b2e8cc..77b18b98c 100644
--- a/apps/web/src/app/(dashboard)/dashboard/page.tsx
+++ b/apps/web/src/app/(dashboard)/dashboard/page.tsx
@@ -5,6 +5,7 @@ import { Clock, File, FileCheck } from 'lucide-react';
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-session';
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import { getStats } from '@documenso/lib/server-only/document/get-stats';
+import { DocumentStatus as InternalDocumentStatus } from '@documenso/prisma/client';
import {
Table,
TableBody,
@@ -21,6 +22,24 @@ import { LocaleDate } from '~/components/formatter/locale-date';
import { UploadDocument } from './upload-document';
+const CARD_DATA = [
+ {
+ icon: FileCheck,
+ title: 'Completed',
+ status: InternalDocumentStatus.COMPLETED,
+ },
+ {
+ icon: File,
+ title: 'Drafts',
+ status: InternalDocumentStatus.DRAFT,
+ },
+ {
+ icon: Clock,
+ title: 'Pending',
+ status: InternalDocumentStatus.PENDING,
+ },
+];
+
export default async function DashboardPage() {
const user = await getRequiredServerComponentSession();
@@ -34,20 +53,14 @@ export default async function DashboardPage() {
}),
]);
- const cardData = [
- { icon: FileCheck, title: 'Completed', status: stats.COMPLETED },
- { icon: File, title: 'Drafts', status: stats.DRAFT },
- { icon: Clock, title: 'Pending', status: stats.PENDING },
- ];
-
return (
Dashboard
- {cardData.map((card) => (
+ {CARD_DATA.map((card) => (
-
+
))}
diff --git a/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx b/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx
index cada25e06..68a61fb67 100644
--- a/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx
@@ -139,9 +139,10 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
/>
)}
- {state === 'signed-text' && signature?.typedSignature && (
+ {state === 'signed-text' && (
- {signature.typedSignature}
+ {/* This optional chaining is intentional, we don't want to move the check into the condition above */}
+ {signature?.typedSignature}
)}
diff --git a/apps/web/src/components/(marketing)/callout.tsx b/apps/web/src/components/(marketing)/callout.tsx
deleted file mode 100644
index d83983141..000000000
--- a/apps/web/src/components/(marketing)/callout.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-'use client';
-
-import Link from 'next/link';
-
-import { Github } from 'lucide-react';
-import { usePlausible } from 'next-plausible';
-
-import { Button } from '@documenso/ui/primitives/button';
-
-export type CalloutProps = {
- starCount?: number;
- [key: string]: unknown;
-};
-
-export const Callout = ({ starCount }: CalloutProps) => {
- const event = usePlausible();
-
- const onSignUpClick = () => {
- const el = document.getElementById('email');
-
- if (el) {
- const { top } = el.getBoundingClientRect();
-
- window.scrollTo({
- top: top - 120,
- behavior: 'smooth',
- });
-
- setTimeout(() => {
- el.focus();
- }, 500);
- }
- };
-
- return (
-
-
- Get the Community Plan
-
- $30/mo. forever!
-
-
-
- event('view-github')}
- >
-
-
- Star on Github
- {starCount && starCount > 0 && (
-
- {starCount.toLocaleString('en-US')}
-
- )}
-
-
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/claim-plan-dialog.tsx b/apps/web/src/components/(marketing)/claim-plan-dialog.tsx
deleted file mode 100644
index 1f78c5292..000000000
--- a/apps/web/src/components/(marketing)/claim-plan-dialog.tsx
+++ /dev/null
@@ -1,150 +0,0 @@
-'use client';
-
-import React, { useState } from 'react';
-
-import { useSearchParams } from 'next/navigation';
-
-import { zodResolver } from '@hookform/resolvers/zod';
-import { Info, Loader } from 'lucide-react';
-import { usePlausible } from 'next-plausible';
-import { useForm } from 'react-hook-form';
-import { z } from 'zod';
-
-import { cn } from '@documenso/ui/lib/utils';
-import { Button } from '@documenso/ui/primitives/button';
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from '@documenso/ui/primitives/dialog';
-import { Input } from '@documenso/ui/primitives/input';
-import { Label } from '@documenso/ui/primitives/label';
-import { useToast } from '@documenso/ui/primitives/use-toast';
-
-import { claimPlan } from '~/api/claim-plan/fetcher';
-
-import { FormErrorMessage } from '../form/form-error-message';
-
-export const ZClaimPlanDialogFormSchema = z.object({
- name: z.string().min(3),
- email: z.string().email(),
-});
-
-export type TClaimPlanDialogFormSchema = z.infer
;
-
-export type ClaimPlanDialogProps = {
- className?: string;
- planId: string;
- children: React.ReactNode;
-};
-
-export const ClaimPlanDialog = ({ className, planId, children }: ClaimPlanDialogProps) => {
- const params = useSearchParams();
- const { toast } = useToast();
- const event = usePlausible();
-
- const [open, setOpen] = useState(() => params?.get('cancelled') === 'true');
-
- const {
- register,
- handleSubmit,
- formState: { errors, isSubmitting },
- } = useForm({
- mode: 'onBlur',
- defaultValues: {
- name: params?.get('name') ?? '',
- email: params?.get('email') ?? '',
- },
- resolver: zodResolver(ZClaimPlanDialogFormSchema),
- });
-
- const onFormSubmit = async ({ name, email }: TClaimPlanDialogFormSchema) => {
- try {
- const delay = new Promise((resolve) => {
- setTimeout(resolve, 1000);
- });
-
- const [redirectUrl] = await Promise.all([
- claimPlan({ name, email, planId, signatureText: name, signatureDataUrl: null }),
- delay,
- ]);
-
- event('claim-plan-pricing');
-
- window.location.href = redirectUrl;
- } 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}
-
-
-
- Claim your plan
-
-
- We're almost there! Please enter your email address and name to claim your plan.
-
-
-
-
-
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/faster-smarter-beautiful-bento.tsx b/apps/web/src/components/(marketing)/faster-smarter-beautiful-bento.tsx
deleted file mode 100644
index 2cbaaef53..000000000
--- a/apps/web/src/components/(marketing)/faster-smarter-beautiful-bento.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { HTMLAttributes } from 'react';
-
-import Image from 'next/image';
-
-import { cn } from '@documenso/ui/lib/utils';
-import { Card, CardContent } from '@documenso/ui/primitives/card';
-
-import backgroundPattern from '~/assets/background-pattern.png';
-import cardBeautifulFigure from '~/assets/card-beautiful-figure.png';
-import cardFastFigure from '~/assets/card-fast-figure.png';
-import cardSmartFigure from '~/assets/card-smart-figure.png';
-
-export type FasterSmarterBeautifulBentoProps = HTMLAttributes;
-
-export const FasterSmarterBeautifulBento = ({
- className,
- ...props
-}: FasterSmarterBeautifulBentoProps) => {
- return (
-
-
-
-
-
- A 10x better signing experience.
- Faster, smarter and more beautiful.
-
-
-
-
-
-
- Fast.
- When it comes to sending or receiving a contract, you can count on lightning-fast
- speeds.
-
-
-
-
-
-
-
-
-
-
-
- Beautiful.
- Because signing should be celebrated. That’s why we care about the smallest detail in
- our product.
-
-
-
-
-
-
-
-
-
-
-
- Smart.
- Our custom templates come with smart rules that can help you save time and energy.
-
-
-
-
-
-
-
-
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/footer.tsx b/apps/web/src/components/(marketing)/footer.tsx
deleted file mode 100644
index a5fadfcf8..000000000
--- a/apps/web/src/components/(marketing)/footer.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { HTMLAttributes } from 'react';
-
-import Image from 'next/image';
-import Link from 'next/link';
-
-import { Github, MessagesSquare, Twitter } from 'lucide-react';
-
-import { cn } from '@documenso/ui/lib/utils';
-
-export type FooterProps = HTMLAttributes;
-
-const SOCIAL_LINKS = [
- { href: 'https://twitter.com/documenso', icon: },
- { href: 'https://github.com/documenso/documenso', icon: },
- { href: 'https://documen.so/discord', icon: },
-];
-
-const FOOTER_LINKS = [
- { href: '/pricing', text: 'Pricing' },
- { href: 'https://status.documenso.com', text: 'Status', target: '_blank' },
- { href: 'mailto:support@documenso.com', text: 'Support' },
- // { href: '/privacy', text: 'Privacy'}
-];
-
-export const Footer = ({ className, ...props }: FooterProps) => {
- return (
-
-
-
-
-
-
-
-
- {SOCIAL_LINKS.map((link, index) => (
-
- {link.icon}
-
- ))}
-
-
-
-
- {FOOTER_LINKS.map((link, index) => (
-
- {link.text}
-
- ))}
-
-
-
-
- © {new Date().getFullYear()} Documenso, Inc. All rights reserved.
-
-
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/header.tsx b/apps/web/src/components/(marketing)/header.tsx
deleted file mode 100644
index 5a1fa3b89..000000000
--- a/apps/web/src/components/(marketing)/header.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { HTMLAttributes } from 'react';
-
-import Image from 'next/image';
-import Link from 'next/link';
-
-import { cn } from '@documenso/ui/lib/utils';
-
-export type HeaderProps = HTMLAttributes;
-
-export const Header = ({ className, ...props }: HeaderProps) => {
- return (
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/hero.tsx b/apps/web/src/components/(marketing)/hero.tsx
deleted file mode 100644
index 7896a010e..000000000
--- a/apps/web/src/components/(marketing)/hero.tsx
+++ /dev/null
@@ -1,225 +0,0 @@
-'use client';
-
-import Image from 'next/image';
-import Link from 'next/link';
-
-import { Variants, motion } from 'framer-motion';
-import { Github } from 'lucide-react';
-import { usePlausible } from 'next-plausible';
-
-import { cn } from '@documenso/ui/lib/utils';
-import { Button } from '@documenso/ui/primitives/button';
-
-import backgroundPattern from '~/assets/background-pattern.png';
-
-import { Widget } from './widget';
-
-export type HeroProps = {
- className?: string;
- starCount?: number;
- [key: string]: unknown;
-};
-
-const BackgroundPatternVariants: Variants = {
- initial: {
- opacity: 0,
- },
-
- animate: {
- opacity: 1,
-
- transition: {
- delay: 1,
- duration: 1.2,
- },
- },
-};
-
-const HeroTitleVariants: Variants = {
- initial: {
- opacity: 0,
- y: 60,
- },
- animate: {
- opacity: 1,
- y: 0,
- transition: {
- duration: 0.5,
- },
- },
-};
-
-export const Hero = ({ className, starCount, ...props }: HeroProps) => {
- const event = usePlausible();
-
- const onSignUpClick = () => {
- const el = document.getElementById('email');
-
- if (el) {
- const { top } = el.getBoundingClientRect();
-
- window.scrollTo({
- top: top - 120,
- behavior: 'smooth',
- });
-
- requestAnimationFrame(() => {
- el.focus();
- });
- }
- };
-
- return (
-
-
-
-
-
-
-
-
-
- Document signing,
- finally open source.
-
-
-
-
- Get the Community Plan
-
- $30/mo. forever!
-
-
-
- event('view-github')}>
-
-
- Star on Github
- {starCount && starCount > 0 && (
-
- {starCount.toLocaleString('en-US')}
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Documenso Supporter Pledge
-
- Our mission is to create an open signing infrastructure that empowers the world,
- enabling businesses to embrace openness, cooperation, and transparency. We believe
- that signing, as a fundamental act, should embody these values. By offering an
- open-source signing solution, we aim to make document signing accessible, transparent,
- and trustworthy.
-
-
-
- Through our platform, called Documenso, we strive to earn your trust by allowing
- self-hosting and providing complete visibility into its inner workings. We value
- inclusivity and foster an environment where diverse perspectives and contributions are
- welcomed, even though we may not implement them all.
-
-
-
- At Documenso, we envision a web-enabled future for business and contracts, and we are
- committed to being the leading provider of open signing infrastructure. By combining
- exceptional product design with open-source principles, we aim to deliver a robust and
- well-designed application that exceeds your expectations.
-
-
-
- We understand that exceptional products are born from exceptional communities, and we
- invite you to join our open-source community. Your contributions, whether technical or
- non-technical, will help shape the future of signing. Together, we can create a better
- future for everyone.
-
-
-
- Today we invite you to join us on this journey: By signing this mission statement you
- signal your support of Documenso's mission{' '}
-
- (in a non-legally binding, but heartfelt way)
- {' '}
- and lock in the early supporter plan for forever, including everything we build this
- year.
-
-
-
-
-
-
Timur Ercan & Lucas Smith
-
Co-Founders, Documenso
-
-
-
-
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/open-build-template-bento.tsx b/apps/web/src/components/(marketing)/open-build-template-bento.tsx
deleted file mode 100644
index e7920500b..000000000
--- a/apps/web/src/components/(marketing)/open-build-template-bento.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import { HTMLAttributes } from 'react';
-
-import Image from 'next/image';
-
-import { cn } from '@documenso/ui/lib/utils';
-import { Card, CardContent } from '@documenso/ui/primitives/card';
-
-import backgroundPattern from '~/assets/background-pattern.png';
-import cardBuildFigure from '~/assets/card-build-figure.png';
-import cardOpenFigure from '~/assets/card-open-figure.png';
-import cardTemplateFigure from '~/assets/card-template-figure.png';
-
-export type OpenBuildTemplateBentoProps = HTMLAttributes;
-
-export const OpenBuildTemplateBento = ({ className, ...props }: OpenBuildTemplateBentoProps) => {
- return (
-
-
-
-
-
- Truly your own.
- Customise and expand.
-
-
-
-
-
-
- Open Source or Hosted.
- It’s up to you. Either clone our repository or rely on our easy to use hosting
- solution.
-
-
-
-
-
-
-
-
-
-
-
- Build on top.
- Make it your own through advanced customization and adjustability.
-
-
-
-
-
-
-
-
-
-
-
- Template Store (Soon).
- Choose a template from the community app store. Or submit your own template for others
- to use.
-
-
-
-
-
-
-
-
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/password-reveal.tsx b/apps/web/src/components/(marketing)/password-reveal.tsx
deleted file mode 100644
index b31765943..000000000
--- a/apps/web/src/components/(marketing)/password-reveal.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-'use client';
-
-import { useToast } from '@documenso/ui/primitives/use-toast';
-
-import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
-
-export type PasswordRevealProps = {
- password: string;
-};
-
-export const PasswordReveal = ({ password }: PasswordRevealProps) => {
- const { toast } = useToast();
- const [, copy] = useCopyToClipboard();
-
- const onCopyClick = () => {
- void copy(password).then(() => {
- toast({
- title: 'Copied to clipboard',
- description: 'Your password has been copied to your clipboard.',
- });
- });
- };
-
- return (
-
- {password}
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/pricing-table.tsx b/apps/web/src/components/(marketing)/pricing-table.tsx
deleted file mode 100644
index 73003abdc..000000000
--- a/apps/web/src/components/(marketing)/pricing-table.tsx
+++ /dev/null
@@ -1,180 +0,0 @@
-'use client';
-
-import { HTMLAttributes, useMemo, useState } from 'react';
-
-import Link from 'next/link';
-import { useSearchParams } from 'next/navigation';
-
-import { AnimatePresence, motion } from 'framer-motion';
-import { usePlausible } from 'next-plausible';
-
-import { cn } from '@documenso/ui/lib/utils';
-import { Button } from '@documenso/ui/primitives/button';
-
-import { ClaimPlanDialog } from './claim-plan-dialog';
-
-export type PricingTableProps = HTMLAttributes;
-
-const SELECTED_PLAN_BAR_LAYOUT_ID = 'selected-plan-bar';
-
-export const PricingTable = ({ className, ...props }: PricingTableProps) => {
- const params = useSearchParams();
- const event = usePlausible();
-
- const [period, setPeriod] = useState<'MONTHLY' | 'YEARLY'>(() =>
- // eslint-disable-next-line turbo/no-undeclared-env-vars
- params?.get('planId') === process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID
- ? 'YEARLY'
- : 'MONTHLY',
- );
-
- const planId = useMemo(() => {
- if (period === 'MONTHLY') {
- // eslint-disable-next-line turbo/no-undeclared-env-vars
- return process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID;
- }
-
- // eslint-disable-next-line turbo/no-undeclared-env-vars
- return process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_YEARLY_PRICE_ID;
- }, [period]);
-
- return (
-
-
-
- setPeriod('MONTHLY')}
- >
- Monthly
- {period === 'MONTHLY' && (
-
- )}
-
-
- setPeriod('YEARLY')}
- >
- Yearly
-
- Save $60
-
- {period === 'YEARLY' && (
-
- )}
-
-
-
-
-
-
-
Self Hosted
-
Free
-
-
- For small teams and individuals who need a simple solution
-
-
-
- event('view-github')}
- >
- View on Github
-
-
-
-
-
Host your own instance
-
Full Control
-
Customizability
-
Docker Ready
-
Community Support
-
Free, Forever
-
-
-
-
-
Community
-
-
- {period === 'MONTHLY' && $30 }
- {period === 'YEARLY' && $300 }
-
-
-
-
- For fast-growing companies that aim to scale across multiple teams.
-
-
-
- Signup Now
-
-
-
-
Documenso Early Adopter Deal:
-
Join the movement
-
Simple signing solution
-
Email and Slack assistance
-
- Includes all upcoming features
-
-
Fixed, straightforward pricing
-
-
-
-
-
Enterprise
-
Pricing on request
-
-
- For large organizations that need extra flexibility and control.
-
-
-
event('enterprise-contact')}
- >
-
Contact Us
-
-
-
-
Everything in Community, plus:
-
Custom Subdomain
-
Compliance Check
-
Guaranteed Uptime
-
Reporting & Analysis
-
24/7 Support
-
-
-
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/share-connect-paid-widget-bento.tsx b/apps/web/src/components/(marketing)/share-connect-paid-widget-bento.tsx
deleted file mode 100644
index 05b6a3232..000000000
--- a/apps/web/src/components/(marketing)/share-connect-paid-widget-bento.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { HTMLAttributes } from 'react';
-
-import Image from 'next/image';
-
-import { cn } from '@documenso/ui/lib/utils';
-import { Card, CardContent } from '@documenso/ui/primitives/card';
-
-import backgroundPattern from '~/assets/background-pattern.png';
-import cardConnectionsFigure from '~/assets/card-connections-figure.png';
-import cardPaidFigure from '~/assets/card-paid-figure.png';
-import cardSharingFigure from '~/assets/card-sharing-figure.png';
-import cardWidgetFigure from '~/assets/card-widget-figure.png';
-
-export type ShareConnectPaidWidgetBentoProps = HTMLAttributes;
-
-export const ShareConnectPaidWidgetBento = ({
- className,
- ...props
-}: ShareConnectPaidWidgetBentoProps) => {
- return (
-
-
-
-
-
- Integrates with all your favourite tools.
- Send, connect, receive and embed everywhere.
-
-
-
-
-
-
- Easy Sharing (Soon).
- Receive your personal link to share with everyone you care about.
-
-
-
-
-
-
-
-
-
-
-
- Connections (Soon).
- Create connections and automations with Zapier and more to integrate with your
- favorite tools.
-
-
-
-
-
-
-
-
-
-
-
- Get paid (Soon).
- Integrated payments with stripe so you don’t have to worry about getting paid.
-
-
-
-
-
-
-
-
-
-
-
- React Widget (Soon).
- Easily embed Documenso into your product. Simply copy and paste our react widget into
- your application.
-
-
-
-
-
-
-
-
-
- );
-};
diff --git a/apps/web/src/components/(marketing)/widget.tsx b/apps/web/src/components/(marketing)/widget.tsx
deleted file mode 100644
index 15e15d04c..000000000
--- a/apps/web/src/components/(marketing)/widget.tsx
+++ /dev/null
@@ -1,402 +0,0 @@
-'use client';
-
-import { HTMLAttributes, KeyboardEvent, 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 { 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 { FormErrorMessage } from '../form/form-error-message';
-
-const ZWidgetFormSchema = z
- .object({
- email: z.string().email({ message: 'Please enter a valid email address.' }),
- name: z.string().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().min(1),
- }),
- ]),
- );
-
-export type TWidgetFormSchema = z.infer;
-
-export type WidgetProps = HTMLAttributes;
-
-export const Widget = ({ className, children, ...props }: WidgetProps) => {
- const { toast } = useToast();
- const event = usePlausible();
-
- const [step, setStep] = useState<'EMAIL' | 'NAME' | 'SIGN'>('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 === 'NAME') {
- return 2;
- }
-
- if (step === 'SIGN') {
- return 1;
- }
-
- return 3;
- }, [step]);
-
- const onNextStepClick = () => {
- if (step === 'EMAIL') {
- setStep('NAME');
-
- setTimeout(() => {
- document.querySelector('#name')?.focus();
- }, 0);
- }
-
- if (step === 'NAME') {
- setStep('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);
- });
-
- // eslint-disable-next-line turbo/no-undeclared-env-vars
- const planId = process.env.NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID;
-
- 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 (
- <>
-
-
-
-
-
-
-
- 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.
-
-
-
-
-
- setShowSigningDialog(false)}>
- Cancel
-
-
- onSignatureConfirmClick()}>Confirm
-
-
-
- >
- );
-};