From 6f71e2f4d871c41735f1211c4e38c61f2ef10210 Mon Sep 17 00:00:00 2001 From: acouch Date: Sat, 29 Jun 2024 18:50:18 -0400 Subject: [PATCH] Add funding filter and update search filters --- .../SearchFilterAccordion.tsx | 148 +++++ .../SearchFilterCheckbox.tsx | 40 ++ .../SearchFilterSection.tsx | 109 ++++ .../SearchFilterSection/SectionLinkCount.tsx | 15 + .../SearchFilterSection/SectionLinkLabel.tsx | 34 ++ .../SearchFilterToggleAll.tsx | 48 ++ .../filterJSONLists/agencyFilterList.ts | 508 ++++++++++++++++++ .../look/SearchFilterFundingInstrument.tsx | 47 ++ .../[locale]/look/SearchOpportunityStatus.tsx | 17 +- frontend/src/app/[locale]/look/page.tsx | 14 +- frontend/src/hooks/useSearchParamUpdater.ts | 2 +- 11 files changed, 968 insertions(+), 14 deletions(-) create mode 100644 frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterAccordion.tsx create mode 100644 frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterCheckbox.tsx create mode 100644 frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SearchFilterSection.tsx create mode 100644 frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SectionLinkCount.tsx create mode 100644 frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SectionLinkLabel.tsx create mode 100644 frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterToggleAll.tsx create mode 100644 frontend/src/app/[locale]/look/SearchFilterAccordion/filterJSONLists/agencyFilterList.ts create mode 100644 frontend/src/app/[locale]/look/SearchFilterFundingInstrument.tsx diff --git a/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterAccordion.tsx b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterAccordion.tsx new file mode 100644 index 000000000..0ff323879 --- /dev/null +++ b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterAccordion.tsx @@ -0,0 +1,148 @@ +import { Accordion } from "@trussworks/react-uswds"; +import { QueryParamKey } from "src/types/search/searchResponseTypes"; +import SearchFilterCheckbox from "./SearchFilterCheckbox"; +import SearchFilterSection from "./SearchFilterSection/SearchFilterSection"; +import SearchFilterToggleAll from "./SearchFilterToggleAll"; +import { QueryContext } from "src/app/[locale]/look/QueryProvider"; +import { useSearchParamUpdater2 } from "src/hooks/useSearchParamUpdater"; +import { useContext } from "react"; + +export interface AccordionItemProps { + title: React.ReactNode | string; + content: React.ReactNode; + expanded: boolean; + id: string; + headingLevel: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; + className?: string; + // handleToggle?: (event: React.MouseEvent) => void; +} + +export interface FilterOption { + id: string; + label: string; + value: string; + isChecked?: boolean; + children?: FilterOption[]; +} + +interface SearchFilterAccordionProps { + options: FilterOption[]; + title: string; // Title in header of accordion + query: Set; + queryParamKey: QueryParamKey; // Ex - In query params, search?{key}=first,second,third +} + +export function SearchFilterAccordion({ + options, + title, + queryParamKey, + query, +}: SearchFilterAccordionProps) { + const { queryTerm } = useContext(QueryContext); + const { updateQueryParams } = useSearchParamUpdater2(); + const totalCheckedCount = query.size + + + const getAccordionTitle = () => ( + <> + {title} + {!!totalCheckedCount && ( + + {totalCheckedCount} + + )} + + ); + + // This should just get the current params and push the update like everything else + const toggleSelectAll = (all: boolean) => { + // TODO: need to clear this. + if (all) { + + } + else { + + } + + } + + const isSectionAllSelected = (options: FilterOption[], params: Set): boolean => { + return false; + } + + const isSectionNoneSelected = (options: FilterOption[], params: Set): boolean => { + return false; + } + + const toggleOptionChecked = (value: string, isChecked: boolean) => { + const updated = new Set(query) + isChecked + ? updated.add(value) + : updated.delete(value); + const key = queryParamKey; + updateQueryParams(updated, key, queryTerm); + } + + const isAllSelected = false; + const isNoneSelected = true; + + const getAccordionContent = () => ( + <> + toggleSelectAll(true)} + onClearAll={() => toggleSelectAll(false)} + isAllSelected={isAllSelected} + isNoneSelected={isNoneSelected} + /> + +
    + {options.map((option) => ( +
  • + {/* If we have children, show a "section" dropdown, otherwise show just a checkbox */} + {option.children ? ( + // SearchFilterSection will map over all children of this option + + ) : ( + + )} +
  • + ))} +
+ + ); + + const accordionOptions: AccordionItemProps[] = [ + { + title: getAccordionTitle(), + content: getAccordionContent(), + expanded: false, + id: `funding-instrument-filter-${queryParamKey}`, + headingLevel: "h2", + }, + ]; + + return ( + + ); +} + +export default SearchFilterAccordion; diff --git a/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterCheckbox.tsx b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterCheckbox.tsx new file mode 100644 index 000000000..14c2bc9ff --- /dev/null +++ b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterCheckbox.tsx @@ -0,0 +1,40 @@ +"use client"; + +import FilterCheckbox from "src/components/FilterCheckbox"; +import { FilterOption } from "./SearchFilterAccordion"; + +interface SearchFilterCheckboxProps { + option: FilterOption; + updateCheckedOption: (optionId: string, isChecked: boolean) => void; + accordionTitle: string; + query: Set; + +} + +const SearchFilterCheckbox: React.FC = ({ + option, + updateCheckedOption, + accordionTitle, + query, +}) => { + const handleChange = (event: React.ChangeEvent) => { + const checked = event.target.checked; + updateCheckedOption(event.target.value, checked); + }; + + const getNameAttribute = () => + accordionTitle === "Agency" ? `agency-${option.id}` : option.id; + + return ( + + ); +}; + +export default SearchFilterCheckbox; diff --git a/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SearchFilterSection.tsx b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SearchFilterSection.tsx new file mode 100644 index 000000000..9f582332f --- /dev/null +++ b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SearchFilterSection.tsx @@ -0,0 +1,109 @@ +"use client"; + +import { useEffect, useState } from "react"; + +import { FilterOption } from "../SearchFilterAccordion"; +import SearchFilterCheckbox from "../SearchFilterCheckbox"; +import SearchFilterToggleAll from "../SearchFilterToggleAll"; +import SectionLinkCount from "./SectionLinkCount"; +import SectionLinkLabel from "./SectionLinkLabel"; + +interface SearchFilterSectionProps { + option: FilterOption; + updateCheckedOption: (optionId: string, isChecked: boolean) => void; + toggleSelectAll: (isSelected: boolean, sectionId: string) => void; + accordionTitle: string; + isSectionAllSelected: boolean; + isSectionNoneSelected: boolean; + query: Set +} + +const SearchFilterSection: React.FC = ({ + option, + updateCheckedOption, + toggleSelectAll, + accordionTitle, + query, + isSectionAllSelected, + isSectionNoneSelected, +}) => { + const [childrenVisible, setChildrenVisible] = useState(false); + + // TODO: Set this number per state/query params + const [sectionCount, setSectionCount] = useState(0); + + const handleSelectAll = () => { + toggleSelectAll(true, option.id); + }; + + const handleClearAll = () => { + toggleSelectAll(false, option.id); + }; + + useEffect(() => { + if (option.children) { + const newCount = option.children.filter( + (child) => child.isChecked, + ).length; + setSectionCount(newCount); + } + }, [option.children]); + + const getHiddenName = (name: string) => + accordionTitle === "Agency" ? `agency-${name}` : name; + + return ( +
+ + {childrenVisible ? ( +
+ +
    + {option.children?.map((child) => ( +
  • + +
  • + ))} +
+
+ ) : ( + // Collapsed sections won't send checked values to the server action. + // So we need hidden inputs. + option.children?.map((child) => + child.isChecked ? ( + + ) : null, + ) + )} +
+ ); +}; + +export default SearchFilterSection; diff --git a/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SectionLinkCount.tsx b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SectionLinkCount.tsx new file mode 100644 index 000000000..d3ac6bf1a --- /dev/null +++ b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SectionLinkCount.tsx @@ -0,0 +1,15 @@ +export default function SectionLinkCount({ + sectionCount, +}: { + sectionCount: number; +}) { + return ( + + {!!sectionCount && ( + + {sectionCount} + + )} + + ); +} diff --git a/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SectionLinkLabel.tsx b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SectionLinkLabel.tsx new file mode 100644 index 000000000..a894f3a42 --- /dev/null +++ b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterSection/SectionLinkLabel.tsx @@ -0,0 +1,34 @@ +import { FilterOption } from "../SearchFilterAccordion"; +import { Icon } from "@trussworks/react-uswds"; + +export default function SectionLinkLabel({ + childrenVisible, + option, +}: { + childrenVisible: boolean; + option: FilterOption; +}) { + // When the arrow is down, the section is collapsed, and we can expand the section + // When the arrow is up, the section is expanded, and we can collapse the section + const ariaLabel = childrenVisible ? "Collapse section" : "Expand section"; + + return ( + + {childrenVisible ? ( + + ) : ( + + )} + {option.label}{" "} + {/* Assuming you want to display the option's label instead of its ID */} + + ); +} diff --git a/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterToggleAll.tsx b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterToggleAll.tsx new file mode 100644 index 000000000..df204e89d --- /dev/null +++ b/frontend/src/app/[locale]/look/SearchFilterAccordion/SearchFilterToggleAll.tsx @@ -0,0 +1,48 @@ +"use client"; + +interface SearchFilterToggleAllProps { + isAllSelected: boolean; + isNoneSelected: boolean; + onSelectAll?: () => void; + onClearAll?: () => void; +} + +const SearchFilterToggleAll: React.FC = ({ + onSelectAll, + onClearAll, + isAllSelected, + isNoneSelected, +}) => { + return ( +
+
+ +
+
+ +
+
+ ); +}; + +export default SearchFilterToggleAll; diff --git a/frontend/src/app/[locale]/look/SearchFilterAccordion/filterJSONLists/agencyFilterList.ts b/frontend/src/app/[locale]/look/SearchFilterAccordion/filterJSONLists/agencyFilterList.ts new file mode 100644 index 000000000..e8b5ab954 --- /dev/null +++ b/frontend/src/app/[locale]/look/SearchFilterAccordion/filterJSONLists/agencyFilterList.ts @@ -0,0 +1,508 @@ +import { FilterOption } from "../SearchFilterAccordion"; + +export const agencyFilterList: FilterOption[] = [ + { + id: "ARPAH", + label: "Advanced Research Projects Agency for Health (ARPAH)", + value: "ARPAH", + }, + { + id: "USAID", + label: "Agency for International Development (USAID)", + value: "USAID", + children: [ + { + id: "USAID-AFG", + label: "Afghanistan USAID-Kabul (USAID-AFG)", + value: "USAID-AFG", + }, + { + id: "USAID", + label: "Agency for International Development (USAID)", + value: "USAID", + }, + { + id: "USAID-ARM", + label: "Armenia USAID-Yerevan (USAID-ARM)", + value: "USAID-ARM", + }, + { + id: "USAID-AZE", + label: "Azerbaijan USAID-Baku (USAID-AZE)", + value: "USAID-AZE", + }, + { + id: "USAID-BAN", + label: "Bangladesh USAID-Dhaka (USAID-BAN)", + value: "USAID-BAN", + }, + { + id: "USAID-BEN", + label: "Benin USAID-Cotonou (USAID-BEN)", + value: "USAID-BEN", + }, + ], + }, + { + id: "AC", + label: "AmeriCorps (AC)", + value: "AC", + }, + { + id: "DC", + label: "Denali Commission (DC)", + value: "DC", + }, + { + id: "USDA", + label: "Department of Agriculture (USDA)", + value: "USDA", + children: [ + { + id: "USDA-AMS", + label: "Agricultural Marketing Service (USDA-AMS)", + value: "USDA-AMS", + }, + { + id: "USDA-FNS1", + label: "Food and Nutrition Service (USDA-FNS1)", + value: "USDA-FNS1", + }, + ], + }, + { + id: "DOC", + label: "Department of Commerce (DOC)", + value: "DOC", + children: [ + { + id: "DOC-DOCNOAAERA", + label: "DOC NOAA - ERA Production (DOC-DOCNOAAERA)", + value: "DOC-DOCNOAAERA", + }, + { + id: "DOC-EDA", + label: "Economic Development Administration (DOC-EDA)", + value: "DOC-EDA", + }, + { + id: "DOC-NIST", + label: "National Institute of Standards and Technology (DOC-NIST)", + value: "DOC-NIST", + }, + ], + }, + { + id: "DOD", + label: "Department of Defense (DOD)", + value: "DOD", + children: [ + { + id: "DOD-AMC-ACCAPGN", + label: "ACC APG - Natick (DOD-AMC-ACCAPGN)", + value: "DOD-AMC-ACCAPGN", + }, + { + id: "DOD-AMC-ACCAPGD", + label: "ACC-APG-Detrick (DOD-AMC-ACCAPGD)", + value: "DOD-AMC-ACCAPGD", + }, + { + id: "DOD-AFRL-AFRLDET8", + label: "AFRL Kirtland AFB (DOD-AFRL-AFRLDET8)", + value: "DOD-AFRL-AFRLDET8", + }, + { + id: "DOD-AFRL", + label: "Air Force -- Research Lab (DOD-AFRL)", + value: "DOD-AFRL", + }, + { + id: "DOD-USAFA", + label: "Air Force Academy (DOD-USAFA)", + value: "DOD-USAFA", + }, + { + id: "DOD-AFOSR", + label: "Air Force Office of Scientific Research (DOD-AFOSR)", + value: "DOD-AFOSR", + }, + { + id: "DOD-DARPA-BTO", + label: "DARPA - Biological Technologies Office (DOD-DARPA-BTO)", + value: "DOD-DARPA-BTO", + }, + ], + }, + { + id: "ED", + label: "Department of Education (ED)", + value: "ED", + }, + { + id: "DOE", + label: "Department of Energy (DOE)", + value: "DOE", + children: [ + { + id: "DOE-ARPAE", + label: "Advanced Research Projects Agency Energy (DOE-ARPAE)", + value: "DOE-ARPAE", + }, + { + id: "DOE-GFO", + label: "Golden Field Office (DOE-GFO)", + value: "DOE-GFO", + }, + { + id: "DOE-01", + label: "Headquarters (DOE-01)", + value: "DOE-01", + }, + ], + }, + { + id: "PAMS", + label: "Department of Energy - Office of Science (PAMS)", + value: "PAMS", + children: [ + { + id: "PAMS-SC", + label: "Office of Science (PAMS-SC)", + value: "PAMS-SC", + }, + ], + }, + { + id: "HHS", + label: "Department of Health and Human Services (HHS)", + value: "HHS", + children: [ + { + id: "HHS-ACF-FYSB", + label: + "Administration for Children & Families - ACYF/FYSB (HHS-ACF-FYSB)", + value: "HHS-ACF-FYSB", + }, + { + id: "HHS-ACF", + label: "Administration for Children and Families (HHS-ACF)", + value: "HHS-ACF", + }, + { + id: "HHS-ACF-CB", + label: + "Administration for Children and Families - ACYF/CB (HHS-ACF-CB)", + value: "HHS-ACF-CB", + }, + ], + }, + { + id: "DHS", + label: "Department of Homeland Security (DHS)", + value: "DHS", + children: [ + { + id: "DHS-DHS", + label: "Department of Homeland Security - FEMA (DHS-DHS)", + value: "DHS-DHS", + }, + { + id: "DHS-OPO", + label: "Office of Procurement Operations - Grants Division (DHS-OPO)", + value: "DHS-OPO", + }, + { + id: "DHS-USCG", + label: "United States Coast Guard (DHS-USCG)", + value: "DHS-USCG", + }, + ], + }, + { + id: "HUD", + label: "Department of Housing and Urban Development (HUD)", + value: "HUD", + }, + { + id: "USDOJ", + label: "Department of Justice (USDOJ)", + value: "USDOJ", + children: [ + { + id: "USDOJ-OJP-BJA", + label: "Bureau of Justice Assistance (USDOJ-OJP-BJA)", + value: "USDOJ-OJP-BJA", + }, + { + id: "USDOJ-OJP-COPS", + label: "Community Oriented Policing Services (USDOJ-OJP-COPS)", + value: "USDOJ-OJP-COPS", + }, + ], + }, + { + id: "DOL", + label: "Department of Labor (DOL)", + value: "DOL", + children: [ + { + id: "DOL-ETA-ILAB", + label: "Bureau of International Labor Affairs (DOL-ETA-ILAB)", + value: "DOL-ETA-ILAB", + }, + { + id: "DOL-ETA-CEO", + label: "Chief Evaluation Office (DOL-ETA-CEO)", + value: "DOL-ETA-CEO", + }, + ], + }, + { + id: "DOS", + label: "Department of State (DOS)", + value: "DOS", + children: [ + { + id: "DOS-NEA-AC", + label: "Assistance Coordination (DOS-NEA-AC)", + value: "DOS-NEA-AC", + }, + { + id: "DOS-DRL", + label: "Bureau of Democracy Human Rights and Labor (DOS-DRL)", + value: "DOS-DRL", + }, + { + id: "DOS-ECA", + label: "Bureau Of Educational and Cultural Affairs (DOS-ECA)", + value: "DOS-ECA", + }, + ], + }, + { + id: "DOI", + label: "Department of the Interior (DOI)", + value: "DOI", + children: [ + { + id: "DOI-BIA", + label: "Bureau of Indian Affairs (DOI-BIA)", + value: "DOI-BIA", + }, + { + id: "DOI-BLM", + label: "Bureau of Land Management (DOI-BLM)", + value: "DOI-BLM", + }, + { + id: "DOI-BOR", + label: "Bureau of Reclamation (DOI-BOR)", + value: "DOI-BOR", + }, + ], + }, + { + id: "USDOT", + label: "Department of the Treasury (USDOT)", + value: "USDOT", + children: [ + { + id: "USDOT-ORP", + label: "Office of Capital Access (USDOT-ORP)", + value: "USDOT-ORP", + }, + { + id: "USDOT-DO-SIPPRA", + label: "SIPPRA (USDOT-DO-SIPPRA)", + value: "USDOT-DO-SIPPRA", + }, + { + id: "USDOT-GCR", + label: "U.S. Dept. of Treasury RESTORE Act Program (USDOT-GCR)", + value: "USDOT-GCR", + }, + ], + }, + { + id: "DOT", + label: "Department of Transportation (DOT)", + value: "DOT", + children: [ + { + id: "DOT-DOT X-50", + label: "69A345 Office of the Under Secretary for Policy (DOT-DOT X-50)", + value: "DOT-DOT X-50", + }, + { + id: "DOT-RITA", + label: "69A355 Research and Technology (DOT-RITA)", + value: "DOT-RITA", + }, + { + id: "DOT-FAA-FAA ARG", + label: "DOT - FAA Aviation Research Grants (DOT-FAA-FAA ARG)", + value: "DOT-FAA-FAA ARG", + }, + { + id: "DOT-FRA", + label: "DOT - Federal Railroad Administration (DOT-FRA)", + value: "DOT-FRA", + }, + { + id: "DOT-FHWA", + label: "DOT Federal Highway Administration (DOT-FHWA)", + value: "DOT-FHWA", + }, + { + id: "DOT-FTA", + label: "DOT/Federal Transit Administration (DOT-FTA)", + value: "DOT-FTA", + }, + { + id: "DOT-FAA-FAA COE-AJFE", + label: "FAA-COE-AJFE (DOT-FAA-FAA COE-AJFE)", + value: "DOT-FAA-FAA COE-AJFE", + }, + { + id: "DOT-FAA-FAA COE-FAA JAMS", + label: "FAA-COE-JAMS (DOT-FAA-FAA COE-FAA JAMS)", + value: "DOT-FAA-FAA COE-FAA JAMS", + }, + { + id: "DOT-FAA-FAA COE-TTHP", + label: "FAA-COE-TTHP (DOT-FAA-FAA COE-TTHP)", + value: "DOT-FAA-FAA COE-TTHP", + }, + { + id: "DOT-MA", + label: "Maritime Administration (DOT-MA)", + value: "DOT-MA", + }, + { + id: "DOT-NHTSA", + label: "National Highway Traffic Safety Administration (DOT-NHTSA)", + value: "DOT-NHTSA", + }, + ], + }, + { + id: "VA", + label: "Department of Veterans Affairs (VA)", + value: "VA", + children: [ + { + id: "VA-CSHF", + label: "Construction of State Home Facilities (VA-CSHF)", + value: "VA-CSHF", + }, + { + id: "VA-HPGPDP", + label: "Homeless Providers Grant and Per Diem Program (VA-HPGPDP)", + value: "VA-HPGPDP", + }, + { + id: "VA-LSV", + label: "Legal Services for Veterans (VA-LSV)", + value: "VA-LSV", + }, + { + id: "VA-NVSP", + label: "National Veterans Sports Programs (VA-NVSP)", + value: "VA-NVSP", + }, + { + id: "VA-NCAC", + label: "NCA Contracting (VA-NCAC)", + value: "VA-NCAC", + }, + { + id: "VA-OMHSP", + label: "Office of Mental Health and Suicide Prevention (VA-OMHSP)", + value: "VA-OMHSP", + }, + { + id: "VA-SSVF", + label: "Supportive Services for Veteran Families (VA-SSVF)", + value: "VA-SSVF", + }, + { + id: "VA-NCA", + label: "VA National Cemetery Administration (VA-NCA)", + value: "VA-NCA", + }, + { + id: "VA-VLGP", + label: "Veterans Legacy Grants Program (VA-VLGP)", + value: "VA-VLGP", + }, + ], + }, + { + id: "EPA", + label: "Environmental Protection Agency (EPA)", + value: "EPA", + }, + { + id: "IMLS", + label: "Institute of Museum and Library Services (IMLS)", + value: "IMLS", + }, + { + id: "MCC", + label: "Millennium Challenge Corporation (MCC)", + value: "MCC", + }, + { + id: "NASA", + label: "National Aeronautics and Space Administration (NASA)", + value: "NASA", + children: [ + { + id: "NASA-HQ", + label: "NASA Headquarters (NASA-HQ)", + value: "NASA-HQ", + }, + { + id: "NASA-JSC", + label: "NASA Johnson Space Center (NASA-JSC)", + value: "NASA-JSC", + }, + { + id: "NASA-SFC", + label: "NASA Marshall Space Flight Center (NASA-SFC)", + value: "NASA-SFC", + }, + { + id: "NASA", + label: "National Aeronautics and Space Administration (NASA)", + value: "NASA", + }, + ], + }, + { + id: "NARA", + label: "National Archives and Records Administration (NARA)", + value: "NARA", + }, + { + id: "NEA", + label: "National Endowment for the Arts (NEA)", + value: "NEA", + }, + { + id: "NEH", + label: "National Endowment for the Humanities (NEH)", + value: "NEH", + }, + { + id: "NSF", + label: "National Science Foundation (NSF)", + value: "NSF", + }, + { + id: "SSA", + label: "Social Security Administration (SSA)", + value: "SSA", + }, +]; diff --git a/frontend/src/app/[locale]/look/SearchFilterFundingInstrument.tsx b/frontend/src/app/[locale]/look/SearchFilterFundingInstrument.tsx new file mode 100644 index 000000000..4a160a882 --- /dev/null +++ b/frontend/src/app/[locale]/look/SearchFilterFundingInstrument.tsx @@ -0,0 +1,47 @@ +"use client"; + +import { + FilterOption, + SearchFilterAccordion, +} from "./SearchFilterAccordion/SearchFilterAccordion"; + +export interface SearchFilterFundingInstrumentProps { + query: Set; +} + +const initialFilterOptions: FilterOption[] = [ + { + id: "funding-instrument-cooperative_agreement", + label: "Cooperative Agreement", + value: "cooperative_agreement", + }, + { + id: "funding-instrument-grant", + label: "Grant", + value: "grant", + }, + { + id: "funding-instrument-procurement_contract", + label: "Procurement Contract ", + value: "procurement_contract", + }, + { + id: "funding-instrument-other", + label: "Other", + value: "other", + }, +]; + +export default function SearchFilterFundingInstrument({ + query, +}: SearchFilterFundingInstrumentProps) { + console.log('render', query); + return ( + + ); +} diff --git a/frontend/src/app/[locale]/look/SearchOpportunityStatus.tsx b/frontend/src/app/[locale]/look/SearchOpportunityStatus.tsx index d516873bb..b9cf816b3 100644 --- a/frontend/src/app/[locale]/look/SearchOpportunityStatus.tsx +++ b/frontend/src/app/[locale]/look/SearchOpportunityStatus.tsx @@ -1,7 +1,6 @@ "use client"; import { useContext } from "react"; - import { Checkbox } from "@trussworks/react-uswds"; import { useDebouncedCallback } from "use-debounce"; import { QueryContext } from "./QueryProvider"; @@ -14,7 +13,7 @@ interface StatusOption { } interface SearchOpportunityStatusProps { - selectedStatuses: Set; + query: Set; } const statusOptions: StatusOption[] = [ @@ -28,7 +27,7 @@ const statusOptions: StatusOption[] = [ // and submitting the form const SEARCH_OPPORTUNITY_STATUS_DEBOUNCE_TIME = 50; -export default function SearchOpportunityStatus({ selectedStatuses }: SearchOpportunityStatusProps) { +export default function SearchOpportunityStatus({ query }: SearchOpportunityStatusProps) { const { queryTerm } = useContext(QueryContext); const { updateQueryParams } = useSearchParamUpdater2(); @@ -40,12 +39,12 @@ export default function SearchOpportunityStatus({ selectedStatuses }: SearchOppo SEARCH_OPPORTUNITY_STATUS_DEBOUNCE_TIME, ); - const handleCheck = (statusValue: string, isChecked: boolean) => { - const updatedStatuses = new Set(selectedStatuses); + const handleCheck = (value: string, isChecked: boolean) => { + const updated = new Set(query); isChecked - ? updatedStatuses.add(statusValue) - : updatedStatuses.delete(statusValue); - debouncedUpdate(updatedStatuses); + ? updated.add(value) + : updated.delete(value); + debouncedUpdate(updated); }; return ( @@ -61,7 +60,7 @@ export default function SearchOpportunityStatus({ selectedStatuses }: SearchOppo label={option.label} tile={true} onChange={(e) => handleCheck(option.value, e.target.checked)} - checked={selectedStatuses.has(option.value)} + checked={query.has(option.value)} /> )})} diff --git a/frontend/src/app/[locale]/look/page.tsx b/frontend/src/app/[locale]/look/page.tsx index 9f8c1c4c6..61c3bc3ed 100644 --- a/frontend/src/app/[locale]/look/page.tsx +++ b/frontend/src/app/[locale]/look/page.tsx @@ -13,6 +13,7 @@ import Loading from "src/app/[locale]/search/loading"; import SearchResultsList from "./SearchResultList"; import QueryProvider from "./QueryProvider"; import SearchOpportunityStatus from "./SearchOpportunityStatus"; +import SearchFilterFundingInstrument from "./SearchFilterFundingInstrument"; import { convertSearchParamsToProperTypes } from "src/utils/search/convertSearchParamsToProperTypes"; export async function generateMetadata() { @@ -29,16 +30,19 @@ export default function Look({ }: { searchParams: { query?: string; - status?: string + status?: string; + fundingInstrument?: string; page?: string; }; }) { unstable_setRequestLocale("en"); const t = useTranslations("Process"); const key = Object.entries(searchParams).join(',') + console.log(searchParams); const convertedSearchParams = convertSearchParamsToProperTypes(searchParams); - const { query, status } = convertedSearchParams; - + const { query, status, fundingInstrument } = convertedSearchParams; + console.log(convertedSearchParams); + console.log('wtf') return ( <> @@ -52,7 +56,9 @@ export default function Look({
- + + +
}> diff --git a/frontend/src/hooks/useSearchParamUpdater.ts b/frontend/src/hooks/useSearchParamUpdater.ts index d26387aaf..128298349 100644 --- a/frontend/src/hooks/useSearchParamUpdater.ts +++ b/frontend/src/hooks/useSearchParamUpdater.ts @@ -33,7 +33,7 @@ export function useSearchParamUpdater2() { } sendGAEvent("event", "search", { key: finalQueryParamValue }); - router.replace(`${pathname}?${params.toString()}`); + router.replace(`${pathname}?${params.toString()}`, { scroll: false }); } return {