LoginForm.jsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import React, { useState } from "react";
  2. import { useAuth } from "../contexts/AuthContext";
  3. import { useNavigate, useLocation } from "react-router-dom";
  4. function LoginForm() {
  5. const [formData, setFormData] = useState({
  6. username: "",
  7. password: "",
  8. });
  9. const [error, setError] = useState("");
  10. const [loading, setLoading] = useState(false);
  11. const { login } = useAuth();
  12. const navigate = useNavigate();
  13. const location = useLocation();
  14. // Redirect to intended location after login, or to admin dashboard
  15. const redirectTo = location.state?.from?.pathname || "/admin";
  16. const handleChange = (e) => {
  17. const { name, value } = e.target;
  18. setFormData((prev) => ({ ...prev, [name]: value }));
  19. };
  20. const handleSubmit = async (e) => {
  21. e.preventDefault();
  22. setLoading(true);
  23. setError("");
  24. if (!formData.username.trim() || !formData.password) {
  25. setError("Please enter both username and password");
  26. setLoading(false);
  27. return;
  28. }
  29. try {
  30. const result = await login(
  31. formData.username.trim(),
  32. formData.password,
  33. );
  34. if (result.success) {
  35. navigate(redirectTo, { replace: true });
  36. } else {
  37. setError(result.error || "Login failed");
  38. }
  39. } catch (err) {
  40. setError("An unexpected error occurred");
  41. } finally {
  42. setLoading(false);
  43. }
  44. };
  45. return (
  46. <div className="min-h-screen theme-bg flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
  47. <div className="max-w-md w-full space-y-8">
  48. <div>
  49. <h2 className="mt-6 text-center text-3xl font-extrabold theme-text">
  50. Sign in to Admin Panel
  51. </h2>
  52. <p className="mt-2 text-center text-sm theme-text-secondary">
  53. Access the{" "}
  54. <span className="theme-primary font-semibold">
  55. Goon
  56. </span>
  57. Blog admin interface
  58. </p>
  59. </div>
  60. <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
  61. <div className="rounded-md shadow-sm -space-y-px">
  62. <div>
  63. <label htmlFor="username" className="sr-only">
  64. Username
  65. </label>
  66. <input
  67. id="username"
  68. name="username"
  69. type="text"
  70. autoComplete="username"
  71. required
  72. value={formData.username}
  73. onChange={handleChange}
  74. className="relative block w-full px-3 py-2 border theme-border placeholder-gray-500 theme-text rounded-t-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
  75. placeholder="Username"
  76. disabled={loading}
  77. />
  78. </div>
  79. <div>
  80. <label htmlFor="password" className="sr-only">
  81. Password
  82. </label>
  83. <input
  84. id="password"
  85. name="password"
  86. type="password"
  87. autoComplete="current-password"
  88. required
  89. value={formData.password}
  90. onChange={handleChange}
  91. className="relative block w-full px-3 py-2 border theme-border placeholder-gray-500 theme-text rounded-b-md focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
  92. placeholder="Password"
  93. disabled={loading}
  94. />
  95. </div>
  96. </div>
  97. {error && (
  98. <div className="rounded-md bg-red-50 p-4">
  99. <div className="flex">
  100. <div className="flex-shrink-0">
  101. <svg
  102. className="h-5 w-5 text-red-400"
  103. viewBox="0 0 20 20"
  104. fill="currentColor"
  105. >
  106. <path
  107. fillRule="evenodd"
  108. d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
  109. clipRule="evenodd"
  110. />
  111. </svg>
  112. </div>
  113. <div className="ml-3">
  114. <h3 className="text-sm font-medium text-red-800">
  115. Error
  116. </h3>
  117. <div className="mt-1 text-sm text-red-700">
  118. {error}
  119. </div>
  120. </div>
  121. </div>
  122. </div>
  123. )}
  124. <div>
  125. <button
  126. type="submit"
  127. disabled={loading}
  128. className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white btn-theme-primary disabled:opacity-50 disabled:cursor-not-allowed"
  129. >
  130. {loading ? (
  131. <div className="flex items-center">
  132. <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
  133. Signing in...
  134. </div>
  135. ) : (
  136. "Sign in"
  137. )}
  138. </button>
  139. </div>
  140. <div className="text-center">
  141. <button
  142. type="button"
  143. onClick={() => navigate("/")}
  144. className="text-sm theme-text-secondary hover:theme-text"
  145. >
  146. ← Back to Blog
  147. </button>
  148. </div>
  149. </form>
  150. </div>
  151. </div>
  152. );
  153. }
  154. export default LoginForm;