From e1774bce13c620032fcd6d1a9e3f8701175f75ce Mon Sep 17 00:00:00 2001 From: camila-carrillo Date: Mon, 1 Dec 2025 03:19:03 -0500 Subject: [PATCH 1/4] implement cost-benefit analysis feature --- .../grant-details/CostBenefitAnalysis.tsx | 133 ++++++++++++++++++ .../main-page/grants/grant-list/GrantItem.tsx | 37 ++++- .../grants/styles/CostBenefitAnalysis.css | 23 +++ frontend/src/styles/notification.css | 2 + 4 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx create mode 100644 frontend/src/main-page/grants/styles/CostBenefitAnalysis.css diff --git a/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx b/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx new file mode 100644 index 0000000..e2747a6 --- /dev/null +++ b/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx @@ -0,0 +1,133 @@ +import React, { useState } from 'react'; +import { Grant } from '../../../../../middle-layer/types/Grant'; +import '../styles/CostBenefitAnalysis.css'; + +interface CostBenefitAnalysisProps { + grant: Grant; + onCalculate: (value: number | null) => void; +} + +export const CostBenefitAnalysis: React.FC = ({ grant, onCalculate }) => { + const [hourlyRate, setHourlyRate] = useState(''); + const [timePerReport, setTimePerReport] = useState(''); + const [netBenefit, setNetBenefit] = useState(null); + + const calculateNetBenefit = () => { + console.log('Called calculate') + console.log('hourlyRate state:', hourlyRate) + console.log('timePerReport state:', timePerReport) + const rate = parseFloat(hourlyRate); + const timeReport = parseFloat(timePerReport); + + console.log('Parsed rate:', rate) + console.log('Parsed timeReport:', timeReport) + + // Validation + if (isNaN(rate) || isNaN(timeReport) || rate <= 0 || timeReport <= 0) { + alert('Please enter valid positive numbers for hourly rate and time per report.'); + return; + } + + const reportCount = grant.report_deadlines?.length ?? 0; + const grantAmount = grant.amount; + const estimatedTime = grant.estimated_completion_time; + + console.log('Grant values - Amount:', grantAmount, 'EstTime:', estimatedTime, 'ReportCount:', reportCount); + + // Formula: NetBenefit = GrantAmount - ((EstimatedCompletionTime + ReportCount * TimePerReport) * StaffHourlyRate) + const result = grantAmount - ((estimatedTime + reportCount * timeReport) * rate); + + console.log('Final result:', result); + + setNetBenefit(result); + onCalculate(result); + }; + + const formatCurrency = (amount: number): string => { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + maximumFractionDigits: 2 + }).format(amount); + }; + + return ( +
+ + +
+ {/* Hourly Rate Input */} +
+ + setHourlyRate(e.target.value)} + className="w-full px-3 py-4 border border-gray-400 rounded" + style={{ backgroundColor: '#F2EBE4' }} + /> +
+ + {/* Time Per Report Input */} +
+ + { + console.log('Time per report changed to:', e.target.value); + setTimePerReport(e.target.value); + }} + className="w-full px-3 py-4 border border-gray-400 rounded" + style={{ backgroundColor: '#F2EBE4' }} + /> +
+ + {/* Calculate Button */} + + + {/* Analysis Button - Shows the net benefit result */} +
+ +
+
+
+ ); +}; \ No newline at end of file diff --git a/frontend/src/main-page/grants/grant-list/GrantItem.tsx b/frontend/src/main-page/grants/grant-list/GrantItem.tsx index 42c1fb5..aa119b8 100644 --- a/frontend/src/main-page/grants/grant-list/GrantItem.tsx +++ b/frontend/src/main-page/grants/grant-list/GrantItem.tsx @@ -9,6 +9,7 @@ import { api } from "../../../api"; import { MdOutlinePerson2 } from "react-icons/md"; import Attachment from "../../../../../middle-layer/types/Attachment"; import NewGrantModal from "../new-grant/NewGrantModal"; +import { CostBenefitAnalysis } from '../grant-details/CostBenefitAnalysis'; interface GrantItemProps { grant: Grant; @@ -24,11 +25,13 @@ const GrantItem: React.FC = ({ const curGrant = grant; const [showNewGrantModal, setShowNewGrantModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); + const [netBenefit, setNetBenefit] = useState(null); const toggleExpand = () => { // Toggle edit mode off now that we are leaving this specific grant in view if (isExpanded) { toggleEdit(); + setNetBenefit(null); } setIsExpanded(!isExpanded); }; @@ -277,10 +280,10 @@ const GrantItem: React.FC = ({ : "N/A"} - {/*Timeline and Amount row*/} + {/*Timeline, Amount, Cost-benefit row*/}
{/*Timeline*/} -
+
{/*Amount */} -
+
- {/*End timeline and amount row */} + {/*Cost-benefit*/} +
+ +
+ {netBenefit !== null ? formatCurrency(netBenefit) : '--'} +
+
+ {/*End timeline, amount, cost-benefit row */}
{/*End column of gray labels */} @@ -574,8 +589,15 @@ const GrantItem: React.FC = ({ {/*End two main left right columns */} - {/*Description*/} -
+ {/*Cost Benefit Analysis and Description Row*/} +
+ {/* Cost Benefit Analysis */} +
+ +
+ + {/*Description */} +
diff --git a/frontend/src/main-page/grants/styles/CostBenefitAnalysis.css b/frontend/src/main-page/grants/styles/CostBenefitAnalysis.css new file mode 100644 index 0000000..d39ff77 --- /dev/null +++ b/frontend/src/main-page/grants/styles/CostBenefitAnalysis.css @@ -0,0 +1,23 @@ +.cost-benefit-analysis { + width: 100%; +} + +.cost-benefit-container { + min-height: 200px; +} + +.cost-benefit-analysis input[type="number"] { + appearance: textfield; + -moz-appearance: textfield; +} + +.cost-benefit-analysis input[type="number"]::-webkit-outer-spin-button, +.cost-benefit-analysis input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +.cost-benefit-analysis button:hover { + opacity: 0.9; + transition: opacity 0.2s; +} \ No newline at end of file diff --git a/frontend/src/styles/notification.css b/frontend/src/styles/notification.css index dd6e0f8..94d0c4d 100644 --- a/frontend/src/styles/notification.css +++ b/frontend/src/styles/notification.css @@ -45,6 +45,8 @@ max-height: 200px; overflow-y: auto; margin-top: 10px; + scrollbar-width: none; + -ms-overflow-style: none; } From 6c6b84172c5dcbca04982df38a0cd71472e991a0 Mon Sep 17 00:00:00 2001 From: prooflesben Date: Mon, 1 Dec 2025 21:03:45 -0500 Subject: [PATCH 2/4] Added in a default estimated completion time --- .../src/main-page/grants/grant-details/CostBenefitAnalysis.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx b/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx index e2747a6..aab629f 100644 --- a/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx +++ b/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx @@ -30,7 +30,7 @@ export const CostBenefitAnalysis: React.FC = ({ grant, const reportCount = grant.report_deadlines?.length ?? 0; const grantAmount = grant.amount; - const estimatedTime = grant.estimated_completion_time; + const estimatedTime = grant.estimated_completion_time | 5; console.log('Grant values - Amount:', grantAmount, 'EstTime:', estimatedTime, 'ReportCount:', reportCount); From 66f5b7a4e7a25133f88329bdc049f825e9e71fcb Mon Sep 17 00:00:00 2001 From: camila-carrillo Date: Tue, 2 Dec 2025 21:03:40 -0500 Subject: [PATCH 3/4] edits from standup complete, also added default estimated completion time --- .../grant-details/CostBenefitAnalysis.tsx | 24 ++++++++++-------- .../main-page/grants/grant-list/GrantItem.tsx | 25 +++++-------------- .../grants/styles/CostBenefitAnalysis.css | 4 +++ 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx b/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx index e2747a6..f39f13b 100644 --- a/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx +++ b/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx @@ -4,10 +4,9 @@ import '../styles/CostBenefitAnalysis.css'; interface CostBenefitAnalysisProps { grant: Grant; - onCalculate: (value: number | null) => void; } -export const CostBenefitAnalysis: React.FC = ({ grant, onCalculate }) => { +export const CostBenefitAnalysis: React.FC = ({ grant }) => { const [hourlyRate, setHourlyRate] = useState(''); const [timePerReport, setTimePerReport] = useState(''); const [netBenefit, setNetBenefit] = useState(null); @@ -30,7 +29,7 @@ export const CostBenefitAnalysis: React.FC = ({ grant, const reportCount = grant.report_deadlines?.length ?? 0; const grantAmount = grant.amount; - const estimatedTime = grant.estimated_completion_time; + const estimatedTime = grant.estimated_completion_time | 5; console.log('Grant values - Amount:', grantAmount, 'EstTime:', estimatedTime, 'ReportCount:', reportCount); @@ -40,7 +39,6 @@ export const CostBenefitAnalysis: React.FC = ({ grant, console.log('Final result:', result); setNetBenefit(result); - onCalculate(result); }; const formatCurrency = (amount: number): string => { @@ -81,7 +79,7 @@ export const CostBenefitAnalysis: React.FC = ({ grant, {/* Time Per Report Input */}
= ({ grant, {/* Analysis Button - Shows the net benefit result */} -
- +
diff --git a/frontend/src/main-page/grants/grant-list/GrantItem.tsx b/frontend/src/main-page/grants/grant-list/GrantItem.tsx index aa119b8..15a388b 100644 --- a/frontend/src/main-page/grants/grant-list/GrantItem.tsx +++ b/frontend/src/main-page/grants/grant-list/GrantItem.tsx @@ -25,13 +25,11 @@ const GrantItem: React.FC = ({ const curGrant = grant; const [showNewGrantModal, setShowNewGrantModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); - const [netBenefit, setNetBenefit] = useState(null); const toggleExpand = () => { // Toggle edit mode off now that we are leaving this specific grant in view if (isExpanded) { toggleEdit(); - setNetBenefit(null); } setIsExpanded(!isExpanded); }; @@ -280,10 +278,10 @@ const GrantItem: React.FC = ({ : "N/A"} - {/*Timeline, Amount, Cost-benefit row*/} + {/*Timeline and Amount row*/}
{/*Timeline*/} -
+
{/*Amount */} -
+
- {/*Cost-benefit*/} -
- -
- {netBenefit !== null ? formatCurrency(netBenefit) : '--'} -
-
- {/*End timeline, amount, cost-benefit row */} + + {/*End timeline and amount row */}
{/*End column of gray labels */} @@ -593,7 +580,7 @@ const GrantItem: React.FC = ({
{/* Cost Benefit Analysis */}
- +
{/*Description */} diff --git a/frontend/src/main-page/grants/styles/CostBenefitAnalysis.css b/frontend/src/main-page/grants/styles/CostBenefitAnalysis.css index d39ff77..a48e245 100644 --- a/frontend/src/main-page/grants/styles/CostBenefitAnalysis.css +++ b/frontend/src/main-page/grants/styles/CostBenefitAnalysis.css @@ -20,4 +20,8 @@ .cost-benefit-analysis button:hover { opacity: 0.9; transition: opacity 0.2s; +} + +.cost-benefit-analysis button::-webkit-scrollbar { + display: none; /* Chrome/Safari/Opera */ } \ No newline at end of file From 3184cd624d8ba31a5286ada1c9631c222431f328 Mon Sep 17 00:00:00 2001 From: Jane Kamata Date: Thu, 4 Dec 2025 22:28:41 -0500 Subject: [PATCH 4/4] Merging changes --- .../grant-details/CostBenefitAnalysis.tsx | 10 +- .../main-page/grants/grant-list/GrantItem.tsx | 158 +++++------------- 2 files changed, 44 insertions(+), 124 deletions(-) diff --git a/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx b/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx index f39f13b..d6569b3 100644 --- a/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx +++ b/frontend/src/main-page/grants/grant-details/CostBenefitAnalysis.tsx @@ -51,7 +51,7 @@ export const CostBenefitAnalysis: React.FC = ({ grant return (
-
@@ -89,7 +89,7 @@ export const CostBenefitAnalysis: React.FC = ({ grant console.log('Time per report changed to:', e.target.value); setTimePerReport(e.target.value); }} - className="w-full px-3 py-4 border border-gray-400 rounded" + className="w-full h-[42px] px-3 py-4 border border-gray-400 rounded-md" style={{ backgroundColor: '#F2EBE4' }} />
@@ -97,7 +97,7 @@ export const CostBenefitAnalysis: React.FC = ({ grant {/* Calculate Button */} - - setShowDeleteModal(false)} - onConfirmDelete={() => { - setShowDeleteModal(false); - }} - /> - - -
- - - {/*End expanded div */}