diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..f78ace0 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,16 @@ +# ======================== +# Server Configuration +# ======================== +PORT=4000 + +# ======================== +# MongoDB Connection +# ======================== +# Replace with your MongoDB connection string +MONGO_URI=your_mongodb_connection_string_here +# ======================== +# JWT Secret +# ======================== +# This secret is used to sign JWT tokens +JWT_SECRET=myverysecurejwtsecret123! + diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..6ffbfa7 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,2 @@ +.env/ +node_modules/ \ No newline at end of file diff --git a/backend/controllers/authController.js b/backend/controllers/authController.js index c7e80c4..c8755b8 100644 --- a/backend/controllers/authController.js +++ b/backend/controllers/authController.js @@ -9,26 +9,23 @@ const generateToken = (id) => { export const registerUser = async (req, res) => { const { name, email, password } = req.body; - // Check if user already exists + const userExists = await User.findOne({ email }); - if (userExists) { - return res.status(400).json({ message: 'User already exists' }); - } + if (userExists) return res.status(400).json({ message: 'User already exists' }); - // Hash password const hashedPassword = await bcrypt.hash(password, 10); - // Create user + const user = await User.create({ name, email, password: hashedPassword }); - // Generate JWT + + const token = generateToken(user._id); - // Set JWT in secure, HTTP-only cookie res.cookie('token', token, { - httpOnly: true, // Prevent JS access (XSS protection) - sameSite: 'strict', // CSRF protection - maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days + httpOnly: true, + sameSite: 'strict', + maxAge: 7 * 24 * 60 * 60 * 1000, }); res.status(201).json({ @@ -41,22 +38,14 @@ export const registerUser = async (req, res) => { export const loginUser = async (req, res) => { const { email, password } = req.body; - // Find user const user = await User.findOne({ email }); - if (!user) { - return res.status(401).json({ message: 'User not found' }); - } + if (!user) return res.status(401).json({ message: 'User not found' }); - // Compare password const isMatch = await bcrypt.compare(password, user.password); - if (!isMatch) { - return res.status(401).json({ message: 'Invalid password' }); - } + if (!isMatch) return res.status(401).json({ message: 'Invalid password' }); - // Generate JWT const token = generateToken(user._id); - // Set JWT in secure, HTTP-only cookie res.cookie('token', token, { httpOnly: true, sameSite: 'strict', @@ -71,7 +60,6 @@ export const loginUser = async (req, res) => { }; export const logoutUser = async (req, res) => { - // Clear the JWT cookie res.cookie('token', '', { httpOnly: true, secure: process.env.NODE_ENV === 'production', @@ -81,3 +69,21 @@ export const logoutUser = async (req, res) => { res.status(200).json({ message: 'User logged out successfully' }); }; + +// ✅ New: Get current user from HTTP-only cookie +export const getCurrentUser = async (req, res) => { + try { + const token = req.cookies?.token; + if (!token) return res.status(401).json({ success: false, message: 'Not authenticated' }); + + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findById(decoded.id).select('-password'); + + if (!user) return res.status(404).json({ success: false, message: 'User not found' }); + + res.json({ success: true, user }); + } catch (err) { + console.error('getCurrentUser error:', err); + res.status(401).json({ success: false, message: 'Invalid or expired token' }); + } +}; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e7dd85f..41e54d3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -25,7 +25,7 @@ "react-to-print": "^3.1.1", "react-toastify": "^11.0.5", "react-tooltip": "^4.5.1", - "vite": "^7.1.1" + "vite": "^7.1.10" }, "devDependencies": { "@eslint/js": "^9.9.0", @@ -5526,9 +5526,9 @@ } }, "node_modules/vite": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", - "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "version": "7.1.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.10.tgz", + "integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", diff --git a/frontend/package.json b/frontend/package.json index 712d250..85bcb02 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,7 +27,7 @@ "react-to-print": "^3.1.1", "react-toastify": "^11.0.5", "react-tooltip": "^4.5.1", - "vite": "^7.1.1" + "vite": "^7.1.10" }, "devDependencies": { "@eslint/js": "^9.9.0", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index fb963b2..139e7b9 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import Navbar from "./components/Navbar/Navbar"; import { Routes, Route, useLocation } from "react-router-dom"; import Home from "./pages/Home/Home"; @@ -20,9 +20,6 @@ import ContactPage from "./pages/Contactpage"; import { Toaster } from "react-hot-toast"; import LoadingAnimation from "./components/LoadingAnimation"; import ScrollToTop from "../utility/ScrollToTop"; -import "./components/FoodDetail/print.css"; -import NotFound from "./pages/Notfound"; -import StoreContextProvider from "./components/context/StoreContext"; import ScrollToBottom from "./components/ScrollToBottomButton/ScrollToBottomButton"; import ReferralProgram from "./components/Referrals/ReferralProgram"; import AboutUs from "./components/Aboutus/Aboutus"; @@ -30,41 +27,85 @@ import FAQ from "./components/FAQ/FAQ"; import MyProfile from "./pages/MyProfile/MyProfile"; import MyOrder from "./pages/MyOrder/MyOrder"; import Privacy from "./components/Privacy/privacy"; +import StoreContextProvider from "./components/context/StoreContext"; +import apiRequest from "./lib/apiRequest"; +import SuccessPopup from "./components/LoginPopup/SuccessPopup"; +import "./components/FoodDetail/print.css"; +import NotFound from "./pages/Notfound"; import TermsOfService from "./components/TermsOfService/TermsOfService"; import Delivery from "./pages/Delivery/Delivery"; const App = () => { const [showLogin, setShowLogin] = useState(false); const [loading, setLoading] = useState(true); - const [isLoggedIn, setIsLoggedIn] = useState(() => { - return !!localStorage.getItem("authToken") || !!localStorage.getItem("user"); - }); + const [isLoggedIn, setIsLoggedIn] = useState(false); + const [user, setUser] = useState(null); + const [successMessage, setSuccessMessage] = useState(""); + // Loading animation useEffect(() => { - const timer = setTimeout(() => setLoading(false), 3000); + const timer = setTimeout(() => setLoading(false), 2000); return () => clearTimeout(timer); }, []); + // Check authentication from backend useEffect(() => { + const checkAuth = async () => { + try { + const response = await apiRequest.get("/api/auth/me", { + withCredentials: true, + }); + if (response.data.success) { + setIsLoggedIn(true); + setUser(response.data.user); + } else { + setIsLoggedIn(false); + setUser(null); + } + } catch (err) { + console.log("Auth check failed:", err); + setIsLoggedIn(false); + setUser(null); + } + }; + + checkAuth(); + + // Listen to localStorage changes (optional, if frontend still uses it) const handleStorageChange = () => { - setIsLoggedIn(!!localStorage.getItem("authToken")); + const token = localStorage.getItem("authToken"); + setIsLoggedIn(!!token); + // Optionally set user info from localStorage }; window.addEventListener("storage", handleStorageChange); return () => window.removeEventListener("storage", handleStorageChange); }, []); - if (loading) { - return ; - } + if (loading) return ; return ( - {showLogin && } + + {successMessage && ( + setSuccessMessage("")} + /> + )} + + {showLogin && ( + + )}
- + @@ -74,26 +115,7 @@ const App = () => { - ) : ( -
-

- Please Log In To Proceed -

-

- Your journey continues after login 🔐 -

-
- ) + isLoggedIn ? : } /> } /> @@ -105,56 +127,8 @@ const App = () => { } /> } /> } /> - - ) : ( -
-

- Please Log In To Proceed -

-

- Your journey continues after login 🔐 -

-
- ) - } - /> - - ) : ( -
-

- Please Log In To Proceed -

-

- Your journey continues after login 🔐 -

-
- ) - } - /> + : } /> + : } /> } /> } /> } /> @@ -164,9 +138,6 @@ const App = () => { - - {/* Removed FeedbackReviews from here */} -
@@ -175,4 +146,22 @@ const App = () => { ); }; +// Reusable login-required component +const LoginRequired = () => ( +
+

