From 584584fc031da1bfa1ae4ce77c5f343c20995db9 Mon Sep 17 00:00:00 2001 From: jameramellyn <107943103+jameramellyn@users.noreply.github.com> Date: Wed, 21 Jan 2026 02:13:25 -0800 Subject: [PATCH 1/8] changed sign out to profile --- frontend/.env.development | 2 +- frontend/src/components/Navbar.tsx | 128 ++++++++++++----------------- 2 files changed, 54 insertions(+), 76 deletions(-) diff --git a/frontend/.env.development b/frontend/.env.development index 3cd2602..735d841 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1,3 +1,3 @@ # Don't stop the React webpack build if there are lint errors. ESLINT_NO_DEV_ERRORS=true -VITE_API_BASE_URL=http://localhost:5000 +VITE_API_BASE_URL=http://localhost:5005 diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index b35b3c6..0e3cc7e 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -1,4 +1,4 @@ -import { faBars, faCartShopping, faUser, faXmark, faHeart } from "@fortawesome/free-solid-svg-icons"; +import { faBars, faCartShopping, faUser, faXmark, faHeart, faChevronDown } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useContext, useEffect, useRef, useState } from "react"; import { FirebaseContext } from "src/utils/FirebaseProvider"; @@ -6,6 +6,9 @@ import { FirebaseContext } from "src/utils/FirebaseProvider"; export function Navbar() { const { user, signOutFromFirebase, openGoogleAuthentication } = useContext(FirebaseContext); const [isMobileMenuOpen, setMobileMenuOpen] = useState(false); + // State for the desktop profile dropdown + const [isProfileDropdownOpen, setIsProfileDropdownOpen] = useState(false); + const menuRef = useRef(null); const buttonRef = useRef(null); @@ -36,32 +39,30 @@ export function Navbar() { return () => { document.removeEventListener("mousedown", handleClickOutside); - window.removeEventListener("resize", handleResize); + window.addEventListener("resize", handleResize); }; }, []); return ( <> - {/* Mobile Menu Blur Effect */} - {isMobileMenuOpen && ( - +
+
+ + {/* Pencil icon */} + + + {/* Hidden file input */} + - )} + + {/* Modal */} + {isAvatarModalOpen && ( +
{ + // Close modal if clicking backdrop + if (e.target === e.currentTarget) { + closeModal(); + } + }} + > +
+
+

Edit profile photo

+ +
+ + {/* Original / Preview */} +
+
+ {localPreviewUrl ? ( + Preview + ) : photo ? ( + Current profile + ) : ( + + )} +
+ +
+
+ + + +
+ +
+ + + +
+
+
+
+
+ )} + {/* User Info */}

@@ -52,7 +285,10 @@ export function Profile() {

- Member since {new Date(user.metadata.creationTime ?? "").toLocaleDateString()} + + Member since{" "} + {new Date(user.metadata.creationTime ?? "").toLocaleDateString()} +
diff --git a/frontend/src/pages/SavedProducts.tsx b/frontend/src/pages/SavedProducts.tsx index 9a722dd..bea1883 100644 --- a/frontend/src/pages/SavedProducts.tsx +++ b/frontend/src/pages/SavedProducts.tsx @@ -1,9 +1,9 @@ import { useEffect, useState, useContext } from "react"; import { Helmet } from "react-helmet-async"; import { Link, useNavigate } from "react-router-dom"; -import Product from "src/components/Product"; -import { get, post } from "src/api/requests"; -import { FirebaseContext } from "src/utils/FirebaseProvider"; +import Product from "/src/components/Product"; +import { get, post } from "/src/api/requests"; +import { FirebaseContext } from "/src/utils/FirebaseProvider"; export function SavedProducts() { const [products, setProducts] = useState< diff --git a/frontend/src/utils/Firebase.ts b/frontend/src/utils/Firebase.ts new file mode 100644 index 0000000..abb91a3 --- /dev/null +++ b/frontend/src/utils/Firebase.ts @@ -0,0 +1,8 @@ +import { initializeApp } from "firebase/app"; +import { getAuth } from "firebase/auth"; +import { getStorage } from "firebase/storage"; +import { firebaseConfig } from "./FirebaseConfig"; + +export const app = initializeApp(firebaseConfig); +export const auth = getAuth(app); +export const storage = getStorage(app); diff --git a/frontend/src/utils/FirebaseConfig.tsx b/frontend/src/utils/FirebaseConfig.tsx index 879a4d1..3baa0df 100644 --- a/frontend/src/utils/FirebaseConfig.tsx +++ b/frontend/src/utils/FirebaseConfig.tsx @@ -6,7 +6,7 @@ export const firebaseConfig = { apiKey: "AIzaSyBssbaMlxIJHYI7G7zOriU0VaWGnGrQv5M", authDomain: "low-price-center.firebaseapp.com", projectId: "low-price-center", - storageBucket: "low-price-center.firebasestorage.app", + storageBucket: "low-price-center.firebasestorage.app", //check later messagingSenderId: "163704233704", appId: "1:163704233704:web:6ee0dc540f6f25d6ceb35d", measurementId: "G-RV7RV9W17W", diff --git a/frontend/src/utils/FirebaseProvider.tsx b/frontend/src/utils/FirebaseProvider.tsx index 7e883f3..8b36500 100644 --- a/frontend/src/utils/FirebaseProvider.tsx +++ b/frontend/src/utils/FirebaseProvider.tsx @@ -1,82 +1,68 @@ -import { FirebaseApp, initializeApp } from "firebase/app"; -import { GoogleAuthProvider, User, getAuth, signInWithPopup, signOut } from "firebase/auth"; -import { MouseEventHandler, ReactNode, createContext, useEffect, useState } from "react"; -import { get, post } from "src/api/requests"; +import type { FirebaseApp } from "firebase/app"; +import type { User } from "firebase/auth"; +import { GoogleAuthProvider, signInWithPopup, signOut } from "firebase/auth"; +import type { MouseEventHandler, ReactNode } from "react"; +import { createContext, useEffect, useState } from "react"; +import { get, post } from "/src/api/requests"; +import { app, auth } from "/src/utils/Firebase"; -import { firebaseConfig } from "src/utils/FirebaseConfig"; - -/** - * Context used by FirebaseProvider to provide app and user to pages - */ -const FirebaseContext = createContext<{ - app: FirebaseApp | undefined; +export const FirebaseContext = createContext<{ + app: FirebaseApp; user: User | null; loading: boolean; openGoogleAuthentication: MouseEventHandler; signOutFromFirebase: MouseEventHandler; }>({ - app: undefined, + app, user: null, loading: true, openGoogleAuthentication: () => {}, signOutFromFirebase: () => {}, }); -/** - * Wraps children in FirebaseContext.Provider to give all - * children access to sustained Firebase app and user - * data. - */ export default function FirebaseProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); + const [loading, setLoading] = useState(true); - const app = initializeApp(firebaseConfig); - const auth = getAuth(app); const provider = new GoogleAuthProvider(); - /*sign in*/ async function openGoogleAuthentication() { - await signInWithPopup(auth, provider).catch((error) => { - console.error(error); - }); + try { + await signInWithPopup(auth, provider); + } catch (e) { + console.error("Popup sign-in error:", e); + } } - /*sign out*/ async function signOutFromFirebase() { await signOut(auth); window.location.href = "/"; } - /** - * Tracks when the user logs in and out to change - * the state of the user. - */ useEffect(() => { const unsubscribe = auth.onAuthStateChanged(async (u) => { - if (!u) setUser(null); - else { - await get(`/api/users/${u.uid}`) - .then(() => { + try { + if (!u) { + setUser(null); + return; + } + + try { + await get(`/api/users/${u.uid}`); + setUser(u); + } catch (e: any) { + if (e?.message === '404 Not Found: {"message":"User not found"}') { + await post(`/api/users`, { firebaseUid: u.uid }); setUser(u); - }) - .catch(async (e) => { - if (e.message === '404 Not Found: {"message":"User not found"}') { - await post(`/api/users`, { firebaseUid: u.uid }) - .then(() => { - setUser(u); - }) - .catch((e2) => { - signOutFromFirebase(); - console.error(e2); - }); - } else { - signOutFromFirebase(); - } - }); + } else { + await signOutFromFirebase(); + } + } + } finally { + setLoading(false); } - setLoading(false); }); + return unsubscribe; }, []); @@ -88,5 +74,3 @@ export default function FirebaseProvider({ children }: { children: ReactNode }) ); } - -export { FirebaseContext }; diff --git a/frontend/src/utils/User.tsx b/frontend/src/utils/User.tsx index df35d81..63fd01c 100644 --- a/frontend/src/utils/User.tsx +++ b/frontend/src/utils/User.tsx @@ -1,6 +1,6 @@ import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; -import firebaseConfig from "src/utils/FirebaseConfig"; +import firebaseConfig from "/src/utils/FirebaseConfig"; export function getToken() { const app = initializeApp(firebaseConfig); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 439c528..32fd5d6 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,26 +1,21 @@ + { "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - - /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - - /* path mapping */ + "skipLibCheck": true, "baseUrl": ".", "paths": { "src/*": ["src/*"] diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index 97ede7e..b383c4f 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -1,11 +1,22 @@ { "compilerOptions": { "composite": true, - "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true, - "strict": true + "skipLibCheck": true, + "types": ["node"] }, "include": ["vite.config.ts"] } + +// { +// "compilerOptions": { +// "composite": true, +// "skipLibCheck": true, +// "module": "ESNext", +// "moduleResolution": "bundler", +// "allowSyntheticDefaultImports": true, +// "strict": true +// }, +// "include": ["vite.config.ts"] +// } diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 6894d3a..e8f9ceb 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,16 +1,21 @@ -/// -import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import * as path from "path"; -// https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], resolve: { - alias: { - src: "/src", - }, + alias: [ + { + find: /^src\/(.*)/, + replacement: path.resolve(__dirname, "src") + "/$1", + }, + ], }, - test: { - environment: "jsdom", + server: { + headers: { + "Cross-Origin-Opener-Policy": "same-origin-allow-popups", + "Cross-Origin-Embedder-Policy": "unsafe-none", + }, }, });