diff --git a/.gitattributes b/.gitattributes index a37a4a8..40f31f9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,3 @@ -*.js filter=lfs diff=lfs merge=lfs -text *.yml filter=lfs diff=lfs merge=lfs -text *.jsx filter=lfs diff=lfs merge=lfs -text *.png filter=lfs diff=lfs merge=lfs -text diff --git a/frontend/src/components/GoogleButton/GoogleButton.js b/frontend/src/components/GoogleButton/GoogleButton.js index 1b6074c..5e7eeae 100644 --- a/frontend/src/components/GoogleButton/GoogleButton.js +++ b/frontend/src/components/GoogleButton/GoogleButton.js @@ -1,3 +1,42 @@ +Add-Education-page version https://git-lfs.github.com/spec/v1 oid sha256:9bf6b48689aaf05c19642a0da40fbcdfaf13f0105b6f51c54b5ad53da00274e7 size 1113 +// src/components/GoogleButton/GoogleButton.js +import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth'; +import React from 'react'; +import { FaGoogle } from 'react-icons/fa'; // Importing Google icon +import { auth } from '../Firebase/firebase'; + +const GoogleButton = () => { + const handleGoogleSignIn = async () => { + const provider = new GoogleAuthProvider(); + try { + const result = await signInWithPopup(auth, provider); + console.log('User signed in: ', result.user); // You can handle the user info as needed + } catch (error) { + console.error('Error during Google sign-in: ', error.message); + } + }; + + return ( +
+ +
+ ); +}; + +export default GoogleButton; diff --git a/frontend/src/components/Login/Login.js b/frontend/src/components/Login/Login.js index 5bdcaab..dac8164 100644 --- a/frontend/src/components/Login/Login.js +++ b/frontend/src/components/Login/Login.js @@ -1,3 +1,164 @@ + Add-Education-page version https://git-lfs.github.com/spec/v1 oid sha256:c91ce68a0bee7662510163b8794eaa07b2fd13e393820343d0a7883f92942b65 size 4196 +import React, { useEffect, useState } from "react"; +import { useSignInWithEmailAndPassword } from "react-firebase-hooks/auth"; +import { Link, useNavigate } from "react-router-dom"; +import { auth } from "../Firebase/firebase"; +import GoogleButton from '../GoogleButton/GoogleButton'; // Import the GoogleButton +import toast from "react-hot-toast"; +import Footer from "../Footer/Footer"; + + + +// LoginHeader Component +const LoginHeader = () => ( +

Login

+); + +// LoginForm Component +const LoginForm = ({ email, setEmail, password, setPassword, showPassword, setShowPassword, handleLogin, loading, error }) => ( +
+ {error &&
{error.message}
} +
+ + setEmail(e.target.value)} + required + /> +
+
+ + + setPassword(e.target.value)} + required + + /> + setShowPassword(!showPassword)} + > + {showPassword ? "visibility_off" : "visibility"} + + +
+ +
+

Or

+ +
+
+); + +// LoginFooter Component +const LoginFooter = ({ navigate }) => ( + <> +

+ Don't have an account?{" "} + navigate("/signup")}> + Sign Up + +

+

+ Forgot Password? +

