diff --git a/src/App.js b/src/App.js index f7f63b2..58d4e37 100644 --- a/src/App.js +++ b/src/App.js @@ -5,7 +5,6 @@ import HomePage from "./homePage/HomePage"; import AboutUs from "./aboutUs/AboutUs"; import ContactUs from "./contactUs/ContactUs"; import SignIn from "./account/signIn/SignIn"; -import LogOut from "./account/logOut/LogOut"; import SignUp from "./account/signUp/SignUp"; import Profile from "./account/profile/Profile"; import NotFound from "./common/NotFound"; diff --git a/src/account/logOut/LogOut.css b/src/account/logOut/LogOut.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/account/logOut/LogOut.jsx b/src/account/logOut/LogOut.jsx deleted file mode 100644 index 0b186d0..0000000 --- a/src/account/logOut/LogOut.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import React, { useState } from "react"; -import { auth, GoogleProvider } from "../../config/firebase"; -import { - createUserWithEmailAndPassword, - signInWithPopup, - signOut, - signInWithRedirect, -} from "firebase/auth"; - -import { - useAccountContext, - useAccountUpdateContext, -} from "../../contexts/AccountContext"; - -export default function LogOut() { - const { userType } = useAccountContext(); - const { updateUserType } = useAccountUpdateContext(); - - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - - const logOut = async () => { - try { - console.log("currentUser: ", auth?.currentUser); - - await signOut(auth); - console.log("Logging out...."); - } catch (error) { - console.error("Log out Error: ", error); - } - }; - - return ( -
- {/*
Log In : {userType}
*/} -
- -
-
- ); -} diff --git a/src/account/profile/Profile.css b/src/account/profile/Profile.css index b46df98..76de6f1 100644 --- a/src/account/profile/Profile.css +++ b/src/account/profile/Profile.css @@ -8,3 +8,7 @@ box-shadow: 0 4px 10px #c8d1d2; transition: transform 0.6s; } +.account-details ul { + padding: 0; + list-style: none; +} diff --git a/src/account/profile/Profile.jsx b/src/account/profile/Profile.jsx index 2737d04..21014bb 100644 --- a/src/account/profile/Profile.jsx +++ b/src/account/profile/Profile.jsx @@ -1,5 +1,7 @@ import React, { useState, useEffect } from "react"; -import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; +import { auth } from "../../config/firebase"; +import { signOut } from "firebase/auth"; import Container from "react-bootstrap/Container"; import Row from "react-bootstrap/Row"; @@ -10,24 +12,62 @@ import Form from "react-bootstrap/Form"; import InputGroup from "react-bootstrap/InputGroup"; import "./Profile.css"; +import { + useAccountContext, + useAccountUpdateContext, +} from "../../contexts/AccountContext"; +import LoadingSpinner from "../../common/LoadingSpinner"; import emptyFileImage from "../../assets/images/emptyFileImage.png"; export const Profile = () => { + const { userType, userEmail, userFName, userLName, userAccDetail } = + useAccountContext(); + const { + updateUserType, + updateUserEmail, + updateUserFName, + updateUserLName, + updateUserAccDetail, + } = useAccountUpdateContext(); + const navigate = useNavigate(); + const [editProfile, setEditProfile] = useState(false); const [profileImage, setProfileImage] = useState(null); - const [profileName, setProfileName] = useState("User Full Name"); - const [profileEmail, setProfileEmail] = useState("user@example.com"); - const [profileType, setProfileType] = useState("Gardner"); - const handleNameChange = (e) => setProfileName(e.target.value); - const handleEmailChange = (e) => setProfileEmail(e.target.value); - const handleTypeChange = (e) => setProfileType(e.target.value); + const handleFirstNameChange = (e) => updateUserFName(e.target.value); + const handleLastNameChange = (e) => updateUserLName(e.target.value); + const handleEmailChange = (e) => updateUserEmail(e.target.value); + const handleTypeChange = (e) => updateUserType(e.target.value); const handleCancelBtn = () => setEditProfile(false); const handleEditBtn = () => setEditProfile(!editProfile); const handleUpdateBtn = () => { setEditProfile(false); + updateUserAccDetail({ + firstName: userFName, + lastName: userLName, + type: userType, + email: userEmail, + // city: "Denver", + // state: "", + // country: "US", + // userInterest: "Other", + }); + }; + + const logOut = async () => { + try { + console.log("currentUser: ", auth?.currentUser); + await signOut(auth); + localStorage.removeItem("userID"); + localStorage.removeItem("userToken"); + console.log("Logging out...."); + navigate("/"); + window.location.reload(); + } catch (error) { + console.error("Log out Error: ", error); + } }; return ( @@ -48,27 +88,39 @@ export const Profile = () => { /> - {/* Name Section */}