+ Please Log In To Proceed +

+

Your journey continues after login 🔐

+
+); + export default App; diff --git a/frontend/src/components/LoginPopup/LoginPopup.jsx b/frontend/src/components/LoginPopup/LoginPopup.jsx index ae4c311..5d1221e 100644 --- a/frontend/src/components/LoginPopup/LoginPopup.jsx +++ b/frontend/src/components/LoginPopup/LoginPopup.jsx @@ -1,4 +1,3 @@ -// LoginPopup.jsx import React, { useState, useEffect, useRef } from 'react'; import './LoginPopup.css'; import { assets } from '../../assets/frontend_assets/assets'; @@ -7,187 +6,70 @@ import { useNavigate } from 'react-router-dom'; import apiRequest from "../../lib/apiRequest"; import { EyeIcon, EyeOffIcon } from "lucide-react"; -const LoginPopup = ({ setShowLogin, setIsLoggedIn }) => { +const LoginPopup = ({ setShowLogin, setIsLoggedIn, setUser, setSuccessMessage }) => { const [currState, setCurrState] = useState("Sign Up"); const [forgotFlow, setForgotFlow] = useState(false); - const [stage, setStage] = useState(1); + const [email, setEmail] = useState(""); - const [otp, setOtp] = useState(Array(6).fill("")); - const [timer, setTimer] = useState(60); - const [newPassword, setNewPassword] = useState(""); + const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); + const [name, setName] = useState(""); + const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [showLoginPassword, setShowLoginPassword] = useState(false); - const navigate = useNavigate(); - - const [password, setPassword] = useState(''); - const [passwordStrength, setPasswordStrength] = useState({ - length: false, - uppercase: false, - lowercase: false, - number: false, - special: false, - }); - - const [signUpConfirmPassword, setSignUpConfirmPassword] = useState(''); - const [passwordMatch, setPasswordMatch] = useState(false); - - const [showPasswordChecker, setShowPasswordChecker] = useState(false); - const [passwordFocused, setPasswordFocused] = useState(false); - const [confirmFocused, setConfirmFocused] = useState(false); + const [loading, setLoading] = useState(false); const popupRef = useRef(); - const otpRefs = useRef([]); + const navigate = useNavigate(); + // Close popup on outside click or Escape useEffect(() => { - const handleClickOutside = (event) => { - if (popupRef.current && !popupRef.current.contains(event.target)) { - setShowLogin(false); - } - }; - - const handleEscapeKey = (event) => { - if (event.key === 'Escape') { - setShowLogin(false); - } - }; + const handleClickOutside = (e) => popupRef.current && !popupRef.current.contains(e.target) && setShowLogin(false); + const handleEscape = (e) => e.key === 'Escape' && setShowLogin(false); document.addEventListener('mousedown', handleClickOutside); - document.addEventListener('keydown', handleEscapeKey); - + document.addEventListener('keydown', handleEscape); return () => { document.removeEventListener('mousedown', handleClickOutside); - document.removeEventListener('keydown', handleEscapeKey); + document.removeEventListener('keydown', handleEscape); }; }, [setShowLogin]); - useEffect(() => { - let interval; - if (stage === 2 && timer > 0) { - interval = setInterval(() => { - setTimer((prev) => prev - 1); - }, 1000); - } - return () => clearInterval(interval); - }, [stage, timer]); - - const handleOTPChange = (e, index) => { - const value = e.target.value; - if (!/^\d?$/.test(value)) return; - const newOtp = [...otp]; - newOtp[index] = value; - setOtp(newOtp); - - if (value && index < 5) { - otpRefs.current[index + 1].focus(); - } - }; - - // Password strength check - useEffect(() => { - if (password) { - const newStrength = { - length: password.length >= 8, - uppercase: /[A-Z]/.test(password), - lowercase: /[a-z]/.test(password), - number: /[0-9]/.test(password), - special: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password), - }; - setPasswordStrength(newStrength); - } else { - setPasswordStrength({ - length: false, - uppercase: false, - lowercase: false, - number: false, - special: false, - }); - } - }, [password]); - - // Check if passwords match - useEffect(() => { - setPasswordMatch(password === signUpConfirmPassword && signUpConfirmPassword !== ''); - }, [password, signUpConfirmPassword]); - - const handleSendOTP = (e) => { - e.preventDefault(); - if (!email) return toast.error("Enter email"); - toast.success("OTP sent to your email"); - setStage(2); - setTimer(60); - }; - - const handleVerifyOTP = (e) => { - e.preventDefault(); - if (otp.join("").length !== 6) return toast.error("Enter 6-digit OTP"); - toast.success("OTP verified"); - setStage(3); - }; - - const handleResetPassword = (e) => { - e.preventDefault(); - if (newPassword !== confirmPassword) return toast.error("Passwords do not match"); - toast.success("Password reset successfully!"); - setForgotFlow(false); - setStage(1); - setOtp(Array(6).fill("")); - setCurrState("Login"); - }; - const handleSubmit = async (e) => { e.preventDefault(); - - const formData = new FormData(e.target); - const name = formData.get("name"); - const email = formData.get("email"); - const password = formData.get("password"); - const confirmPassword = formData.get("confirmPassword"); - - if (!email || !password || (currState === "Sign Up" && !name)) { - return toast.error("Please fill all fields"); - } - - if (currState === "Sign Up" && password !== confirmPassword) { - return toast.error("Passwords do not match"); + setLoading(true); + + const payload = { email, password }; + if (currState === "Sign Up") { + payload.name = e.target.name.value; + if (password !== confirmPassword) { + toast.error("Passwords do not match"); + setLoading(false); + return; + } } - const endpoint = - currState === "Sign Up" ? "/api/auth/register" : "/api/auth/login"; + const endpoint = currState === "Sign Up" ? "/api/auth/register" : "/api/auth/login"; try { - const response = await apiRequest.post(endpoint, { name, email, password }); - const data = response.data; - - if (data.success) { - toast.success(data.message); - - // Store user info and auth token locally - localStorage.setItem("user", JSON.stringify(data.user)); - if (data.token) { - localStorage.setItem("authToken", data.token); - } else { - localStorage.setItem("authToken", "authenticated"); // fallback - } - - // Update authentication state in parent component - if (setIsLoggedIn) { - setIsLoggedIn(true); - } - + const res = await apiRequest.post(endpoint, payload, { withCredentials: true }); + if (res.data.success) { + setIsLoggedIn(true); + setUser(res.data.user); setShowLogin(false); - // Navigate to home page - navigate("/"); - window.location.reload(); + // Trigger SuccessPopup in App + setSuccessMessage(res.data.message); + } else { - toast.error(data.message || `${currState} failed. Please try again.`); + toast.error(res.data.message || `${currState} failed`); } } catch (err) { - const message = err.response?.data?.message || `${currState} failed. Please try again.`; - toast.error(message); - console.error(err); + console.error("Error response:", err.response?.data || err); + toast.error(err.response?.data?.message || `${currState} failed`); + } finally { + setLoading(false); } }; @@ -201,204 +83,90 @@ const LoginPopup = ({ setShowLogin, setIsLoggedIn }) => {
- {!forgotFlow && currState !== "Login" && ( - + {!forgotFlow && currState === "Sign Up" && ( + setName(e.target.value)} + required + /> )} {!forgotFlow && ( <> - - {currState === "Sign Up" && ( -
- setPassword(e.target.value)} - onFocus={() => { - setPasswordFocused(true); - setShowPasswordChecker(true); - }} - onBlur={() => { - setPasswordFocused(false); - if (!confirmFocused) setShowPasswordChecker(false); - }} - required - /> - setShowPassword(!showPassword)} - > - {showPassword ? : } - -
- )} - - {currState === "Sign Up" && ( -
- setSignUpConfirmPassword(e.target.value)} - onFocus={() => { - setConfirmFocused(true); - setShowPasswordChecker(true); - }} - onBlur={() => { - setConfirmFocused(false); - if (!passwordFocused) setShowPasswordChecker(false); - }} - required - /> - setShowConfirmPassword(!showConfirmPassword)} - > - {showConfirmPassword ? : } - -
- )} - - {currState === "Sign Up" && showPasswordChecker && ( -
- {passwordFocused && ( -
-

- {passwordStrength.length ? '✔️' : '❌'} At least 8 characters long -

-

- {passwordStrength.uppercase ? '✔️' : '❌'} Contains at least one uppercase letter -

-

- {passwordStrength.lowercase ? '✔️' : '❌'} Contains at least one lowercase letter -

-

- {passwordStrength.number ? '✔️' : '❌'} Contains at least one number -

-

- {passwordStrength.special ? '✔️' : '❌'} Contains at least one special character -

-
- )} - {confirmFocused && signUpConfirmPassword && ( -
-

- {passwordMatch ? '✅ Passwords match' : '❌ Passwords do not match'} -

-
- )} -
- )} - - {currState === "Sign Up" && ( -
- -

By continuing, I agree to the terms of use & privacy policy.

-
- )} + setEmail(e.target.value)} + required + /> - {currState === "Login" && ( + {currState === "Sign Up" ? ( + <> +
+ setPassword(e.target.value)} + required + /> + setShowPassword(!showPassword)}> + {showPassword ? : } + +
+ +
+ setConfirmPassword(e.target.value)} + required + /> + setShowConfirmPassword(!showConfirmPassword)}> + {showConfirmPassword ? : } + +
+ + ) : (
setPassword(e.target.value)} required /> - setShowLoginPassword(!showLoginPassword)} - > + setShowLoginPassword(!showLoginPassword)}> {showLoginPassword ? : }
)} - + + {currState === "Login" && ( -

{ - setForgotFlow(true); - setStage(1); - }}> - Forgot Password? -

+

setForgotFlow(true)}>Forgot Password?

)} )} - {forgotFlow && stage === 1 && ( - <> - setEmail(e.target.value)} - required - /> - - - )} - - {forgotFlow && stage === 2 && ( - <> -
- {otp.map((digit, i) => ( - otpRefs.current[i] = el} - onChange={(e) => handleOTPChange(e, i)} - /> - ))} -
- - - - )} - - {forgotFlow && stage === 3 && ( - <> - setNewPassword(e.target.value)} - required - /> - setConfirmPassword(e.target.value)} - required - /> - - + {!forgotFlow && ( + currState === "Login" ? ( +

Create a new account? { setCurrState("Sign Up"); setPassword(""); setConfirmPassword(""); }}>Click Here

+ ) : ( +

Already have an account? { setCurrState("Login"); setPassword(""); setConfirmPassword(""); }}>Login Here

+ ) )}
- - {!forgotFlow && ( - currState === "Login" ? ( -

Create a new account? setCurrState("Sign Up")}>Click Here

- ) : ( -

Already have an account? setCurrState("Login")}>Login Here

- ) - )} ); diff --git a/frontend/src/components/LoginPopup/SuccessPopup.css b/frontend/src/components/LoginPopup/SuccessPopup.css new file mode 100644 index 0000000..8f17530 --- /dev/null +++ b/frontend/src/components/LoginPopup/SuccessPopup.css @@ -0,0 +1,47 @@ +.success-popup-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; /* Make sure it's above everything else */ +} + +.success-popup { + background: #fff; + padding: 2rem; + border-radius: 12px; + text-align: center; + box-shadow: 0 5px 25px rgba(0, 0, 0, 0.3); + animation: popupFadeIn 0.3s ease; +} + +.success-popup h2 { + margin-bottom: 10px; + color: #28a745; +} + +.success-popup button { + margin-top: 10px; + padding: 8px 16px; + border: none; + background-color: #28a745; + color: white; + border-radius: 6px; + cursor: pointer; +} + +@keyframes popupFadeIn { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +} diff --git a/frontend/src/components/LoginPopup/SuccessPopup.jsx b/frontend/src/components/LoginPopup/SuccessPopup.jsx new file mode 100644 index 0000000..6971f9b --- /dev/null +++ b/frontend/src/components/LoginPopup/SuccessPopup.jsx @@ -0,0 +1,18 @@ +import React from "react"; +import "./SuccessPopup.css"; + +const SuccessPopup = ({ message, onClose }) => { + if (!message) return null; // safety guard + + return ( +
+
e.stopPropagation()}> +

