From 371eee63b0f7ded2b000d87627d5e3fa9c3943cf Mon Sep 17 00:00:00 2001 From: josieek Date: Sat, 27 Sep 2025 23:54:04 -0400 Subject: [PATCH 01/13] top left corner more resembles the new modal --- .../grant-info/components/NewGrantModal.tsx | 129 ++++++++++++------ .../components/styles/NewGrantModal.css | 24 +++- 2 files changed, 103 insertions(+), 50 deletions(-) diff --git a/frontend/src/grant-info/components/NewGrantModal.tsx b/frontend/src/grant-info/components/NewGrantModal.tsx index 837fb07..7cd303a 100644 --- a/frontend/src/grant-info/components/NewGrantModal.tsx +++ b/frontend/src/grant-info/components/NewGrantModal.tsx @@ -196,18 +196,90 @@ const NewGrantModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { return (
-

Add New Grant

+

New Grant

{errorMessage &&

{errorMessage}

} {/* Row 1 */} - - setOrganization(e.target.value)} - /> + +
+ +
+ + setOrganization(e.target.value)} + /> +
+ +
+ +
+ + setApplicationDate(e.target.value)} + /> +
+ +
+ + setGrantStartDate(e.target.value)} + /> +
+ +
+ + setGrantStartDate(e.target.value)} + /> +
+ +
+ + + setEstimatedCompletionTimeInHours(Number(e.target.value))} + style={{ width: '66%' }} + /> + +
+ + setTimelineInYears(Number(e.target.value))} + /> +
+ + + setAmount(Number(e.target.value))} + /> + + +
+ + + + + +
@@ -222,21 +294,8 @@ const NewGrantModal: React.FC<{ onClose: () => void }> = ({ onClose }) => {
{/* Row 2: Dates */} - - setApplicationDate(e.target.value)} - /> - - setGrantStartDate(e.target.value)} - /> + void }> = ({ onClose }) => { /> {/* Row 3: Times */} - - setEstimatedCompletionTimeInHours(Number(e.target.value))} - /> - - setTimelineInYears(Number(e.target.value))} - /> + {/* Row 4: Does BCAN Qualify & Status */} @@ -286,14 +332,7 @@ const NewGrantModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { {/* Row 5: Amount */} - - setAmount(Number(e.target.value))} - /> + {/* Row 6: Scope Documents (Attachments) */} diff --git a/frontend/src/grant-info/components/styles/NewGrantModal.css b/frontend/src/grant-info/components/styles/NewGrantModal.css index d9225d3..c6dc7e1 100644 --- a/frontend/src/grant-info/components/styles/NewGrantModal.css +++ b/frontend/src/grant-info/components/styles/NewGrantModal.css @@ -44,7 +44,7 @@ */ .new-grant-grid { display: grid; - grid-template-columns: 1fr 2fr; /* label-col is narrower, input-col is wider */ + grid-template-columns: 1fr 1fr 1fr 1fr 1fr; /* label-col is narrower, input-col is wider */ grid-auto-rows: auto; column-gap: 1rem; row-gap: 1rem; @@ -55,11 +55,24 @@ align-self: center; /* vertically center label text */ font-weight: bold; } - - .input-col { - /* We'll let these fill the space as needed */ + + + .left-cols { + grid-column: 1 / span 3; + text-align: left; + } - + + .date-entry { + text-align: left; + display: flex; + align-self : center; + gap: 10px; + + } + + + /* If you want certain rows to span the entire width, you can place them outside the grid or use grid-column: span 2; @@ -73,6 +86,7 @@ font-size: 1.8rem; font-weight: 600; text-align: center; + text-align : left; } /* Error message styling */ From 26a2a296e2b01d5b997870698654003e2f219ebe Mon Sep 17 00:00:00 2001 From: josieek Date: Sun, 28 Sep 2025 22:19:39 -0400 Subject: [PATCH 02/13] added report deadlines box --- .../grant-info/components/NewGrantModal.tsx | 71 +++++++++++-------- .../components/styles/NewGrantModal.css | 5 +- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/frontend/src/grant-info/components/NewGrantModal.tsx b/frontend/src/grant-info/components/NewGrantModal.tsx index 7cd303a..f8fee16 100644 --- a/frontend/src/grant-info/components/NewGrantModal.tsx +++ b/frontend/src/grant-info/components/NewGrantModal.tsx @@ -203,10 +203,10 @@ const NewGrantModal: React.FC<{ onClose: () => void }> = ({ onClose }) => {
-
+
void }> = ({ onClose }) => { />
-
+
-
+
setApplicationDate(e.target.value)} @@ -235,26 +236,44 @@ const NewGrantModal: React.FC<{ onClose: () => void }> = ({ onClose }) => {
- - Report Deadlines +
+ setGrantStartDate(e.target.value)} - /> + /> + +
+
- - + + setEstimatedCompletionTimeInHours(Number(e.target.value))} - style={{ width: '66%' }} - /> + type="number" + value={estimatedCompletionTimeInHours} + onChange={(e) => setEstimatedCompletionTimeInHours(Number(e.target.value))} + style={{ width: '60%' }} + /> + +
+ -
+
void }> = ({ onClose }) => { onChange={(e) => setAmount(Number(e.target.value))} /> + + +
- - - - - -
@@ -371,13 +392,7 @@ const NewGrantModal: React.FC<{ onClose: () => void }> = ({ onClose }) => {
{/* Row 7: Description */} - - + {/* Row 8: Buttons */}
diff --git a/frontend/src/grant-info/components/styles/NewGrantModal.css b/frontend/src/grant-info/components/styles/NewGrantModal.css index c6dc7e1..7289d13 100644 --- a/frontend/src/grant-info/components/styles/NewGrantModal.css +++ b/frontend/src/grant-info/components/styles/NewGrantModal.css @@ -60,7 +60,6 @@ .left-cols { grid-column: 1 / span 3; text-align: left; - } .date-entry { @@ -71,6 +70,10 @@ } + .inputs { + padding-bottom : 20px; + } + /* From 3734f65c92545423b5964406d0c934736a97cd26 Mon Sep 17 00:00:00 2001 From: josieek Date: Mon, 29 Sep 2025 21:54:14 -0400 Subject: [PATCH 03/13] switching to tailwind --- .../grant-info/components/NewGrantModal.tsx | 232 ++-------- .../grant-info/components/OldGrantModal.tsx | 407 ++++++++++++++++++ .../components/styles/NewGrantModal.css | 11 +- 3 files changed, 446 insertions(+), 204 deletions(-) create mode 100644 frontend/src/grant-info/components/OldGrantModal.tsx diff --git a/frontend/src/grant-info/components/NewGrantModal.tsx b/frontend/src/grant-info/components/NewGrantModal.tsx index f8fee16..bbff74e 100644 --- a/frontend/src/grant-info/components/NewGrantModal.tsx +++ b/frontend/src/grant-info/components/NewGrantModal.tsx @@ -195,212 +195,56 @@ const NewGrantModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { return (
-
-

New Grant

- {errorMessage &&

{errorMessage}

} - - {/* Row 1 */} - -
- -
- - setOrganization(e.target.value)} - /> +
+
+

