diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ad7c2..7d6365e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.0.1] - 2024-07-19 + +### Added +- Added the percentage display into the EQIP IRA map tips [#285](https://github.com/policy-design-lab/pdl-frontend/issues/285) + +### Changed +- Update the practice list to reflect current and predicted practices for EQIP IRA[#286](https://github.com/policy-design-lab/pdl-frontend/issues/286) + +### Fixed +- Fixed the NaN values in percentage table in the EQIP IRA page [#290](https://github.com/policy-design-lab/pdl-frontend/issues/290) + ## [1.0.0] - 2024-07-03 ### Added @@ -246,6 +257,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Map data json [#12](https://github.com/policy-design-lab/pdl-frontend/issues/12) - Final landing page changes for initial milestone [#15](https://github.com/policy-design-lab/pdl-frontend/issues/15) +[1.0.1]: https://github.com/policy-design-lab/pdl-frontend/compare/1.0.0...1.0.1 [1.0.0]: https://github.com/policy-design-lab/pdl-frontend/compare/0.20.0...1.0.0 [0.20.0]: https://github.com/policy-design-lab/pdl-frontend/compare/0.19.0...0.20.0 [0.19.0]: https://github.com/policy-design-lab/pdl-frontend/compare/0.18.0...0.19.0 diff --git a/package-lock.json b/package-lock.json index f31eecc..bd19a3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "policy-design-lab", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 57f104c..2148c66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "policy-design-lab", - "version": "1.0.0", + "version": "1.0.1", "description": "the front end of policy design lab", "repository": "https://github.com/policy-design-lab/pdl-frontend", "main": "src/app.tsx", diff --git a/src/components/ira/IRAMap.tsx b/src/components/ira/IRADollarMap.tsx similarity index 90% rename from src/components/ira/IRAMap.tsx rename to src/components/ira/IRADollarMap.tsx index 4ee7211..d7c4b10 100644 --- a/src/components/ira/IRAMap.tsx +++ b/src/components/ira/IRADollarMap.tsx @@ -34,7 +34,8 @@ const MapChart = ({ statePerformance, stateCodes, allStates, - colorScale + colorScale, + summary }) => { const classes = useStyles(); const geoUrl = "https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json"; @@ -72,6 +73,20 @@ const MapChart = ({ } }); } + // calculate the total of all practices and get the percentage of the practices + const totalNationwidePayment = practices.includes("Total") + ? summary[year].totalPaymentInDollars + : summary[year].practices + .filter((p) => practices.includes(p.practiceName)) + .reduce((acc, p) => acc + p.totalPaymentInDollars, 0); + let totalPaymentPercentageNationwide = ( + (practicePayment / totalNationwidePayment) * + 100 + ).toFixed(2); + totalPaymentPercentageNationwide = + totalPaymentPercentageNationwide === "NaN" + ? "0" + : totalPaymentPercentageNationwide; const hoverContent = (
@@ -80,14 +95,24 @@ const MapChart = ({ {practices.includes("Total") ? ( - - - - + + + + + + + + + + ) : ( {practices.length > 1 && @@ -175,17 +200,18 @@ const MapChart = ({ )} + + + + )} - - {/* - - - */}
- Total Benefits: - - ${ShortFormat(practicePayment, undefined, 2)} -
+ Total Benefits: + + ${ShortFormat(practicePayment, undefined, 2)} +
+ PCT. Nationwide: + + {totalPaymentPercentageNationwide}% +
+ {practices.length === 1 + ? "PCT. Nationwide:" + : "PCT. Nationwide (All Practices):"} + + {totalPaymentPercentageNationwide}% +
- PCT. Nationwide: - - {totalPaymentInPercentage} % -
{/* )} */} @@ -393,7 +419,7 @@ MapChart.propTypes = { maxValue: PropTypes.number }; -const IRAMap = ({ +const IRADollarMap = ({ subtitle, practices, predict, @@ -401,7 +427,8 @@ const IRAMap = ({ mapColor, statePerformance, stateCodes, - allStates + allStates, + summary }: { subtitle: string; practices: any; @@ -411,6 +438,7 @@ const IRAMap = ({ statePerformance: any; stateCodes: any; allStates: any; + summary: any; }): JSX.Element => { const [content, setContent] = useState(""); const quantizeArray: number[] = []; @@ -524,6 +552,7 @@ const IRAMap = ({ stateCodes={stateCodes} allStates={allStates} colorScale={colorScale} + summary={summary} />
@@ -550,4 +579,4 @@ const titleElement = ({ subtitle, year }): JSX.Element => { ); }; -export default IRAMap; +export default IRADollarMap; diff --git a/src/components/ira/IRAPercentageTable.tsx b/src/components/ira/IRAPercentageTable.tsx index c824f25..9e433ff 100644 --- a/src/components/ira/IRAPercentageTable.tsx +++ b/src/components/ira/IRAPercentageTable.tsx @@ -165,6 +165,7 @@ function IRAPercentageTable({ } else { newRecord[attr] = "0"; } + newRecord[attr] = newRecord[attr].includes("NaN") ? "0.00%" : newRecord[attr]; }); resultData.push(newRecord); }); diff --git a/src/components/ira/IRAPredictedDollarTable.tsx b/src/components/ira/IRAPredictedDollarTable.tsx index 2fd82f1..029bac4 100644 --- a/src/components/ira/IRAPredictedDollarTable.tsx +++ b/src/components/ira/IRAPredictedDollarTable.tsx @@ -95,7 +95,6 @@ function IRAPredictedDollarTable({ }); resultData.push(newRecord); }); - const columnPrep = []; columnPrep.push({ Header: "STATE", accessor: "state", sortType: compareWithAlphabetic }); const attrs = resultData[0] ? Object.keys(resultData[0]).filter((item) => item.toLowerCase() !== "state") : []; diff --git a/src/components/ira/IRAPredictedMap.tsx b/src/components/ira/IRAPredictedMap.tsx index f2adb83..3033790 100644 --- a/src/components/ira/IRAPredictedMap.tsx +++ b/src/components/ira/IRAPredictedMap.tsx @@ -36,7 +36,8 @@ const MapChart = ({ predictedPerformance, stateCodes, allStates, - colorScale + colorScale, + summary }) => { const classes = useStyles(); return ( @@ -69,6 +70,17 @@ const MapChart = ({ } }); } + const totalNationwidePayment = practices.includes("Total") + ? summary[year].predictedTotalPaymentInDollars + : summary[year].practices + .filter((p) => practices.includes(p.practiceName)) + .reduce((acc, p) => acc + p.predictedTotalPaymentInDollars, 0); + let totalPaymentPercentageNationwide = ( + (practicePayment / totalNationwidePayment) * + 100 + ).toFixed(2); + totalPaymentPercentageNationwide = + totalPaymentPercentageNationwide === "NaN" ? "0" : totalPaymentPercentageNationwide; const hoverContent = (
@@ -77,14 +89,24 @@ const MapChart = ({ {practices.includes("Total") ? ( - - - - + + + + + + + + + + ) : ( {practices.length > 1 && @@ -164,6 +186,16 @@ const MapChart = ({ )} + + + + )} @@ -250,7 +282,8 @@ const IRAPredictedMap = ({ mapColor, predictedPerformance, stateCodes, - allStates + allStates, + summary }: { subtitle: string; practices: any; @@ -260,6 +293,7 @@ const IRAPredictedMap = ({ predictedPerformance: any; stateCodes: any; allStates: any; + summary: any; }): JSX.Element => { const [content, setContent] = useState(""); const quantizeArray: number[] = []; @@ -339,6 +373,7 @@ const IRAPredictedMap = ({ stateCodes={stateCodes} allStates={allStates} colorScale={colorScale} + summary={summary} />
diff --git a/src/components/ira/IRAPredictedPercentageTable.tsx b/src/components/ira/IRAPredictedPercentageTable.tsx index d0a38a9..7f7e61a 100644 --- a/src/components/ira/IRAPredictedPercentageTable.tsx +++ b/src/components/ira/IRAPredictedPercentageTable.tsx @@ -35,7 +35,7 @@ function IRAPredictedPercentageTable({ hashmap[state][attribute] = attributeData; // Calculate percentage nationwide if (attribute === "predictedTotalPaymentInDollars") { - hashmap[state][`${attribute}PercentageNationwide`] = `${( + hashmap[state][`${attribute}PredictedPercentageNationwide`] = `${( (attributeData / summary[year].predictedTotalPaymentInDollars) * 100 ).toFixed(2)}%`; @@ -60,7 +60,7 @@ function IRAPredictedPercentageTable({ hashmap[state][new_key] = 0; practices_total[state][attribute] += 0; // Set percentage to 0 for missing practices - hashmap[state][`${new_key}PercentageNationwide`] = "0.00%"; + hashmap[state][`${new_key}PredictedPercentageNationwide`] = "0.00%"; }); } else { const attributeData = practiceData[0]; @@ -75,7 +75,7 @@ function IRAPredictedPercentageTable({ ? nationalPracticeData.predictedTotalPaymentInDollars : nationalPracticeData.totalPracticeInstanceCount; - hashmap[state][`${new_key}PercentageNationwide`] = `${( + hashmap[state][`${new_key}PredictedPercentageNationwide`] = `${( (attributeData[attribute] / nationalTotal) * 100 ).toFixed(2)}%`; @@ -85,9 +85,7 @@ function IRAPredictedPercentageTable({ }); let totalStatePayments = 0; - const totalStateInstances = 0; let totalNationalPayments = 0; - const totalNationalInstances = 0; practices.forEach((practice) => { const statePracticeData = stateData.practices.find((p) => p.practiceName === practice); @@ -108,13 +106,13 @@ function IRAPredictedPercentageTable({ if (!practices_total[state] || practices_total[state][attribute] === undefined) { if (!hashmap[state]) hashmap[state] = {}; hashmap[state][`All Practices: ${attribute}`] = 0; - hashmap[state][`All Practices: ${attribute}PercentageNationwide`] = "0.00%"; + hashmap[state][`All Practices: ${attribute}PredictedPercentageNationwide`] = "0.00%"; } else { hashmap[state][`All Practices: ${attribute}`] = practices_total[state][attribute]; // Calculate percentage for all practices combined if (attribute === "predictedTotalPaymentInDollars") { - hashmap[state][`All Practices: ${attribute}PercentageNationwide`] = `${( + hashmap[state][`All Practices: ${attribute}PredictedPercentageNationwide`] = `${( (totalStatePayments / totalNationalPayments) * 100 ).toFixed(2)}%`; @@ -128,14 +126,14 @@ function IRAPredictedPercentageTable({ Object.entries(hashmap[s]).forEach(([attr, value]) => { if (value) { if (attr.includes("Dollar")) { - if (attr.includes("PercentageNationwide")) { + if (attr.includes("PredictedPercentageNationwide")) { newRecord[attr] = value; // Keep as percentage } else { newRecord[attr] = `$${parseFloat(value).toLocaleString(undefined, { minimumFractionDigits: 2 })}`; } - } else if (attr.includes("PercentageNationwide")) { + } else if (attr.includes("PredictedPercentageNationwide")) { newRecord[attr] = value; // Keep as percentage } else { newRecord[attr] = parseFloat(value).toLocaleString(undefined, { minimumFractionDigits: 2 }); @@ -145,6 +143,7 @@ function IRAPredictedPercentageTable({ } else { newRecord[attr] = "0"; } + newRecord[attr] = newRecord[attr].includes("NaN") ? "0.00%" : newRecord[attr]; }); resultData.push(newRecord); }); @@ -152,7 +151,6 @@ function IRAPredictedPercentageTable({ const columnPrep = []; columnPrep.push({ Header: "STATE", accessor: "state", sortType: compareWithAlphabetic }); // filter out all data attributes with the word "percentage" in them - resultData = resultData.map( (item) => Object.keys(item) diff --git a/src/components/ira/TabPanel.tsx b/src/components/ira/TabPanel.tsx index 34b5fbf..319babb 100644 --- a/src/components/ira/TabPanel.tsx +++ b/src/components/ira/TabPanel.tsx @@ -20,7 +20,7 @@ import { styled } from "@mui/system"; import { CurrencyDollar, Percent } from "react-bootstrap-icons"; import useWindowSize from "../shared/WindowSizeHook"; -import IRAMap from "./IRAMap"; +import IRADollarMap from "./IRADollarMap"; import IRADollarTable from "./IRADollarTable"; import IRAPercentageTable from "./IRAPercentageTable"; import IRAPredictedMap from "./IRAPredictedMap"; @@ -55,13 +55,14 @@ function TabPanel({ const years = stateDistributionData ? Object.keys(stateDistributionData).map(Number) : []; const [updatedData, setUpdatedData] = useState(stateDistributionData); const [updatedPredictedData, setUpdatedPredictedData] = useState(predictedData); - const minYear = Math.min(...years); + const minYear = Math.min(...years).toString(); const maxYear = Math.max(...years); const [isPlaying, setIsPlaying] = useState(false); const [intervalId, setIntervalId] = useState | null>(null); - const [selectedYear, setSelectedYear] = useState(minYear); + const [selectedYear, setSelectedYear] = useState(minYear.toString()); const [selectedPredict, setSelectedPredict] = useState("Min"); - const [selectedPractices, setSelectedPractices] = useState([]); + const [selectedPractices, setSelectedPractices] = useState([]); + const [practices, setPractices] = useState([]); const mapColor = ["#F0F9E8", "#BAE4BC", "#7BCCC4", "#43A2CA", "#0868AC"]; // title II color const the = useTheme(); const isSmallScreen = useMediaQuery(the.breakpoints.down("sm")); @@ -82,8 +83,16 @@ function TabPanel({ setTab(newTab); } }; - const handleSwitchChange = (event) => { - setIsPredictionOn(event.target.checked); + const handleSwitchChange = (event: React.ChangeEvent) => { + const isOn = event.target.checked; + setIsPredictionOn(isOn); + const year = isOn ? predictedYear : minYear; + setSelectedYear(year); + const currentPractices = practiceNames[year].slice().sort((a, b) => a.localeCompare(b)); + const newPractices = ["Total", ...currentPractices]; + setPractices(newPractices); + const overlapped_practices: string[] = newPractices.filter((element) => selectedPractices.includes(element)); + setSelectedPractices(overlapped_practices.length === 0 ? ["Total"] : overlapped_practices); }; const handleYearChange = (event) => { @@ -93,8 +102,6 @@ function TabPanel({ value: year, label: year.toString() })); - const currentPractices = practiceNames[selectedYear].sort((a, b) => a.localeCompare(b)); // sort the practice list from a to z - const practices: any[] = ["Total", ...currentPractices]; const handlePracticeChange = (event) => { const { target: { value } @@ -157,14 +164,20 @@ function TabPanel({ if (years.length) { setSelectedYear(minYear); } - if (selectedPractices.length === 0) { - setSelectedPractices(["Total"]); + if (Object.keys(practiceNames).length > 0) { + const currentPractices = isPredictionOn + ? practiceNames[predictedYear].sort((a, b) => a.localeCompare(b)) + : practiceNames[minYear].sort((a, b) => a.localeCompare(b)); + setPractices(["Total", ...currentPractices]); + if (selectedPractices.length === 0) { + setSelectedPractices(["Total"]); + } } - }, [minYear, selectedPractices]); + }, [minYear, selectedYear, selectedPractices, practiceNames]); React.useEffect(() => { updateData(); + updatePredictedData(); }, [selectedPractices, selectedYear, isPredictionOn]); - return ( @@ -249,7 +265,7 @@ function TabPanel({ position: "relative" }} > - )} @@ -435,18 +452,20 @@ function TabPanel({ className="offset-sm-1" sx={{ display: tab !== 0 ? "none" : "div" }} > - + {Object.keys(updatedPredictedData).length > 0 && ( + + )} - + {Object.keys(updatedPredictedData).length > 0 && ( + + )} )}
- Total Predicted Benefits: - - ${ShortFormat(practicePayment, undefined, 2)} -
+ Total Predicted Benefits: + + ${ShortFormat(practicePayment, undefined, 2)} +
+ PCT. Nationwide: + + {totalPaymentPercentageNationwide}% +
+ {practices.length === 1 + ? "PCT. Nationwide:" + : "PCT. Nationwide (All Practices):"} + + {totalPaymentPercentageNationwide}% +