From 5053f8cb69cda3d394f19ea230807b58d682a3de Mon Sep 17 00:00:00 2001 From: lyannne Date: Wed, 18 Feb 2026 01:59:11 -0500 Subject: [PATCH 01/14] massive commit with component and edits made to main grant page --- frontend/src/main-page/MainPage.tsx | 2 +- frontend/src/main-page/grants/GrantPage.tsx | 49 ++++++++++---- .../grants/filter-bar/GrantSearch.tsx | 2 +- .../main-page/grants/grant-list/GrantCard.tsx | 66 +++++++++++++++++++ .../main-page/grants/new-grant/AddGrant.tsx | 4 +- .../main-page/grants/styles/GrantButton.css | 8 ++- .../main-page/grants/styles/GrantSearch.css | 4 +- frontend/src/styles/notification.css | 4 +- 8 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 frontend/src/main-page/grants/grant-list/GrantCard.tsx diff --git a/frontend/src/main-page/MainPage.tsx b/frontend/src/main-page/MainPage.tsx index 84bf706..17b132b 100644 --- a/frontend/src/main-page/MainPage.tsx +++ b/frontend/src/main-page/MainPage.tsx @@ -14,7 +14,7 @@ function MainPage() {
-
+
} /> diff --git a/frontend/src/main-page/grants/GrantPage.tsx b/frontend/src/main-page/grants/GrantPage.tsx index e6e287a..613e5ee 100644 --- a/frontend/src/main-page/grants/GrantPage.tsx +++ b/frontend/src/main-page/grants/GrantPage.tsx @@ -1,12 +1,13 @@ import "./styles/GrantPage.css"; -import GrantList from "./grant-list/GrantList.tsx"; +// import GrantList from "./grant-list/GrantList.tsx"; import AddGrantButton from "./new-grant/AddGrant.tsx"; import GrantSearch from "./filter-bar/GrantSearch.tsx"; import NewGrantModal from "./new-grant/NewGrantModal.tsx"; import { useEffect, useState } from "react"; import { Grant } from "../../../../middle-layer/types/Grant.ts"; -import FilterBar from "./filter-bar/FilterBar.tsx"; +//import FilterBar from "./filter-bar/FilterBar.tsx"; +import GrantItem from "./grant-view/GrantView.tsx"; import { useAuthContext } from "../../context/auth/authContext"; import { updateEndDateFilter, @@ -16,11 +17,12 @@ import { updateYearFilter, } from "../../external/bcanSatchel/actions.ts"; import { toJS } from "mobx"; - +import { ProcessGrantData } from "./filter-bar/processGrantData.ts"; 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"; +import GrantCard from "./grant-list/GrantCard.tsx"; interface GrantPageProps { showOnlyMyGrants?: boolean; //if true, filters grants by user email @@ -29,7 +31,8 @@ interface GrantPageProps { function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) { const [showNewGrantModal, setShowNewGrantModal] = useState(false); const [wasGrantSubmitted, setWasGrantSubmitted] = useState(false); - const [selectedGrant, setSelectedGrant] = useState(null); + const [grants, setGrants] = useState(ProcessGrantData().grants) + const [selectedGrant, setSelectedGrant] = useState(grants[0]); const [openModal, setOpenModal] = useState(false); const { user } = useAuthContext(); //gets current logged in user @@ -57,16 +60,37 @@ function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) { return user ? ( user?.position !== UserStatus.Inactive ? ( -
-
-
- +
+
+ +
+ +
+ + FILTERS GO HERE + setShowNewGrantModal(true)} /> -
- +
+ +
+
+ {grants.map((grant) => ( + setSelectedGrant(grant)} + /> + ))} +
+
+
-
+ + {/*
@@ -76,13 +100,12 @@ function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) { selectedGrantId={ selectedGrant ? selectedGrant.grantId : undefined } - onClearSelectedGrant={() => setSelectedGrant(null)} currentUserEmail={currentUserEmail} showOnlyMyGrants={showOnlyMyGrants} />
-
+
*/}
{showNewGrantModal && ( void; +} + +const GrantCard: React.FC = ({ grant, isSelected, onClick }) => { + + const formattedDate = new Date(grant.application_deadline).toLocaleDateString('en-US', { + month: 'numeric', + day: 'numeric', + year: '2-digit', + }); + + return ( +
+ {/* Colored left border indicator + Grant Name + Amount*/} +
+
+ {/* Eligibility Badge - Shows on hover over the line */} +
+ {grant.does_bcan_qualify ? 'Eligible' : 'Not Eligible'} +
+
+
+
+ {grant.organization} +
+
+ ${grant.amount} +
+
+
+ + {/* Card content */} +
+ + Due: {formattedDate} + + +
+
+ ); +}; + +export default GrantCard; diff --git a/frontend/src/main-page/grants/new-grant/AddGrant.tsx b/frontend/src/main-page/grants/new-grant/AddGrant.tsx index e853ef3..146408c 100644 --- a/frontend/src/main-page/grants/new-grant/AddGrant.tsx +++ b/frontend/src/main-page/grants/new-grant/AddGrant.tsx @@ -4,8 +4,8 @@ import '../styles/GrantButton.css'; const AddGrantButton = ({ onClick } : any) => { return ( - ); }; diff --git a/frontend/src/main-page/grants/styles/GrantButton.css b/frontend/src/main-page/grants/styles/GrantButton.css index b270a22..9166ddc 100644 --- a/frontend/src/main-page/grants/styles/GrantButton.css +++ b/frontend/src/main-page/grants/styles/GrantButton.css @@ -12,9 +12,13 @@ } .add-grant-button { - width: 168.42px; + border-radius: 0.85rem; + padding: 0.5rem; + height: 40px; + width: 40px; + border-radius: 50%; font-weight: 600; - border-color: transparent; + border-color: var(--color-grey-500); } diff --git a/frontend/src/main-page/grants/styles/GrantSearch.css b/frontend/src/main-page/grants/styles/GrantSearch.css index fe6b4fb..9195436 100644 --- a/frontend/src/main-page/grants/styles/GrantSearch.css +++ b/frontend/src/main-page/grants/styles/GrantSearch.css @@ -1,4 +1,5 @@ .search-bar-main-container { + width: 100%; min-width: 300px; min-width: 300px; } @@ -22,8 +23,9 @@ color: black; padding: 8px; border-radius: 15px; - border: 0.1rem solid black; + border: 0.1rem solid var(--color-grey-500); width: 100%; + background-color: var(--color-grey-100); } .search-input:focus { diff --git a/frontend/src/styles/notification.css b/frontend/src/styles/notification.css index f47b9cc..7f794c1 100644 --- a/frontend/src/styles/notification.css +++ b/frontend/src/styles/notification.css @@ -1,7 +1,7 @@ .notification-popup { position: absolute; - right: 3rem; - top: 75px; + right: 7rem; + top: 110px; width: min(340px, 70%); background-color: white; border: 1px solid black; From d7ef579ed2526196dc7fa4c759635a616fa2586f Mon Sep 17 00:00:00 2001 From: lyannne Date: Wed, 18 Feb 2026 03:04:40 -0500 Subject: [PATCH 02/14] implemented frontend for entire grant display --- frontend/src/Login.tsx | 2 +- frontend/src/main-page/grants/GrantPage.tsx | 97 ++++++++++++++----- .../main-page/grants/grant-list/GrantCard.tsx | 2 +- .../main-page/grants/grant-view/GrantView.tsx | 63 +++--------- 4 files changed, 92 insertions(+), 72 deletions(-) diff --git a/frontend/src/Login.tsx b/frontend/src/Login.tsx index 6a85663..931085f 100644 --- a/frontend/src/Login.tsx +++ b/frontend/src/Login.tsx @@ -21,7 +21,7 @@ const Login = observer(() => { const success = await login(username, password); if (success) { - navigate("/grant-info"); + navigate("/main/all-grants"); } else { setFailure(true); } diff --git a/frontend/src/main-page/grants/GrantPage.tsx b/frontend/src/main-page/grants/GrantPage.tsx index 613e5ee..5579974 100644 --- a/frontend/src/main-page/grants/GrantPage.tsx +++ b/frontend/src/main-page/grants/GrantPage.tsx @@ -6,7 +6,7 @@ import GrantSearch from "./filter-bar/GrantSearch.tsx"; import NewGrantModal from "./new-grant/NewGrantModal.tsx"; import { useEffect, useState } from "react"; import { Grant } from "../../../../middle-layer/types/Grant.ts"; -//import FilterBar from "./filter-bar/FilterBar.tsx"; +// import FilterBar from "./filter-bar/FilterBar.tsx"; import GrantItem from "./grant-view/GrantView.tsx"; import { useAuthContext } from "../../context/auth/authContext"; import { @@ -15,24 +15,81 @@ import { updateSearchQuery, updateStartDateFilter, updateYearFilter, + fetchAllGrants, } from "../../external/bcanSatchel/actions.ts"; import { toJS } from "mobx"; +import { observer } from "mobx-react-lite"; import { ProcessGrantData } from "./filter-bar/processGrantData.ts"; -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"; import GrantCard from "./grant-list/GrantCard.tsx"; +import { api } from "../../api.ts"; interface GrantPageProps { showOnlyMyGrants?: boolean; //if true, filters grants by user email } -function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) { +function GrantPage({}: GrantPageProps) { const [showNewGrantModal, setShowNewGrantModal] = useState(false); const [wasGrantSubmitted, setWasGrantSubmitted] = useState(false); - const [grants, setGrants] = useState(ProcessGrantData().grants) - const [selectedGrant, setSelectedGrant] = useState(grants[0]); + + // Use ProcessGrantData reactively to get filtered grants + const { grants } = ProcessGrantData(); + const [curGrant, setCurGrant] = useState(null); + + // Set the first grant when grants are loaded (only on initial mount) + useEffect(() => { + if (grants.length > 0 && curGrant === null) { + setCurGrant(grants[0]); + } + }, [grants]); + + // If the NewGrantModal has been closed and a new grant submitted (or existing grant edited), + // refetch the grants list and update the current grant to reflect any changes + useEffect(() => { + if (!wasGrantSubmitted || !curGrant) return; + + const updateGrant = async () => { + try { + const response = await api(`/grant/${curGrant.grantId}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + if (response.ok) { + const updatedGrant = await response.json(); + setCurGrant(updatedGrant); + console.log("✅ Grant refreshed:", updatedGrant); + } else { + console.error("❌ Failed to fetch updated grant"); + } + } catch (err) { + console.error("Error fetching updated grant:", err); + } + }; + + const updateGrants = async () => { + try { + const response = await api("/grant"); + if (!response.ok) { + throw new Error(`HTTP Error, Status: ${response.status}`); + } + const updatedGrants: Grant[] = await response.json(); + fetchAllGrants(updatedGrants); + console.log("✅ Grants list refreshed"); + } catch (error) { + console.error("Error fetching grants:", error); + } + }; + + updateGrants(); + updateGrant(); + setWasGrantSubmitted(false); + }, [wasGrantSubmitted]); + const [openModal, setOpenModal] = useState(false); const { user } = useAuthContext(); //gets current logged in user @@ -50,14 +107,6 @@ function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) { updateSearchQuery(""); }, []); - useEffect(() => { - if (!showNewGrantModal && wasGrantSubmitted) { - fetchGrants(); - setWasGrantSubmitted(false); - console.log("Use effect called in GrantPage"); - } - }, [showNewGrantModal, wasGrantSubmitted]); - return user ? ( user?.position !== UserStatus.Inactive ? (
@@ -73,20 +122,24 @@ function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) {
-
+
{grants.map((grant) => ( setSelectedGrant(grant)} + isSelected={curGrant?.grantId === grant.grantId} + onClick={() => setCurGrant(grant)} /> ))}
- + {curGrant ? ( + + ) : ( +
+ No grants found. +
+ )}
@@ -98,14 +151,14 @@ function GrantPage({ showOnlyMyGrants = false }: GrantPageProps) {
-
*/} +
*/}
{showNewGrantModal && ( = ({ grant, isSelected, onClick }) =>
= observer(({ grant }) => { - const [curGrant, setCurGrant] = useState(grant); - const [wasGrantSubmitted, setWasGrantSubmitted] = useState(false); - const useTruncatedElement = ({ ref, }: { @@ -54,36 +51,6 @@ const GrantItem: React.FC = observer(({ grant }) => { ref, }); - // If the NewGrantModal has been closed and a new grant submitted (or existing grant edited), - // fetch the grant at this index so that all new changes are immediately reflected - useEffect(() => { - const updateGrant = async () => { - if (wasGrantSubmitted) { - try { - const response = await api(`/grant/${grant.grantId}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - if (response.ok) { - const updatedGrant = await response.json(); - setCurGrant(updatedGrant); - console.log("✅ Grant refreshed:", updatedGrant); - } else { - console.error("❌ Failed to fetch updated grant"); - } - } catch (err) { - console.error("Error fetching updated grant:", err); - } - setWasGrantSubmitted(false); - } - }; - - updateGrant(); - }, [wasGrantSubmitted]); - function formatDate(isoString: string): string { if (!isoString) return "N/A"; const date = new Date(isoString); @@ -104,8 +71,8 @@ const GrantItem: React.FC = observer(({ grant }) => {
{/* Left side */}
-

{curGrant.organization}

- +

{grant.organization}

+
{/* Right side */}
@@ -134,7 +101,7 @@ const GrantItem: React.FC = observer(({ grant }) => { className={` ${!isShowingMore && "line-clamp-3"}`} onClick={toggleIsShowingMore} > - {curGrant?.description || "N/A"} + {grant?.description || "N/A"}

{isTruncated && ( +