From c743e56f32ae4d13ed257d4871984d577cbb4faa Mon Sep 17 00:00:00 2001 From: "Wendy(Pengyin) Shan" Date: Mon, 19 Aug 2024 08:57:53 -0500 Subject: [PATCH] #299, #313: create and improve CSP IRA page --- CHANGELOG.md | 7 ++++ src/components/ira/IRADollarMap.tsx | 9 +++-- src/components/ira/IRAPredictedMap.tsx | 9 +++-- src/components/ira/TabPanel.tsx | 2 +- src/components/shared/ConvertionFormats.tsx | 10 +++-- src/components/shared/DrawLegend.tsx | 8 ++-- src/pages/IRAPage.tsx | 41 +++++++++++++++++---- 7 files changed, 63 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6928300..dabb598 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [unreleased] +### Added +- Added the CSP session to the IRA page [#299](https://github.com/policy-design-lab/pdl-frontend/issues/299) +- Improved the map legend to cover more maps on the IRA page [#313](https://github.com/policy-design-lab/pdl-frontend/issues/313) + +## [1.0.4] - 2024-08-16 + ### Added - Added the top info session for IRA page [#309](https://github.com/policy-design-lab/pdl-frontend/issues/309) - Added "Other CSP" statues and related categories to the CSP pages [#303](https://github.com/policy-design-lab/pdl-frontend/issues/303) @@ -280,6 +286,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.4]: https://github.com/policy-design-lab/pdl-frontend/compare/1.0.3...1.0.4 [1.0.3]: https://github.com/policy-design-lab/pdl-frontend/compare/1.0.2...1.0.3 [1.0.2]: https://github.com/policy-design-lab/pdl-frontend/compare/1.0.1...1.0.2 [1.0.1]: https://github.com/policy-design-lab/pdl-frontend/compare/1.0.0...1.0.1 diff --git a/src/components/ira/IRADollarMap.tsx b/src/components/ira/IRADollarMap.tsx index d7c4b10..bde90a3 100644 --- a/src/components/ira/IRADollarMap.tsx +++ b/src/components/ira/IRADollarMap.tsx @@ -484,13 +484,14 @@ const IRADollarMap = ({ // since IRA data is not predictable in legend config, separate the scale to five equal parts const sortedData = quantizeArray.sort((a, b) => a - b); const numIntervals = 5; - const intervalSize = Math.floor(sortedData.length / numIntervals); - const thresholds = []; + const nonZeroData = sortedData.filter((value) => value > 0); + const intervalSize = Math.ceil(nonZeroData.length / numIntervals); + const thresholds: number[] = []; for (let i = 1; i < numIntervals; i += 1) { const thresholdIndex = i * intervalSize - 1; - thresholds.push(sortedData[thresholdIndex]); + const adjustedIndex = Math.min(thresholdIndex, nonZeroData.length - 1); + thresholds.push(nonZeroData[adjustedIndex]); } - if (thresholds[0] === 0) thresholds.unshift(1000); const colorScale = d3.scaleThreshold().domain(thresholds).range(mapColor); // For IRA, only if all practices are zero, the state will be colored as grey let zeroPoints = []; diff --git a/src/components/ira/IRAPredictedMap.tsx b/src/components/ira/IRAPredictedMap.tsx index 3033790..debd52e 100644 --- a/src/components/ira/IRAPredictedMap.tsx +++ b/src/components/ira/IRAPredictedMap.tsx @@ -321,13 +321,14 @@ const IRAPredictedMap = ({ // since IRA data is not predictable in legend config, separate the scale to five equal parts const sortedData = quantizeArray.sort((a, b) => a - b); const numIntervals = 5; - const intervalSize = Math.floor(sortedData.length / numIntervals); - const thresholds = []; + const nonZeroData = sortedData.filter((value) => value > 0); + const intervalSize = Math.ceil(nonZeroData.length / numIntervals); + const thresholds: number[] = []; for (let i = 1; i < numIntervals; i += 1) { const thresholdIndex = i * intervalSize - 1; - thresholds.push(sortedData[thresholdIndex]); + const adjustedIndex = Math.min(thresholdIndex, nonZeroData.length - 1); + thresholds.push(nonZeroData[adjustedIndex]); } - if (thresholds[0] === 0) thresholds.unshift(1000); const colorScale = d3.scaleThreshold().domain(thresholds).range(mapColor); // For IRA, only if all practices are zero, the state will be colored as grey let zeroPoints = []; diff --git a/src/components/ira/TabPanel.tsx b/src/components/ira/TabPanel.tsx index 319babb..512ba83 100644 --- a/src/components/ira/TabPanel.tsx +++ b/src/components/ira/TabPanel.tsx @@ -55,7 +55,7 @@ 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).toString(); + const minYear = 2023; const maxYear = Math.max(...years); const [isPlaying, setIsPlaying] = useState(false); const [intervalId, setIntervalId] = useState | null>(null); diff --git a/src/components/shared/ConvertionFormats.tsx b/src/components/shared/ConvertionFormats.tsx index 331adc3..d991018 100644 --- a/src/components/shared/ConvertionFormats.tsx +++ b/src/components/shared/ConvertionFormats.tsx @@ -36,10 +36,14 @@ export function ShortFormat(labelValue, position?: number, decimal?: number) { } if (decimalPart.length > 0) { const numberPart = result.match(/^(.*).$/)?.[1]; + if (position === -1) { + return result; + } if (position === 0) { - result = result.replace(/^(.*)(.)$/, `${Math.floor(Number(numberPart))}$2`); - } else if (position !== undefined) { - result = result.replace(/^(.*)(.)$/, `${Math.ceil(Number(numberPart))}$2`); + return result.replace(/^(.*)(.)$/, `${Math.floor(Number(numberPart))}$2`); + } + if (position !== undefined) { + return result.replace(/^(.*)(.)$/, `${Math.ceil(Number(numberPart))}$2`); } } return result; diff --git a/src/components/shared/DrawLegend.tsx b/src/components/shared/DrawLegend.tsx index d3776ee..7421014 100644 --- a/src/components/shared/DrawLegend.tsx +++ b/src/components/shared/DrawLegend.tsx @@ -112,10 +112,10 @@ export default function DrawLegend({ return `${Math.round(cut_points[i] * 100)}%`; } if (i === 0 && !notDollar) { - const res = ShortFormat(Math.round(cut_points[i]), i); + const res = ShortFormat(cut_points[i].toFixed(2), -1, 2); return res.indexOf("-") < 0 ? `$${res}` : `-$${res.substring(1)}`; } - return ShortFormat(Math.round(cut_points[i]), i, 1); + return ShortFormat(cut_points[i].toFixed(2), -1, 2); }); } else { baseSVG @@ -136,10 +136,10 @@ export default function DrawLegend({ return `${Math.round(cut_points[i] * 100)}%`; } if (i === 0 && !notDollar) { - const res = ShortFormat(Math.round(cut_points[i]), i); + const res = ShortFormat(cut_points[i].toFixed(2), -1, 2); return res.indexOf("-") < 0 ? `$${res}` : `-$${res.substring(1)}`; } - return ShortFormat(Math.round(cut_points[i]), i, 2); + return ShortFormat(cut_points[i].toFixed(2), -1, 2); }); } if (emptyState.length !== 0) { diff --git a/src/pages/IRAPage.tsx b/src/pages/IRAPage.tsx index bc34d99..cc10389 100644 --- a/src/pages/IRAPage.tsx +++ b/src/pages/IRAPage.tsx @@ -1,4 +1,4 @@ -import { Box, Typography, Grid, Tabs, Button } from "@mui/material"; +import { Box, Typography, Grid, Tabs, Button, Divider } from "@mui/material"; import * as React from "react"; import { config } from "../app.config"; import { convertAllState, getJsonDataFromUrl } from "../utils/apiutil"; @@ -21,6 +21,10 @@ export default function IRAPage(): JSX.Element { const [eqipPredictedData, setEqipPredictedData] = React.useState({}); const [eqipSummaryData, setEqipSummaryData] = React.useState({}); const [eqipPracticeNames, setEqipPracticeNames] = React.useState({}); + const [cspStateDistributionData, setCspStateDistributionData] = React.useState({}); + const [cspPredictedData, setCspPredictedData] = React.useState({}); + const [cspSummaryData, setCspSummaryData] = React.useState({}); + const [cspPracticeNames, setCspPracticeNames] = React.useState({}); const [stateCodesData, setStateCodesData] = React.useState({}); const [stateCodesArray, setStateCodesArray] = React.useState({}); const [allStatesData, setAllStatesData] = React.useState([]); @@ -37,14 +41,22 @@ export default function IRAPage(): JSX.Element { eqipStateDistributionResponse, eqipPredictedResponse, eqipSummaryResponse, - eqipPracticeNamesResponse + eqipPracticeNamesResponse, + cspStateDistributionResponse, + cspPredictedResponse, + cspSummaryResponse, + cspPracticeNamesResponse ] = await Promise.all([ getJsonDataFromUrl(`${config.apiUrl}/states`), getJsonDataFromUrl(`${config.apiUrl}/statecodes`), getJsonDataFromUrl(`${config.apiUrl}/titles/title-ii/programs/eqip-ira/state-distribution`), getJsonDataFromUrl(`${config.apiUrl}/titles/title-ii/programs/eqip-ira/predicted`), getJsonDataFromUrl(`${config.apiUrl}/titles/title-ii/programs/eqip-ira/summary`), - getJsonDataFromUrl(`${config.apiUrl}/titles/title-ii/programs/eqip-ira/practice-names`) + getJsonDataFromUrl(`${config.apiUrl}/titles/title-ii/programs/eqip-ira/practice-names`), + getJsonDataFromUrl(`${config.apiUrl}/titles/title-ii/programs/csp-ira/state-distribution`), + getJsonDataFromUrl(`${config.apiUrl}/titles/title-ii/programs/csp-ira/predicted`), + getJsonDataFromUrl(`${config.apiUrl}/titles/title-ii/programs/csp-ira/summary`), + getJsonDataFromUrl(`${config.apiUrl}/titles/title-ii/programs/csp-ira/practice-names`) ]); setAllStatesData(allStatesResponse); setStateCodesArray(stateCodesResponse); @@ -53,6 +65,10 @@ export default function IRAPage(): JSX.Element { setEqipPredictedData(eqipPredictedResponse); setEqipSummaryData(eqipSummaryResponse); setEqipPracticeNames(eqipPracticeNamesResponse); + setCspStateDistributionData(cspStateDistributionResponse); + setCspPredictedData(cspPredictedResponse); + setCspSummaryData(cspSummaryResponse); + setCspPracticeNames(cspPracticeNamesResponse); setIsDataReady(true); } catch (error) { console.error("Error fetching data:", error); @@ -167,9 +183,9 @@ export default function IRAPage(): JSX.Element { sx={{ mb: 1 }} > EQIP} customSx={tabStyle} selectedSX={selectedStyle} /> + + CSP} customSx={tabStyle} selectedSX={selectedStyle} /> {/* - CSP} customSx={tabStyle} selectedSX={selectedStyle} /> - RCPP} customSx={tabStyle} selectedSX={selectedStyle} /> ACEP} customSx={tabStyle} selectedSX={selectedStyle} /> */} @@ -191,8 +207,19 @@ export default function IRAPage(): JSX.Element { allStates={allStatesData} summaryData={eqipSummaryData} /> - {/* - + + {/* */} ) : (