| [3ebe47c] | 1 | import React, { useEffect, useState } from "react";
|
|---|
| 2 | import { Link, useNavigate } from "react-router-dom";
|
|---|
| 3 |
|
|---|
| 4 | import api from "../../api/axios";
|
|---|
| 5 |
|
|---|
| 6 | const Register = () => {
|
|---|
| 7 | const navigate = useNavigate();
|
|---|
| 8 | const [email, setEmail] = useState("");
|
|---|
| 9 | const [username, setUsername] = useState("");
|
|---|
| 10 | const [password, setPassword] = useState("");
|
|---|
| 11 | const [error, setError] = useState("");
|
|---|
| 12 | const [isSubmitting, setIsSubmitting] = useState(false);
|
|---|
| 13 |
|
|---|
| 14 | useEffect(() => {
|
|---|
| 15 | const token = localStorage.getItem("authToken");
|
|---|
| 16 | if (token) navigate("/dashboard", { replace: true });
|
|---|
| 17 | }, [navigate]);
|
|---|
| 18 |
|
|---|
| 19 | const onSubmit = async (e) => {
|
|---|
| 20 | e.preventDefault();
|
|---|
| 21 | setError("");
|
|---|
| 22 | setIsSubmitting(true);
|
|---|
| [37307aa] | 23 |
|
|---|
| 24 | const normalizedEmail = email.trim().toLowerCase();
|
|---|
| 25 | const normalizedUsername = username.trim();
|
|---|
| 26 | const normalizedPassword = password;
|
|---|
| 27 |
|
|---|
| 28 | if (normalizedUsername.length < 3 || normalizedUsername.length > 50) {
|
|---|
| 29 | setError("Username must be between 3 and 50 characters");
|
|---|
| 30 | setIsSubmitting(false);
|
|---|
| 31 | return;
|
|---|
| 32 | }
|
|---|
| 33 |
|
|---|
| 34 | if (normalizedPassword.length < 6) {
|
|---|
| 35 | setError("Password must be at least 6 characters");
|
|---|
| 36 | setIsSubmitting(false);
|
|---|
| 37 | return;
|
|---|
| 38 | }
|
|---|
| 39 |
|
|---|
| [3ebe47c] | 40 | try {
|
|---|
| 41 | const res = await api.post("/auth/register", {
|
|---|
| [37307aa] | 42 | email: normalizedEmail,
|
|---|
| 43 | username: normalizedUsername,
|
|---|
| 44 | password: normalizedPassword,
|
|---|
| [3ebe47c] | 45 | });
|
|---|
| 46 | localStorage.setItem("authToken", res.data.token);
|
|---|
| 47 | localStorage.setItem(
|
|---|
| 48 | "authUser",
|
|---|
| 49 | JSON.stringify({
|
|---|
| 50 | userId: res.data.userId,
|
|---|
| 51 | username: res.data.username,
|
|---|
| 52 | email: res.data.email,
|
|---|
| 53 | }),
|
|---|
| 54 | );
|
|---|
| 55 | navigate("/dashboard");
|
|---|
| 56 | } catch (err) {
|
|---|
| [37307aa] | 57 | const fieldErrors = err?.response?.data?.errors ?? {};
|
|---|
| 58 | const firstFieldError = Object.values(fieldErrors)[0];
|
|---|
| [3ebe47c] | 59 | const message =
|
|---|
| [37307aa] | 60 | firstFieldError ||
|
|---|
| [3ebe47c] | 61 | err?.response?.data?.message ||
|
|---|
| 62 | "Registration failed";
|
|---|
| 63 | setError(message);
|
|---|
| 64 | } finally {
|
|---|
| 65 | setIsSubmitting(false);
|
|---|
| 66 | }
|
|---|
| 67 | };
|
|---|
| 68 |
|
|---|
| 69 | return (
|
|---|
| 70 | <div className="min-h-screen flex items-center justify-center p-4">
|
|---|
| 71 | <form
|
|---|
| 72 | onSubmit={onSubmit}
|
|---|
| 73 | className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4"
|
|---|
| 74 | >
|
|---|
| 75 | <legend className="fieldset-legend">Register</legend>
|
|---|
| 76 |
|
|---|
| 77 | <label className="label">Email</label>
|
|---|
| 78 | <input
|
|---|
| 79 | type="email"
|
|---|
| 80 | className="input"
|
|---|
| 81 | placeholder="Email"
|
|---|
| 82 | value={email}
|
|---|
| 83 | onChange={(e) => setEmail(e.target.value)}
|
|---|
| 84 | autoComplete="email"
|
|---|
| 85 | required
|
|---|
| 86 | />
|
|---|
| 87 |
|
|---|
| 88 | <label className="label">Username</label>
|
|---|
| 89 | <input
|
|---|
| 90 | type="text"
|
|---|
| 91 | className="input"
|
|---|
| 92 | placeholder="Username"
|
|---|
| 93 | value={username}
|
|---|
| 94 | onChange={(e) => setUsername(e.target.value)}
|
|---|
| 95 | autoComplete="username"
|
|---|
| [37307aa] | 96 | minLength={3}
|
|---|
| 97 | maxLength={50}
|
|---|
| [3ebe47c] | 98 | required
|
|---|
| 99 | />
|
|---|
| 100 |
|
|---|
| 101 | <label className="label">Password</label>
|
|---|
| 102 | <input
|
|---|
| 103 | type="password"
|
|---|
| 104 | className="input"
|
|---|
| 105 | placeholder="Password"
|
|---|
| 106 | value={password}
|
|---|
| 107 | onChange={(e) => setPassword(e.target.value)}
|
|---|
| 108 | autoComplete="new-password"
|
|---|
| [37307aa] | 109 | minLength={6}
|
|---|
| [3ebe47c] | 110 | required
|
|---|
| 111 | />
|
|---|
| 112 |
|
|---|
| 113 | {error ? <p className="text-error mt-2 text-sm">{error}</p> : null}
|
|---|
| 114 |
|
|---|
| 115 | <button
|
|---|
| 116 | className="btn btn-neutral mt-4"
|
|---|
| 117 | type="submit"
|
|---|
| 118 | disabled={isSubmitting}
|
|---|
| 119 | >
|
|---|
| 120 | {isSubmitting ? "Creating account..." : "Register"}
|
|---|
| 121 | </button>
|
|---|
| 122 |
|
|---|
| 123 | <p className="mt-3 text-sm">
|
|---|
| 124 | Already have an account?{" "}
|
|---|
| 125 | <Link className="link link-primary" to="/login">
|
|---|
| 126 | Login
|
|---|
| 127 | </Link>
|
|---|
| 128 | </p>
|
|---|
| 129 | </form>
|
|---|
| 130 | </div>
|
|---|
| 131 | );
|
|---|
| 132 | };
|
|---|
| 133 |
|
|---|
| 134 | export default Register;
|
|---|