diff --git a/package-lock.json b/package-lock.json
index 2a30e3a..55b6683 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,7 @@
"@mui/icons-material": "^6.1.5",
"@mui/material": "^6.1.5",
"firebase": "^11.0.1",
- "react": "^18.3.0",
+ "react": "^18.3.1",
"react-dom": "^18.3.0",
"react-router-dom": "^6.27.0"
},
diff --git a/package.json b/package.json
index a0be452..3f8346c 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
"@mui/icons-material": "^6.1.5",
"@mui/material": "^6.1.5",
"firebase": "^11.0.1",
- "react": "^18.3.0",
+ "react": "^18.3.1",
"react-dom": "^18.3.0",
"react-router-dom": "^6.27.0"
},
diff --git a/src/components/common/Header.jsx b/src/components/common/Header.jsx
new file mode 100644
index 0000000..84ef4c6
--- /dev/null
+++ b/src/components/common/Header.jsx
@@ -0,0 +1,63 @@
+import { AppBar, Avatar, Box, Button, IconButton, Toolbar, Typography } from '@mui/material';
+import { handleSignIn, handleSignOut } from '@utils/auth';
+import { auth } from '@utils/firebaseConfig';
+import { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+const Header = () => {
+ const [user, setUser] = useState(null);
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ const unsubscribe = auth.onAuthStateChanged((user) => {
+ setUser(user);
+ });
+ return () => unsubscribe();
+ }, []);
+
+ const handleAuthClick = async () => {
+ if (user) {
+ await handleSignOut(navigate);
+ } else {
+ await handleSignIn();
+ }
+ };
+
+ return (
+
+
+
+ {/* Placeholder Box for back button if needed in the future */}
+
+
+
+
+ Stepwise
+
+
+
+ {user ? (
+
+
+
+ ) : (
+
+ )}
+
+
+
+ );
+};
+
+export default Header;
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
index 6270bf7..ced5c3b 100644
--- a/src/pages/Home.jsx
+++ b/src/pages/Home.jsx
@@ -1,5 +1,12 @@
+import Header from '@components/common/Header';
+
const Home = () => {
- return
Home
;
+ return (
+
+
+
Home
+
+ );
};
export default Home;
diff --git a/src/utils/auth.js b/src/utils/auth.js
new file mode 100644
index 0000000..1cf069b
--- /dev/null
+++ b/src/utils/auth.js
@@ -0,0 +1,47 @@
+import { createFirstUserProfile, fetchUserProfile } from '@utils/firebase/createUserProfile';
+import { auth } from '@utils/firebaseConfig';
+import { GoogleAuthProvider, signInWithPopup, signOut } from 'firebase/auth';
+
+const provider = new GoogleAuthProvider();
+
+// Google Sign-In function
+const signInWithGoogle = async () => {
+ try {
+ const result = await signInWithPopup(auth, provider);
+ return result.user;
+ } catch (error) {
+ console.error('Error during sign-in:', error);
+ return null;
+ }
+};
+
+// Handle Sign-In
+// Returns true if the user already exists in the database, false if a new profile was created
+export const handleSignIn = async () => {
+ const user = await signInWithGoogle();
+
+ if (user) {
+ // Try to fetch the existing user profile
+ const { profile } = await fetchUserProfile(user.uid);
+
+ // If no profile found, create one with default data
+ if (!profile) {
+ await createFirstUserProfile(user);
+ return false; // New user profile created
+ }
+ return true; // User profile already exists
+ }
+ return false; // Sign-in failed or cancelled
+};
+
+// Handle Sign-Out
+// Redirects to homepage after sign-out
+export const handleSignOut = async (navigate) => {
+ try {
+ await signOut(auth);
+ console.log('Sign out successful');
+ navigate('/');
+ } catch (error) {
+ console.error('Error during sign-out:', error);
+ }
+};
diff --git a/src/utils/firebase/createUserProfile.js b/src/utils/firebase/createUserProfile.js
new file mode 100644
index 0000000..7656c7d
--- /dev/null
+++ b/src/utils/firebase/createUserProfile.js
@@ -0,0 +1,77 @@
+import { db } from '@utils/firebaseConfig';
+import { doc, getDoc, setDoc, updateDoc } from 'firebase/firestore';
+
+// Unified function to fetch and listen to user profile changes by UID
+// (supports both regular and transaction-based fetches)
+export const fetchUserProfile = async (uid, transaction = null) => {
+ try {
+ const userRef = doc(db, 'users', uid);
+
+ // If transaction is provided, use it to fetch the document
+ const userSnapshot = transaction ? await transaction.get(userRef) : await getDoc(userRef);
+
+ if (!userSnapshot.exists()) {
+ console.error(`User profile for ${uid} does not exist`);
+ return { ref: userRef, profile: null }; // Return consistent format with null profile
+ }
+
+ const profile = userSnapshot.data();
+ return { ref: userRef, profile };
+ } catch (error) {
+ console.error('Error fetching user profile:', error);
+ return { ref: null, profile: null };
+ }
+};
+
+// Check or create user profile in Firestore (uses fetchUserProfile to streamline code)
+export const createFirstUserProfile = async (user) => {
+ try {
+ const { uid, photoURL, displayName } = user;
+ const defaultProfile = {
+ uid,
+ profilePic: photoURL || '',
+ name: displayName || '',
+ goals: [
+ {
+ name: '',
+ progress: 0,
+ microgoals: [
+ {
+ name: '',
+ progress: 0,
+ tasks: [{ name: '', completed: false }],
+ },
+ ],
+ },
+ ],
+ streak: [],
+ };
+
+ // If the profile does not exist, create it with the default data
+ await setDoc(doc(db, 'users', uid), defaultProfile);
+ console.warn('New user profile created with default data.');
+
+ return true;
+ } catch (error) {
+ console.error('Error checking or creating user profile:', error);
+ return false; // Return false if an error occurs
+ }
+};
+
+// Get user profile by UID
+// (simple wrapper around fetchUserProfile for non-transaction use)
+export const getUserProfile = async (uid) => {
+ const fetchedUser = await fetchUserProfile(uid);
+ return fetchedUser ? fetchedUser.profile : null; // Return only the profile data
+};
+
+// Update user profile by UID
+export const updateUserProfile = async (uid, updates) => {
+ try {
+ const userDocRef = doc(db, 'users', uid);
+ await updateDoc(userDocRef, updates);
+ console.warn('User profile updated');
+ } catch (error) {
+ console.error('Error updating user profile:', error);
+ }
+};
diff --git a/src/utils/firebaseConfig.js b/src/utils/firebaseConfig.js
index 98f07d7..3ac43db 100644
--- a/src/utils/firebaseConfig.js
+++ b/src/utils/firebaseConfig.js
@@ -1,5 +1,12 @@
// Import the functions you need from the SDKs you need
import { initializeApp } from 'firebase/app';
+import { getAuth } from 'firebase/auth';
+import {
+ CACHE_SIZE_UNLIMITED,
+ initializeFirestore,
+ persistentLocalCache,
+ persistentSingleTabManager,
+} from 'firebase/firestore';
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
@@ -17,6 +24,13 @@ const firebaseConfig = {
// Initialize Firebase
const app = initializeApp(firebaseConfig);
-const db = getFirestore(app);
-export { db };
+// Enable IndexedDB persistence with single-tab manager
+export const db = initializeFirestore(app, {
+ localCache: persistentLocalCache({
+ tabManager: persistentSingleTabManager(),
+ cacheSizeBytes: CACHE_SIZE_UNLIMITED,
+ }),
+});
+
+export const auth = getAuth(app);