mirror of
https://github.com/documenso/documenso.git
synced 2025-11-17 02:01:33 +10:00
feat: updated ui items
Signed-off-by: Adithya Krishna <adithya@documenso.com>
This commit is contained in:
@ -45,7 +45,7 @@ export default function MarketingLayout({ children }: MarketingLayoutProps) {
|
|||||||
<Image
|
<Image
|
||||||
src={backgroundPattern}
|
src={backgroundPattern}
|
||||||
alt="background pattern"
|
alt="background pattern"
|
||||||
className="!h-34 w-full object-cover"
|
className="h-[2rem] w-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<AnnouncementBar className="relative" isShown={true} />
|
<AnnouncementBar className="relative" isShown={true} />
|
||||||
|
|||||||
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { BadgeCheck, File } from 'lucide-react';
|
import { File } from 'lucide-react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import Check from '@documenso/assets/Check.svg';
|
||||||
import Lucas from '@documenso/assets/images/Lucas.png';
|
import Lucas from '@documenso/assets/images/Lucas.png';
|
||||||
import Timur from '@documenso/assets/images/Timur.png';
|
import Timur from '@documenso/assets/images/Timur.png';
|
||||||
import type { User } from '@documenso/prisma/client';
|
import type { User } from '@documenso/prisma/client';
|
||||||
@ -60,7 +63,7 @@ export const PublicProfileIntro = ({ user }: PublicProfileIntroProps) => {
|
|||||||
|
|
||||||
const isProfileURLClaimed = user.profileURL ? false : true;
|
const isProfileURLClaimed = user.profileURL ? false : true;
|
||||||
const [showClaimingDialog, setShowClaimingDialog] = useState(isProfileURLClaimed);
|
const [showClaimingDialog, setShowClaimingDialog] = useState(isProfileURLClaimed);
|
||||||
const [showClaimedDialog, setShowClaimedDialog] = useState(false);
|
const [showClaimedDialog, setShowClaimedDialog] = useState(true);
|
||||||
|
|
||||||
const onFormSubmit = async ({ profileURL }: TPublicProfileFormSchema) => {
|
const onFormSubmit = async ({ profileURL }: TPublicProfileFormSchema) => {
|
||||||
try {
|
try {
|
||||||
@ -92,40 +95,38 @@ export const PublicProfileIntro = ({ user }: PublicProfileIntroProps) => {
|
|||||||
<Dialog open={showClaimingDialog} onOpenChange={setShowClaimingDialog}>
|
<Dialog open={showClaimingDialog} onOpenChange={setShowClaimingDialog}>
|
||||||
<DialogContent position="center" className="pb-4">
|
<DialogContent position="center" className="pb-4">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="font-semi-bold text-center text-xl">
|
<DialogTitle className="font-semi-bold text-center text-2xl">
|
||||||
Introducing public profile!
|
Introducing public profile!
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-center">
|
<DialogDescription className="text-muted-foreground/60 text-center text-sm">
|
||||||
Reserve your Documenso public profile username
|
Reserve your Documenso public profile username
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<Card className="relative px-6 py-6">
|
<Card className="relative flex flex-col items-center border-none bg-gray-50 px-6 py-6 pb-0 shadow-none">
|
||||||
<Card className="flex flex-col items-center px-6 py-6">
|
<code className="rounded-md border-2 border-gray-200 px-1 py-1 text-sm">
|
||||||
<code className="bg-muted rounded-md px-1 py-1 text-sm">
|
<span>documenso.com/u/timur</span>
|
||||||
<span>documenso.com/u/timur</span>
|
</code>
|
||||||
</code>
|
<Avatar className="dark:border-border mt-2 h-20 w-20 border-2 border-solid border-white bg-white">
|
||||||
<Avatar className="dark:border-border mt-2 h-12 w-12 border-2 border-solid border-white">
|
<AvatarImage className="AvatarImage" src={Timur.src} alt="Timur" />
|
||||||
<AvatarImage className="AvatarImage" src={Timur.src} alt="Timur" />
|
<AvatarFallback className="text-xs text-gray-400">Timur</AvatarFallback>
|
||||||
<AvatarFallback className="text-xs text-gray-400">Timur</AvatarFallback>
|
</Avatar>
|
||||||
</Avatar>
|
<div className="flex flex-row gap-x-2">
|
||||||
<div className="flex flex-row gap-x-2">
|
Timur Ercan <Image alt="Check" src={Check} />
|
||||||
Timur Ercan <BadgeCheck fill="#A2E771" />
|
</div>
|
||||||
</div>
|
<span className="text-muted-foreground/60 text-center">
|
||||||
<span className="text-center">
|
Hey I’m Timur <br /> Pick any of the following agreements below and start signing to
|
||||||
Hey I’m Timur <br /> Pick any of the following agreements below and start signing to
|
get started
|
||||||
get started
|
</span>
|
||||||
</span>
|
<Card className="bg mt-2 w-full items-center shadow-none">
|
||||||
</Card>
|
<CardHeader className="p-4 text-gray-500">Documents</CardHeader>
|
||||||
<Card className="mt-2 items-center">
|
|
||||||
<CardHeader className="p-2">Documents</CardHeader>
|
|
||||||
<hr className="mb-2" />
|
<hr className="mb-2" />
|
||||||
<div className="mb-2 flex flex-row items-center justify-between">
|
<div className="mb-2 flex flex-row items-center justify-between">
|
||||||
<div className="flex flex-row items-center gap-x-2">
|
<div className="flex flex-row items-center gap-x-2">
|
||||||
<File className="ml-3" />
|
<File className="ml-3" />
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="text-md">NDA.pdf</span>
|
<span className="text-md">NDA.pdf</span>
|
||||||
<span className="text-muted-foregroun mt-0.5 text-xs">
|
<span className="text-muted-foreground mt-0.5 text-xs">
|
||||||
Like to discuss about my work?
|
Like to discuss about my work?
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -135,6 +136,12 @@ export const PublicProfileIntro = ({ user }: PublicProfileIntroProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
<div
|
||||||
|
className="fade-overlay bg-black-100 absolute bottom-0 h-1/4 w-full"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, white 75%, white 100%)`,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
@ -173,7 +180,7 @@ export const PublicProfileIntro = ({ user }: PublicProfileIntroProps) => {
|
|||||||
</Form>
|
</Form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<Dialog open={showClaimedDialog} onOpenChange={setShowClaimedDialog}>
|
<Dialog open={false} onOpenChange={setShowClaimedDialog}>
|
||||||
<DialogContent position="center" className="pb-4">
|
<DialogContent position="center" className="pb-4">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="font-semi-bold text-center text-xl">All set!</DialogTitle>
|
<DialogTitle className="font-semi-bold text-center text-xl">All set!</DialogTitle>
|
||||||
@ -192,7 +199,7 @@ export const PublicProfileIntro = ({ user }: PublicProfileIntroProps) => {
|
|||||||
<AvatarFallback className="text-xs text-gray-400">Timur</AvatarFallback>
|
<AvatarFallback className="text-xs text-gray-400">Timur</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="flex flex-row gap-x-2">
|
<div className="flex flex-row gap-x-2">
|
||||||
Lucas Smith <BadgeCheck fill="#A2E771" />
|
Lucas Smith <Image alt="Check" src={Check} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex inline-flex h-full w-full flex-col items-center justify-center gap-3 py-2">
|
<div className="flex inline-flex h-full w-full flex-col items-center justify-center gap-3 py-2">
|
||||||
<Skeleton className="w-75 h-4 animate-none rounded-full" />
|
<Skeleton className="w-75 h-4 animate-none rounded-full" />
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Card } from '@documenso/ui/primitives/card';
|
|
||||||
|
|
||||||
import ClaimUsernameCard from '../../../components/(dashboard)/claim-username-card/claim-username-card';
|
|
||||||
import { NewHeader } from '../../../components/(dashboard)/layout/new/new-header';
|
import { NewHeader } from '../../../components/(dashboard)/layout/new/new-header';
|
||||||
|
|
||||||
type SignUpLayoutProps = {
|
type SignUpLayoutProps = {
|
||||||
@ -13,13 +10,11 @@ export default function SignUpLayout({ children }: SignUpLayoutProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NewHeader className="mx-auto h-16 max-w-screen-xl px-4 md:h-20 lg:px-8" />
|
<NewHeader className="mx-auto h-16 max-w-screen-xl px-4 md:h-20 lg:px-8" />
|
||||||
<main className="bg-sand-100 flex flex-col items-center justify-center overflow-hidden px-4 py-12 md:p-12 lg:p-[7.2rem]">
|
<main
|
||||||
<div className="flex w-full items-center gap-x-8">
|
className="bg-sand-100 scale-90 items-center justify-center px-4 md:h-20 lg:mx-28 lg:px-8"
|
||||||
<ClaimUsernameCard />
|
style={{ height: 'calc(100vh - 80px)' }}
|
||||||
<Card className="px-6 py-6">
|
>
|
||||||
<div className="w-full">{children}</div>
|
<div className="grid grid-cols-12 gap-y-8 overflow-hidden p-2 lg:gap-x-8">{children}</div>
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
import { IS_GOOGLE_SSO_ENABLED } from '@documenso/lib/constants/auth';
|
|
||||||
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
|
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
|
||||||
|
|
||||||
import { SignUpForm } from '~/components/forms/signup';
|
import { SignUpForm } from '~/components/forms/signup';
|
||||||
@ -31,25 +29,7 @@ export default function SignUpPage({ searchParams }: SignUpPageProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-3xl font-semibold">Create a new account</h1>
|
<SignUpForm className="mt-1" initialEmail={email || undefined} isGoogleSSOEnabled={true} />
|
||||||
|
|
||||||
<p className="text-muted-foreground/60 mt-2 text-sm">
|
|
||||||
Create your account and start using state-of-the-art document signing. Open and beautiful
|
|
||||||
signing is within your grasp.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<SignUpForm
|
|
||||||
className="mt-1"
|
|
||||||
initialEmail={email || undefined}
|
|
||||||
isGoogleSSOEnabled={IS_GOOGLE_SSO_ENABLED}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<p className="text-muted-foreground mt-6 text-center text-sm">
|
|
||||||
Already have an account?{' '}
|
|
||||||
<Link href="/signin" className="text-primary duration-200 hover:opacity-70">
|
|
||||||
Sign in instead
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import React from 'react';
|
|||||||
|
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
|
||||||
import { BadgeCheck, File } from 'lucide-react';
|
import { File } from 'lucide-react';
|
||||||
|
|
||||||
|
import Check from '@documenso/assets/Check.svg';
|
||||||
import Timur from '@documenso/assets/images/Timur.png';
|
import Timur from '@documenso/assets/images/Timur.png';
|
||||||
import backgroundPattern from '@documenso/assets/images/background-blog-og.png';
|
import backgroundPattern from '@documenso/assets/images/background-blog-og.png';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
@ -13,68 +14,71 @@ import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/av
|
|||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
import { Card, CardFooter, CardHeader } from '@documenso/ui/primitives/card';
|
import { Card, CardFooter, CardHeader } from '@documenso/ui/primitives/card';
|
||||||
|
|
||||||
export default function ClaimUsernameCard() {
|
type ClaimUsernameCardProps = {
|
||||||
|
className: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ClaimUsernameCard({ className }: ClaimUsernameCardProps) {
|
||||||
const onSignUpClick = () => {};
|
const onSignUpClick = () => {};
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className={cn('relative', className)}>
|
||||||
<div className="absolute -inset-96 -z-[1] flex items-center justify-center bg-contain opacity-50">
|
<Card className={cn('relative h-full overflow-hidden px-16 py-16 shadow-none')}>
|
||||||
<Image
|
<Image
|
||||||
src={backgroundPattern}
|
src={backgroundPattern}
|
||||||
alt="background pattern"
|
alt="background pattern"
|
||||||
className="dark:brightness-95 dark:contrast-[100%] dark:invert"
|
className="absolute left-0 top-0 h-full w-full bg-cover opacity-50 dark:brightness-95 dark:contrast-[100%] dark:invert"
|
||||||
/>
|
/>
|
||||||
</div>
|
<Card className="mt-28 flex flex-col items-center px-6 py-6 shadow-none">
|
||||||
<Card className={cn('relative px-16 py-16')}>
|
<code className="rounded-md border-2 border-gray-200 px-1 py-1 text-sm">
|
||||||
<Card className="flex flex-col items-center px-6 py-6">
|
|
||||||
<code className="bg-muted rounded-md px-1 py-1 text-sm">
|
|
||||||
<span>documenso.com/u/timur</span>
|
<span>documenso.com/u/timur</span>
|
||||||
</code>
|
</code>
|
||||||
<Avatar className="dark:border-border mt-2 h-12 w-12 border-2 border-solid border-white">
|
<Avatar className="dark:border-border mt-2 h-20 w-20 border-2 border-solid border-white">
|
||||||
<AvatarImage className="AvatarImage" src={Timur.src} alt="Timur" />
|
<AvatarImage className="AvatarImage" src={Timur.src} alt="Timur" />
|
||||||
<AvatarFallback className="text-xs text-gray-400">Timur</AvatarFallback>
|
<AvatarFallback className="text-xs text-gray-400">Timur</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="flex flex-row gap-x-2">
|
<div className="mb-2 flex flex-row gap-x-2">
|
||||||
Timur Ercan <BadgeCheck fill="#A2E771" />
|
Timur Ercan <Image alt="Check" src={Check} />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-center">
|
<span className="text-muted-foreground/60 text-center ">
|
||||||
Hey I’m Timur <br /> Pick any of the following agreements below and start signing to get
|
Hey I’m Timur <br /> Pick any of the following agreements below and <br /> start signing
|
||||||
started
|
to get started
|
||||||
</span>
|
</span>
|
||||||
</Card>
|
<Card className="mt-2 w-full items-center shadow-none">
|
||||||
<Card className="mt-2 items-center">
|
<CardHeader className="p-4 text-gray-500">Documents</CardHeader>
|
||||||
<CardHeader className="p-2">Documents</CardHeader>
|
<hr className="mb-2" />
|
||||||
<hr className="mb-2" />
|
<div className="mb-2 flex flex-row items-center justify-between">
|
||||||
<div className="mb-2 flex flex-row items-center justify-between">
|
<div className="flex flex-row items-center gap-x-2">
|
||||||
<div className="flex flex-row items-center gap-x-2">
|
<File className="text-muted-foreground ml-3" />
|
||||||
<File className="ml-3" />
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-col">
|
<span className="text-md">NDA.pdf</span>
|
||||||
<span className="text-md">NDA.pdf</span>
|
<span className="text-muted-foreground mt-0.5 text-xs">
|
||||||
<span className="text-muted-foregroun mt-0.5 text-xs">
|
Like to discuss about my work?
|
||||||
Like to discuss about my work?
|
</span>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Button className="mr-3 px-6" variant="default">
|
||||||
|
Sign
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Button className="mr-3" variant="default">
|
<hr className="mb-2" />
|
||||||
Sign
|
<div className="mb-2 flex flex-row items-center justify-between">
|
||||||
</Button>
|
<div className="flex flex-row items-center gap-x-2">
|
||||||
</div>
|
<File className="text-muted-foreground ml-3" />
|
||||||
<hr className="mb-2" />
|
<div className="flex flex-col">
|
||||||
<div className="mb-2 flex flex-row items-center justify-between">
|
<span className="text-md">NDA.pdf</span>
|
||||||
<div className="flex flex-row items-center gap-x-2">
|
<span className="text-muted-foreground mt-0.5 text-xs">
|
||||||
<File className="ml-3" />
|
Like to discuss about my work?
|
||||||
<div className="flex flex-col">
|
</span>
|
||||||
<span className="text-md">NDA.pdf</span>
|
</div>
|
||||||
<span className="text-muted-foregroun mt-0.5 text-xs">
|
|
||||||
Like to discuss about my work?
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Button className="mr-3 px-6" variant="default">
|
||||||
|
Sign
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Button className="mr-3" variant="default">
|
</Card>
|
||||||
Sign
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Card>
|
</Card>
|
||||||
<CardFooter className="mt-20 justify-center">
|
|
||||||
|
<CardFooter className="mt-32 justify-center">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
@ -14,6 +17,7 @@ import { trpc } from '@documenso/trpc/react';
|
|||||||
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
import { ZPasswordSchema } from '@documenso/trpc/server/auth-router/schema';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
import { Card } from '@documenso/ui/primitives/card';
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -27,6 +31,16 @@ import { PasswordInput } from '@documenso/ui/primitives/password-input';
|
|||||||
import { SignaturePad } from '@documenso/ui/primitives/signature-pad';
|
import { SignaturePad } from '@documenso/ui/primitives/signature-pad';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
import ClaimUsernameCard from '../(dashboard)/claim-username-card/claim-username-card';
|
||||||
|
|
||||||
|
export const STEP = {
|
||||||
|
SIGNUP: 'SIGNUP',
|
||||||
|
CLAIM: 'CLAIM',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
type StepKeys = keyof typeof STEP;
|
||||||
|
type StepValues = (typeof STEP)[StepKeys];
|
||||||
|
|
||||||
const SIGN_UP_REDIRECT_PATH = '/documents';
|
const SIGN_UP_REDIRECT_PATH = '/documents';
|
||||||
|
|
||||||
export const ZSignUpFormSchema = z
|
export const ZSignUpFormSchema = z
|
||||||
@ -35,6 +49,7 @@ export const ZSignUpFormSchema = z
|
|||||||
email: z.string().email().min(1),
|
email: z.string().email().min(1),
|
||||||
password: ZPasswordSchema,
|
password: ZPasswordSchema,
|
||||||
signature: z.string().min(1, { message: 'We need your signature to sign documents' }),
|
signature: z.string().min(1, { message: 'We need your signature to sign documents' }),
|
||||||
|
profileURL: z.string().trim().min(1, { message: 'Please enter a valid URL slug.' }),
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
(data) => {
|
(data) => {
|
||||||
@ -58,6 +73,7 @@ export const SignUpForm = ({ className, initialEmail, isGoogleSSOEnabled }: Sign
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const analytics = useAnalytics();
|
const analytics = useAnalytics();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
const [step, setStep] = useState<StepValues>(STEP.SIGNUP);
|
||||||
|
|
||||||
let src: string | null = null;
|
let src: string | null = null;
|
||||||
if (searchParams) {
|
if (searchParams) {
|
||||||
@ -70,18 +86,33 @@ export const SignUpForm = ({ className, initialEmail, isGoogleSSOEnabled }: Sign
|
|||||||
email: initialEmail ?? '',
|
email: initialEmail ?? '',
|
||||||
password: '',
|
password: '',
|
||||||
signature: '',
|
signature: '',
|
||||||
|
profileURL: '',
|
||||||
},
|
},
|
||||||
resolver: zodResolver(ZSignUpFormSchema),
|
resolver: zodResolver(ZSignUpFormSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
const isSubmitting = form.formState.isSubmitting;
|
const isSubmitting = form.formState.isSubmitting;
|
||||||
|
const isValid = form.formState.isValid;
|
||||||
|
|
||||||
|
const signature = form.watch('signature');
|
||||||
|
|
||||||
const { mutateAsync: signup } = trpc.auth.signup.useMutation();
|
const { mutateAsync: signup } = trpc.auth.signup.useMutation();
|
||||||
|
const { mutateAsync: updatePublicProfile } = trpc.profile.updatePublicProfile.useMutation();
|
||||||
|
|
||||||
const onFormSubmit = async ({ name, email, password, signature }: TSignUpFormSchema) => {
|
const onFormSubmit = async ({
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
signature,
|
||||||
|
profileURL,
|
||||||
|
}: TSignUpFormSchema) => {
|
||||||
try {
|
try {
|
||||||
await signup({ name, email, password, signature });
|
await signup({ name, email, password, signature });
|
||||||
|
|
||||||
|
await updatePublicProfile({
|
||||||
|
profileURL,
|
||||||
|
});
|
||||||
|
|
||||||
await signIn('credentials', {
|
await signIn('credentials', {
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
@ -111,6 +142,16 @@ export const SignUpForm = ({ className, initialEmail, isGoogleSSOEnabled }: Sign
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onNextStepClick = () => {
|
||||||
|
if (step === STEP.SIGNUP) {
|
||||||
|
setStep(STEP.CLAIM);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.querySelector<HTMLElement>('#signature')?.focus();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onSignUpWithGoogleClick = async () => {
|
const onSignUpWithGoogleClick = async () => {
|
||||||
try {
|
try {
|
||||||
await signIn('google', { callbackUrl: SIGN_UP_REDIRECT_PATH });
|
await signIn('google', { callbackUrl: SIGN_UP_REDIRECT_PATH });
|
||||||
@ -124,107 +165,199 @@ export const SignUpForm = ({ className, initialEmail, isGoogleSSOEnabled }: Sign
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stepsRemaining = useMemo(() => {
|
||||||
|
if (step === STEP.CLAIM) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}, [step]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<>
|
||||||
<form
|
<ClaimUsernameCard className="col-span-12 gap-y-4 lg:col-span-7" />
|
||||||
className={cn('flex w-full flex-col gap-y-4', className)}
|
<Card className="col-span-12 gap-y-4 bg-gray-50 px-6 py-6 shadow-none lg:col-span-5">
|
||||||
onSubmit={form.handleSubmit(onFormSubmit)}
|
<div className="w-full">
|
||||||
>
|
{step === STEP.SIGNUP && (
|
||||||
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
|
<>
|
||||||
<FormField
|
<h1 className="text-3xl font-semibold">Create a new account</h1>
|
||||||
control={form.control}
|
|
||||||
name="name"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Full Name</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input type="text" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
<p className="text-muted-foreground/60 mt-2 text-sm">
|
||||||
control={form.control}
|
Create your account and start using state-of-the-art document signing. Open and
|
||||||
name="email"
|
beautiful signing is within your grasp.
|
||||||
render={({ field }) => (
|
</p>
|
||||||
<FormItem>
|
</>
|
||||||
<FormLabel>Email Address</FormLabel>
|
)}
|
||||||
<FormControl>
|
{step === STEP.CLAIM && (
|
||||||
<Input type="email" {...field} />
|
<>
|
||||||
</FormControl>
|
<h1 className="text-3xl font-semibold">Claim your username now</h1>
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
<p className="text-muted-foreground/60 mt-2 text-sm">
|
||||||
control={form.control}
|
You will get notified & be able to set up your documenso public profile when we
|
||||||
name="password"
|
launch the feature
|
||||||
render={({ field }) => (
|
</p>
|
||||||
<FormItem>
|
</>
|
||||||
<FormLabel>Password</FormLabel>
|
)}
|
||||||
<FormControl>
|
<hr className="mb-6 mt-4" />
|
||||||
<PasswordInput {...field} />
|
<Form {...form}>
|
||||||
</FormControl>
|
<form
|
||||||
<FormMessage />
|
className={cn('flex h-full w-full flex-col gap-y-4', className)}
|
||||||
</FormItem>
|
onSubmit={form.handleSubmit(onFormSubmit)}
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="signature"
|
|
||||||
render={({ field: { onChange } }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Sign Here</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<SignaturePad
|
|
||||||
className="h-36 w-full"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
containerClassName="mt-2 rounded-lg border bg-background"
|
|
||||||
onChange={(v) => onChange(v ?? '')}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
size="lg"
|
|
||||||
loading={isSubmitting}
|
|
||||||
className="dark:bg-documenso dark:hover:opacity-90"
|
|
||||||
>
|
|
||||||
{isSubmitting ? 'Signing up...' : 'Sign Up'}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{isGoogleSSOEnabled && (
|
|
||||||
<>
|
|
||||||
<div className="relative flex items-center justify-center gap-x-4 py-2 text-xs uppercase">
|
|
||||||
<div className="bg-border h-px flex-1" />
|
|
||||||
<span className="text-muted-foreground bg-transparent">Or</span>
|
|
||||||
<div className="bg-border h-px flex-1" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="lg"
|
|
||||||
variant={'outline'}
|
|
||||||
className="bg-background text-muted-foreground border"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
onClick={onSignUpWithGoogleClick}
|
|
||||||
>
|
>
|
||||||
<FcGoogle className="mr-2 h-5 w-5" />
|
<fieldset className="flex w-full flex-col gap-y-4" disabled={isSubmitting}>
|
||||||
Sign Up with Google
|
<div className={cn(step === STEP.SIGNUP && 'hidden')}>
|
||||||
</Button>
|
<FormField
|
||||||
</>
|
control={form.control}
|
||||||
)}
|
name="profileURL"
|
||||||
</form>
|
render={({ field }) => (
|
||||||
</Form>
|
<FormItem>
|
||||||
|
<FormLabel>Public profile URL</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<>
|
||||||
|
<Input id="username" type="text" className="mb-2 mt-2" {...field} />
|
||||||
|
<div className="mt-2">
|
||||||
|
<code className="bg-muted rounded-md px-1 py-1 text-sm">
|
||||||
|
documenso.com/u/
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={cn('space-y-2', step === STEP.CLAIM && 'invisible')}>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Full Name</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input type="text" className="bg-white" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="email"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Email Address</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input type="email" className="bg-white" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="password"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Password</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<PasswordInput className="bg-white" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="signature"
|
||||||
|
render={({ field: { onChange } }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Sign Here</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<SignaturePad
|
||||||
|
id="signatureText"
|
||||||
|
className="w-full"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
containerClassName="mt-2 rounded-lg border bg-background"
|
||||||
|
onChange={(v) => onChange(v ?? '')}
|
||||||
|
height={200}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
{isGoogleSSOEnabled && (
|
||||||
|
<>
|
||||||
|
<div className="relative flex items-center justify-center gap-x-4 py-2 text-xs uppercase">
|
||||||
|
<div className="bg-border h-px flex-1" />
|
||||||
|
<span className="text-muted-foreground bg-transparent">Or</span>
|
||||||
|
<div className="bg-border h-px flex-1" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
size="lg"
|
||||||
|
variant={'outline'}
|
||||||
|
className="bg-background text-muted-foreground border"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
onClick={onSignUpWithGoogleClick}
|
||||||
|
>
|
||||||
|
<FcGoogle className="mr-2 h-5 w-5" />
|
||||||
|
Sign Up with Google
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<p className="text-muted-foreground text-left text-sm">
|
||||||
|
Already have an account?{' '}
|
||||||
|
<Link href="/signin" className="text-primary duration-200 hover:opacity-70">
|
||||||
|
Sign in instead
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
<div className="mt-6 flex items-center justify-between">
|
||||||
|
<p className="text-muted-foreground text-xs">
|
||||||
|
{isValid ? 'Claim username' : `Basic details ${stepsRemaining}/2`}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-muted-foreground block text-xs md:hidden">Minimise contract</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-background relative h-[2px] w-full">
|
||||||
|
<div
|
||||||
|
className={cn('bg-primary/60 absolute inset-y-0 left-0 duration-200', {
|
||||||
|
'w-1/2': stepsRemaining === 1,
|
||||||
|
'w-full': isValid,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{!isValid && (
|
||||||
|
<Button
|
||||||
|
loading={isSubmitting}
|
||||||
|
className="dark:bg-documenso ml-auto w-52 dark:hover:opacity-90"
|
||||||
|
onClick={() => onNextStepClick()}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{isValid && (
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
loading={isSubmitting}
|
||||||
|
className="dark:bg-documenso ml-auto w-52 dark:hover:opacity-90"
|
||||||
|
>
|
||||||
|
Complete
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
3
packages/assets/Check.svg
Normal file
3
packages/assets/Check.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.54572 1.10579C9.57786 -0.0821356 11.4223 -0.0821393 12.4545 1.10579L13.2585 2.03112C13.3917 2.18452 13.5947 2.25838 13.7954 2.22654L15.0061 2.03449C16.5603 1.78794 17.9732 2.97353 18.0003 4.54698L18.0214 5.77262C18.0249 5.97581 18.1329 6.16284 18.3071 6.26746L19.358 6.89855C20.7071 7.70873 21.0274 9.52517 20.0368 10.7479L19.2651 11.7004C19.1372 11.8583 19.0997 12.0709 19.1659 12.2631L19.5652 13.422C20.0779 14.9098 19.1557 16.5072 17.6109 16.8071L16.4075 17.0407C16.208 17.0794 16.0426 17.2182 15.9698 17.408L15.5308 18.5525C14.9672 20.0218 13.234 20.6526 11.8578 19.8893L10.7858 19.2948C10.6081 19.1962 10.3921 19.1962 10.2144 19.2948L9.14242 19.8893C7.76623 20.6526 6.033 20.0218 5.4694 18.5525L5.03038 17.408C4.9576 17.2182 4.79216 17.0794 4.59268 17.0407L3.38932 16.8071C1.84448 16.5072 0.922245 14.9098 1.43495 13.422L1.83431 12.2631C1.90052 12.0709 1.86302 11.8583 1.7351 11.7004L0.963432 10.7479C-0.0272148 9.52517 0.293068 7.70873 1.64218 6.89855L2.69306 6.26746C2.86728 6.16284 2.97526 5.97581 2.97875 5.77263L2.99985 4.54699C3.02694 2.97354 4.43987 1.78794 5.99413 2.03449L7.20481 2.22654C7.40551 2.25838 7.60845 2.18452 7.74173 2.03112L8.54572 1.10579ZM13.7072 9.42195C14.0977 9.03143 14.0977 8.39826 13.7072 8.00774C13.3167 7.61721 12.6835 7.61721 12.293 8.00774L9.5001 10.8006L8.7072 10.0077C8.31667 9.61721 7.68351 9.61721 7.29298 10.0077C6.90246 10.3983 6.90246 11.0314 7.29298 11.422L8.43944 12.5684C9.02522 13.1542 9.97497 13.1542 10.5608 12.5684L13.7072 9.42195Z" fill="#7AC455"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -17,6 +17,7 @@ export type SignaturePadProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'onChang
|
|||||||
onChange?: (_signatureDataUrl: string | null) => void;
|
onChange?: (_signatureDataUrl: string | null) => void;
|
||||||
containerClassName?: string;
|
containerClassName?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
height?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignaturePad = ({
|
export const SignaturePad = ({
|
||||||
@ -25,6 +26,7 @@ export const SignaturePad = ({
|
|||||||
defaultValue,
|
defaultValue,
|
||||||
onChange,
|
onChange,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
height,
|
||||||
...props
|
...props
|
||||||
}: SignaturePadProps) => {
|
}: SignaturePadProps) => {
|
||||||
const $el = useRef<HTMLCanvasElement>(null);
|
const $el = useRef<HTMLCanvasElement>(null);
|
||||||
@ -230,6 +232,7 @@ export const SignaturePad = ({
|
|||||||
onPointerUp={(event) => onMouseUp(event)}
|
onPointerUp={(event) => onMouseUp(event)}
|
||||||
onPointerLeave={(event) => onMouseLeave(event)}
|
onPointerLeave={(event) => onMouseLeave(event)}
|
||||||
onPointerEnter={(event) => onMouseEnter(event)}
|
onPointerEnter={(event) => onMouseEnter(event)}
|
||||||
|
height={height}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user