✅ Success!

+

{message}

+ +
+
+ ); +}; + +export default SuccessPopup; diff --git a/frontend/src/components/Navbar/Navbar.jsx b/frontend/src/components/Navbar/Navbar.jsx index dfae7b2..3e2acea 100644 --- a/frontend/src/components/Navbar/Navbar.jsx +++ b/frontend/src/components/Navbar/Navbar.jsx @@ -16,223 +16,88 @@ import { Moon, HelpCircle, Utensils, - Users, - Info, CircleDollarSign, } from "lucide-react"; +import toast from "react-hot-toast"; +import apiRequest from "../../lib/apiRequest"; -const Navbar = ({ setShowLogin, setIsLoggedIn }) => { +const Navbar = ({ setShowLogin, user, setUser, isLoggedIn, setIsLoggedIn }) => { const [menu, setMenu] = useState("home"); - const { cartItems, wishlistItems, toggleWishlist, getTotalCartAmount } = - useContext(StoreContext); + const { cartItems, wishlistItems } = useContext(StoreContext); const { theme, toggleTheme } = useContext(ThemeContext); - const [user, setUser] = useState(null); const navigate = useNavigate(); const location = useLocation(); - useEffect(() => { - const storedUser = JSON.parse(localStorage.getItem("user")); - setUser(storedUser); - }, []); - - // Listen for storage changes to update user state - useEffect(() => { - const handleStorageChange = () => { - const storedUser = JSON.parse(localStorage.getItem("user")); - setUser(storedUser); - }; - - window.addEventListener('storage', handleStorageChange); - return () => window.removeEventListener('storage', handleStorageChange); - }, []); - const handleNavMenuClick = (menuName, id) => { setMenu(menuName); - if (location.pathname !== "/") { - navigate("/", {state: {scrollTo: id } }); - } else { - const section = document.getElementById(id); - if (section) section.scrollIntoView({ behavior: "smooth" }); - } + if (location.pathname !== "/") { + navigate("/", { state: { scrollTo: id } }); + } else { + const section = document.getElementById(id); + if (section) section.scrollIntoView({ behavior: "smooth" }); + } }; const handleLogout = async () => { try { - // Call logout endpoint to clear HTTP-only cookie - await fetch("http://localhost:4000/api/auth/logout", { - method: "POST", - credentials: "include", - }); - } catch (error) { - console.error("Logout error:", error); - } - - // Clear local storage - localStorage.removeItem("user"); - localStorage.removeItem("authToken"); - setUser(null); - - // Update authentication state in parent component - if (setIsLoggedIn) { + const response = await apiRequest.post("/api/auth/logout", {}, { withCredentials: true }); + if (response.data.success) { + toast.success(response.data.message || "Logged out successfully"); setIsLoggedIn(false); + setUser(null); + navigate("/"); + } + } catch (err) { + console.error("Logout error:", err); + toast.error("Logout failed. Try again."); } - - window.location.reload(); }; - - // to trigger the dark theme on scroll bar + useEffect(() => { document.documentElement.setAttribute("data-theme", theme); }, [theme]); const navMenu = ( <> - { - e.preventDefault(); - setMenu("home"); - if(location.pathname === "/"){ - // already on home, just scroll to top - window.scrollTo({top: 0, behavior: "smooth"}); - } - else{ - navigate("/"); - } - }} - className={`nav-item ${menu === "home" ? "active" : ""}`} - > - - Home - - setMenu("restaurants")} - className={`nav-item ${menu === "restaurants" ? "active" : ""}`} - > - - Restaurant - - setMenu("menu")} - className={`nav-item ${menu === "menu" ? "active" : ""}`} - > - - Menu - - setMenu("mobile-app")} - className={`nav-item ${menu === "mobile-app" ? "active" : ""}`} - > - - Mobile App - - setMenu("wishlist")} - className={`nav-item ${menu === "wishlist" ? "active" : ""}`} - > - - Wishlist - {Object.keys(wishlistItems).length > 0 && ( -
{Object.keys(wishlistItems).length}
-)} - - - - - setMenu("aboutus")} - className={`nav-item ${menu === "aboutus" ? "active" : ""}`} - > - - About Us - - setMenu("contact-us")} - className={`nav-item ${menu === "contact-us" ? "active" : ""}`} - > - - Contact - - setMenu("referral")} - className={`nav-item ${menu === "referral" ? "active" : ""}`} - > - - - Refer & Earn - + { e.preventDefault(); handleNavMenuClick("home", null); }} className={`nav-item ${menu === "home" ? "active" : ""}`}>Home + setMenu("restaurants")} className={`nav-item ${menu === "restaurants" ? "active" : ""}`}>Restaurant + setMenu("menu")} className={`nav-item ${menu === "menu" ? "active" : ""}`}>Menu + setMenu("mobile-app")} className={`nav-item ${menu === "mobile-app" ? "active" : ""}`}>Mobile App + setMenu("wishlist")} className={`nav-item ${menu === "wishlist" ? "active" : ""}`}>Wishlist{Object.keys(wishlistItems).length > 0 &&
{Object.keys(wishlistItems).length}
} + setMenu("aboutus")} className={`nav-item ${menu === "aboutus" ? "active" : ""}`}>About Us + setMenu("contact-us")} className={`nav-item ${menu === "contact-us" ? "active" : ""}`}>Contact + setMenu("referral")} className={`nav-item ${menu === "referral" ? "active" : ""}`}>Refer & Earn ); - const totalCartItems = Object.values(cartItems || {}).reduce( - (sum, qty) => sum + qty, - 0 - ); + const totalCartItems = Object.values(cartItems || {}).reduce((sum, qty) => sum + qty, 0); return ( <> - {/* Top Navigation Bar */}
- {/* Logo */} - - app icon - - - {/* Desktop menu (center, hidden on mobile) */} + app icon - - {/* Right action buttons */}
- {/* Theme Toggle */} - - - {/* Cart */} +
- {totalCartItems > 0 && ( -
{totalCartItems}
- )} + {totalCartItems > 0 &&
{totalCartItems}
}
- {/* User / Auth */} - {user ? ( + {isLoggedIn && user ? (
-
- {user.name?.charAt(0).toUpperCase()} -
+
{user.name?.charAt(0).toUpperCase()}
{user.name} - +
) : ( - + )}
- - {/* Mobile bottom nav */} );