}
- {events.map((event) => (
+ {events.length > 0 && events.map((event) => (
{
+ const API_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:3000';
+ const [data, setData] = useState({
+ email: '',
+ password: '',
+ });
+ const [hidden, setHidden] = useState(true);
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const {user, setUser} = useUser();
+
+ const navigate = useNavigate();
+
+ const handleChange = (e) => {
+ setData({ ...data, [e.target.name]: e.target.value });
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(data.email)) {
+ setError('Please enter a valid email address');
+ return;
+ }
+ if (data.password.length < 8) {
+ setError('Password must be at least 8 characters long');
+ return;
+ }
+ setIsLoading(true);
+ setError(null);
+ try {
+ const response = await fetch(`${API_URL}/api/admin/login`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ });
+ const result = await response.json();
+ if (!response.ok) {
+ throw new Error(result.message || 'Login failed');
+ }
+ const res = JSON.stringify(result.admin)
+ Cookies.set('authToken', result.token, { expires: 1, secure: true });
+ Cookies.set("authenticatedUser", res, {expires: 1, secure: true})
+ setUser(result.admin)
+ message.success('Login successful');
+ navigate('/admin');
+ } catch (err) {
+ setError(err.message || 'An error occurred. Please try again.');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ window.scrollTo(0, 0);
+ }, []);
+
+ return (
+
+ {/* Background Image */}
+
+ {/* Login Form */}
+
+
+ );
+};
+
+export default AdminLogin;
diff --git a/frontend/src/components/Pages/Admin/AdminSignup.jsx b/frontend/src/components/Pages/Admin/AdminSignup.jsx
new file mode 100644
index 0000000..cc5baf9
--- /dev/null
+++ b/frontend/src/components/Pages/Admin/AdminSignup.jsx
@@ -0,0 +1,183 @@
+import { useState, useEffect } from 'react';
+import photo from '../../../assets/login.png';
+import { useNavigate, Link } from 'react-router-dom';
+import { FaEye } from 'react-icons/fa';
+import { FaEyeSlash } from 'react-icons/fa6';
+import zxcvbn from 'zxcvbn'; // Password strength checker
+
+const AdminSignup = () => {
+ const API_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:3000';
+ const navigate = useNavigate();
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [passwordStrength, setPasswordStrength] = useState(0);
+ const [data, setData] = useState({ name: '', email: '', password: '' });
+ const [hidden, setHidden] = useState(true);
+
+ const handleChange = (e) => {
+ setData({ ...data, [e.target.name]: e.target.value });
+ if (e.target.name === 'password') {
+ const result = zxcvbn(e.target.value);
+ setPasswordStrength(result.score);
+ }
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setIsLoading(true);
+
+ // Input validation
+ if (!data.email || !data.password || !data.name) {
+ setError('Please fill in all fields');
+ setIsLoading(false);
+ return;
+ }
+ if (data.password.length < 8) {
+ setError('Password must be at least 8 characters long');
+ setIsLoading(false);
+ return;
+ }
+ if (data.name.length < 3) {
+ setError('Name must be at least 3 characters long');
+ setIsLoading(false);
+ return;
+ }
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(data.email)) {
+ setError('Please enter a valid email address');
+ setIsLoading(false);
+ return;
+ }
+
+ try {
+ const response = await fetch(`${API_URL}/api/admin/register`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(data),
+ });
+ const result = await response.json();
+
+ if (!response.ok) {
+ setIsLoading(false);
+ setError(result.error);
+ return;
+ }
+
+
+ // alert('OTP sent to your email. Verify to complete registration.');
+ navigate('/admin-login');
+
+ } catch (error) {
+ setError(error.message);
+ console.error('Error:', error);
+ } finally {
+ setIsLoading(false); // Ensure loading state is reset after request
+ }
+ };
+
+
+ useEffect(() => {
+ window.scrollTo(0, 0);
+ }, []);
+
+ const getPasswordStrengthColor = (score) => {
+ const colors = ['#ff4d4d', '#ff944d', '#ffd24d', '#d2ff4d', '#4dff88'];
+ return colors[score];
+ };
+
+ const getPasswordStrengthText = (score) => {
+ const strengthLevels = ['Very Weak', 'Weak', 'Okay', 'Good', 'Strong'];
+ return strengthLevels[score];
+ };
+
+ return (
+
+
+
+
+ );
+};
+
+export default AdminSignup;
diff --git a/frontend/src/components/Pages/Login.jsx b/frontend/src/components/Pages/Login.jsx
index e3e5c1a..62c027c 100644
--- a/frontend/src/components/Pages/Login.jsx
+++ b/frontend/src/components/Pages/Login.jsx
@@ -32,9 +32,15 @@ const Login = () => {
body: JSON.stringify({ ...data, rememberMe }), // Include rememberMe in the body
});
const result = await response.json();
- if (!response.ok) throw new Error(result.message || 'Login failed');
-
- // Set cookie expiration based on Remember Me option
+ // console.log(result);
+
+ if (!response) {
+ throw new Error(result.message || 'Login failed');
+ }
+ const res = JSON.stringify(result.user)
+
+ Cookies.set("authenticatedUser", res, {expires: 1, secure: true, sameSite: 'strict'})
+
Cookies.set('authToken', result.token, {
expires: rememberMe ? 7 : 1 / 24, // 7 days if Remember Me is checked, 1 hour otherwise
secure: process.env.NODE_ENV === "production",
@@ -44,6 +50,8 @@ const Login = () => {
message.success('Login successful');
navigate('/');
} catch (err) {
+ console.log(err);
+
setError(err.message || 'An error occurred. Please try again.');
} finally {
setIsLoading(false);
diff --git a/frontend/src/components/Pages/Signup.jsx b/frontend/src/components/Pages/Signup.jsx
index 37498c3..ce678fe 100644
--- a/frontend/src/components/Pages/Signup.jsx
+++ b/frontend/src/components/Pages/Signup.jsx
@@ -95,7 +95,7 @@ const Signup = () => {
src={photo}
alt="login"
loading="lazy"
- className="w-3/4 absolute inset-0"
+ className="absolute w-3/4 lg:w-auto lg:opacity-100 opacity-10 object-cover"
/>