From 942aac691225f4895c159a0260890ad2c576e0c9 Mon Sep 17 00:00:00 2001 From: MohamedBassem Date: Thu, 22 Feb 2024 15:32:40 +0000 Subject: feature: Add support for credentials registration and sign in --- .../web/app/signin/components/CredentialsForm.tsx | 222 +++++++++++++++++++++ packages/web/app/signin/components/SignInForm.tsx | 35 +++- packages/web/app/signin/page.tsx | 18 +- 3 files changed, 262 insertions(+), 13 deletions(-) create mode 100644 packages/web/app/signin/components/CredentialsForm.tsx (limited to 'packages/web/app') diff --git a/packages/web/app/signin/components/CredentialsForm.tsx b/packages/web/app/signin/components/CredentialsForm.tsx new file mode 100644 index 00000000..60b61156 --- /dev/null +++ b/packages/web/app/signin/components/CredentialsForm.tsx @@ -0,0 +1,222 @@ +"use client"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { ActionButton } from "@/components/ui/action-button"; +import { zSignUpSchema } from "@/lib/types/api/users"; +import { signIn } from "next-auth/react"; +import { useState } from "react"; +import { api } from "@/lib/trpc"; +import { useRouter } from "next/navigation"; +import { TRPCClientError } from "@trpc/client"; + +const signInSchema = z.object({ + email: z.string().email(), + password: z.string(), +}); + +function SignIn() { + const [signinError, setSigninError] = useState(false); + const router = useRouter(); + const form = useForm>({ + resolver: zodResolver(signInSchema), + }); + + return ( +
+ { + const resp = await signIn("credentials", { + redirect: false, + email: value.email, + password: value.password, + }); + if (!resp || !resp?.ok) { + setSigninError(true); + return; + } + router.replace("/"); + })} + > +
+ {signinError && ( +

+ Incorrect username or password +

+ )} + { + return ( + + Email + + + + + + ); + }} + /> + { + return ( + + Password + + + + + + ); + }} + /> + + Sign In + +
+
+ + ); +} + +function SignUp() { + const form = useForm>({ + resolver: zodResolver(zSignUpSchema), + }); + const [errorMessage, setErrorMessage] = useState(""); + + const router = useRouter(); + + const createUserMutation = api.users.create.useMutation(); + + return ( +
+ { + try { + await createUserMutation.mutateAsync(value); + } catch (e) { + if (e instanceof TRPCClientError) { + setErrorMessage(e.message); + } + return; + } + const resp = await signIn("credentials", { + redirect: false, + email: value.email, + password: value.password, + }); + if (!resp || !resp.ok) { + setErrorMessage("Hit an unexpected error while signing in"); + return; + } + router.replace("/"); + })} + > +
+ {errorMessage && ( +

{errorMessage}

+ )} + { + return ( + + Name + + + + + + ); + }} + /> + { + return ( + + Email + + + + + + ); + }} + /> + { + return ( + + Password + + + + + + ); + }} + /> + { + return ( + + Confirm Password + + + + + + ); + }} + /> + + Sign Up + +
+
+ + ); +} + +export default function CredentialsForm() { + return ( + + + Sign In + Sign Up + + + + + + + + + ); +} diff --git a/packages/web/app/signin/components/SignInForm.tsx b/packages/web/app/signin/components/SignInForm.tsx index 0b625f1e..986718bf 100644 --- a/packages/web/app/signin/components/SignInForm.tsx +++ b/packages/web/app/signin/components/SignInForm.tsx @@ -1,16 +1,37 @@ import { getProviders } from "next-auth/react"; import SignInProviderButton from "./SignInProviderButton"; +import CredentialsForm from "./CredentialsForm"; export default async function SignInForm() { - const providers = (await getProviders()) ?? []; + const providers = await getProviders(); + let providerValues; + if (providers) { + providerValues = Object.values(providers).filter( + // Credentials are handled manually by the sign in form + (p) => p.id != "credentials", + ); + } return ( -
- {Object.values(providers).map((provider) => ( -
- -
- ))} +
+ + + {providerValues && ( + <> +
+
+ Or +
+
+
+ {providerValues.map((provider) => ( +
+ +
+ ))} +
+ + )}
); } diff --git a/packages/web/app/signin/page.tsx b/packages/web/app/signin/page.tsx index 1556ff2c..f578a845 100644 --- a/packages/web/app/signin/page.tsx +++ b/packages/web/app/signin/page.tsx @@ -1,17 +1,23 @@ import { PackageOpen } from "lucide-react"; import SignInForm from "./components/SignInForm"; +import { redirect } from "next/dist/client/components/navigation"; +import { getServerAuthSession } from "@/server/auth"; export default async function SignInPage() { - // TODO Add support for email and credential signin form + const session = await getServerAuthSession(); + if (session) { + redirect("/"); + } + return ( -
-
+
+
- + - Hoarder +

Hoarder

-
+
-- cgit v1.2.3-70-g09d2