New Grant

+ +
+
+ + +
-
- -
- - setApplicationDate(e.target.value)} - /> -
- -
- - setGrantStartDate(e.target.value)} - /> +
+
+ +
- -
- -
- setGrantStartDate(e.target.value)} - /> - +
+ +
+
-
- -
- -
- - setEstimatedCompletionTimeInHours(Number(e.target.value))} - style={{ width: '60%' }} - /> - -
- - -
- - setTimelineInYears(Number(e.target.value))} - /> -
- - - setAmount(Number(e.target.value))} - /> - - - - - -
- - -
- {bcanPocComponents} - -
- - -
- {grantProviderPocComponents} - -
+
+ +
+ + +
- {/* Row 2: Dates */} - - - - - setReportDate(e.target.value)} - /> - - {/* Row 3: Times */} - - - - {/* Row 4: Does BCAN Qualify & Status */} - - - - - - - {/* Row 5: Amount */} - - - {/* Row 6: Scope Documents (Attachments) */} - -
- {attachments.map((attachment, index) => ( -
- - handleAttachmentChange(index, "attachment_name", e.target.value) - } - /> - - handleAttachmentChange(index, "url", e.target.value) - } - /> - -
- ))} - -
- - {/* Row 7: Description */} - - - {/* Row 8: Buttons */} +
+
-
+ +
+
+ + ); }; diff --git a/frontend/src/grant-info/components/OldGrantModal.tsx b/frontend/src/grant-info/components/OldGrantModal.tsx new file mode 100644 index 0000000..f8fee16 --- /dev/null +++ b/frontend/src/grant-info/components/OldGrantModal.tsx @@ -0,0 +1,407 @@ +// frontend/src/grant-info/components/NewGrantModal.tsx +import React, { useState, createRef, RefObject } from "react"; +import { fetchAllGrants } from "../../external/bcanSatchel/actions"; +import "../components/styles/NewGrantModal.css"; +import POCEntry from "./POCEntry"; +import { Grant } from "../../../../middle-layer/types/Grant"; +import { TDateISO } from "../../../../backend/src/utils/date"; +import { Status } from "../../../../middle-layer/types/Status"; +import { api } from "../../api"; + +/** Attachment type from your middle layer */ +enum AttachmentType { + SCOPE_DOCUMENT = 0, + SUPPORTING_RESOURCE = 1, +} + +/** Attachment interface */ +interface Attachment { + attachment_name: string; + url: string; + type: AttachmentType; +} + +/** POC ref type from POCEntry */ +export interface POCEntryRef { + getPOC: () => string; +} + +const NewGrantModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { + // Form fields, renamed to match your screenshot + const [organization, setOrganization] = useState(""); + const [bcanPocComponents, setBcanPocComponents] = useState([]); + const [bcanPocRefs, setBcanPocRefs] = useState[]>([]); + + const [grantProviderPocComponents, setGrantProviderPocComponents] = useState([]); + const [grantProviderPocRefs, setGrantProviderPocRefs] = useState[]>([]); + + const [applicationDate, setApplicationDate] = useState(""); + const [grantStartDate, setGrantStartDate] = useState(""); + const [reportDate, setReportDate] = useState(""); + + const [timelineInYears, setTimelineInYears] = useState(0); + const [estimatedCompletionTimeInHours, setEstimatedCompletionTimeInHours] = useState(0); + + const [doesBcanQualify, setDoesBcanQualify] = useState(false); + const [status, setStatus] = useState(Status.Potential); + + const [amount, setAmount] = useState(0); + const [description, setDescription] = useState(""); + + // Attachments array + const [attachments, setAttachments] = useState([]); + + // For error handling + const [errorMessage, setErrorMessage] = useState(""); + + /** Add a new BCAN POC entry */ + const addBcanPoc = () => { + const newRef = createRef(); + const newPOC = ; + setBcanPocComponents([...bcanPocComponents, newPOC]); + setBcanPocRefs([...bcanPocRefs, newRef]); + }; + + /** Add a new Grant Provider POC entry */ + const addGrantProviderPoc = () => { + const newRef = createRef(); + const newPOC = ; + setGrantProviderPocComponents([...grantProviderPocComponents, newPOC]); + setGrantProviderPocRefs([...grantProviderPocRefs, newRef]); + }; + + // Add an empty attachment row + const addAttachment = () => { + setAttachments([ + ...attachments, + { + attachment_name: "", + url: "", + type: AttachmentType.SCOPE_DOCUMENT, + }, + ]); + }; + + // Remove a specific attachment row + const removeAttachment = (index: number) => { + const updated = [...attachments]; + updated.splice(index, 1); + setAttachments(updated); + }; + + // Update a field in one attachment + const handleAttachmentChange = ( + index: number, + field: keyof Attachment, + value: string | AttachmentType + ) => { + const updated = [...attachments]; + // @ts-ignore + updated[index][field] = value; + setAttachments(updated); + }; + + /** Basic validations based on your screenshot fields */ + const validateInputs = (): boolean => { + if (!organization) { + setErrorMessage("Organization Name is required."); + return false; + } + if (!applicationDate || !grantStartDate || !reportDate) { + setErrorMessage("Please fill out all date fields."); + return false; + } + if (amount <= 0) { + setErrorMessage("Amount must be greater than 0."); + return false; + } + return true; + }; + + /** On submit, POST the new grant, then re-fetch from the backend */ + const handleSubmit = async () => { + if (!validateInputs()) return; + + // Gather BCAN POC values + const bcanPocList: string[] = []; + bcanPocRefs.forEach((ref) => { + if (ref.current) { + bcanPocList.push(ref.current.getPOC()); + } + }); + + // Gather Grant Provider POC values + const providerPocList: string[] = []; + grantProviderPocRefs.forEach((ref) => { + if (ref.current) { + providerPocList.push(ref.current.getPOC()); + } + }); + + // Convert attachments array + const attachmentsArray = attachments.map((att) => ({ + attachment_name: att.attachment_name.trim(), + url: att.url.trim(), + type: att.type, + })); + + /* Matches middle layer definition */ + const newGrant: Grant = { + grantId: -1, + organization, + grantmaker_poc: providerPocList, + application_deadline: applicationDate as TDateISO, + report_deadline: reportDate as TDateISO, + timeline: timelineInYears, + estimated_completion_time: estimatedCompletionTimeInHours, + does_bcan_qualify: doesBcanQualify, + status: status, // Potential = 0, Active = 1, Inactive = 2 + amount, + description, + attachments: attachmentsArray, + notification_date: applicationDate as TDateISO, + application_requirements : "", + additional_notes : "", + }; + console.log(newGrant); + try { + const response = await api("/grant/new-grant", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(newGrant), + }); + + if (!response.ok) { + const errorData = await response.json(); + setErrorMessage(errorData.errMessage || "Failed to add grant."); + return; + } + + // Re-fetch the full list of grants + const grantsResponse = await api("/grant"); + if (!grantsResponse.ok) { + throw new Error("Failed to re-fetch grants."); + } + const updatedGrants = await grantsResponse.json(); + // Update the store + fetchAllGrants(updatedGrants); + + onClose(); + } catch (error) { + setErrorMessage("Server error. Please try again."); + console.error(error); + } + }; + + return ( +
+
+