Name:

-
{!editProfile && profileName}
+
{!editProfile && userFName + " " + userLName}
{editProfile && ( - - - Name - - - + <> + + + First Name + + + + + + Last Name + + + + )}

Email:

-
{!editProfile && profileEmail}
+
{!editProfile && userEmail}
{editProfile && ( @@ -76,7 +128,7 @@ export const Profile = () => { Email { )}

Account Type:

-
{!editProfile && profileType}
+
{!editProfile && userType}
{editProfile && ( @@ -99,6 +151,26 @@ export const Profile = () => { )} +
+
    +
  • + City: + {userAccDetail.city} +
  • +
  • + State: {" "} + {userAccDetail.state} +
  • +
  • + Country: {" "} + {userAccDetail.country} +
  • +
  • + Use of AgriLens: {" "} + {userAccDetail.userInterest} +
  • +
+
{ ) : ( - + <> + + + )} @@ -139,22 +216,3 @@ export const Profile = () => { }; export default Profile; - -/* import React from "react"; -import { - useAccountContext, - useAccountUpdateContext, -} from "../contexts/AccountContext"; - -export default function Profile() { - const { userType } = useAccountContext(); - const { updateUserType } = useAccountUpdateContext(); - - return ( -
-
Profile : {userType}
- -
- ); -} -*/ diff --git a/src/account/signIn/SignIn.jsx b/src/account/signIn/SignIn.jsx index 14b6ec2..2013810 100644 --- a/src/account/signIn/SignIn.jsx +++ b/src/account/signIn/SignIn.jsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from "react"; import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import { auth, GoogleProvider } from "../../config/firebase"; import { @@ -21,11 +22,24 @@ import { } from "../../contexts/AccountContext"; export default function SignIn() { - const { updateUserType, updateUserEmail } = useAccountUpdateContext(); + const { updateUserType, updateUserEmail, updateUserID, updateUserToken } = + useAccountUpdateContext(); + const navigate = useNavigate(); + const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); + useEffect(() => { + const savedUserID = localStorage.getItem("userID"); + const savedUserToken = localStorage.getItem("userToken"); + + if (savedUserID && savedUserToken) { + // If the user has a valid user Id and Token, navigate to the homepage. + navigate("/"); + } + }, [navigate]); + const handleLogin = async (e) => { e.preventDefault(); setError(""); @@ -39,10 +53,23 @@ export default function SignIn() { console.log("Logging in...."); console.log("login currentUser: ", auth.currentUser); - // Update user email and type after successful login - const user = userCredential.user; - await updateUserEmail(user.email); - updateUserType("User"); // or whatever value is appropriate + const user = auth.currentUser; + const userEmail = user.email; + const userID = user.uid; + const userToken = await user.getIdToken(); + + console.log("User details:", { userEmail, userID, userToken }); + + updateUserEmail(userEmail); + updateUserID(userID); + updateUserToken(userToken); + updateUserType("User"); + + // Save user ID and token in localStorage + localStorage.setItem("userID", userID); + localStorage.setItem("userToken", userToken); + + navigate("/"); } catch (err) { console.log("Logging in Error", err); setError(err.message); @@ -57,7 +84,6 @@ export default function SignIn() { console.log("Login With Google Error: ", error); } - // Listen for authentication state changes onAuthStateChanged(auth, (user) => { if (user) { console.log("User is logged in:", user); @@ -75,15 +101,25 @@ export default function SignIn() {
-
+ Email address* - + setEmail(e.target.value)} + /> Password* - + setPassword(e.target.value)} + />
Don't have an account?
@@ -102,47 +138,6 @@ export default function SignIn() {
- {/*
- setEmail(e.target.value)} - > - setPassword(e.target.value)} - > - -
*/}
- //
- //
- //
- // - // setEmail(e.target.value)} - // required - // /> - //
- //
- // - // setPassword(e.target.value)} - // required - // /> - //
- // - // {error &&

{error}

} - //
- //
- - //
- // - //
- //
); } diff --git a/src/account/signUp/SignUp.jsx b/src/account/signUp/SignUp.jsx index c7bf788..200f8dc 100644 --- a/src/account/signUp/SignUp.jsx +++ b/src/account/signUp/SignUp.jsx @@ -1,10 +1,15 @@ import React, { useState } from "react"; +import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; + +import axios from "axios"; import { auth } from "../../config/firebase"; import { createUserWithEmailAndPassword } from "firebase/auth"; import Col from "react-bootstrap/Col"; import Button from "react-bootstrap/Button"; import Form from "react-bootstrap/Form"; +import Alert from "react-bootstrap/Alert"; import "./SignUp.css"; @@ -12,40 +17,157 @@ import { useAccountContext, useAccountUpdateContext, } from "../../contexts/AccountContext"; -import { Link } from "react-router-dom"; - -const accountTypes = ["Gardner", "Farmer", "Researcher"]; +import LoadingSpinner from "../../common/LoadingSpinner"; export default function SignUp() { - const { userType } = useAccountContext(); - const { updateUserType } = useAccountUpdateContext(); + const { userType, userEmail, userName, userID, userToken, userAccDetail } = + useAccountContext(); + const { + updateUserType, + updateUserEmail, + updateUserName, + updateUserID, + updateUserToken, + updateUserAccDetail, + } = useAccountUpdateContext(); + const navigate = useNavigate(); + + const [firstName, setFirstName] = useState(""); + const [lastName, setLastName] = useState(""); + const [accountType, setAccountType] = useState("Gardner"); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); + const [city, setCity] = useState(""); + const [state, setState] = useState(""); + const [country, setCountry] = useState(""); + const [userInterest, setUserInterest] = useState(""); + + const [errors, setErrors] = useState({}); + const [serverError, setServerError] = useState(null); + const [loading, setLoading] = useState(false); + const [successUserId, setSuccessUserId] = useState(null); + + const createUserDBUrl_dev = + "http://127.0.0.1:5001/agrilens-web/us-central1/app/users/customer"; + const createUserDBUrl_prod = + "https://app-id543mmv6a-uc.a.run.app/users/customer"; + + const accountTypes = ["Gardner", "Farmer", "Researcher"]; + + const validateForm = () => { + const newErrors = {}; + if (!firstName) newErrors.firstName = "First Name is required"; + if (!email) newErrors.email = "Email address is required"; + if (!password) newErrors.password = "Password is required"; + return newErrors; + }; const signUp = async () => { try { await createUserWithEmailAndPassword(auth, email, password); - console.log("currentUser: ", auth?.currentUser); + console.log("currentUser: ", auth?.currentUser?.uid); + return auth?.currentUser; } catch (error) { console.log("currentUser: ", auth?.currentUser); console.error("Sign up error", error); } }; + const createUserDb = async (url, data, headers = {}) => { + try { + setLoading(true); + const response = await axios.post(url, data, headers); + console.log("Response:", response.data); + console.log("Response:", response.status); + let jsonString = response?.data; + + console.log("data: ", jsonString); + } catch (err) { + console.error("fetchData() Error:", err); + setServerError(err.message); + } finally { + setLoading(false); + } + }; + + const handleSignUp = async (e) => { + e.preventDefault(); + const formErrors = validateForm(); + if (Object.keys(formErrors).length === 0) { + console.log(">>>> No Invalid inputs detected."); + + const currentUser = await signUp(); + const UID = currentUser?.uid; + console.log("currentUser: ", currentUser); + console.log("currentUser UID: ", currentUser?.uid); + + if (UID) { + console.log("Valid UID: ", UID); + setSuccessUserId(() => UID); + const accountDetail = { + firstName: firstName, + lastName: lastName, + type: accountType, + email: email, + city: city, + state: state, + country: country, + userInterest: userInterest, + uid: UID, + }; + + console.log("accountDetail: ", accountDetail); + await createUserDb(createUserDBUrl_dev, accountDetail); + await updateUserAccDetail(accountDetail); + await updateUserEmail(accountDetail.email); + await updateUserName(accountDetail.firstName + accountDetail.lastName); + await updateUserType(accountDetail.type); + + navigate("/profile"); + } + } else { + console.log(">>>> Invalid inputs detected."); + + setErrors(formErrors); + } + }; + return (
-
+ {loading && } + {Object.keys(errors).length > 0 && ( + + {Object.values(errors).map((error, index) => ( +
{error}
+ ))} +
+ )} + First Name* - + setFirstName(e.target.value)} + /> + + {errors.firstName} + Last Name - + setLastName(e.target.value)} + /> Account Type* @@ -56,24 +178,44 @@ export default function SignUp() { type="radio" id={`default-${type}`} label={type} + value={type} + checked={accountType === type} + onChange={(e) => setAccountType(e.target.value)} />
))} Email address* - + setEmail(e.target.value)} + /> Your email won't be shared with anyone else. + + {errors.email} + Password* - + setPassword(e.target.value)} + /> + + {errors.password} + - {/* */}

