diff --git a/app/routes/login.tsx b/app/routes/login.tsx index 639632f..6207898 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -1,16 +1,73 @@ -"use client" - -import { Link } from "react-router"; -import { useNavigate } from "react-router"; +import { useState, useEffect } from "react" +import { Link, useNavigate } from "react-router" import { FormInput } from "@/components/shared/form-input" import { FormButton } from "@/components/shared/form-button" +import { useAuth } from "@/app/context/auth-context" +import { setTokens } from "@/lib/auth" export default function LoginPage() { - const navigate = useNavigate(); + const navigate = useNavigate() + const { user, loading: authLoading, refreshUser } = useAuth() - function handleSubmit(e: React.FormEvent) { + useEffect(() => { + if (!authLoading && user) { + navigate("/forms") + } + }, [user, authLoading, navigate]) + + const [form, setForm] = useState({ email: "", password: "" }) + const [rememberMe, setRememberMe] = useState(false) + const [errors, setErrors] = useState>({}) + const [loading, setLoading] = useState(false) + + function handleChange(e: React.ChangeEvent) { + setForm((prev) => ({ ...prev, [e.target.name]: e.target.value })) + setErrors((prev) => ({ ...prev, [e.target.name]: "" })) + } + + function validate() { + const newErrors: Partial = {} + if (!form.email.trim()) newErrors.email = "Email is required" + if (!form.password) newErrors.password = "Password is required" + return newErrors + } + + async function handleSubmit(e: React.FormEvent) { e.preventDefault() - navigate("/forms") + const newErrors = validate() + if (Object.keys(newErrors).length > 0) { + setErrors(newErrors) + return + } + + setLoading(true) + try { + const res = await fetch("http://localhost:8080/api/auth/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + email: form.email, + password: form.password, + }), + }) + + if (!res.ok) { + const data = await res.json() + setErrors({ general: data.message ?? "Invalid email or password" }) + return + } + + const data = await res.json() + setTokens(data.access_token, data.refresh_token, rememberMe) + refreshUser() + + navigate("/forms") + } catch (err) { + console.error(err) + setErrors({ general: "Something went wrong. Please try again." }) + } finally { + setLoading(false) + } } return ( @@ -28,25 +85,41 @@ export default function LoginPage() {
+ {errors.general && ( +

+ {errors.general} +

+ )} +
- - Sign in + + {loading ? "Signing in..." : "Sign in"} - -

@@ -80,4 +156,4 @@ export default function LoginPage() { ) -} +} \ No newline at end of file diff --git a/app/routes/register.tsx b/app/routes/register.tsx index 5fae3fe..af53515 100644 --- a/app/routes/register.tsx +++ b/app/routes/register.tsx @@ -1,93 +1,172 @@ -"use client" - -import { Link } from "react-router"; -import { useNavigate } from "react-router"; +import { useState, useEffect } from "react" +import { Link, useNavigate } from "react-router" import { FormInput } from "@/components/shared/form-input" import { FormButton } from "@/components/shared/form-button" +import { useAuth } from "@/app/context/auth-context" export default function RegisterPage() { - const navigate = useNavigate(); + const navigate = useNavigate() + const { user, loading: authLoading } = useAuth() - function handleSubmit(e: React.FormEvent) { - e.preventDefault() - navigate("/forms") - } + useEffect(() => { + if (!authLoading && user) { + navigate("/forms") + } + }, [user, authLoading, navigate]) - return ( -

-
-
-
-

- Create your account -

-

- Start building beautiful forms in minutes -

-
+ const [form, setForm] = useState({ + firstName: "", + lastName: "", + email: "", + password: "", + confirmPassword: "", + }) -
-
-
- - -
- - - + const [errors, setErrors] = useState>({}) - - Create account - - + function handleChange(e: React.ChangeEvent) { + setForm((prev) => ({ ...prev, [e.target.name]: e.target.value })) + setErrors((prev) => ({ ...prev, [e.target.name]: "" })) + } + function validate() { + const newErrors: Partial = {} -
+ if (!form.firstName.trim()) newErrors.firstName = "First name is required" + if (!form.lastName.trim()) newErrors.lastName = "Last name is required" + if (!form.email.trim()) newErrors.email = "Email is required" + if (form.password.length < 8) + newErrors.password = "Must be at least 8 characters" + if (form.confirmPassword !== form.password) + newErrors.confirmPassword = "Passwords do not match" -

- Already have an account?{" "} - - Sign in - -

+ return newErrors + } + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault() + const newErrors = validate() + if (Object.keys(newErrors).length > 0) { + setErrors(newErrors) + return + } + + try { + const res = await fetch("http://localhost:8080/api/auth/register", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + email: form.email, + password: form.password, + }), + }) + + if (!res.ok) { + const data = await res.json() + setErrors({ email: data.message ?? "Registration failed" }) + return + } + + navigate("/login") + } catch (err) { + console.error(err) + setErrors({ email: "Something went wrong. Please try again." }) + } + } + + return ( +
+
+
+
+

+ Create your account +

+

+ Start building beautiful forms in minutes +

+
+ +
+
+
+ + +
+ + + + + + Create account + + +
+ +

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

+
+
-
-
- ) -} + ) +} \ No newline at end of file