diff --git a/src/Assets/citizenshipFilterFormControlLabels.tsx b/src/Assets/citizenshipFilterFormControlLabels.tsx index 844b1ac20..429cf3dec 100644 --- a/src/Assets/citizenshipFilterFormControlLabels.tsx +++ b/src/Assets/citizenshipFilterFormControlLabels.tsx @@ -1,7 +1,6 @@ import { FormattedMessage } from 'react-intl'; export type CitizenLabels = - | 'citizen' | 'non_citizen' | 'green_card' | 'refugee' @@ -11,52 +10,36 @@ export type CitizenLabels = | 'gc_under19_pregnant_no5'; const citizenshipFilterFormControlLabels = { - citizen: ( - - ), non_citizen: ( - ), - green_card: ( - - ), - refugee: ( - - ), - gc_5plus: ( - ), + green_card: , + gc_5plus: , gc_18plus_no5: ( ), gc_under18_no5: ( ), gc_under19_pregnant_no5: ( + ), + refugee: ( + ), }; diff --git a/src/Components/FilterSection/CitizenshipPopover.css b/src/Components/FilterSection/CitizenshipPopover.css new file mode 100644 index 000000000..abc06d425 --- /dev/null +++ b/src/Components/FilterSection/CitizenshipPopover.css @@ -0,0 +1,3 @@ +.MuiFormControlLabel-root.gc-subcitizen-indentation { + margin-left: 1.25rem; +} diff --git a/src/Components/FilterSection/CitizenshipPopover.tsx b/src/Components/FilterSection/CitizenshipPopover.tsx index 4a444ad13..d08eca3c7 100644 --- a/src/Components/FilterSection/CitizenshipPopover.tsx +++ b/src/Components/FilterSection/CitizenshipPopover.tsx @@ -1,9 +1,11 @@ +import { FormattedMessage } from 'react-intl'; import FormControlLabel from '@mui/material/FormControlLabel'; import { Checkbox, Stack } from '@mui/material'; import { GridFilterItem, GridFilterOperator } from '@mui/x-data-grid'; import { UpdateFilterArg } from '../Results/Results'; import citizenshipFilterFormControlLabels from '../../Assets/citizenshipFilterFormControlLabels'; import type { CitizenLabels } from '../../Assets/citizenshipFilterFormControlLabels'; +import './CitizenshipPopover.css'; export const citizenshipFilterOperators: GridFilterOperator[] = [ { @@ -39,49 +41,126 @@ const CitizenshipPopover = ({ citizenshipFilterIsChecked, setCitizenshipFilterIsChecked, }: CitizenshipPopoverProps) => { + const hasAtLeastOneCitizenshipFilter = (currentCitizenshipFilters: Record) => { + const citizenshipFilterValues = Object.values(currentCitizenshipFilters); + + return citizenshipFilterValues.some((citizenshipFilterValue) => { + return citizenshipFilterValue === true; + }); + }; + const handleFilterSelect = (citizenshipType: CitizenLabels) => { const isChecked = citizenshipFilterIsChecked[citizenshipType]; - const updatedCitizenshipFilterIsChecked: Record = { + let updatedCitizenshipFilterIsChecked: Record = { ...citizenshipFilterIsChecked, [citizenshipType]: !isChecked, }; + + if (citizenshipType === 'green_card') { + // if the citizenshipType is `green_card`, then set green_card and all the gc_options to true or false + // i.e. green_card and all the gc_options should be the same when citizenshipType is `green_card` + updatedCitizenshipFilterIsChecked = { + ...updatedCitizenshipFilterIsChecked, + gc_5plus: !isChecked, + gc_18plus_no5: !isChecked, + gc_under18_no5: !isChecked, + gc_under19_pregnant_no5: !isChecked, + }; + } const typedUpdatedCitizenshipFilterIsChecked = Object.keys(updatedCitizenshipFilterIsChecked) as CitizenLabels[]; const selectedCitizenshipFilters = typedUpdatedCitizenshipFilterIsChecked.filter((citizenshipType) => { return updatedCitizenshipFilterIsChecked[citizenshipType]; }); - updateFilter({ - name: 'citizen', - filter: { - id: 1, - columnField: 'citizenship', - operatorValue: 'customCitizenshipOperator', - value: selectedCitizenshipFilters, - }, - }); + //update the MUI filter that is being passed to the citizenship column + if (hasAtLeastOneCitizenshipFilter(updatedCitizenshipFilterIsChecked)) { + updateFilter({ + name: 'citizen', + filter: { + id: 1, + columnField: 'citizenship', + operatorValue: 'customCitizenshipOperator', + value: selectedCitizenshipFilters, + }, + }); + } else { + // set the citizenship filter back to the default + updateFilter({ + name: 'citizen', + filter: { + id: 1, + columnField: 'citizenship', + operatorValue: 'customCitizenshipOperator', + value: ['citizen'], + }, + }); + } + //update citizenshipFilterIsChecked state setCitizenshipFilterIsChecked(updatedCitizenshipFilterIsChecked); }; const typedCitizenshipFilterIsChecked = Object.keys(citizenshipFilterIsChecked) as CitizenLabels[]; - const citizenshipCheckboxFilters = typedCitizenshipFilterIsChecked.map((citizenshipType) => { - return ( - handleFilterSelect(citizenshipType)} - /> - } - /> - ); - }); - - return {citizenshipCheckboxFilters}; + + const renderMainAndSubFilters = (citizenshipFilters: Record) => { + return typedCitizenshipFilterIsChecked.map((citizenshipType) => { + //here we need to add an sx prop to indent them if they're the gc_filters + const isGreenCardSubCitizenshipType = [ + 'gc_5plus', + 'gc_18plus_no5', + 'gc_under18_no5', + 'gc_under19_pregnant_no5', + ].includes(citizenshipType); + + return ( + handleFilterSelect(citizenshipType)} + /> + } + /> + ); + }); + }; + + const renderMainFilters = (citizenshipFilters: Record) => { + //green_card is false + const initialThreeFilters: CitizenLabels[] = ['non_citizen', 'green_card', 'refugee']; + return initialThreeFilters.map((initialFilter) => { + return ( + handleFilterSelect(initialFilter)} /> + } + /> + ); + }); + }; + + const renderCitizenshipFilters = (citizenshipFilters: Record) => { + if (citizenshipFilters.green_card) { + return renderMainAndSubFilters(citizenshipFilters); + } else { + return renderMainFilters(citizenshipFilters); + } + }; + + return ( + + + + + {renderCitizenshipFilters(citizenshipFilterIsChecked)} + + ); }; export default CitizenshipPopover; diff --git a/src/Components/FilterSection/FilterSection.tsx b/src/Components/FilterSection/FilterSection.tsx index ca0c0d8ea..01ff1d161 100644 --- a/src/Components/FilterSection/FilterSection.tsx +++ b/src/Components/FilterSection/FilterSection.tsx @@ -78,13 +78,12 @@ const FilterSection = ({ //this resets the radio buttons setCitizenshipFilterIsChecked({ non_citizen: false, - citizen: true, green_card: false, - refugee: false, gc_5plus: false, gc_18plus_no5: false, gc_under18_no5: false, gc_under19_pregnant_no5: false, + refugee: false, }); categoryState[1]('All Categories'); eligibilityState[1]('eligibleBenefits'); diff --git a/src/Components/Results/Results.tsx b/src/Components/Results/Results.tsx index c41918f1d..ee47e3690 100644 --- a/src/Components/Results/Results.tsx +++ b/src/Components/Results/Results.tsx @@ -23,7 +23,6 @@ import { GridValueFormatterParams, GridFilterItem, GridAlignment, - gridVisibleRowCountSelector, useGridApiRef, gridVisibleSortedRowEntriesSelector, } from '@mui/x-data-grid-pro'; @@ -64,13 +63,12 @@ const Results = () => { const [filterResultsButton, setFilterResultsButton] = useState('benefits'); const [citizenshipFilterIsChecked, setCitizenshipFilterIsChecked] = useState>({ non_citizen: false, - citizen: true, green_card: false, - refugee: false, gc_5plus: false, gc_18plus_no5: false, gc_under18_no5: false, gc_under19_pregnant_no5: false, + refugee: false, }); const categoryState = useState('All Categories'); const eligibilityState = useState('eligibleBenefits'); @@ -121,22 +119,49 @@ const Results = () => { category: false, }); - const [visibleRowCount, setVisibleRowCount] = useState(1); - const [totalEligibleDollarValue, setTotalEligibleDollarValue] = useState(0); + const [citizenshipRowCount, setCitizenshipRowCount] = useState(1); + const [totalCitizenshipDollarValue, setTotalCitizenshipDollarValue] = useState(0); + const [totalVisibleRowDollarValue, setTotalVisibleRowDollarValue] = useState(0); const apiRef = useGridApiRef(); useEffect(() => { - //use the visible row count so that we don't have to calculate the total eligible programs - //because doing so would require rewriting all of DGPro's filtering logic - //but the mui docs say that if you try to reference apiRef before the datagrid is rendered then it'll crash the app - //hence the if statements prevent us from accessing the apiRef before it's ready - if (apiRef && apiRef.current && Object.keys(apiRef.current).length) { - setVisibleRowCount(gridVisibleRowCountSelector(apiRef)); + let count = 0; + const eligiblePrograms = results.programs.filter((program) => program.eligible); + + //this will calculate the number of eligible programs/rows + eligiblePrograms.forEach((program) => { + const hasOverlap = program.legal_status_required.some((legalStatusType) => { + return filt.citizen.value.includes(legalStatusType); + }); + + if (hasOverlap) { + count += 1; + } + }); + + //used this instead of the real total to take into account the preschool category value cap at 8640 + const categoryValuesArray = Object.values(categoryValues(eligiblePrograms)); + const cappedCatValuesTotalDollarAmount = categoryValuesArray.reduce((acc, categoryAmt) => { + return (acc += categoryAmt); + }, 0); + setCitizenshipRowCount(count); + setTotalCitizenshipDollarValue(cappedCatValuesTotalDollarAmount); + + //this is for the category header + if (apiRef && apiRef.current && Object.keys(apiRef.current).length) { const updatedTotalEligibleDollarValue = gridVisibleSortedRowEntriesSelector(apiRef).reduce((acc, row) => { return (acc += row.model.value.value); }, 0); - setTotalEligibleDollarValue(updatedTotalEligibleDollarValue); + + //this is only to cap the totalVisibleRowDollarValue for preschool + const typedFiltCategory = filt.category as GridFilterItem; + if (typedFiltCategory.value === preschoolProgramCategory && updatedTotalEligibleDollarValue > 8640) { + setTotalVisibleRowDollarValue(8640); + return; + } + + setTotalVisibleRowDollarValue(updatedTotalEligibleDollarValue); } }, [results, filt]); @@ -190,28 +215,29 @@ const Results = () => { const preschoolProgramCategory = 'Child Care, Youth, and Education'; const categoryValues = (programs: Program[]) => { - const preschoolPrograms = [0, 0]; + const preschoolPrograms = { numOfPreSchoolPrograms: 0, totalEstVal: 0 }; //i=0 => num of preschool prog, i=1 => prog.est.value const categoryValues: { [key: string]: number } = {}; for (let program of programs) { + //add this category to the categoryValues dictionary if the key doesn't already exist if (categoryValues[program.category.default_message] === undefined) { categoryValues[program.category.default_message] = 0; } - if (program.legal_status_required.includes(filt.citizen.value)) { + const hasOverlap = program.legal_status_required.some((status) => { + return filt.citizen.value.includes(status); + }); + + if (hasOverlap) { categoryValues[program.category.default_message] += program.estimated_value; - if (preschoolProgramCategory == program.category.default_message) { - preschoolPrograms[0]++; - preschoolPrograms[1] += program.estimated_value; + if (program.category.default_message === preschoolProgramCategory) { + preschoolPrograms.numOfPreSchoolPrograms++; + preschoolPrograms.totalEstVal += program.estimated_value; } } } - for (const category in preschoolPrograms) { - if (Object.keys(preschoolPrograms).includes(category)) { - if (preschoolPrograms[1] > 8640 && preschoolPrograms[0] > 1) { - categoryValues[category] = 8640; - } - } + if (preschoolPrograms.totalEstVal > 8640 && preschoolPrograms.numOfPreSchoolPrograms > 1) { + categoryValues[preschoolProgramCategory] = 8640; } return categoryValues; @@ -323,7 +349,7 @@ const Results = () => { }; const displaySubheader = () => { - if (visibleRowCount === 0) { + if (citizenshipRowCount === 0) { return (

@@ -335,14 +361,14 @@ const Results = () => { return (

- {visibleRowCount} + {citizenshipRowCount} - ${totalEligibleDollarValue.toLocaleString()} + ${totalCitizenshipDollarValue.toLocaleString()} $ - {Math.round(totalEligibleDollarValue / 12).toLocaleString()} + {Math.round(totalCitizenshipDollarValue / 12).toLocaleString()} { failed_tests: TestMessage[]; category: string; navigators: ProgramNavigator[]; - citizenship: string; + citizenship: string[]; eligible: boolean; }; const DataGridRows = (programs: Program[]): DataRow[] => { @@ -402,7 +428,7 @@ const Results = () => { application_time: programs[i].estimated_application_time, delivery_time: programs[i].estimated_delivery_time, description: programs[i].description, - citizenship: '', + citizenship: [], application_link: programs[i].apply_button_link, passed_tests: programs[i].passed_tests, failed_tests: programs[i].failed_tests, @@ -651,7 +677,7 @@ const Results = () => { - ${totalEligibleDollarValue.toLocaleString()}{' '} + ${totalVisibleRowDollarValue.toLocaleString()}{' '} diff --git a/src/Types/Results.ts b/src/Types/Results.ts index 2972606ad..e56d50532 100644 --- a/src/Types/Results.ts +++ b/src/Types/Results.ts @@ -28,7 +28,7 @@ export type Program = { value_type: Translation; learn_more_link: Translation; apply_button_link: Translation; - legal_status_required: string; + legal_status_required: string[]; category: Translation; eligible: boolean; failed_tests: TestMessage[];