From 8a812eefd7c670cb845405f9bbc269ef6090b745 Mon Sep 17 00:00:00 2001
From: nkabembo <71999441+nkabembo@users.noreply.github.com>
Date: Fri, 28 Nov 2025 01:12:44 +0200
Subject: [PATCH 1/4] Profile page ui and part of contexts for profile
---
src/App.css | 8 ++--
src/components/Baseinput.jsx | 2 +-
src/contexts/ProfileContext.js | 5 ++
src/contexts/ProfileProvider.jsx | 38 +++++++++++++++
src/main.jsx | 11 +++--
src/pages/Profile.jsx | 82 ++++++++++++++++++++++++++++++++
6 files changed, 138 insertions(+), 8 deletions(-)
create mode 100644 src/contexts/ProfileContext.js
create mode 100644 src/contexts/ProfileProvider.jsx
create mode 100644 src/pages/Profile.jsx
diff --git a/src/App.css b/src/App.css
index c829a2b..7f31b0a 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,13 +1,13 @@
-button:not(.toggle-btn):focus-visible {
+button:not(.toggle-btn):not(.delete-btn):focus-visible {
outline: solid 4px var(--color-persianblue);
outline-offset: 8px;
}
-button:not(.toggle-btn):hover {
+button:not(.toggle-btn):not(.delete-btn):hover {
background-color: var(--color-zinc);
color: white;
cursor: pointer;
}
-button:not(.toggle-btn):active {
+button:not(.toggle-btn):not(.delete-btn):active {
background-color: var(--color-persianblue);
color: white;
}
@@ -17,4 +17,4 @@ button.active {
}
button.active:hover {
background-color: var(--color-persianblue);
-}
\ No newline at end of file
+}
diff --git a/src/components/Baseinput.jsx b/src/components/Baseinput.jsx
index cb5ecbf..5a388fc 100644
--- a/src/components/Baseinput.jsx
+++ b/src/components/Baseinput.jsx
@@ -33,7 +33,7 @@ function Baseinput({
block w-full h-11 px-4 py-2
bg-white text-black
${inputClassName}
- disabled:bg-gray-100 disabled:text-gray-500`}
+ disabled:bg-white disabled:text-eerie`}
aria-invalid={ariaInvalid}
aria-describedby={ariaDescribedBy}
disabled={disabled}
diff --git a/src/contexts/ProfileContext.js b/src/contexts/ProfileContext.js
new file mode 100644
index 0000000..9a6eede
--- /dev/null
+++ b/src/contexts/ProfileContext.js
@@ -0,0 +1,5 @@
+import { createContext } from 'react';
+
+const ProfileContext = createContext();
+
+export default ProfileContext;
diff --git a/src/contexts/ProfileProvider.jsx b/src/contexts/ProfileProvider.jsx
new file mode 100644
index 0000000..ec10f6c
--- /dev/null
+++ b/src/contexts/ProfileProvider.jsx
@@ -0,0 +1,38 @@
+import { useState } from 'react';
+import ProfileContext from './ProfileContext';
+
+export function ProfileProvider({ children }) {
+ const [user, setUser] = useState(null); // login info
+ const [extraData, setExtraData] = useState([]); // other component data
+
+ const handleLogout = () => {
+ setUser(null);
+ setExtraData([]);
+ localStorage.removeItem('user');
+ localStorage.removeItem('extraData');
+ };
+
+ const handleDeleteAccount = () => {
+ setUser(null);
+ setExtraData([]);
+ localStorage.removeItem('user');
+ localStorage.removeItem('extraData');
+ };
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/main.jsx b/src/main.jsx
index 1eadd95..90cf314 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -2,6 +2,7 @@ import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
+import { ProfileProvider } from './contexts/ProfileContext';
import Home from './pages/Home';
import Onboarding from './pages/Onboarding';
import Achievements from './pages/Achievements';
@@ -12,6 +13,7 @@ import Login from './pages/Login';
import Signup from './pages/Signup';
import Journal from './pages/journal';
import Logout from './pages/Logout';
+import Profile from './pages/Profile';
import App from './App';
const router = createBrowserRouter([
@@ -28,8 +30,9 @@ const router = createBrowserRouter([
{ path: 'achievements', element: },
{ path: 'journal', element: },
{ path: 'logout', element: },
+ { path: 'profile', element: },
{ path: 'error', element: },
- ],
+ ],
},
{
path: '*',
@@ -38,6 +41,8 @@ const router = createBrowserRouter([
]);
createRoot(document.getElementById('root')).render(
-
+
+
+
-);
\ No newline at end of file
+);
diff --git a/src/pages/Profile.jsx b/src/pages/Profile.jsx
new file mode 100644
index 0000000..5659ea4
--- /dev/null
+++ b/src/pages/Profile.jsx
@@ -0,0 +1,82 @@
+import Button from '../components/Button';
+import Baseinput from '../components/Baseinput';
+import { useContext } from 'react';
+import { ProfileContext } from '../contexts/ProfileContext';
+function Profile() {
+ let inputClassName =
+ 'rounded-none border-0 border-b border-eerie bg-transparent focus:border-stone-600 focus:ring-0';
+ const { handleLogout, handleDeleteAccount } = useContext(ProfileContext);
+ return (
+
+ );
+}
+
+export default Profile;
From fc65754986f491e310016c2cb3e482f029eb1524 Mon Sep 17 00:00:00 2001
From: nkabembo <71999441+nkabembo@users.noreply.github.com>
Date: Sat, 29 Nov 2025 15:42:59 +0200
Subject: [PATCH 2/4] profile logout, display onboarding and email data
---
src/components/Navbar.jsx | 21 +++++---
src/firebase.js | 11 ++--
src/pages/Login.jsx | 26 ++++++++--
src/pages/Onboarding.jsx | 103 +++++++++++++++++++++++++-------------
src/pages/Profile.jsx | 60 +++++++++++++++++-----
src/pages/Signup.jsx | 20 +++++++-
6 files changed, 177 insertions(+), 64 deletions(-)
diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx
index f67b214..eb185bf 100644
--- a/src/components/Navbar.jsx
+++ b/src/components/Navbar.jsx
@@ -9,6 +9,10 @@ import { HiMenu } from 'react-icons/hi';
//components
import MobileMenu from './MobileMenu';
+//firebase
+import { auth } from '../firebase';
+import { signOut } from 'firebase/auth';
+
function Navbar() {
const [isMenuOpen, setIsMenuOpen] = useState(false); // mobile menu state
const [isAuthenticated, setIsAuthenticated] = useState(false); // user authentication state
@@ -27,10 +31,15 @@ function Navbar() {
const toggleMenu = () => setIsMenuOpen(!isMenuOpen);
- const handleLogout = () => {
- localStorage.removeItem('authToken');
- setIsAuthenticated(false);
- navigate('/logout');
+ const handleLogout = async () => {
+ try {
+ await signOut(auth);
+ localStorage.removeItem('authToken');
+ setIsAuthenticated(false);
+ navigate('/logout');
+ } catch (error) {
+ console.error('Logout failed:', error);
+ }
};
// navbar for NON-authenticated user
@@ -123,7 +132,7 @@ function Navbar() {
Detox Challenge
-
+
-
+
{
setFormValues((prevValue) => ({ ...prevValue, [fieldName]: value }));
};
- const handleSubmit = (e) => {
+ const handleSubmit = async (e) => {
e.preventDefault();
if (!isValidEmail || !isValidPassword) {
setFormSubmitMessage('Fill form properly');
} else {
- localStorage.setItem('authToken', 'my-auth-token');
+ //localStorage.setItem('authToken', 'my-auth-token');
const loginFormData = new FormData();
for (const key in formValues) {
loginFormData.append(key, formValues[key]);
}
- navigate('/dashboard');
+ try {
+ const userCredential = await signinWithEmailAndPassWord(
+ auth,
+ formValues.loginEmail,
+ formValues.loginPassword
+ );
+ navigate('/dashboard');
+ } catch (error) {
+ if (error.code === 'auth/user-not-found') {
+ setFormSubmitMessage('No user found with this email');
+ } else if (error.code === 'auth/wrong-password') {
+ setFormSubmitMessage('No usr found with this email');
+ } else {
+ setFormSubmitMessage('Login failed. Try again.');
+ }
+ }
+
console.log('Login Submission Data:', loginFormData);
}
};
@@ -100,4 +118,4 @@ function Login() {
);
}
-export default Login;
\ No newline at end of file
+export default Login;
diff --git a/src/pages/Onboarding.jsx b/src/pages/Onboarding.jsx
index dc68c7c..1d24090 100644
--- a/src/pages/Onboarding.jsx
+++ b/src/pages/Onboarding.jsx
@@ -1,14 +1,44 @@
import Button from '../components/Button';
import { useEffect, useState } from 'react';
-
+import { doc, setDoc } from 'firebase/firestore';
+import { auth, db } from '../firebase';
+import { useNavigate } from 'react-router-dom';
export default function Onboarding() {
const [selectedConcerns, setSelectedConcerns] = useState([]);
-
+ const navigate = useNavigate();
// log the selectedConcerns after the array changes to update automatically
useEffect(() => {
console.log('Updated Concerns:', selectedConcerns);
}, [selectedConcerns]);
+ async function handleNext() {
+ if (!auth.currentUser) {
+ return;
+ }
+ const uid = auth.currentUser.uid;
+ await setDoc(
+ doc(db, 'users', uid),
+ { onboardingConcerns: selectedConcerns },
+ { merge: true }
+ )
+ .then(() => {
+ console.log('Onboarding data for profile is saved');
+ navigate('/dashboard');
+ })
+ .catch((error) => console.error('Error saving data:', error));
+ }
+ async function handleSkip() {
+ const uid = auth.currentUser.uid;
+ await setDoc(
+ doc(db, 'users', uid),
+ {
+ onboardingConcerns: [],
+ },
+ { merge: true }
+ );
+ navigate('/dashboard');
+ }
+
// function for toggling active state and storing the user's selected concern
function toggleConcern(concern) {
// if the clicked concern is not selectedConcerns, setSelectedConcerns
@@ -20,7 +50,7 @@ export default function Onboarding() {
} else {
// log the deselection
console.log(`${concern} button deselected!`);
- setSelectedConcerns(selectedConcerns.filter(item => item !== concern));
+ setSelectedConcerns(selectedConcerns.filter((item) => item !== concern));
}
}
@@ -39,53 +69,58 @@ export default function Onboarding() {
{/* button quiz grid */}
-
{/* next and skip buttons */}
-
-
+
+
diff --git a/src/pages/Profile.jsx b/src/pages/Profile.jsx
index 5659ea4..040304c 100644
--- a/src/pages/Profile.jsx
+++ b/src/pages/Profile.jsx
@@ -1,11 +1,47 @@
import Button from '../components/Button';
import Baseinput from '../components/Baseinput';
-import { useContext } from 'react';
-import { ProfileContext } from '../contexts/ProfileContext';
+import { useState, useEffect } from 'react';
+//import { useContext } from 'react';
+import { signOut } from 'firebase/auth';
+import { auth, db } from '../firebase';
+import { doc, getDoc } from 'firebase/firestore';
+import { useNavigate } from 'react-router-dom';
+//import { ProfileContext } from '../contexts/ProfileContext';
function Profile() {
+ const { profileData, setProfileData } = useState(null);
+ const { loading, setLoading } = useState(true);
+ const navigate = useNavigate();
let inputClassName =
'rounded-none border-0 border-b border-eerie bg-transparent focus:border-stone-600 focus:ring-0';
- const { handleLogout, handleDeleteAccount } = useContext(ProfileContext);
+ //const { handleLogout, handleDeleteAccount } = useContext(ProfileContext);
+ useEffect(() => {
+ async function fetchProfileData() {
+ try {
+ const uid = auth.currentUser.uid;
+
+ const docRef = doc(db, 'users', uid);
+ const docSnap = await getDoc(docRef);
+
+ if (docSnap.exists()) {
+ setProfileData(docSnap.data());
+ } else {
+ console.log('No user document found');
+ }
+ } catch (error) {
+ console.error('Error fetching user data:', error);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ fetchProfileData();
+ }, []);
+ const handleLogout = async () => {
+ await signOut(auth);
+ localStorage.removeItem('authToken');
+ navigate('/logout');
+ };
+ if (loading) return Loading
;
return (