From 0c680e0111e7ccfdf5228d589ad25aa91139e7da Mon Sep 17 00:00:00 2001 From: nafees nazik Date: Fri, 1 Sep 2023 16:25:00 +0530 Subject: [PATCH 1/8] fix: component style --- packages/ui/primitives/alert.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/ui/primitives/alert.tsx b/packages/ui/primitives/alert.tsx index 190f7781d..a53d8fc33 100644 --- a/packages/ui/primitives/alert.tsx +++ b/packages/ui/primitives/alert.tsx @@ -5,13 +5,13 @@ import { VariantProps, cva } from 'class-variance-authority'; import { cn } from '../lib/utils'; const alertVariants = cva( - 'relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11', + 'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground', { variants: { variant: { default: 'bg-background text-foreground', destructive: - 'text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive', + 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', }, }, defaultVariants: { @@ -19,7 +19,6 @@ const alertVariants = cva( }, }, ); - const Alert = React.forwardRef< HTMLDivElement, React.HTMLAttributes & VariantProps From fe90aa3b7b2437bf48582a243ebac5ffa5ea0e1d Mon Sep 17 00:00:00 2001 From: nafees nazik Date: Fri, 1 Sep 2023 16:25:27 +0530 Subject: [PATCH 2/8] feat: add api error --- apps/web/src/components/forms/signin.tsx | 143 ++++++++++++++--------- 1 file changed, 88 insertions(+), 55 deletions(-) diff --git a/apps/web/src/components/forms/signin.tsx b/apps/web/src/components/forms/signin.tsx index 2ffb2798e..5322dcf78 100644 --- a/apps/web/src/components/forms/signin.tsx +++ b/apps/web/src/components/forms/signin.tsx @@ -1,18 +1,29 @@ 'use client'; +/* eslint-disable @typescript-eslint/consistent-type-assertions */ +import { useSearchParams } from 'next/navigation'; + import { zodResolver } from '@hookform/resolvers/zod'; -import { Loader } from 'lucide-react'; +import { AlertTriangle, Loader } from 'lucide-react'; import { signIn } from 'next-auth/react'; import { useForm } from 'react-hook-form'; import { FcGoogle } from 'react-icons/fc'; import { z } from 'zod'; +import { ErrorCodes } from '@documenso/lib/next-auth/error-codes'; import { cn } from '@documenso/ui/lib/utils'; +import { Alert, AlertDescription } from '@documenso/ui/primitives/alert'; import { Button } from '@documenso/ui/primitives/button'; import { Input } from '@documenso/ui/primitives/input'; import { Label } from '@documenso/ui/primitives/label'; import { useToast } from '@documenso/ui/primitives/use-toast'; +const ErrorMessages = { + [ErrorCodes.CredentialsNotFound]: 'Credentials not found', + [ErrorCodes.IncorrectEmailPassword]: 'Incorrect email or password', + [ErrorCodes.UserMissingPassword]: 'User is missing password', +}; + export const ZSignInFormSchema = z.object({ email: z.string().email().min(1), password: z.string().min(6).max(72), @@ -25,6 +36,7 @@ export type SignInFormProps = { }; export const SignInForm = ({ className }: SignInFormProps) => { + const searchParams = useSearchParams(); const { toast } = useToast(); const { @@ -74,62 +86,83 @@ export const SignInForm = ({ className }: SignInFormProps) => { }; return ( -
-
- + <> + {searchParams?.get('error') && ( +
+ + - + + {ErrorMessages[searchParams?.get('error') as keyof typeof ErrorMessages] ?? + 'an unknown error occured'} + + +
+ )} - {errors.email && {errors.email.message}} -
- -
- - - - - {errors.password && ( - {errors.password.message} - )} -
- - - -
-
- Or continue with -
-
- - - +
+ + + + + {errors.email && ( + {errors.email.message} + )} +
+ +
+ + + + + {errors.password && ( + {errors.password.message} + )} +
+ + + +
+
+ Or continue with +
+
+ + + + ); }; From d9da09c1e7d7f24ef99ba0ad00dac2c81a53bcc5 Mon Sep 17 00:00:00 2001 From: nafees nazik Date: Fri, 1 Sep 2023 16:25:49 +0530 Subject: [PATCH 3/8] fix: typo --- apps/web/src/components/forms/signin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/components/forms/signin.tsx b/apps/web/src/components/forms/signin.tsx index 5322dcf78..2dcad75dc 100644 --- a/apps/web/src/components/forms/signin.tsx +++ b/apps/web/src/components/forms/signin.tsx @@ -94,7 +94,7 @@ export const SignInForm = ({ className }: SignInFormProps) => { {ErrorMessages[searchParams?.get('error') as keyof typeof ErrorMessages] ?? - 'an unknown error occured'} + 'an unknown error occurred'}
From 5540fcf0d21ed62e5f05ae7dad61b4f531f082e3 Mon Sep 17 00:00:00 2001 From: nafees nazik Date: Sat, 2 Sep 2023 11:46:12 +0530 Subject: [PATCH 4/8] fix: use toast --- apps/web/src/components/forms/signin.tsx | 159 ++++++++++++----------- 1 file changed, 81 insertions(+), 78 deletions(-) diff --git a/apps/web/src/components/forms/signin.tsx b/apps/web/src/components/forms/signin.tsx index 2dcad75dc..4d5e2b444 100644 --- a/apps/web/src/components/forms/signin.tsx +++ b/apps/web/src/components/forms/signin.tsx @@ -1,10 +1,11 @@ 'use client'; -/* eslint-disable @typescript-eslint/consistent-type-assertions */ +import { useEffect, useRef } from 'react'; + import { useSearchParams } from 'next/navigation'; import { zodResolver } from '@hookform/resolvers/zod'; -import { AlertTriangle, Loader } from 'lucide-react'; +import { Loader } from 'lucide-react'; import { signIn } from 'next-auth/react'; import { useForm } from 'react-hook-form'; import { FcGoogle } from 'react-icons/fc'; @@ -12,7 +13,6 @@ import { z } from 'zod'; import { ErrorCodes } from '@documenso/lib/next-auth/error-codes'; import { cn } from '@documenso/ui/lib/utils'; -import { Alert, AlertDescription } from '@documenso/ui/primitives/alert'; import { Button } from '@documenso/ui/primitives/button'; import { Input } from '@documenso/ui/primitives/input'; import { Label } from '@documenso/ui/primitives/label'; @@ -51,6 +51,30 @@ export const SignInForm = ({ className }: SignInFormProps) => { resolver: zodResolver(ZSignInFormSchema), }); + const timer = useRef(null); + + useEffect(() => { + const error = searchParams?.get('error'); + if (error) { + timer.current = setTimeout(() => { + toast({ + variant: 'destructive', + description: + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + ErrorMessages[searchParams?.get('error') as keyof typeof ErrorMessages] ?? + 'an unknown error occurred', + }); + }, 100); + } + return () => { + if (timer.current) { + clearInterval(timer.current); + } + timer.current = null; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const onFormSubmit = async ({ email, password }: TSignInFormSchema) => { try { await signIn('credentials', { @@ -86,83 +110,62 @@ export const SignInForm = ({ className }: SignInFormProps) => { }; return ( - <> - {searchParams?.get('error') && ( -
- - +
+
+ - - {ErrorMessages[searchParams?.get('error') as keyof typeof ErrorMessages] ?? - 'an unknown error occurred'} - - -
- )} + - {errors.email.message}} +
+ +
+ + + + + {errors.password && ( + {errors.password.message} + )} +
+ + + +
+
+ Or continue with +
+
+ + - -
-
- Or continue with -
-
- - - - + + Google + + ); }; From c7993807870425f7d6504b79447ae846c9edf610 Mon Sep 17 00:00:00 2001 From: nafees nazik Date: Sat, 2 Sep 2023 11:51:21 +0530 Subject: [PATCH 5/8] chore: add comments --- apps/web/src/components/forms/signin.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/src/components/forms/signin.tsx b/apps/web/src/components/forms/signin.tsx index 4d5e2b444..837bc180c 100644 --- a/apps/web/src/components/forms/signin.tsx +++ b/apps/web/src/components/forms/signin.tsx @@ -57,6 +57,7 @@ export const SignInForm = ({ className }: SignInFormProps) => { const error = searchParams?.get('error'); if (error) { timer.current = setTimeout(() => { + // FIXME: Toast not firing without the TimeOut toast({ variant: 'destructive', description: From 692722d32eb86a94f7a6f87d4e7d860a2af5ff87 Mon Sep 17 00:00:00 2001 From: nafees nazik Date: Sat, 2 Sep 2023 11:55:44 +0530 Subject: [PATCH 6/8] revert: fix: component style --- packages/ui/primitives/alert.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/ui/primitives/alert.tsx b/packages/ui/primitives/alert.tsx index a53d8fc33..190f7781d 100644 --- a/packages/ui/primitives/alert.tsx +++ b/packages/ui/primitives/alert.tsx @@ -5,13 +5,13 @@ import { VariantProps, cva } from 'class-variance-authority'; import { cn } from '../lib/utils'; const alertVariants = cva( - 'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground', + 'relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11', { variants: { variant: { default: 'bg-background text-foreground', destructive: - 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', + 'text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive', }, }, defaultVariants: { @@ -19,6 +19,7 @@ const alertVariants = cva( }, }, ); + const Alert = React.forwardRef< HTMLDivElement, React.HTMLAttributes & VariantProps From 6e095921e6370119957523812ee7080ae1e6bff9 Mon Sep 17 00:00:00 2001 From: Mythie Date: Tue, 5 Sep 2023 11:29:23 +1000 Subject: [PATCH 7/8] fix: tidy up code --- apps/web/src/components/forms/signin.tsx | 39 +++++++++++------------- packages/lib/next-auth/auth-options.ts | 10 +++--- packages/lib/next-auth/error-codes.ts | 14 ++++++--- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/apps/web/src/components/forms/signin.tsx b/apps/web/src/components/forms/signin.tsx index 837bc180c..e88495730 100644 --- a/apps/web/src/components/forms/signin.tsx +++ b/apps/web/src/components/forms/signin.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useRef } from 'react'; +import { useEffect } from 'react'; import { useSearchParams } from 'next/navigation'; @@ -11,7 +11,7 @@ import { useForm } from 'react-hook-form'; import { FcGoogle } from 'react-icons/fc'; import { z } from 'zod'; -import { ErrorCodes } from '@documenso/lib/next-auth/error-codes'; +import { ErrorCode, isErrorCode } from '@documenso/lib/next-auth/error-codes'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { Input } from '@documenso/ui/primitives/input'; @@ -19,9 +19,10 @@ import { Label } from '@documenso/ui/primitives/label'; import { useToast } from '@documenso/ui/primitives/use-toast'; const ErrorMessages = { - [ErrorCodes.CredentialsNotFound]: 'Credentials not found', - [ErrorCodes.IncorrectEmailPassword]: 'Incorrect email or password', - [ErrorCodes.UserMissingPassword]: 'User is missing password', + [ErrorCode.CREDENTIALS_NOT_FOUND]: 'The email or password provided is incorrect', + [ErrorCode.INCORRECT_EMAIL_PASSWORD]: 'The email or password provided is incorrect', + [ErrorCode.USER_MISSING_PASSWORD]: + 'This account appears to be using a social login method, please sign in using that method', }; export const ZSignInFormSchema = z.object({ @@ -36,8 +37,8 @@ export type SignInFormProps = { }; export const SignInForm = ({ className }: SignInFormProps) => { - const searchParams = useSearchParams(); const { toast } = useToast(); + const searchParams = useSearchParams(); const { register, @@ -51,30 +52,26 @@ export const SignInForm = ({ className }: SignInFormProps) => { resolver: zodResolver(ZSignInFormSchema), }); - const timer = useRef(null); + const errorCode = searchParams?.get('error'); useEffect(() => { - const error = searchParams?.get('error'); - if (error) { - timer.current = setTimeout(() => { - // FIXME: Toast not firing without the TimeOut + const timeout: NodeJS.Timeout | null = null; + + if (isErrorCode(errorCode)) { + setTimeout(() => { toast({ variant: 'destructive', - description: - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - ErrorMessages[searchParams?.get('error') as keyof typeof ErrorMessages] ?? - 'an unknown error occurred', + description: ErrorMessages[errorCode] ?? 'An unknown error occurred', }); - }, 100); + }, 0); } + return () => { - if (timer.current) { - clearInterval(timer.current); + if (timeout) { + clearTimeout(timeout); } - timer.current = null; }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [errorCode, toast]); const onFormSubmit = async ({ email, password }: TSignInFormSchema) => { try { diff --git a/packages/lib/next-auth/auth-options.ts b/packages/lib/next-auth/auth-options.ts index 7417fbfc8..cd31926c2 100644 --- a/packages/lib/next-auth/auth-options.ts +++ b/packages/lib/next-auth/auth-options.ts @@ -7,7 +7,7 @@ import GoogleProvider, { GoogleProfile } from 'next-auth/providers/google'; import { prisma } from '@documenso/prisma'; import { getUserByEmail } from '../server-only/user/get-user-by-email'; -import { ErrorCodes } from './error-codes'; +import { ErrorCode } from './error-codes'; export const NEXT_AUTH_OPTIONS: AuthOptions = { adapter: PrismaAdapter(prisma), @@ -24,23 +24,23 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = { }, authorize: async (credentials, _req) => { if (!credentials) { - throw new Error(ErrorCodes.CredentialsNotFound); + throw new Error(ErrorCode.CREDENTIALS_NOT_FOUND); } const { email, password } = credentials; const user = await getUserByEmail({ email }).catch(() => { - throw new Error(ErrorCodes.IncorrectEmailPassword); + throw new Error(ErrorCode.INCORRECT_EMAIL_PASSWORD); }); if (!user.password) { - throw new Error(ErrorCodes.UserMissingPassword); + throw new Error(ErrorCode.USER_MISSING_PASSWORD); } const isPasswordsSame = await compare(password, user.password); if (!isPasswordsSame) { - throw new Error(ErrorCodes.IncorrectEmailPassword); + throw new Error(ErrorCode.INCORRECT_EMAIL_PASSWORD); } return { diff --git a/packages/lib/next-auth/error-codes.ts b/packages/lib/next-auth/error-codes.ts index f0a7caada..26e8f5b97 100644 --- a/packages/lib/next-auth/error-codes.ts +++ b/packages/lib/next-auth/error-codes.ts @@ -1,5 +1,11 @@ -export const ErrorCodes = { - IncorrectEmailPassword: 'incorrect-email-password', - UserMissingPassword: 'missing-password', - CredentialsNotFound: 'credentials-not-found', +export const isErrorCode = (code: unknown): code is ErrorCode => { + return typeof code === 'string' && code in ErrorCode; +}; + +export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode]; + +export const ErrorCode = { + INCORRECT_EMAIL_PASSWORD: 'INCORRECT_EMAIL_PASSWORD', + USER_MISSING_PASSWORD: 'USER_MISSING_PASSWORD', + CREDENTIALS_NOT_FOUND: 'CREDENTIALS_NOT_FOUND', } as const; From 17af4d25bd4ea7d65fabf60956802918988b1255 Mon Sep 17 00:00:00 2001 From: Mythie Date: Tue, 5 Sep 2023 11:33:49 +1000 Subject: [PATCH 8/8] fix: actually make timeouts clear --- apps/web/src/components/forms/signin.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/forms/signin.tsx b/apps/web/src/components/forms/signin.tsx index e88495730..5e44146ea 100644 --- a/apps/web/src/components/forms/signin.tsx +++ b/apps/web/src/components/forms/signin.tsx @@ -55,10 +55,10 @@ export const SignInForm = ({ className }: SignInFormProps) => { const errorCode = searchParams?.get('error'); useEffect(() => { - const timeout: NodeJS.Timeout | null = null; + let timeout: NodeJS.Timeout | null = null; if (isErrorCode(errorCode)) { - setTimeout(() => { + timeout = setTimeout(() => { toast({ variant: 'destructive', description: ErrorMessages[errorCode] ?? 'An unknown error occurred',