diff --git a/frontend/src/main-page/MainPage.tsx b/frontend/src/main-page/MainPage.tsx index 5c96ac0..2a47678 100644 --- a/frontend/src/main-page/MainPage.tsx +++ b/frontend/src/main-page/MainPage.tsx @@ -1,26 +1,30 @@ import { Routes, Route } from "react-router-dom"; import Dashboard from "./dashboard/Dashboard"; import GrantPage from "./grants/GrantPage"; -import Header from "./header/Header"; +import NavBar from "./navbar/NavBar"; import Users from "./users/Users"; import RestrictedPage from "./restricted/RestrictedPage"; - +import CashFlowPage from "./cash-flow/CashFlowPage"; +import SettingsPage from "./settings/SettingsPage"; function MainPage() { - return ( -
-
+
+ +
} /> } /> } /> } /> } /> + } /> + } /> {/* fallback route */} } /> +
); } diff --git a/frontend/src/main-page/cash-flow/CashFlowPage.tsx b/frontend/src/main-page/cash-flow/CashFlowPage.tsx new file mode 100644 index 0000000..cc9a511 --- /dev/null +++ b/frontend/src/main-page/cash-flow/CashFlowPage.tsx @@ -0,0 +1,10 @@ +function CashFlowPage() { + return ( +
+

PLACEHOLDER

+ {/* Add more content and components related to cash flow here */} +
+ ); +} + +export default CashFlowPage; \ No newline at end of file diff --git a/frontend/src/main-page/grants/GrantPage.tsx b/frontend/src/main-page/grants/GrantPage.tsx index a50c8c7..e6e287a 100644 --- a/frontend/src/main-page/grants/GrantPage.tsx +++ b/frontend/src/main-page/grants/GrantPage.tsx @@ -20,6 +20,7 @@ import { toJS } from "mobx"; import { fetchGrants } from "./filter-bar/processGrantData.ts"; import { UserStatus } from "../../../../middle-layer/types/UserStatus.ts"; import { Navigate } from "react-router-dom"; +import BellButton from "../navbar/Bell.tsx"; interface GrantPageProps { showOnlyMyGrants?: boolean; //if true, filters grants by user email @@ -29,6 +30,7 @@ function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) { const [showNewGrantModal, setShowNewGrantModal] = useState(false); const [wasGrantSubmitted, setWasGrantSubmitted] = useState(false); const [selectedGrant, setSelectedGrant] = useState(null); + const [openModal, setOpenModal] = useState(false); const { user } = useAuthContext(); //gets current logged in user const userObj = toJS(user); @@ -60,6 +62,9 @@ function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) {
setShowNewGrantModal(true)} /> +
+ +
diff --git a/frontend/src/main-page/header/AccountInfo.tsx b/frontend/src/main-page/header/AccountInfo.tsx deleted file mode 100644 index 066c736..0000000 --- a/frontend/src/main-page/header/AccountInfo.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { useState } from 'react'; -import './styles/AccountInfo.css'; -import { logoutUser, -} from "../../external/bcanSatchel/actions.ts"; -import { useNavigate } from 'react-router-dom'; -import { useAuthContext } from "../../context/auth/authContext"; -import { createPortal } from 'react-dom'; - -interface AccountInfoProps { - email: string; - username: string; - role: string; - setOpenModal: (modal: string | null) => void; -} - -const AccountInfo: React.FC = ({ - email, - username, - role, - setOpenModal, -}) => { - - const navigate = useNavigate(); - const {user} = useAuthContext(); - - const [isAdmin] = useState(user?.position === 'Admin'); - - const handleUserListClick = () => { - navigate('users'); - setOpenModal(null); - }; - - const handleLogoutClick = () => { - logoutUser(); - setOpenModal(null); - navigate('/login') - }; - - return createPortal( -
e.stopPropagation()}> -
-

Account Info

- -
-
- E-mail: - {email} -
-
- Username: - {username} -
-
- Role: - {role} -
-
-
- {isAdmin && - - } - -
-
-
, - document.body - ); -}; - - -export default AccountInfo; diff --git a/frontend/src/main-page/header/Header.tsx b/frontend/src/main-page/header/Header.tsx deleted file mode 100644 index 3c2766f..0000000 --- a/frontend/src/main-page/header/Header.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { Link } from "react-router-dom"; -import "./styles/Header.css"; -import logo from "../../images/logo.svg"; -import { - Status, - stringToStatus, -} from "../../../../middle-layer/types/Status.ts"; -import { - updateFilter, -} from "../../external/bcanSatchel/actions.ts"; -import { observer } from "mobx-react-lite"; -import BellButton from "./Bell.tsx"; -import { useLocation } from 'react-router-dom'; -import UserButton from "./UserButton.tsx"; -import { useState } from "react"; - -interface NavBarProps { - name: string; - linkTo?: string; - filter?: Status; -} - -const linkList: NavBarProps[] = [ - { name: "Dashboard", linkTo: "/main/dashboard" }, - { name: "All Grants", linkTo: "/main/all-grants" }, - { name: "My Grants", linkTo: "/main/my-grants" }, -]; - -/** - * Header component provides the main navigation along with a settings cog. - * The cog displays a dropdown with "My Account" and "Logout" options. - */ -const Header: React.FC = observer(() => { - const [openModal, setOpenModal] = useState(null); - - function categoryClicked( - e: React.MouseEvent, - category: string, - linkTo?: string - ) { - if (!linkTo) { - e.preventDefault(); - updateFilter(stringToStatus(category)); - } - } - - - return ( -
-
- BCAN Logo -
-
-
    - {linkList.map((item, index) => ( -
  • - categoryClicked(e, item.name, item.linkTo)} - to={item.linkTo ? item.linkTo : "#"} - > -
    - {item.name} -
    - -
  • - ))} -
-
-
setOpenModal(openModal === "bell" ? null : "bell")}> - -
-
setOpenModal(openModal === "user" ? null : "user")}> - -
-
-
-
- ); -}); - -export default Header; diff --git a/frontend/src/main-page/header/UserButton.tsx b/frontend/src/main-page/header/UserButton.tsx deleted file mode 100644 index eee16ac..0000000 --- a/frontend/src/main-page/header/UserButton.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { faUser } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import AccountInfo from "./AccountInfo"; -import { useAuthContext } from "../../context/auth/authContext"; - -interface UserButtonProps { - setOpenModal: (modal: string | null) => void; - openModal: string | null; -} - -const UserButton: React.FC = ({ setOpenModal, openModal }) => { - const { user } = useAuthContext(); - const toggleAccountInfo = () => { - setOpenModal(openModal === "user" ? null : "user"); - }; - - - return ( -
-
- - - {openModal === "user" && ( - - )} -
-
- ); -}; - -export default UserButton; \ No newline at end of file diff --git a/frontend/src/main-page/header/Bell.tsx b/frontend/src/main-page/navbar/Bell.tsx similarity index 88% rename from frontend/src/main-page/header/Bell.tsx rename to frontend/src/main-page/navbar/Bell.tsx index ed8f0b3..81f2dc0 100644 --- a/frontend/src/main-page/header/Bell.tsx +++ b/frontend/src/main-page/navbar/Bell.tsx @@ -13,8 +13,8 @@ import { api } from "../../api"; interface BellButtonProps { // onClick handler to open notification popup - setOpenModal: (modal: string | null) => void; - openModal: string | null; + setOpenModal: (open: boolean) => void; + openModal: boolean; } const BellButton: React.FC = observer(({ setOpenModal, openModal }) => { @@ -38,7 +38,7 @@ const BellButton: React.FC = observer(({ setOpenModal, openModa console.log(response); const currNotifications = await response.json(); setNotificationsAction(currNotifications); - setOpenModal(openModal === "bell" ? null : "bell"); + setOpenModal(!openModal); return notifications; }; @@ -48,7 +48,7 @@ const BellButton: React.FC = observer(({ setOpenModal, openModa className="bell-wrapper inline-block relative p-2 hover:bg-primary-700 rounded-md" >
- {(openModal === "bell" ? ( + {(openModal ? ( diff --git a/frontend/src/main-page/navbar/NavBar.tsx b/frontend/src/main-page/navbar/NavBar.tsx new file mode 100644 index 0000000..7a1a40c --- /dev/null +++ b/frontend/src/main-page/navbar/NavBar.tsx @@ -0,0 +1,89 @@ +import { useNavigate } from "react-router-dom"; +import "./styles/Header.css"; +import { + logoutUser +} from "../../external/bcanSatchel/actions.ts"; +import { observer } from "mobx-react-lite"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faGear, faRightFromBracket, faUser, faBorderNone } from "@fortawesome/free-solid-svg-icons"; +import { getAppStore } from "../../external/bcanSatchel/store"; +import { UserStatus } from "../../../../middle-layer/types/UserStatus"; +import NavTab, { NavTabProps } from "./NavTab.tsx"; +import { faChartLine, faMoneyBill, faClipboardCheck } from "@fortawesome/free-solid-svg-icons"; +import { NavBarBranding } from "../../translations/general.ts"; + +const tabs: NavTabProps[] = [ + { name: "Dashboard", linkTo: "/main/dashboard", icon: faChartLine }, + { name: "Grants", linkTo: "/main/all-grants", icon: faClipboardCheck }, + { name: "Cash Flow", linkTo: "/main/cash-flow", icon: faMoneyBill }, +]; + +/** + * Sidebar component provides the main navigation. + */ +const NavBar: React.FC = observer(() => { + const navigate = useNavigate(); + const user = getAppStore().user; + const isAdmin = user?.position === UserStatus.Admin; + + const handleLogout = () => { + logoutUser(); + navigate("/login"); + }; + + return ( + + ); +}); + +export default NavBar; diff --git a/frontend/src/main-page/navbar/NavTab.tsx b/frontend/src/main-page/navbar/NavTab.tsx new file mode 100644 index 0000000..9c698d0 --- /dev/null +++ b/frontend/src/main-page/navbar/NavTab.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { Link, useLocation } from "react-router-dom"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; + +export interface NavTabProps { + name: string; + linkTo: string; + icon: IconDefinition; +} + +const NavTab: React.FC = ({ name, linkTo, icon }) => { + const location = useLocation(); + const isActive = location.pathname === linkTo; + + return ( + + + {name} + + ); +}; + +export default NavTab; \ No newline at end of file diff --git a/frontend/src/main-page/header/styles/AccountInfo.css b/frontend/src/main-page/navbar/styles/AccountInfo.css similarity index 100% rename from frontend/src/main-page/header/styles/AccountInfo.css rename to frontend/src/main-page/navbar/styles/AccountInfo.css diff --git a/frontend/src/main-page/header/styles/Header.css b/frontend/src/main-page/navbar/styles/Header.css similarity index 100% rename from frontend/src/main-page/header/styles/Header.css rename to frontend/src/main-page/navbar/styles/Header.css diff --git a/frontend/src/main-page/notifications/NotificationPopup.tsx b/frontend/src/main-page/notifications/NotificationPopup.tsx index ca7e466..65b5b83 100644 --- a/frontend/src/main-page/notifications/NotificationPopup.tsx +++ b/frontend/src/main-page/notifications/NotificationPopup.tsx @@ -8,7 +8,7 @@ import { getAppStore } from "../../external/bcanSatchel/store"; import { observer } from 'mobx-react-lite'; interface NotificationPopupProps { - setOpenModal: (value: string | null) => void; + setOpenModal: (open: boolean) => void; } const NotificationPopup: React.FC = observer(({ @@ -54,7 +54,7 @@ const NotificationPopup: React.FC = observer(({

Alerts

-
diff --git a/frontend/src/main-page/settings/SettingsPage.tsx b/frontend/src/main-page/settings/SettingsPage.tsx new file mode 100644 index 0000000..4164017 --- /dev/null +++ b/frontend/src/main-page/settings/SettingsPage.tsx @@ -0,0 +1,10 @@ +function SettingsPage() { + return ( +
+

Settings

+

Placeholder page! :D

+
+ ); +} + +export default SettingsPage; \ No newline at end of file diff --git a/frontend/src/styles/notification.css b/frontend/src/styles/notification.css index 30d6a1a..f47b9cc 100644 --- a/frontend/src/styles/notification.css +++ b/frontend/src/styles/notification.css @@ -1,6 +1,6 @@ .notification-popup { position: absolute; - right: 7.5rem; + right: 3rem; top: 75px; width: min(340px, 70%); background-color: white; diff --git a/frontend/src/translations/general.ts b/frontend/src/translations/general.ts index 5d0a3a1..f924fb5 100644 --- a/frontend/src/translations/general.ts +++ b/frontend/src/translations/general.ts @@ -1,3 +1,5 @@ +import logo from "../images/logo.svg"; + export enum FooterText { C4C_Motto = "Made with love by ", C4C = "Code4Community", @@ -11,4 +13,9 @@ export enum FooterText { export enum DoesBcanQualifyText { Yes = "Yes", No = "No" -} \ No newline at end of file +} + +export const NavBarBranding = { + logo: logo, + name: "BostonCan" +}; \ No newline at end of file