| 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 Login = () => {
|
|---|
| 7 | const navigate = useNavigate();
|
|---|
| 8 | const [usernameOrEmail, setUsernameOrEmail] = useState("");
|
|---|
| 9 | const [password, setPassword] = useState("");
|
|---|
| 10 | const [error, setError] = useState("");
|
|---|
| 11 | const [isSubmitting, setIsSubmitting] = useState(false);
|
|---|
| 12 |
|
|---|
| 13 | useEffect(() => {
|
|---|
| 14 | const token = localStorage.getItem("authToken");
|
|---|
| 15 | if (token) navigate("/dashboard", { replace: true });
|
|---|
| 16 | }, [navigate]);
|
|---|
| 17 |
|
|---|
| 18 | const onSubmit = async (e) => {
|
|---|
| 19 | e.preventDefault();
|
|---|
| 20 | setError("");
|
|---|
| 21 | setIsSubmitting(true);
|
|---|
| 22 | try {
|
|---|
| 23 | const res = await api.post("/auth/login", {
|
|---|
| 24 | usernameOrEmail,
|
|---|
| 25 | password,
|
|---|
| 26 | });
|
|---|
| 27 | localStorage.setItem("authToken", res.data.token);
|
|---|
| 28 | localStorage.setItem(
|
|---|
| 29 | "authUser",
|
|---|
| 30 | JSON.stringify({
|
|---|
| 31 | userId: res.data.userId,
|
|---|
| 32 | username: res.data.username,
|
|---|
| 33 | email: res.data.email,
|
|---|
| 34 | }),
|
|---|
| 35 | );
|
|---|
| 36 | navigate("/dashboard");
|
|---|
| 37 | } catch (err) {
|
|---|
| 38 | const message =
|
|---|
| 39 | err?.response?.data?.message ||
|
|---|
| 40 | err?.response?.data?.errors?.usernameOrEmail ||
|
|---|
| 41 | "Login failed";
|
|---|
| 42 | setError(message);
|
|---|
| 43 | } finally {
|
|---|
| 44 | setIsSubmitting(false);
|
|---|
| 45 | }
|
|---|
| 46 | };
|
|---|
| 47 |
|
|---|
| 48 | return (
|
|---|
| 49 | <div className="min-h-screen flex items-center justify-center p-4">
|
|---|
| 50 | <form
|
|---|
| 51 | onSubmit={onSubmit}
|
|---|
| 52 | className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4"
|
|---|
| 53 | >
|
|---|
| 54 | <legend className="fieldset-legend">Login</legend>
|
|---|
| 55 |
|
|---|
| 56 | <label className="label">Username or Email</label>
|
|---|
| 57 | <input
|
|---|
| 58 | type="text"
|
|---|
| 59 | className="input"
|
|---|
| 60 | placeholder="Username or Email"
|
|---|
| 61 | value={usernameOrEmail}
|
|---|
| 62 | onChange={(e) => setUsernameOrEmail(e.target.value)}
|
|---|
| 63 | autoComplete="username"
|
|---|
| 64 | required
|
|---|
| 65 | />
|
|---|
| 66 |
|
|---|
| 67 | <label className="label">Password</label>
|
|---|
| 68 | <input
|
|---|
| 69 | type="password"
|
|---|
| 70 | className="input"
|
|---|
| 71 | placeholder="Password"
|
|---|
| 72 | value={password}
|
|---|
| 73 | onChange={(e) => setPassword(e.target.value)}
|
|---|
| 74 | autoComplete="current-password"
|
|---|
| 75 | required
|
|---|
| 76 | />
|
|---|
| 77 |
|
|---|
| 78 | {error ? <p className="text-error mt-2 text-sm">{error}</p> : null}
|
|---|
| 79 |
|
|---|
| 80 | <button
|
|---|
| 81 | className="btn btn-neutral mt-4"
|
|---|
| 82 | type="submit"
|
|---|
| 83 | disabled={isSubmitting}
|
|---|
| 84 | >
|
|---|
| 85 | {isSubmitting ? "Logging in..." : "Login"}
|
|---|
| 86 | </button>
|
|---|
| 87 |
|
|---|
| 88 | <p className="mt-3 text-sm">
|
|---|
| 89 | Don't have an account?{" "}
|
|---|
| 90 | <Link className="link link-primary" to="/register">
|
|---|
| 91 | Register
|
|---|
| 92 | </Link>
|
|---|
| 93 | </p>
|
|---|
| 94 | </form>
|
|---|
| 95 | </div>
|
|---|
| 96 | );
|
|---|
| 97 | };
|
|---|
| 98 |
|
|---|
| 99 | export default Login;
|
|---|