diff options
author | Jeff Kleinaitis <jeffkleinaitis@gmail.com> | 2024-04-18 10:28:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-18 10:28:13 -0700 |
commit | 0af66ac5c814b9cf01e60f13a4c174722d33d939 (patch) | |
tree | 56812cfb8e5401981015f0b77ffcd76244d02d29 /src | |
parent | 04694856be7624ae9e80f673effc3104d7842e34 (diff) | |
parent | 3a5e9dfee0f26c03e12bb2940ca42bba1f172f7c (diff) |
Merge pull request #27 from digadoo/develop
4/18 PR
Diffstat (limited to 'src')
-rw-r--r-- | src/App.js | 7 | ||||
-rw-r--r-- | src/components/FlexibleForm.js | 30 | ||||
-rw-r--r-- | src/components/Footer.js | 2 | ||||
-rw-r--r-- | src/components/Home.js | 41 | ||||
-rw-r--r-- | src/components/Navbar.js | 171 | ||||
-rw-r--r-- | src/layouts/AuthenticatedLayout.js | 17 | ||||
-rw-r--r-- | src/layouts/GridLayout.js | 72 | ||||
-rw-r--r-- | src/pages/Dashboard.js | 12 | ||||
-rw-r--r-- | src/pages/Login.js | 65 | ||||
-rw-r--r-- | src/pages/SignUp.js | 52 | ||||
-rw-r--r-- | src/providers/AuthContext.js | 58 |
11 files changed, 421 insertions, 106 deletions
@@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { BrowserRouter as Router, Routes, Route, useLocation } from 'react-router-dom'; import Navbar from './components/Navbar'; import Footer from './components/Footer'; @@ -13,12 +13,15 @@ import Careers from "./pages/Careers"; import HelpCenter from "./pages/HelpCenter"; import TermsOfService from "./pages/TermsOfService"; import PrivacyPolicy from "./pages/PrivacyPolicy"; +import Dashboard from "./pages/Dashboard"; +import {AuthProvider} from "./providers/AuthContext"; function App() { const location = useLocation(); const excludedRoutes = ['/login', '/sign-up', '/forgot-password']; return ( + <AuthProvider> <div className="App"> {!excludedRoutes.includes(location.pathname) && <Navbar />} <Routes> @@ -30,12 +33,14 @@ function App() { <Route path="/terms-of-service" element={<TermsOfService />} /> <Route path="/privacy-policy" element={<PrivacyPolicy />} /> <Route path="/login" element={<Login />} /> + <Route path="/dashboard" element={<Dashboard />} /> <Route path="/forgot-password" element={<ForgotPassword />} /> <Route path="/sign-up" element={<SignUp />} /> </Routes> {!excludedRoutes.includes(location.pathname) && <Footer />} {!excludedRoutes.includes(location.pathname) && <PopupWidget />} </div> + </AuthProvider> ); } diff --git a/src/components/FlexibleForm.js b/src/components/FlexibleForm.js index 831f972..d8c959d 100644 --- a/src/components/FlexibleForm.js +++ b/src/components/FlexibleForm.js @@ -2,27 +2,13 @@ import React from 'react'; import { Link } from "react-router-dom"; import logo from "../assets/img/logo.svg"; -const FlexibleForm = ({ data, onSuccess }) => { - const handleSubmit = async (event) => { - event.preventDefault(); - const formData = new FormData(event.target); - - try { - const response = await fetch(process.env.NEXT_PUBLIC_HOST + data.formAction, { - method: data.formMethod, - body: JSON.stringify(Object.fromEntries(formData)), - headers: { - 'Content-Type': 'application/json' - }, - }); - - const token = response.headers.get('Authorization'); - onSuccess(token); - - } catch (error) { - console.error('Error submitting form:', error); - } - } +const FlexibleForm = ({ data, onFormSubmit }) => { + const handleSubmit = (event) => { + event.preventDefault(); + if (typeof onFormSubmit === 'function') { + onFormSubmit(event); + } + }; return ( <div className="flex flex-col items-center justify-center min-h-screen"> @@ -56,6 +42,8 @@ const FlexibleForm = ({ data, onSuccess }) => { type={field.type} autoComplete={field.autoComplete} required={field.required} + value={field.value} + onChange={field.onChange} className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" /> </div> diff --git a/src/components/Footer.js b/src/components/Footer.js index 2878c5c..bb29839 100644 --- a/src/components/Footer.js +++ b/src/components/Footer.js @@ -18,7 +18,7 @@ export default function Footer() { return ( <div className="relative"> <Container> - <div className="grid max-w-screen-xl grid-cols-1 gap-10 pt-10 mx-auto mt-5 border-t border-gray-100 dark:border-trueGray-700 lg:grid-cols-5"> + <div className="grid max-w-screen grid-cols-1 gap-10 pt-10 mx-auto mt-5 border-t border-gray-100 dark:border-trueGray-700 lg:grid-cols-5"> <div className="lg:col-span-2"> <div> {" "} diff --git a/src/components/Home.js b/src/components/Home.js index c0e2b24..dd368ed 100644 --- a/src/components/Home.js +++ b/src/components/Home.js @@ -4,26 +4,35 @@ import { benefitOne, benefitTwo } from "./Data"; import Benefits from "../components/Benefits"; import Cta from "../components/Cta"; import Faq from "../components/Faq"; +import {AuthContext} from "../providers/AuthContext"; +import React, {useContext} from "react"; const Home = () => { + const {user} = useContext(AuthContext); + return ( <> - <Hero /> - <SectionTitle - pretitle="How It Works" - title="Getting Started with Helping Hands" - > - Helping Hands simplifies the process of finding or offering volunteer opportunities. Here's a step-by-step guide: - </SectionTitle> - <Benefits data={benefitOne} /> - <Benefits imgPos="right" data={benefitTwo} /> - <SectionTitle pretitle="FAQ" title="Frequently Asked Questions"> - Welcome to our FAQ section! Browse through common questions for quick answers. Your clarity and support matter to us. For any unanswered queries, reach out to our support team. - </SectionTitle> - <Faq /> - <Cta /> + {!user ? ( + <> + <Hero/> + <SectionTitle pretitle="How It Works" title="Getting Started with Helping Hands"> + Helping Hands simplifies the process of finding or offering volunteer opportunities. Here's a + step-by-step guide: + </SectionTitle> + <Benefits data={benefitOne}/> + <Benefits imgPos="right" data={benefitTwo}/> + <SectionTitle pretitle="FAQ" title="Frequently Asked Questions"> + Welcome to our FAQ section! Browse through common questions for quick answers. Your clarity and + support matter to us. For any unanswered queries, reach out to our support team. + </SectionTitle> + <Faq/> + <Cta/> + </> + ) : ( + <> + </> + )} </> ); } - -export default Home;
\ No newline at end of file + export default Home;
\ No newline at end of file diff --git a/src/components/Navbar.js b/src/components/Navbar.js index 9b40882..3fdf94d 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -1,20 +1,36 @@ -import React from 'react'; +import React, {useContext} from 'react'; import { Disclosure } from "@headlessui/react"; import { Link } from "react-router-dom"; import logo from "../assets/img/logo.svg"; +import {AuthContext} from "../providers/AuthContext"; +import Container from "./Container"; +import {ChevronDownIcon} from "@heroicons/react/24/solid"; +import {BellIcon} from "@heroicons/react/24/outline"; + + const Navbar = () => { - const navigation = [ + const { user } = useContext(AuthContext); + + const loggedOutNavigation = [ "Find Opportunities", "Recruit Volunteers", - "Help Center", - "About Us", + "Sign Up", + "Login" + ]; + + const loggedInNavigation = [ + "Edit Profile", + "Settings", + "Help", + "Sign Out" ]; return ( + <Container> <div className="w-full"> - <nav className="container relative flex flex-wrap items-center justify-between p-8 mx-auto lg:justify-between lg:flex-nowrap lg:px-0.5 xl:px-0"> - {/* Logo */} + <nav className="container relative flex flex-wrap border-b border-gray-100 items-center justify-between p-8 mx-auto lg:justify-between lg:flex-nowrap lg:px-0.5 xl:px-0"> + {/* Logo */} <Disclosure> {({ open }) => ( <> @@ -34,67 +50,114 @@ const Navbar = () => { </span> </Link> - <Disclosure.Button - aria-label="Toggle Menu" - className="px-2 py-1 ml-auto text-gray-500 rounded-md lg:hidden hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100 focus:outline-none dark:text-gray-300 dark:focus:bg-trueGray-700"> - <svg - className="w-6 h-6 fill-current" - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 24 24"> - {open && ( - <path - fillRule="evenodd" - clipRule="evenodd" - d="M18.278 16.864a1 1 0 0 1-1.414 1.414l-4.829-4.828-4.828 4.828a1 1 0 0 1-1.414-1.414l4.828-4.829-4.828-4.828a1 1 0 0 1 1.414-1.414l4.829 4.828 4.828-4.828a1 1 0 1 1 1.414 1.414l-4.828 4.829 4.828 4.828z" - /> - )} - {!open && ( - <path - fillRule="evenodd" - d="M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z" - /> - )} - </svg> - </Disclosure.Button> + {/* Hamburger drop down menu */} + {!user ? ( + <Disclosure.Button aria-label="Toggle Menu" className="px-2 py-1 ml-auto text-gray-500 rounded-md lg:hidden hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100 focus:outline-none dark:text-gray-300 dark:focus:bg-trueGray-700"> + <svg className="w-6 h-6 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + {open && ( + <path fillRule="evenodd" clipRule="evenodd" d="M18.278 16.864a1 1 0 0 1-1.414 1.414l-4.829-4.828-4.828 4.828a1 1 0 0 1-1.414-1.414l4.828-4.829-4.828-4.828a1 1 0 0 1 1.414-1.414l4.829 4.828 4.828-4.828a1 1 0 1 1 1.414 1.414l-4.828 4.829 4.828 4.828z"/> + )} + {!open && ( + <path fillRule="evenodd" d="M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z"/> + )} + </svg> + </Disclosure.Button> + ) : ( + <Disclosure.Button aria-label="Toggle Menu" className="px-2 py-1 ml-auto text-gray-500 rounded-md lg:hidden hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100 focus:outline-none dark:text-gray-300 dark:focus:bg-trueGray-700"> + <svg className="w-6 h-6 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + {open && ( + <path fillRule="evenodd" clipRule="evenodd" d="M18.278 16.864a1 1 0 0 1-1.414 1.414l-4.829-4.828-4.828 4.828a1 1 0 0 1-1.414-1.414l4.828-4.829-4.828-4.828a1 1 0 0 1 1.414-1.414l4.829 4.828 4.828-4.828a1 1 0 1 1 1.414 1.414l-4.828 4.829 4.828 4.828z"/> + )} + {!open && ( + <path fillRule="evenodd" d="M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z"/> + )} + </svg> + </Disclosure.Button> + )} + - <Disclosure.Panel className="flex flex-wrap w-full my-5 lg:hidden"> - <> - {navigation.map((item, index) => ( - <Link key={index} to="/" className="w-full px-4 py-2 -ml-4 text-gray-500 rounded-md dark:text-gray-300 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100 dark:focus:bg-gray-800 focus:outline-none"> + {/* Hamburger drop down menu options */} + {!user ? ( + <Disclosure.Panel className="flex flex-wrap w-full my-5 lg:hidden"> + {loggedOutNavigation.map((item, index) => ( + <Link key={index} to={`/${item.toLowerCase().replace(/\s+/g, '-')}`} className="w-full px-4 py-2 -ml-4 text-gray-500 rounded-md dark:text-gray-300 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100 dark:focus:bg-gray-800 focus:outline-none"> {item} </Link> ))} <Link to="/login" className="w-full px-6 py-2 mt-3 text-center text-white bg-indigo-600 rounded-md lg:ml-5"> Login </Link> - </> - </Disclosure.Panel> + </Disclosure.Panel> + ) : ( + <Disclosure.Panel className="w-full flex flex-wrap my-5 lg:hidden"> + <div className="w-full border-b-2 border-gray-150 px-4 py-2 -ml-4 flex items-center"> + <img className="w-10 h-10 rounded-full" src={logo} alt="user-img" /> + <div className="ml-4"> + <strong>{user.name}</strong> + <br /> + <span>{user.email}</span> + </div> + </div> + {loggedInNavigation.map((item, index) => ( + <Link key={index} to={`/${item.toLowerCase().replace(/\s+/g, '-')}`} className="w-full px-4 py-2 -ml-4 text-gray-500 rounded-md dark:text-gray-300 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100 dark:focus:bg-gray-800 focus:outline-none"> + {item} + </Link> + ))} + </Disclosure.Panel> + )} </div> </> )} </Disclosure> - {/* menu */} - <div className="hidden text-center lg:flex lg:items-center"> - <ul className="items-center justify-end flex-1 pt-6 list-none lg:pt-0 lg:flex"> - {navigation.map((menu, index) => ( - <li className="mr-3 nav__item" key={index}> - <Link to="/" className="inline-block px-4 py-2 text-lg font-normal text-gray-800 no-underline rounded-md dark:text-gray-200 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100 focus:outline-none dark:focus:bg-gray-800"> - {menu} - </Link> - </li> - ))} - </ul> - </div> - - <div className="hidden mr-3 space-x-4 lg:flex nav__item"> - <Link to="/login" className="px-6 py-2 text-white bg-indigo-600 rounded-md md:ml-5"> - Login - </Link> - </div> + {/* Right side header bar */} + {!user ? ( + <div className="hidden text-center lg:flex lg:items-center"> + <ul className="items-center justify-end flex-1 pt-6 list-none lg:pt-0 lg:flex"> + {loggedOutNavigation.map((menu, index) => ( + <li className="mr-3 nav__item" key={index}> + <Link key={index} to={`/${menu.toLowerCase().replace(/\s+/g, '-')}`} className="inline-block px-4 py-2 text-lg font-normal text-gray-800 no-underline rounded-md dark:text-gray-200 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100 focus:outline-none dark:focus:bg-gray-800"> + {menu} + </Link> + </li> + ))} + </ul> + </div> + ) : ( + <div className="hidden lg:flex lg:items-center"> + <div className="flex items-center space-x-3"> + <button className="w-5 h-5 text-gray-400"> <BellIcon /></button> + <span className="border-l border-gray-300 h-6"></span> + <Disclosure as="div" className="relative"> + <Disclosure.Button as="button" aria-label="Toggle Profile Menu" className="flex items-center justify-center text-sm rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"> + <img className="w-8 h-8 rounded-full" src={logo} alt="user-img" /> + <ChevronDownIcon className="w-4 h-4 ml-1 text-gray-400" /> + </Disclosure.Button> + <Disclosure.Panel className="absolute right-0 mt-2 bg-white overflow-hidden whitespace-nowrap overflow-ellipsis divide-y divide-gray-100 rounded-lg shadow w-64 dark:bg-gray-700 dark:divide-gray-600 z-10"> + <div className="flex items-center mb-2 mt-2"> + <img className="w-10 h-10 rounded-full" src={logo} alt="user-img" /> + <div className="ml-2 ,b flex flex-col"> + <strong className="text-sm">{user.name}</strong> + <span className="text-xs text-gray-600">{user.email}</span> + </div> + </div> + <ul className="py-2 text-sm text-gray-700 dark:text-gray-200"> + {loggedInNavigation.map((menu, index) => ( + <li className="mr-3 nav__item" key={index}> + <Link key={index} to={`/${menu.toLowerCase().replace(/\s+/g, '-')}`} className="inline-block px-4 py-2 text-md font-normal text-gray-800 no-underline rounded-md dark:text-gray-200 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100 focus:outline-none dark:focus:bg-gray-800"> + {menu} + </Link> + </li> + ))} + </ul> + </Disclosure.Panel> + </Disclosure> + </div> + </div> + )} </nav> </div> - ); -} + </Container> + )} export default Navbar; diff --git a/src/layouts/AuthenticatedLayout.js b/src/layouts/AuthenticatedLayout.js new file mode 100644 index 0000000..7c39672 --- /dev/null +++ b/src/layouts/AuthenticatedLayout.js @@ -0,0 +1,17 @@ +import React from 'react'; +import Navbar from './components/Navbar'; +import Footer from './components/Footer'; +import PopupWidget from './components/PopupWidget'; + +const AuthenticatedLayout = ({ children }) => { + return ( + <> + <Navbar /> + {children} {/* This will render the content */} + <Footer /> + <PopupWidget /> + </> + ); +}; + +export default AuthenticatedLayout;
\ No newline at end of file diff --git a/src/layouts/GridLayout.js b/src/layouts/GridLayout.js new file mode 100644 index 0000000..a96b351 --- /dev/null +++ b/src/layouts/GridLayout.js @@ -0,0 +1,72 @@ +import React from "react"; +import { + HomeIcon, + EnvelopeIcon, + UserIcon, + BuildingStorefrontIcon, + BriefcaseIcon, +} from "@heroicons/react/24/outline" + +const GridLayout = ({ children }) => { + return ( + <div className="container px-8 mx-auto grid grid-cols-[96px,1fr,96px] gap-10"> + + {/*Left column - sticky */} + <div className="self-start sticky top-0 col-span-1 pt-8 "> + <nav className="p-2 "> + <a href="/" className="flex items-center flex-col mb-4 text-trueGray-500 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100"> + <HomeIcon className="w-8 h-8" /> + <button + rel="noopener" + className="mb-1 text-xs font-light"> + Home + </button> + </a> + <a href="/profile" className="flex items-center flex-col mb-4 text-trueGray-500 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100"> + <UserIcon className="w-8 h-8" /> + <button + rel="noopener" + className="mb-1 text-xs font-light"> + Profile + </button> + </a> + <a href="/volunteer" className="flex items-center flex-col mb-4 text-trueGray-500 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100"> + <BriefcaseIcon className="w-8 h-8" /> + <button + rel="noopener" + className="mb-1 text-xs font-light whitespace-nowrap"> + Find Opportunities + </button> + </a> + <a href="/volunteer" className="flex items-center flex-col mb-4 text-trueGray-500 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100"> + <BuildingStorefrontIcon className="w-8 h-8" /> + <button + rel="noopener" + className="mb-1 text-xs font-light whitespace-nowrap"> + Recruit Volunteers + </button> + </a> + <a href="/" className="flex items-center flex-col mb-4 text-trueGray-500 hover:text-indigo-500 focus:text-indigo-500 focus:bg-indigo-100"> + <EnvelopeIcon className="w-8 h-8 " /> + <button + rel="noopener" + className="mb-1 text-xs font-light"> + Messages + </button> + </a> + </nav> + </div> + + {/*Middle column */} + <div className="overflow-auto bg-blue-300 h-screen">01 + <div>01 asdfasdf</div> + </div> + + {/*Right column */} + <div className="bg-green-300">01</div> + {children} + </div> + ); +} + +export default GridLayout;
\ No newline at end of file diff --git a/src/pages/Dashboard.js b/src/pages/Dashboard.js new file mode 100644 index 0000000..c7f136d --- /dev/null +++ b/src/pages/Dashboard.js @@ -0,0 +1,12 @@ +import Sidebar from "../components/Sidebar"; + +const Dashboard = () => { + return ( + <> + <Sidebar /> + </> + ); +} + + +export default Dashboard;
\ No newline at end of file diff --git a/src/pages/Login.js b/src/pages/Login.js index 700de0d..2aa90f5 100644 --- a/src/pages/Login.js +++ b/src/pages/Login.js @@ -1,20 +1,73 @@ -import React from "react"; +import React, {useContext, useState} from "react"; import FlexibleForm from "../components/FlexibleForm"; import benefitTwoImg from "../assets/img/benefit-two.png"; +import { AuthContext } from '../providers/AuthContext'; +import {useNavigate} from "react-router-dom"; const Login = () => { + const navigate = useNavigate(); + const { login } = useContext(AuthContext); + const [formData, setFormData] = useState({ + email: "", + password: "", + }); + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData((prevData) => ({ + ...prevData, + [name]: value, + })); + }; + + const handleSubmit = async (event) => { + event.preventDefault(); + const { email, password } = formData; + const basicAuth = btoa(`${email}:${password}`) + + try { + const response = await fetch(process.env.REACT_APP_LOGIN_ROUTE, { + method: "GET", + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Basic ${basicAuth}`, + }, + }); + if (response.ok) { + const jwt = await response.text(); + const tokenParts = jwt.split(".") + const decodedPayload = atob(tokenParts[1]); + const payload = JSON.parse(decodedPayload); + const userId = payload.sub; + + const userDetailsResponse = await fetch(process.env.REACT_APP_REGISTER_ROUTE + '/' + userId , { + method: "GET", + }); + + if (userDetailsResponse.ok) { + const userDetails = await userDetailsResponse.json(); + login(userDetails, jwt); + navigate("/") + + } else { console.error("Failed to fetch details") } + + } else { console.error("Login failed") } + + } catch (error) { + console.error('Error submitting form:', error); + } + } + return ( <> <FlexibleForm - onSuccess={''} + onFormSubmit={handleSubmit} data={{ image: benefitTwoImg, title: "Sign in to your account", subtitle: "Sign in please", - formAction: "/login", - formMethod: "POST", formFields: [ - { label: "Email address", name: "email", type: "email", autoComplete: "email", required: true }, - { label: "Password", name: "password", type: "password", autoComplete: "current-password", required: true }, + { label: "Email address", name: "email", type: "email", autoComplete: "email", required: true, value: formData.email, onChange: handleChange }, + { label: "Password", name: "password", type: "password", autoComplete: "current-password", required: true, value: formData.password, onChange: handleChange }, ], ctaText: "Login", ctaLink: { text: "Don't have an account?", linkText: "Create an account instead!", url: "/sign-up" }, diff --git a/src/pages/SignUp.js b/src/pages/SignUp.js index 038b75c..11ab367 100644 --- a/src/pages/SignUp.js +++ b/src/pages/SignUp.js @@ -1,23 +1,61 @@ -import React from "react"; +import React, {useState} from "react"; +import { useNavigate } from "react-router-dom"; import FlexibleForm from "../components/FlexibleForm"; import benefitTwoImg from "../assets/img/benefit-two.png"; const SignUp = () => { + const navigate = useNavigate(); + + const [formData, setFormData] = useState({ + name: "", + email: "", + password: "", + }); + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData((prevData) => ({ + ...prevData, + [name]: value, + })); + }; + + + const handleSubmit = async (event) => { + event.preventDefault(); + + try { + const response = await fetch(process.env.REACT_APP_REGISTER_ROUTE, { + method: "POST", + body: JSON.stringify(formData), + headers: { + 'Content-Type': 'application/json' + }, + }); + + if (!response.ok) { + throw new Error(await response.text()); + } + + console.log('Form submitted successfully'); + navigate("/login"); + } catch (error) { + console.error('Error submitting form:', error); + } + } return ( <> <FlexibleForm - onSuccess={''} + onFormSubmit={handleSubmit} data={{ image: benefitTwoImg, title: "Create your account", subtitle: "maybe edit this text hmm", - formAction: "/register", - formMethod: "POST", formFields: [ - { label: "Full Name", name: "name", type: "text", autoComplete: "name", required: true }, - { label: "Email address", name: "email", type: "email", autoComplete: "email", required: true }, - { label: "Password", name: "password", type: "password", autoComplete: "new-password", required: true }, + { label: "Full Name", name: "name", type: "text", autoComplete: "name", required: true, value: formData.name, onChange: handleChange }, + { label: "Email address", name: "email", type: "email", autoComplete: "email", required: true, value: formData.email, onChange: handleChange }, + { label: "Password", name: "password", type: "password", autoComplete: "new-password", required: true, value: formData.password, onChange: handleChange }, ], ctaText: "Create Account", ctaLink: { text: "Already have an account?", linkText: "Log in instead!", url: "/login" }, diff --git a/src/providers/AuthContext.js b/src/providers/AuthContext.js new file mode 100644 index 0000000..a32fb01 --- /dev/null +++ b/src/providers/AuthContext.js @@ -0,0 +1,58 @@ +import React, { createContext, useEffect, useState } from 'react'; + +export const AuthContext = createContext(); + +export const AuthProvider = ({ children }) => { + const [user, setUser] = useState(null); + const [refreshToken, setRefreshToken] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const initializeAuth = async () => { + try { + const storedUser = localStorage.getItem('user'); + const storedRefreshToken = localStorage.getItem('refreshToken'); + if (storedUser && storedRefreshToken) { + setUser(JSON.parse(storedUser)); + setRefreshToken(storedRefreshToken); + } + } catch (error) { + console.error('Error initializing auth:', error); + } finally { + setLoading(false); + } + }; + + initializeAuth(); + }, []); + + const login = (userData, token) => { + setUser(userData); + setRefreshToken(token); + localStorage.setItem('user', JSON.stringify(userData)); + localStorage.setItem('refreshToken', token); + }; + + const logout = () => { + setUser(null); + setRefreshToken(null); + localStorage.removeItem('user'); + localStorage.removeItem('refreshToken'); + }; + + const authContextValue = { + user, + refreshToken, + login, + logout, + }; + + // Maybe make a loading screen animation/component to use? + return ( + <AuthContext.Provider value={authContextValue}> + {loading ? <div></div> : children} + </AuthContext.Provider> + ); +}; + +export default AuthProvider; |