+ +); + +function Login() { + const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const navigate = useNavigate(); + const [signInWithEmailAndPassword, user, loading, error] = useSignInWithEmailAndPassword(auth); + + const handleLogin = async (e) => { + e.preventDefault(); + if (!regex.test(email)) { + toast.error("Invalid Email"); + return; + } + await signInWithEmailAndPassword(email, password); + }; + + useEffect(() => { + const unsubscribe = auth.onAuthStateChanged((currentUser) => { + if (currentUser) { + navigate("/dashboard", { replace: true }); + } + }); + return () => unsubscribe(); + }, [navigate]); + + return ( + <> + +
+
+
+
+
+ + + +
+
+
+
+
+ + ); +} + +export default Login; diff --git a/frontend/src/components/Navbar/Navbar.css b/frontend/src/components/Navbar/Navbar.css index e8c13c5..94661c4 100644 --- a/frontend/src/components/Navbar/Navbar.css +++ b/frontend/src/components/Navbar/Navbar.css @@ -15,6 +15,8 @@ height: 60px; width: 100%; padding: 0; +/* for responsiveness */ + flex-wrap: nowrap; } .logo { diff --git a/frontend/src/components/Signup/Signup.js b/frontend/src/components/Signup/Signup.js index 36936aa..e57ca91 100644 --- a/frontend/src/components/Signup/Signup.js +++ b/frontend/src/components/Signup/Signup.js @@ -1,3 +1,279 @@ + Add-Education-page version https://git-lfs.github.com/spec/v1 oid sha256:7c0b8b8d06f5718c86683e66c3522cb4d632eac8f164c2a0c89d63748eb063f0 size 9140 +import React, { useEffect, useState } from "react"; +import { useCreateUserWithEmailAndPassword } from "react-firebase-hooks/auth"; +import { useNavigate } from "react-router-dom"; +import { registerValidation } from "../../validations/validation"; +import { auth, signInWithGoogle } from "../Firebase/firebase"; // Import Firebase auth and Google sign-in +import GoogleButton from '../GoogleButton/GoogleButton'; +import toast from "react-hot-toast"; +import Footer from "../Footer/Footer.js"; + +function Signup() { + const trustedDomains = ["gmail.com", "yahoo.com", "outlook.com", "icloud.com", "hotmail.com"]; + const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [error, setError] = useState(""); + const [successMessage, setSuccessMessage] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [errors, setErrors] = useState({}); + const [emailError, setEmailError] = useState(""); // State for email validation feedback + const navigate = useNavigate(); + + const [createUserWithEmailAndPassword, user, loading, firebaseError] = + useCreateUserWithEmailAndPassword(auth); + + const handlePasswordVisibility = () => { + setShowPassword(!showPassword); + }; + + const handleConfirmPasswordVisibility = () => { + setShowConfirmPassword(!showConfirmPassword); + }; + + useEffect(() => { + const unsubscribe = auth.onAuthStateChanged((currentUser) => { + if (currentUser) { + navigate("/dashboard", { replace: true }); + } + }); + return () => unsubscribe(); + }, [navigate]); + + const handleSignup = async (e) => { + e.preventDefault(); + + // Check email format + if (!regex.test(email)) { + toast.error("Invalid email"); + return; + } + + // Check email domain + const emailDomain = email.split("@")[1]; + if (!trustedDomains.includes(emailDomain)) { + setEmailError("Please use a trusted email provider (Gmail, Yahoo, Outlook, iCloud, Hotmail)."); + return; + } else { + setEmailError(""); // Clear error if valid + } + + try { + await registerValidation.validate( + { email, password, confirmPassword }, + { abortEarly: false } + ); + setErrors({}); + } catch (error) { + const newErrors = {}; + error.inner.forEach((err) => { + newErrors[err.path] = err.message; + }); + + setErrors(newErrors); + return; + } + + // Firebase signup + try { + await createUserWithEmailAndPassword(email, password); + setSuccessMessage("Signup successful! Redirecting to login..."); + setEmail(""); + setPassword(""); + setConfirmPassword(""); + setTimeout(() => { + navigate("/login"); + }, 2000); // Redirect after 2 seconds + } catch (firebaseError) { + setError(firebaseError?.message || "Error signing up."); + } + }; + + const handleGoogleSignup = async () => { + try { + await signInWithGoogle(); + navigate("/dashboard"); // Redirect after successful Google sign-in + } catch (error) { + setError("Error signing in with Google."); + } + }; + + return ( + <> + +
+
+
+
+
+

Sign Up

+ {error &&
{error}
} + {successMessage && ( +
{successMessage}
+ )} + {emailError &&
{emailError}
} {/* Email error message */} +
+
+ + { + setEmail(e.target.value); + setEmailError(""); // Clear email error on change + }} + /> + {errors.email &&
{errors.email}
} +
+
+ + setPassword(e.target.value)} + pattern="^(?=.*\d)(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9])\S{8,}$" + title="Password must contain at least one number, one alphabet, one symbol, and be at least 8 characters long" + required + /> + {errors.password &&
{errors.password}
} + + {showPassword ? "visibility_off" : "visibility"} + +
+ +
+ + setConfirmPassword(e.target.value)} + pattern="^(?=.*\d)(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9])\S{8,}$" + title="Password must contain at least one number, one alphabet, one symbol, and be at least 8 characters long" + required + /> + {errors.confirmPassword &&
{errors.confirmPassword}
} + + {showConfirmPassword ? "visibility_off" : "visibility"} + +
+ +
+
+ +
+

+ Already have an account?{" "} + navigate("/login")} + > + Login + +

+
+
+
+
+
+