From 74526990f2743a25c1e52db5efaf00f0a0282106 Mon Sep 17 00:00:00 2001 From: valhize Date: Thu, 21 May 2026 04:44:20 +0700 Subject: [PATCH] feat: add auth pages --- app/components/login.tsx | 71 ++++++++++++++++++++++++++++++++++ app/components/signup.tsx | 76 +++++++++++++++++++++++++++++++++++++ app/components/ui/input.tsx | 19 ++++++++++ app/lib/auth-client.ts | 4 +- app/routes/_index.tsx | 15 ++++++++ app/routes/log-in.tsx | 20 ++++++++++ app/routes/sign-up.tsx | 20 ++++++++++ 7 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 app/components/login.tsx create mode 100644 app/components/signup.tsx create mode 100644 app/components/ui/input.tsx create mode 100644 app/routes/log-in.tsx create mode 100644 app/routes/sign-up.tsx diff --git a/app/components/login.tsx b/app/components/login.tsx new file mode 100644 index 0000000..7815674 --- /dev/null +++ b/app/components/login.tsx @@ -0,0 +1,71 @@ +import { useState } from "react"; +import { useNavigate, Form, Link } from "react-router"; +import { authClient } from "~/lib/auth-client"; +import { Input } from "~/components/ui/input"; +import { Button } from "~/components/ui/button"; + +export default function Login() { + const navigate = useNavigate(); + const [error, setError] = useState(null); + const [pending, setPending] = useState(false); + + async function onSubmit(event: React.FormEvent) { + event.preventDefault(); + + setError(null); + setPending(true); + + const formData = new FormData(event.currentTarget); + + const { error } = await authClient.signIn.email({ + email: String(formData.get("email")), + password: String(formData.get("password")), + }); + + setPending(false); + + if (error) { + setError(error.message ?? "Could not log in."); + return; + } + + navigate("/"); + } + + return ( +
+
+
+ Log in + + Continue to Coppie. + +
+ +
+ + + + + {error ?

{error}

: null} + + +
+ +

+ No account?{" "} + + Sign up + +

+
+
+ ); +} diff --git a/app/components/signup.tsx b/app/components/signup.tsx new file mode 100644 index 0000000..567a6db --- /dev/null +++ b/app/components/signup.tsx @@ -0,0 +1,76 @@ +import { Form, Link, redirect, useNavigate } from "react-router"; +import { useState } from "react"; +import { auth } from "../../lib/auth"; +import { authClient } from "~/lib/auth-client"; +import { Input } from "~/components/ui/input"; +import { Button } from "~/components/ui/button"; + +export default function Signup() { + const navigate = useNavigate(); + const [error, setError] = useState(null); + const [pending, setPending] = useState(false); + + async function onSubmit(event: React.FormEvent) { + event.preventDefault(); + + setError(null); + setPending(true); + + const formData = new FormData(event.currentTarget); + + const { error } = await authClient.signUp.email({ + name: String(formData.get("name")), + email: String(formData.get("email")), + password: String(formData.get("password")), + }); + + setPending(false); + + if (error) { + setError(error.message ?? "Could not create account."); + return; + } + + navigate("/"); + } + + return ( +
+
+
+ Sign up + + Create your Coppie account. + +
+ +
+ + + + + + + {error ?

{error}

: null} + + +
+ +

+ Already have an account?{" "} + + Log in + +

+
+
+ ); +} diff --git a/app/components/ui/input.tsx b/app/components/ui/input.tsx new file mode 100644 index 0000000..deebd50 --- /dev/null +++ b/app/components/ui/input.tsx @@ -0,0 +1,19 @@ +import * as React from "react"; + +import { cn } from "~/lib/utils"; + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ); +} + +export { Input }; diff --git a/app/lib/auth-client.ts b/app/lib/auth-client.ts index ecae7d3..f1012dd 100644 --- a/app/lib/auth-client.ts +++ b/app/lib/auth-client.ts @@ -1,5 +1,3 @@ import { createAuthClient } from "better-auth/react"; -export const authClient = createAuthClient({ - baseURL: "http://localhost:3000", -}); +export const authClient = createAuthClient(); diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index dfc8ba7..dfc7faa 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,4 +1,19 @@ import Home from "~/components/home"; +import type { Route } from "./+types/_index"; +import { auth } from "../../lib/auth"; +import { redirect } from "react-router"; + +export async function loader({ request }: Route.LoaderArgs) { + const session = await auth.api.getSession({ + headers: request.headers, + }); + + if (!session) { + throw redirect("/log-in"); + } + + return { session }; +} export default function HomePage() { return ; diff --git a/app/routes/log-in.tsx b/app/routes/log-in.tsx new file mode 100644 index 0000000..d091ba9 --- /dev/null +++ b/app/routes/log-in.tsx @@ -0,0 +1,20 @@ +import { redirect } from "react-router"; +import type { Route } from "./+types/log-in"; +import { auth } from "../../lib/auth"; +import Login from "~/components/login"; + +export async function loader({ request }: Route.LoaderArgs) { + const session = await auth.api.getSession({ + headers: request.headers, + }); + + if (session) { + throw redirect("/"); + } + + return null; +} + +export default function LogInPage() { + return ; +} diff --git a/app/routes/sign-up.tsx b/app/routes/sign-up.tsx new file mode 100644 index 0000000..cd7632c --- /dev/null +++ b/app/routes/sign-up.tsx @@ -0,0 +1,20 @@ +import { redirect } from "react-router"; +import SignUp from "~/components/signup"; +import type { Route } from "../+types/root"; +import { auth } from "../../lib/auth"; + +export async function loader({ request }: Route.LoaderArgs) { + const session = await auth.api.getSession({ + headers: request.headers, + }); + + if (session) { + throw redirect("/"); + } + + return null; +} + +export default function SignUpPage() { + return ; +}