diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 2a21084..3b12d45 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,66 +1,71 @@
import { HelmetProvider } from "react-helmet-async";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
-import { Footer } from "src/components/Footer";
import { Navbar } from "src/components/Navbar";
+import { PrivateRoute } from "src/components/PrivateRoute";
+import { RootLayout } from "src/components/RootLayout";
import { Home } from "src/pages";
+import { AddProduct } from "src/pages/AddProduct";
+import { EditProduct } from "src/pages/EditProduct";
+import { IndividualProductPage } from "src/pages/Individual-product-page";
import { Marketplace } from "src/pages/Marketplace";
-
-import { PrivateRoute } from "../src/components/PrivateRoute";
-import { AddProduct } from "../src/pages/AddProduct";
-import { EditProduct } from "../src/pages/EditProduct";
-import { IndividualProductPage } from "../src/pages/Individual-product-page";
-import { PageNotFound } from "../src/pages/PageNotFound";
-import FirebaseProvider from "../src/utils/FirebaseProvider";
-import { SavedProducts } from "./pages/SavedProducts";
+import { PageNotFound } from "src/pages/PageNotFound";
+import { SavedProducts } from "src/pages/SavedProducts";
+import FirebaseProvider from "src/utils/FirebaseProvider";
const router = createBrowserRouter([
{
path: "/",
- element: ,
- },
- {
- path: "/products",
- element: (
-
-
-
- ),
- },
- {
- path: "/add-product",
- element: (
-
-
-
- ),
- },
- {
- path: "/edit-product/:id",
- element: (
-
-
-
- ),
- },
- {
- path: "/products/:id",
- element: (
-
-
-
- ),
- },
- {
- path: "/saved-products",
- element: (
-
-
-
- ),
- },
- {
- path: "*",
- element: ,
+ element: ,
+ children: [
+ {
+ path: "/",
+ element: ,
+ },
+ {
+ path: "/products",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "/add-product",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "/edit-product/:id",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "/products/:id",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "/saved-products",
+ element: (
+
+
+
+ ),
+ },
+ {
+ path: "*",
+ element: ,
+ },
+ ],
},
]);
@@ -69,11 +74,9 @@ export default function App() {
diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx
new file mode 100644
index 0000000..3c3d6c6
--- /dev/null
+++ b/frontend/src/components/Header.tsx
@@ -0,0 +1,8 @@
+export function Header() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx
index b35b3c6..990a826 100644
--- a/frontend/src/components/Navbar.tsx
+++ b/frontend/src/components/Navbar.tsx
@@ -1,18 +1,79 @@
-import { faBars, faCartShopping, faUser, faXmark, faHeart } from "@fortawesome/free-solid-svg-icons";
+import { faHeart } from "@fortawesome/free-regular-svg-icons";
+import {
+ faBars,
+ faCartShopping,
+ faMagnifyingGlass,
+ faUser,
+ faXmark,
+} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useContext, useEffect, useRef, useState } from "react";
+import { HTMLAttributes, forwardRef, useContext, useEffect, useRef, useState } from "react";
+import { useNavigate } from "react-router-dom";
import { FirebaseContext } from "src/utils/FirebaseProvider";
+interface MiniSearchbarProps extends HTMLAttributes {
+ open: boolean;
+ onSubmit: React.FormEventHandler;
+}
+
+const MiniSearchbar = forwardRef(
+ ({ open, onSubmit, ...props }, ref) => {
+ return (
+
+
Search the marketplace
+
+
+ );
+ },
+);
+
+MiniSearchbar.displayName = "MiniSearchbar";
+
export function Navbar() {
const { user, signOutFromFirebase, openGoogleAuthentication } = useContext(FirebaseContext);
const [isMobileMenuOpen, setMobileMenuOpen] = useState(false);
+ const [isSearchBarOpen, setSearchbarOpen] = useState(false);
const menuRef = useRef(null);
const buttonRef = useRef(null);
+ const searchRef = useRef(null);
+ const navigate = useNavigate();
const toggleMobileMenu = () => {
setMobileMenuOpen(!isMobileMenuOpen);
};
+ const handleSearch = (e: React.FormEvent) => {
+ if (!searchRef.current) return;
+ e.preventDefault();
+ const formData = new FormData(searchRef.current);
+ const url = new URL("/products", window.location.origin);
+ url.searchParams.set("query", formData.get("query") as string);
+ navigate(url.pathname + url.search);
+ return;
+ };
+
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
@@ -23,6 +84,13 @@ export function Navbar() {
) {
setMobileMenuOpen(false);
}
+
+ if (searchRef.current && !searchRef.current.contains(event.target as Node)) {
+ setSearchbarOpen(false);
+ setTimeout(() => {
+ if (searchRef.current) searchRef.current.reset();
+ }, 100);
+ }
};
const handleResize = () => {
@@ -40,65 +108,73 @@ export function Navbar() {
};
}, []);
+ const tabStyling = "text-gray-400 hover:text-gray-800";
+ const selectedTabStyling = "text-ucsd-blue";
+
return (
<>
-