Optional

@@ -84,7 +226,7 @@ export default function SignUp() {
Address:
City: @@ -92,16 +234,23 @@ export default function SignUp() { className="ms-1" type="text" placeholder="Enter City" + value={city} + onChange={(e) => setCity(e.target.value)} /> State/ Province: - + setState(e.target.value)} + /> setCountry(e.target.value)} /> setUserInterest(e.target.value)} > - - + - - - + + +
@@ -146,18 +305,6 @@ export default function SignUp() {
- {/*
- setEmail(e.target.value)} - > - setPassword(e.target.value)} - > - -
*/}
); } diff --git a/src/contexts/AccountContext.jsx b/src/contexts/AccountContext.jsx index ec047b8..4442bbf 100644 --- a/src/contexts/AccountContext.jsx +++ b/src/contexts/AccountContext.jsx @@ -1,4 +1,12 @@ -import { createContext, useState, useContext } from "react"; +import { useContext, createContext, useState, useEffect } from "react"; +import { onAuthStateChanged } from "firebase/auth"; +import { auth } from "../config/firebase"; +import axios from "axios"; + +const createUserDBUrl_dev = + "http://127.0.0.1:5001/agrilens-web/us-central1/app/users/pTGGKTxkAPf6rQa5PTKSbqrECm82/account"; +const createUserDBUrl_prod = + "https://app-id543mmv6a-uc.a.run.app/users/pTGGKTxkAPf6rQa5PTKSbqrECm82/account"; // Create contexts const AccountContext = createContext(); @@ -14,28 +22,156 @@ export function useAccountUpdateContext() { } export const AccountProvider = ({ children }) => { - const [userType, setUserType] = useState("Guest"); + const [userType, setUserType] = useState(""); const [userEmail, setUserEmail] = useState(""); const [userName, setUserName] = useState(""); - const [userToken, setUserToken] = useState(""); + const [userFName, setUserFName] = useState(""); + const [userLName, setUserLName] = useState(""); + const [userAccInfo, setUserAccInfo] = useState(""); + const [userAccDetail, setUserAccDetail] = useState({}); + const [userID, setUserID] = useState(localStorage.getItem("userID") || ""); + const [userToken, setUserToken] = useState( + localStorage.getItem("userToken") || "" + ); + + const getUserAccInfo = async (userId, headers = {}) => { + try { + const getUserInfoUrl_dev = `http://127.0.0.1:5001/agrilens-web/us-central1/app/users/${userId}/account`; + const getUserInfoUrl_prod = `https://app-id543mmv6a-uc.a.run.app/users/${userId}/account`; + + const response = await axios.get(getUserInfoUrl_dev, headers); + console.log("Response:", response.data); + console.log("Response:", response.status); + const data = response?.data; + setUserAccInfo(() => data?.account); + + console.log("data: ", data); + return data; + } catch (err) { + console.error("fetchData() Error:", err); + } finally { + } + }; + const updateUserAccInfoDB = async (userId, data, headers = {}) => { + try { + console.log(">> updateUserAccInfoDB: ", data); + const getUserInfoUrl_dev = `http://127.0.0.1:5001/agrilens-web/us-central1/app/users/${userId}/account`; + const getUserInfoUrl_prod = `https://app-id543mmv6a-uc.a.run.app/users/${userId}/account`; + + const response = await axios.put(getUserInfoUrl_dev, data, headers); + // console.log("Response:", response.data); + // console.log("Response:", response.status); + const updatedData = response?.data; + setUserAccInfo(() => updatedData?.account); + + // console.log("data: ", updatedData); + return updatedData; + } catch (err) { + console.error("fetchData() Error:", err); + } finally { + } + }; + + // useEffect(() => { + // const savedUserID = localStorage.getItem("userID"); + // const savedUserToken = localStorage.getItem("userToken"); + + // if (savedUserID && savedUserToken) { + // setUserID(savedUserID); + // setUserToken(savedUserToken); + // } + // }, []); + + useEffect(() => { + const unsubscribe = onAuthStateChanged(auth, async (user) => { + if (user) { + const userID = user.uid; + const userToken = user.accessToken; + + setUserID(userID); + setUserToken(userToken); + localStorage.setItem("userID", userID); + localStorage.setItem("userToken", userToken); + setUserEmail(user.email); + + // Wait for the account information to be fetched + const data = await getUserAccInfo(userID); + const accountInfo = data?.account; + console.log(">> 2 Context user account Data :", accountInfo); + + setUserType(accountInfo?.type); + setUserFName(accountInfo?.firstName); + setUserLName(accountInfo?.lastName); + setUserName(accountInfo?.firstName + " " + accountInfo?.lastName); + + setUserAccDetail(() => accountInfo); + } else { + // User is signed out + setUserID(""); + setUserToken(""); + setUserEmail(""); + } + }); + + // Cleanup subscription on unmount + return () => unsubscribe(); + }, []); // Update functions const updateUserType = (type) => setUserType(type); const updateUserEmail = (email) => setUserEmail(email); const updateUserName = (name) => setUserName(name); - const updateUserToken = (token) => setUserToken(token); + const updateUserFName = (name) => setUserFName(name); + const updateUserLName = (name) => setUserLName(name); + const updateUserAccDetail = (detail) => { + console.log(">> 1 first: ", detail); + + // Update user account detail state + setUserAccDetail((prevDetails) => { + const updatedDetails = { + ...prevDetails, + ...detail, + }; + + // console.log(">> 2. Updated Details: ", updatedDetails); + updateUserAccInfoDB(userID, updatedDetails); + + return updatedDetails; + }); + }; + const updateUserID = (id) => { + setUserID(id); + localStorage.setItem("userID", id); + }; + const updateUserToken = (token) => { + setUserToken(token); + localStorage.setItem("userToken", token); + }; // Provide state and update functions as objects return ( {children} diff --git a/src/navBar/NavBar.css b/src/navBar/NavBar.css index 09cac59..31acde6 100644 --- a/src/navBar/NavBar.css +++ b/src/navBar/NavBar.css @@ -53,7 +53,7 @@ font-weight: bold; border: 1px solid; border-radius: 4px; - min-width: 160px; + min-width: 190px; margin: 20px auto; } diff --git a/src/navBar/NavBar.jsx b/src/navBar/NavBar.jsx index 7816d38..6edde5d 100644 --- a/src/navBar/NavBar.jsx +++ b/src/navBar/NavBar.jsx @@ -1,4 +1,4 @@ -import React, { useState, useContext } from "react"; +import React, { useState, useEffect, useContext } from "react"; import { Link } from "react-router-dom"; import "./NavBar.css"; import { useAccountContext } from "../contexts/AccountContext"; @@ -77,12 +77,9 @@ export default function NavBar() { > Contact Us - - Profile -
-
+
{userType}