New Grant

+ {errorMessage &&

{errorMessage}

} + + {/* Row 1 */} + +
+ +
+ + setOrganization(e.target.value)} + /> +
+ +
+ +
+ + setApplicationDate(e.target.value)} + /> +
+ +
+ + setGrantStartDate(e.target.value)} + /> +
+ +
+ +
+ setGrantStartDate(e.target.value)} + /> + +
+ +
+ +
+ +
+ + setEstimatedCompletionTimeInHours(Number(e.target.value))} + style={{ width: '60%' }} + /> + +
+ + +
+ + setTimelineInYears(Number(e.target.value))} + /> +
+ + + setAmount(Number(e.target.value))} + /> + + + + + +
+ + +
+ {bcanPocComponents} + +
+ + +
+ {grantProviderPocComponents} + +
+ + {/* Row 2: Dates */} + + + + + setReportDate(e.target.value)} + /> + + {/* Row 3: Times */} + + + + {/* Row 4: Does BCAN Qualify & Status */} + + + + + + + {/* Row 5: Amount */} + + + {/* Row 6: Scope Documents (Attachments) */} + +
+ {attachments.map((attachment, index) => ( +
+ + handleAttachmentChange(index, "attachment_name", e.target.value) + } + /> + + handleAttachmentChange(index, "url", e.target.value) + } + /> + + +
+ ))} + +
+ + {/* Row 7: Description */} + + + {/* Row 8: Buttons */} +
+ + +
+
+
+ ); +}; + +export default NewGrantModal; diff --git a/frontend/src/grant-info/components/styles/NewGrantModal.css b/frontend/src/grant-info/components/styles/NewGrantModal.css index 7289d13..8ba9b92 100644 --- a/frontend/src/grant-info/components/styles/NewGrantModal.css +++ b/frontend/src/grant-info/components/styles/NewGrantModal.css @@ -13,10 +13,9 @@ padding-top: 2rem; padding-bottom: 2rem; background-color: rgba(0, 0, 0, 0.5); - z-index: 1000; display: flex; justify-content: center; - align-items: flex-start; + align-items: center; } /* @@ -25,17 +24,9 @@ */ .modal-content { background: #f7bfa6; /* Peach background */ - border-radius: 8px; width: 90%; max-width: 900px; /* More width to accommodate multiple columns */ padding: 2rem; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, - "Helvetica Neue", Arial, sans-serif; - font-size: 16px; - line-height: 1.5; - color: #000; - max-height: 90vh; - overflow-y: auto; } /* From 3cb17316b8c90d8e9a36dbefb3e8c99c767b0075 Mon Sep 17 00:00:00 2001 From: josieek Date: Tue, 30 Sep 2025 18:55:29 -0400 Subject: [PATCH 04/13] added comments --- .../grant-info/components/NewGrantModal.tsx | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/frontend/src/grant-info/components/NewGrantModal.tsx b/frontend/src/grant-info/components/NewGrantModal.tsx index bbff74e..914bf83 100644 --- a/frontend/src/grant-info/components/NewGrantModal.tsx +++ b/frontend/src/grant-info/components/NewGrantModal.tsx @@ -194,35 +194,52 @@ const NewGrantModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { }; return ( -
-
-
+ +
{/*Greyed out background */} +
{/*Popup container */} + {/*Widget container */}

New Grant

-
-
+ {/*Organization name and input */} +
-
-
+ +
+
-
-
+ + {/*Grant Start Date and input */} +
-
- -
+
+ + {/*Report deadlines label and grey box */}