From 532446d78bddec2e07958c10dd7a1df93e8cf720 Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Tue, 19 Mar 2024 11:03:44 -0500 Subject: [PATCH 01/12] feat(listing service search): change query construction to allow for inclusive or filter --- .../listings/listings-filter-params.dto.ts | 12 +++--- api/src/services/listing.service.ts | 41 +++++++++++++++++-- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/api/src/dtos/listings/listings-filter-params.dto.ts b/api/src/dtos/listings/listings-filter-params.dto.ts index d2c8771efbf..3b075eba33d 100644 --- a/api/src/dtos/listings/listings-filter-params.dto.ts +++ b/api/src/dtos/listings/listings-filter-params.dto.ts @@ -38,17 +38,17 @@ export class ListingFilterParams extends BaseFilter { @Expose() @ApiPropertyOptional({ - example: '3', + example: ['3'], }) - @IsNumberString({}, { groups: [ValidationsGroupsEnum.default] }) - [ListingFilterKeys.bedrooms]?: number; + @IsArray({ groups: [ValidationsGroupsEnum.default] }) + [ListingFilterKeys.bedrooms]?: string[]; @Expose() @ApiPropertyOptional({ - example: '3', + example: ['3'], }) - @IsNumberString({}, { groups: [ValidationsGroupsEnum.default] }) - [ListingFilterKeys.bathrooms]?: number; + @IsArray({ groups: [ValidationsGroupsEnum.default] }) + [ListingFilterKeys.bathrooms]?: string[]; @Expose() @ApiPropertyOptional({ diff --git a/api/src/services/listing.service.ts b/api/src/services/listing.service.ts index 142abe139e0..954af318da2 100644 --- a/api/src/services/listing.service.ts +++ b/api/src/services/listing.service.ts @@ -5,6 +5,7 @@ import { NotFoundException, OnModuleInit, HttpException, + BadRequestException, } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; @@ -256,10 +257,44 @@ export class ListingService implements OnModuleInit { ); } if (filter[ListingFilterKeys.bedrooms]) { + const bedroomsWhere = ''; + const inclusiveWhereArray = []; + filter[ListingFilterKeys.bedrooms].forEach((bedroom) => { + switch (bedroom) { + case 'Studio': + inclusiveWhereArray.push( + "((combined_units->>'numBedrooms')::INTEGER =0)", + ); + break; + case '1': + inclusiveWhereArray.push( + "((combined_units->>'numBedrooms')::INTEGER =1)", + ); + break; + case '2': + inclusiveWhereArray.push( + "((combined_units->>'numBedrooms')::INTEGER =2)", + ); + break; + case '3': + inclusiveWhereArray.push( + "((combined_units->>'numBedrooms')::INTEGER =3)", + ); + break; + case '4+': + inclusiveWhereArray.push( + "((combined_units->>'numBedrooms')::INTEGER >=4)", + ); + break; + default: + throw new BadRequestException( + `Invalid input for bedrooms filter: "${bedroom}"`, + ); + } + }); + whereClauseArray.push( - `(combined_units->>'numBedrooms') = '${ - filter[ListingFilterKeys.bedrooms] - }'`, + `(${bedroomsWhere}${inclusiveWhereArray.join(' OR ')})`, ); } if (filter[ListingFilterKeys.bathrooms]) { From 62937660177a09e22e05671e52d4d9b05155b926 Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Fri, 5 Apr 2024 13:40:25 -0500 Subject: [PATCH 02/12] feat: create ButtonCheckboxGroup component --- .../listings/search/ButtonSelect.tsx | 62 +++++++++++++++++++ .../listings/search/ListingsSearchModal.tsx | 39 ++++-------- sites/public/src/lib/listings/search.ts | 13 ++-- 3 files changed, 81 insertions(+), 33 deletions(-) create mode 100644 sites/public/src/components/listings/search/ButtonSelect.tsx diff --git a/sites/public/src/components/listings/search/ButtonSelect.tsx b/sites/public/src/components/listings/search/ButtonSelect.tsx new file mode 100644 index 00000000000..30d43ff6d34 --- /dev/null +++ b/sites/public/src/components/listings/search/ButtonSelect.tsx @@ -0,0 +1,62 @@ +import { Button, FormOption } from "@bloom-housing/doorway-ui-components" +import React from "react" + +interface ButtonCheckboxProps { + name?: string + value: string[] + options: FormOption[] + onChange: (name: string, values: string[]) => void + spacing?: ButtonGroupSpacing + fullwidthMobile?: boolean + reversed?: boolean + pagination?: boolean + showBorder?: boolean + className?: string +} + +export enum ButtonGroupSpacing { + between = "between", + even = "even", + left = "justify-left", +} + +const ButtonCheckboxGroup = ({ name, options, value, onChange, ...props }: ButtonCheckboxProps) => { + const handleButtonClick = (optionValue: string) => { + if (value.includes(optionValue)) { + // If the option is already selected, unselect it + return value.filter((prevOption) => prevOption !== optionValue) + } else { + // If the option is not selected, select it + return [...value, optionValue] + } + } + + let spacing = ButtonGroupSpacing.between + if (props.spacing) { + spacing = props.spacing + } + + const spacingClassName = `has-${spacing}-spacing` + const classNames = ["button-group", spacingClassName] + if (props.fullwidthMobile) classNames.push("has-fullwidth-mobile-buttons") + if (props.reversed) classNames.push("is-reversed") + if (props.pagination) classNames.push("pagination") + if (props.className) classNames.push(props.className) + + return ( +
+ {options.map((option) => ( + + ))} +
+ ) +} + +export default ButtonCheckboxGroup diff --git a/sites/public/src/components/listings/search/ListingsSearchModal.tsx b/sites/public/src/components/listings/search/ListingsSearchModal.tsx index 9b408dd207e..b18b801ae39 100644 --- a/sites/public/src/components/listings/search/ListingsSearchModal.tsx +++ b/sites/public/src/components/listings/search/ListingsSearchModal.tsx @@ -3,15 +3,15 @@ import { ListingSearchParams, parseSearchString } from "../../../lib/listings/se import { t } from "@bloom-housing/ui-components" import { Modal, - ButtonGroup, ButtonGroupSpacing, - Button, Field, FieldGroup, FieldSingle, } from "@bloom-housing/doorway-ui-components" +import { Button } from "@bloom-housing/ui-seeds" import { useForm } from "react-hook-form" import { numericSearchFieldGenerator } from "./helpers" +import ButtonCheckboxGroup from "./ButtonSelect" const inputSectionStyle: React.CSSProperties = { margin: "0px 15px", @@ -81,8 +81,8 @@ export function ListingsSearchModal(props: ListingsSearchModalProps) { }) const nullState: ListingSearchParams = { - bedrooms: null, - bathrooms: null, + bedrooms: [], + bathrooms: [], minRent: "", monthlyRent: "", counties: countyLabels, @@ -137,39 +137,26 @@ export function ListingsSearchModal(props: ListingsSearchModalProps) { // console.log(`${name} has been set to ${value}`) // uncomment to debug } - const updateValueMulti = (name: string, labels: string[]) => { + const updateValueMulti = (name: string, values: string[]) => { const newValues = { ...formValues } as ListingSearchParams - newValues[name] = labels + newValues[name] = values setFormValues(newValues) - // console.log(`${name} has been set to ${value}`) // uncomment to debug + // console.log(`${name} has been set to ${values}`) // uncomment to debug } const translatedBedroomOptions: FormOption[] = [ - { - label: t("listings.unitTypes.any"), - value: null, - }, { label: t("listings.unitTypes.studio"), value: "0", }, ] - const translatedBathroomOptions: FormOption[] = [ - { - label: t("listings.unitTypes.any"), - value: null, - }, - ] - const bedroomOptions: FormOption[] = [ ...translatedBedroomOptions, ...numericSearchFieldGenerator(1, 4), ] - const bathroomOptions: FormOption[] = [ - ...translatedBathroomOptions, - ...numericSearchFieldGenerator(1, 4), - ] + + const bathroomOptions: FormOption[] = [...numericSearchFieldGenerator(1, 4)] const mkCountyFields = (counties: FormOption[]): FieldSingle[] => { const countyFields: FieldSingle[] = [] as FieldSingle[] @@ -245,21 +232,21 @@ export function ListingsSearchModal(props: ListingsSearchModalProps) { >
{t("t.bedrooms")}
-
{t("t.bathrooms")}
- diff --git a/sites/public/src/lib/listings/search.ts b/sites/public/src/lib/listings/search.ts index 69d281180c4..fe06286bffc 100644 --- a/sites/public/src/lib/listings/search.ts +++ b/sites/public/src/lib/listings/search.ts @@ -1,8 +1,8 @@ import { ListingQueryBuilder } from "./listing-query-builder" export type ListingSearchParams = { - bedrooms: string - bathrooms: string + bedrooms: string[] + bathrooms: string[] minRent: string monthlyRent: string counties: string[] @@ -101,13 +101,12 @@ export function generateSearchQuery(params: ListingSearchParams) { const qb = new ListingQueryBuilder() // Find listings that have units with greater than or equal number of bedrooms - if (params.bedrooms != null) { - qb.whereGreaterThanEqual("bedrooms", params.bedrooms) + if (Array.isArray(params.bedrooms) && params.bedrooms.length > 0) { + qb.whereIn("bedrooms", params.bedrooms) } - // Find listings that have units with greater than or equal number of bathrooms - if (params.bathrooms != null) { - qb.whereGreaterThanEqual("bathrooms", params.bathrooms) + if (Array.isArray(params.bathrooms) && params.bathrooms.length > 0) { + qb.whereIn("bathrooms", params.bathrooms) } if (params.minRent && params.minRent != "") { From c71f19dc77392e982c6e9de96fb3e3db1600351b Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Tue, 7 May 2024 16:08:15 -0500 Subject: [PATCH 03/12] feat: uptake seeds and use CheckboxGroup --- api/src/services/listing.service.ts | 45 ++++++++++++++++++- sites/partners/package.json | 2 +- sites/public/package.json | 2 +- .../search/ListingsSearchModal.module.scss | 13 ++++++ .../listings/search/ListingsSearchModal.tsx | 42 ++++++++--------- sites/public/src/lib/listings/search.ts | 15 +++++-- yarn.lock | 25 +++++++++++ 7 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 sites/public/src/components/listings/search/ListingsSearchModal.module.scss diff --git a/api/src/services/listing.service.ts b/api/src/services/listing.service.ts index 72cb10c54d8..9651e0ea386 100644 --- a/api/src/services/listing.service.ts +++ b/api/src/services/listing.service.ts @@ -262,7 +262,7 @@ export class ListingService implements OnModuleInit { const inclusiveWhereArray = []; filter[ListingFilterKeys.bedrooms].forEach((bedroom) => { switch (bedroom) { - case 'Studio': + case '0': inclusiveWhereArray.push( "((combined_units->>'numBedrooms')::INTEGER =0)", ); @@ -282,7 +282,7 @@ export class ListingService implements OnModuleInit { "((combined_units->>'numBedrooms')::INTEGER =3)", ); break; - case '4+': + case '4': inclusiveWhereArray.push( "((combined_units->>'numBedrooms')::INTEGER >=4)", ); @@ -298,6 +298,47 @@ export class ListingService implements OnModuleInit { `(${bedroomsWhere}${inclusiveWhereArray.join(' OR ')})`, ); } + if (filter[ListingFilterKeys.bathrooms]) { + const bathroomsWhere = ''; + const inclusiveWhereArray = []; + filter[ListingFilterKeys.bathrooms].forEach((bathroom) => { + switch (bathroom) { + case '0': + inclusiveWhereArray.push( + "((combined_units->>'numBathrooms')::INTEGER =0)", + ); + break; + case '1': + inclusiveWhereArray.push( + "((combined_units->>'numBathrooms')::INTEGER =1)", + ); + break; + case '2': + inclusiveWhereArray.push( + "((combined_units->>'numBathrooms')::INTEGER =2)", + ); + break; + case '3': + inclusiveWhereArray.push( + "((combined_units->>'numBathrooms')::INTEGER =3)", + ); + break; + case '4': + inclusiveWhereArray.push( + "((combined_units->>'numBathrooms')::INTEGER >=4)", + ); + break; + default: + throw new BadRequestException( + `Invalid input for bathrooms filter: "${bathroom}"`, + ); + } + }); + + whereClauseArray.push( + `(${bathroomsWhere}${inclusiveWhereArray.join(' OR ')})`, + ); + } if (filter[ListingFilterKeys.bathrooms]) { whereClauseArray.push( `(combined_units->>'numBathrooms') = '${ diff --git a/sites/partners/package.json b/sites/partners/package.json index bc67b145ced..a73257afdea 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -32,7 +32,7 @@ "@bloom-housing/shared-helpers": "^7.7.1", "@bloom-housing/doorway-ui-components": "^1.0.0", "@bloom-housing/ui-components": "12.1.0", - "@bloom-housing/ui-seeds": "1.12.1", + "@bloom-housing/ui-seeds": "1.15.0", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", "ag-grid-react": "^26.0.0", diff --git a/sites/public/package.json b/sites/public/package.json index f2b52c5db8d..e7aaf9aeb0f 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -32,7 +32,7 @@ "@bloom-housing/doorway-ui-components": "^1.0.0", "@bloom-housing/shared-helpers": "^7.7.1", "@bloom-housing/ui-components": "12.1.0", - "@bloom-housing/ui-seeds": "1.12.1", + "@bloom-housing/ui-seeds": "1.15.0", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/sites/public/src/components/listings/search/ListingsSearchModal.module.scss b/sites/public/src/components/listings/search/ListingsSearchModal.module.scss new file mode 100644 index 00000000000..ea31d322aec --- /dev/null +++ b/sites/public/src/components/listings/search/ListingsSearchModal.module.scss @@ -0,0 +1,13 @@ +.checkbox-group { + --inner-button-gap: var(--seeds-s3); + padding-top: var(--seeds-s4); + padding-bottom: var(--seeds-s4); +} + +.checkbox-group > div > label { + color: var(--seeds-color-primary-dark) !important; +} + +.checkbox-group > div > label:hover { + color: var(--seeds-color-white) !important; +} \ No newline at end of file diff --git a/sites/public/src/components/listings/search/ListingsSearchModal.tsx b/sites/public/src/components/listings/search/ListingsSearchModal.tsx index b18b801ae39..eef4dfc2a6a 100644 --- a/sites/public/src/components/listings/search/ListingsSearchModal.tsx +++ b/sites/public/src/components/listings/search/ListingsSearchModal.tsx @@ -1,17 +1,12 @@ import React, { useEffect, useState } from "react" import { ListingSearchParams, parseSearchString } from "../../../lib/listings/search" import { t } from "@bloom-housing/ui-components" -import { - Modal, - ButtonGroupSpacing, - Field, - FieldGroup, - FieldSingle, -} from "@bloom-housing/doorway-ui-components" -import { Button } from "@bloom-housing/ui-seeds" +import { Modal, Field, FieldGroup, FieldSingle } from "@bloom-housing/doorway-ui-components" +import { CheckboxGroup, Button } from "@bloom-housing/ui-seeds" import { useForm } from "react-hook-form" import { numericSearchFieldGenerator } from "./helpers" -import ButtonCheckboxGroup from "./ButtonSelect" +import { CheckboxItem } from "@bloom-housing/ui-seeds/src/forms/CheckboxGroup" +import styles from "./ListingsSearchModal.module.scss" const inputSectionStyle: React.CSSProperties = { margin: "0px 15px", @@ -137,11 +132,10 @@ export function ListingsSearchModal(props: ListingsSearchModalProps) { // console.log(`${name} has been set to ${value}`) // uncomment to debug } - const updateValueMulti = (name: string, values: string[]) => { + const updateValueMulti = (name: string, values: CheckboxItem[] | string[]) => { const newValues = { ...formValues } as ListingSearchParams newValues[name] = values setFormValues(newValues) - // console.log(`${name} has been set to ${values}`) // uncomment to debug } const translatedBedroomOptions: FormOption[] = [ @@ -232,23 +226,29 @@ export function ListingsSearchModal(props: ListingsSearchModalProps) { >
{t("t.bedrooms")}
- updateValueMulti("bedrooms", values)} + size="md" + variant="secondary-outlined" + checkedVariant="secondary" + className={styles["checkbox-group"]} />
{t("t.bathrooms")}
- updateValueMulti("bathrooms", values)} + size="md" + variant="secondary-outlined" + checkedVariant="secondary" + className={styles["checkbox-group"]} />
diff --git a/sites/public/src/lib/listings/search.ts b/sites/public/src/lib/listings/search.ts index fe06286bffc..3888cf43e92 100644 --- a/sites/public/src/lib/listings/search.ts +++ b/sites/public/src/lib/listings/search.ts @@ -1,8 +1,9 @@ +import { CheckboxItem } from "@bloom-housing/ui-seeds/src/forms/CheckboxGroup" import { ListingQueryBuilder } from "./listing-query-builder" export type ListingSearchParams = { - bedrooms: string[] - bathrooms: string[] + bedrooms: CheckboxItem[] + bathrooms: CheckboxItem[] minRent: string monthlyRent: string counties: string[] @@ -102,11 +103,17 @@ export function generateSearchQuery(params: ListingSearchParams) { // Find listings that have units with greater than or equal number of bedrooms if (Array.isArray(params.bedrooms) && params.bedrooms.length > 0) { - qb.whereIn("bedrooms", params.bedrooms) + qb.whereIn( + "bedrooms", + params.bedrooms.map((bedroom) => bedroom.value) + ) } if (Array.isArray(params.bathrooms) && params.bathrooms.length > 0) { - qb.whereIn("bathrooms", params.bathrooms) + qb.whereIn( + "bathrooms", + params.bathrooms.map((bathroom) => bathroom.value) + ) } if (params.minRent && params.minRent != "") { diff --git a/yarn.lock b/yarn.lock index 8d536c829b8..168213111c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3186,6 +3186,26 @@ react-tabs "^6.0.0" typescript "^4.4.2" +"@bloom-housing/ui-seeds@1.15.0": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@bloom-housing/ui-seeds/-/ui-seeds-1.15.0.tgz#033d17fe33d4fb18a3bbbb0cc9605f758d70b6fc" + integrity sha512-WrCObkbw2E6zoDnsf3q2OU5yxN9MrS+a8/mT6Bpb5J/jF896iyFMNkMzqZWqx24s6tERXdGe7rzKtwXzZl4nqA== + dependencies: + "@fortawesome/fontawesome-svg-core" "^6.3.0" + "@fortawesome/react-fontawesome" "^0.2.0" + "@heroicons/react" "^2.0.18" + "@types/markdown-to-jsx" "^6.11.2" + "@types/mdx" "^2.0.3" + "@types/node" "^16.7.13" + "@types/react" "^18.0.0" + "@types/react-dom" "^18.0.0" + focus-trap-react "^10.2.1" + markdown-to-jsx "^6.11.4" + react "^18.2.0" + react-dom "^18.2.0" + react-tabs "^6.0.0" + typescript "^4.4.2" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz" @@ -3814,6 +3834,11 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@heroicons/react@^2.0.18": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.1.3.tgz#78a2a7f504a7370283d07eabcddc7fec04f503db" + integrity sha512-fEcPfo4oN345SoqdlCDdSa4ivjaKbk0jTd+oubcgNxnNgAfzysfwWfQUr+51wigiWHQQRiZNd1Ao0M5Y3M2EGg== + "@humanwhocodes/config-array@^0.11.8": version "0.11.8" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" From ac4ec191f817990d36add806a55d39a44f7479d9 Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Tue, 7 May 2024 16:39:19 -0500 Subject: [PATCH 04/12] refactor(remove buttonselect): remove ButtonSelect --- .../listings/search/ButtonSelect.tsx | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 sites/public/src/components/listings/search/ButtonSelect.tsx diff --git a/sites/public/src/components/listings/search/ButtonSelect.tsx b/sites/public/src/components/listings/search/ButtonSelect.tsx deleted file mode 100644 index 30d43ff6d34..00000000000 --- a/sites/public/src/components/listings/search/ButtonSelect.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Button, FormOption } from "@bloom-housing/doorway-ui-components" -import React from "react" - -interface ButtonCheckboxProps { - name?: string - value: string[] - options: FormOption[] - onChange: (name: string, values: string[]) => void - spacing?: ButtonGroupSpacing - fullwidthMobile?: boolean - reversed?: boolean - pagination?: boolean - showBorder?: boolean - className?: string -} - -export enum ButtonGroupSpacing { - between = "between", - even = "even", - left = "justify-left", -} - -const ButtonCheckboxGroup = ({ name, options, value, onChange, ...props }: ButtonCheckboxProps) => { - const handleButtonClick = (optionValue: string) => { - if (value.includes(optionValue)) { - // If the option is already selected, unselect it - return value.filter((prevOption) => prevOption !== optionValue) - } else { - // If the option is not selected, select it - return [...value, optionValue] - } - } - - let spacing = ButtonGroupSpacing.between - if (props.spacing) { - spacing = props.spacing - } - - const spacingClassName = `has-${spacing}-spacing` - const classNames = ["button-group", spacingClassName] - if (props.fullwidthMobile) classNames.push("has-fullwidth-mobile-buttons") - if (props.reversed) classNames.push("is-reversed") - if (props.pagination) classNames.push("pagination") - if (props.className) classNames.push(props.className) - - return ( -
- {options.map((option) => ( - - ))} -
- ) -} - -export default ButtonCheckboxGroup From af3904c0fd69ef13464104c5788ccb39f4ee8bf2 Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Wed, 5 Jun 2024 18:17:15 -0500 Subject: [PATCH 05/12] refactor: create helper for unit filters --- api/src/services/listing.service.ts | 83 +++------------------- api/src/utilities/unit-filter-utilities.ts | 53 ++++++++++++++ 2 files changed, 62 insertions(+), 74 deletions(-) create mode 100644 api/src/utilities/unit-filter-utilities.ts diff --git a/api/src/services/listing.service.ts b/api/src/services/listing.service.ts index 14542e90181..03ed7c4c6c0 100644 --- a/api/src/services/listing.service.ts +++ b/api/src/services/listing.service.ts @@ -49,6 +49,7 @@ import { startCronJob } from '../utilities/cron-job-starter'; import { PermissionService } from './permission.service'; import { permissionActions } from '../enums/permissions/permission-actions-enum'; import Unit from '../dtos/units/unit.dto'; +import { buildInclusiveWhereQuery } from 'src/utilities/unit-filter-utilities'; export type getListingsArgs = { skip: number; @@ -259,85 +260,19 @@ export class ListingService implements OnModuleInit { ); } if (filter[ListingFilterKeys.bedrooms]) { - const bedroomsWhere = ''; - const inclusiveWhereArray = []; - filter[ListingFilterKeys.bedrooms].forEach((bedroom) => { - switch (bedroom) { - case '0': - inclusiveWhereArray.push( - "((combined_units->>'numBedrooms')::INTEGER =0)", - ); - break; - case '1': - inclusiveWhereArray.push( - "((combined_units->>'numBedrooms')::INTEGER =1)", - ); - break; - case '2': - inclusiveWhereArray.push( - "((combined_units->>'numBedrooms')::INTEGER =2)", - ); - break; - case '3': - inclusiveWhereArray.push( - "((combined_units->>'numBedrooms')::INTEGER =3)", - ); - break; - case '4': - inclusiveWhereArray.push( - "((combined_units->>'numBedrooms')::INTEGER >=4)", - ); - break; - default: - throw new BadRequestException( - `Invalid input for bedrooms filter: "${bedroom}"`, - ); - } - }); - whereClauseArray.push( - `(${bedroomsWhere}${inclusiveWhereArray.join(' OR ')})`, + buildInclusiveWhereQuery( + ListingFilterKeys.bedrooms, + filter[ListingFilterKeys.bedrooms], + ), ); } if (filter[ListingFilterKeys.bathrooms]) { - const bathroomsWhere = ''; - const inclusiveWhereArray = []; - filter[ListingFilterKeys.bathrooms].forEach((bathroom) => { - switch (bathroom) { - case '0': - inclusiveWhereArray.push( - "((combined_units->>'numBathrooms')::INTEGER =0)", - ); - break; - case '1': - inclusiveWhereArray.push( - "((combined_units->>'numBathrooms')::INTEGER =1)", - ); - break; - case '2': - inclusiveWhereArray.push( - "((combined_units->>'numBathrooms')::INTEGER =2)", - ); - break; - case '3': - inclusiveWhereArray.push( - "((combined_units->>'numBathrooms')::INTEGER =3)", - ); - break; - case '4': - inclusiveWhereArray.push( - "((combined_units->>'numBathrooms')::INTEGER >=4)", - ); - break; - default: - throw new BadRequestException( - `Invalid input for bathrooms filter: "${bathroom}"`, - ); - } - }); - whereClauseArray.push( - `(${bathroomsWhere}${inclusiveWhereArray.join(' OR ')})`, + buildInclusiveWhereQuery( + ListingFilterKeys.bathrooms, + filter[ListingFilterKeys.bathrooms], + ), ); } if (filter[ListingFilterKeys.monthlyRent]) { diff --git a/api/src/utilities/unit-filter-utilities.ts b/api/src/utilities/unit-filter-utilities.ts new file mode 100644 index 00000000000..80afddc2d1d --- /dev/null +++ b/api/src/utilities/unit-filter-utilities.ts @@ -0,0 +1,53 @@ +import { BadRequestException } from '@nestjs/common'; + +enum ColumnName { + bedrooms = 'numBedrooms', + bathrooms = 'numBathrooms', +} +/* Takes a ColumnName and selected values to build a query that includes the values in the ColumnName + Returns a query String +*/ + +export const buildInclusiveWhereQuery = ( + key: keyof typeof ColumnName, + values: string[], +): string => { + const columnName = ColumnName[key]; + + const inclusiveWhereArray = []; + values.forEach((value) => { + switch (value) { + case '0': + inclusiveWhereArray.push( + `((combined_units->>'${columnName}')::INTEGER =0)`, + ); + break; + case '1': + inclusiveWhereArray.push( + `((combined_units->>'${columnName}')::INTEGER =1)`, + ); + break; + case '2': + inclusiveWhereArray.push( + `((combined_units->>'${columnName}')::INTEGER =2)`, + ); + break; + case '3': + inclusiveWhereArray.push( + `((combined_units->>'${columnName}')::INTEGER =3)`, + ); + break; + case '4': + inclusiveWhereArray.push( + `((combined_units->>'${columnName}')::INTEGER >=4)`, + ); + break; + default: + throw new BadRequestException( + `Invalid input for ${key} filter: "${value}"`, + ); + } + }); + + return `(${inclusiveWhereArray.join(' OR ')})`; +}; From 1f4c6839e2daded358120da38b2f3fdf429bb56c Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Fri, 7 Jun 2024 10:53:05 -0500 Subject: [PATCH 06/12] feat: checkboxGroup on LandingSearch --- api/src/services/listing.service.ts | 1 - shared-helpers/src/types/backend-swagger.ts | 4 +-- .../listings/search/LandingSearch.tsx | 33 ++++++++----------- .../search/ListingsSearchCombined.tsx | 4 +-- .../search/ListingsSearchModal.module.scss | 8 ----- .../listings/search/ListingsSearchModal.tsx | 21 ++++++------ .../src/components/listings/search/helpers.ts | 9 +++++ sites/public/src/lib/listings/search.ts | 15 +++------ 8 files changed, 41 insertions(+), 54 deletions(-) diff --git a/api/src/services/listing.service.ts b/api/src/services/listing.service.ts index 03ed7c4c6c0..8b9b1346914 100644 --- a/api/src/services/listing.service.ts +++ b/api/src/services/listing.service.ts @@ -5,7 +5,6 @@ import { NotFoundException, OnModuleInit, HttpException, - BadRequestException, } from '@nestjs/common'; import { HttpService } from '@nestjs/axios'; import { ConfigService } from '@nestjs/config'; diff --git a/shared-helpers/src/types/backend-swagger.ts b/shared-helpers/src/types/backend-swagger.ts index 80bf4e0241f..60bbcb8f937 100644 --- a/shared-helpers/src/types/backend-swagger.ts +++ b/shared-helpers/src/types/backend-swagger.ts @@ -2207,10 +2207,10 @@ export interface ListingFilterParams { neighborhood?: string /** */ - bedrooms?: number + bedrooms?: string[] /** */ - bathrooms?: number + bathrooms?: string[] /** */ zipcode?: string diff --git a/sites/public/src/components/listings/search/LandingSearch.tsx b/sites/public/src/components/listings/search/LandingSearch.tsx index 3ed8807bee5..5e9c516ba37 100644 --- a/sites/public/src/components/listings/search/LandingSearch.tsx +++ b/sites/public/src/components/listings/search/LandingSearch.tsx @@ -2,12 +2,10 @@ import React, { useState, useEffect } from "react" import { ListingSearchParams, buildSearchString } from "../../../lib/listings/search" import { Modal, - ButtonGroup, FieldGroup, FieldSingle, Card, Button, - ButtonGroupSpacing, Field, AppearanceSizeType, } from "@bloom-housing/doorway-ui-components" @@ -15,7 +13,8 @@ import { useForm } from "react-hook-form" import { LinkButton, t } from "@bloom-housing/ui-components" import styles from "./LandingSearch.module.scss" import { FormOption } from "./ListingsSearchModal" -import { numericSearchFieldGenerator } from "./helpers" +import { getCheckboxValues, getFormValues, numericSearchFieldGenerator } from "./helpers" +import { CheckboxGroup } from "@bloom-housing/ui-seeds" type LandingSearchProps = { bedrooms: FormOption[] @@ -34,8 +33,8 @@ export function LandingSearch(props: LandingSearchProps) { }) const nullState: ListingSearchParams = { - bedrooms: null, - bathrooms: null, + bedrooms: [], + bathrooms: [], minRent: "", monthlyRent: "", counties: countyLabels, @@ -58,18 +57,13 @@ export function LandingSearch(props: LandingSearchProps) { // console.log(`${name} has been set to ${value}`) // uncomment to debug } - const updateValueMulti = (name: string, labels: string[]) => { + const updateValueMulti = (name: string, values: string[]) => { const newValues = { ...formValues } as ListingSearchParams - newValues[name] = labels + newValues[name] = values setFormValues(newValues) - // console.log(`${name} has been set to ${value}`) // uncomment to debug } const translatedBedroomOptions: FormOption[] = [ - { - label: t("listings.unitTypes.any"), - value: null, - }, { label: t("listings.unitTypes.studio"), value: "0", @@ -77,7 +71,7 @@ export function LandingSearch(props: LandingSearchProps) { ] const bedroomOptions: FormOption[] = [ ...translatedBedroomOptions, - ...numericSearchFieldGenerator(1, 3), + ...numericSearchFieldGenerator(1, 4), ] const mkCountyFields = (counties: FormOption[]): FieldSingle[] => { @@ -132,13 +126,14 @@ export function LandingSearch(props: LandingSearchProps) {
{t("t.bedrooms")}
- updateValueMulti("bedrooms", getFormValues(values))} + values={getCheckboxValues(formValues.bedrooms)} + size="md" + variant="primary-outlined" + checkedVariant="primary" />
diff --git a/sites/public/src/components/listings/search/ListingsSearchCombined.tsx b/sites/public/src/components/listings/search/ListingsSearchCombined.tsx index 6b64720e642..8387e61489e 100644 --- a/sites/public/src/components/listings/search/ListingsSearchCombined.tsx +++ b/sites/public/src/components/listings/search/ListingsSearchCombined.tsx @@ -31,8 +31,8 @@ function ListingsSearchCombined(props: ListingsSearchCombinedProps) { // Store the current search params for pagination const searchParams = useRef({ - bedrooms: null, - bathrooms: null, + bedrooms: [], + bathrooms: [], monthlyRent: null, counties: [], } as ListingSearchParams) diff --git a/sites/public/src/components/listings/search/ListingsSearchModal.module.scss b/sites/public/src/components/listings/search/ListingsSearchModal.module.scss index ea31d322aec..3af5724cdc2 100644 --- a/sites/public/src/components/listings/search/ListingsSearchModal.module.scss +++ b/sites/public/src/components/listings/search/ListingsSearchModal.module.scss @@ -2,12 +2,4 @@ --inner-button-gap: var(--seeds-s3); padding-top: var(--seeds-s4); padding-bottom: var(--seeds-s4); -} - -.checkbox-group > div > label { - color: var(--seeds-color-primary-dark) !important; -} - -.checkbox-group > div > label:hover { - color: var(--seeds-color-white) !important; } \ No newline at end of file diff --git a/sites/public/src/components/listings/search/ListingsSearchModal.tsx b/sites/public/src/components/listings/search/ListingsSearchModal.tsx index eef4dfc2a6a..aff8bd835a6 100644 --- a/sites/public/src/components/listings/search/ListingsSearchModal.tsx +++ b/sites/public/src/components/listings/search/ListingsSearchModal.tsx @@ -4,8 +4,7 @@ import { t } from "@bloom-housing/ui-components" import { Modal, Field, FieldGroup, FieldSingle } from "@bloom-housing/doorway-ui-components" import { CheckboxGroup, Button } from "@bloom-housing/ui-seeds" import { useForm } from "react-hook-form" -import { numericSearchFieldGenerator } from "./helpers" -import { CheckboxItem } from "@bloom-housing/ui-seeds/src/forms/CheckboxGroup" +import { getCheckboxValues, getFormValues, numericSearchFieldGenerator } from "./helpers" import styles from "./ListingsSearchModal.module.scss" const inputSectionStyle: React.CSSProperties = { @@ -132,7 +131,7 @@ export function ListingsSearchModal(props: ListingsSearchModalProps) { // console.log(`${name} has been set to ${value}`) // uncomment to debug } - const updateValueMulti = (name: string, values: CheckboxItem[] | string[]) => { + const updateValueMulti = (name: string, values: string[]) => { const newValues = { ...formValues } as ListingSearchParams newValues[name] = values setFormValues(newValues) @@ -229,11 +228,11 @@ export function ListingsSearchModal(props: ListingsSearchModalProps) { updateValueMulti("bedrooms", values)} + values={getCheckboxValues(formValues.bedrooms)} + onChange={(values) => updateValueMulti("bedrooms", getFormValues(values))} size="md" - variant="secondary-outlined" - checkedVariant="secondary" + variant="primary-outlined" + checkedVariant="primary" className={styles["checkbox-group"]} />
@@ -243,11 +242,11 @@ export function ListingsSearchModal(props: ListingsSearchModalProps) { updateValueMulti("bathrooms", values)} + values={getCheckboxValues(formValues.bathrooms)} + onChange={(values) => updateValueMulti("bathrooms", getFormValues(values))} size="md" - variant="secondary-outlined" - checkedVariant="secondary" + variant="primary-outlined" + checkedVariant="primary" className={styles["checkbox-group"]} />
diff --git a/sites/public/src/components/listings/search/helpers.ts b/sites/public/src/components/listings/search/helpers.ts index a0623c872a4..b96286e0464 100644 --- a/sites/public/src/components/listings/search/helpers.ts +++ b/sites/public/src/components/listings/search/helpers.ts @@ -1,4 +1,5 @@ import { FormOption } from "@bloom-housing/doorway-ui-components" +import { CheckboxItem } from "@bloom-housing/ui-seeds/src/forms/CheckboxGroup" // ie. [{label : "1" value: "1"}, {label : "2+" value: "2"} if includeMore is true export const numericSearchFieldGenerator = ( @@ -16,3 +17,11 @@ export const numericSearchFieldGenerator = ( } return fieldValues } + +export const getCheckboxValues = (formValues: string[]) => { + return formValues.map((value) => ({ label: value, value: value })) +} + +export const getFormValues = (checkboxValues: CheckboxItem[]) => { + return checkboxValues.map((value) => value.value) +} diff --git a/sites/public/src/lib/listings/search.ts b/sites/public/src/lib/listings/search.ts index 3888cf43e92..fe06286bffc 100644 --- a/sites/public/src/lib/listings/search.ts +++ b/sites/public/src/lib/listings/search.ts @@ -1,9 +1,8 @@ -import { CheckboxItem } from "@bloom-housing/ui-seeds/src/forms/CheckboxGroup" import { ListingQueryBuilder } from "./listing-query-builder" export type ListingSearchParams = { - bedrooms: CheckboxItem[] - bathrooms: CheckboxItem[] + bedrooms: string[] + bathrooms: string[] minRent: string monthlyRent: string counties: string[] @@ -103,17 +102,11 @@ export function generateSearchQuery(params: ListingSearchParams) { // Find listings that have units with greater than or equal number of bedrooms if (Array.isArray(params.bedrooms) && params.bedrooms.length > 0) { - qb.whereIn( - "bedrooms", - params.bedrooms.map((bedroom) => bedroom.value) - ) + qb.whereIn("bedrooms", params.bedrooms) } if (Array.isArray(params.bathrooms) && params.bathrooms.length > 0) { - qb.whereIn( - "bathrooms", - params.bathrooms.map((bathroom) => bathroom.value) - ) + qb.whereIn("bathrooms", params.bathrooms) } if (params.minRent && params.minRent != "") { From 03212cebc276dee8d82a1f44cc2757a313aaf6ea Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Fri, 7 Jun 2024 12:16:37 -0500 Subject: [PATCH 07/12] fix: fix import --- api/src/services/listing.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/services/listing.service.ts b/api/src/services/listing.service.ts index 8b9b1346914..fd9e6ba2185 100644 --- a/api/src/services/listing.service.ts +++ b/api/src/services/listing.service.ts @@ -48,7 +48,7 @@ import { startCronJob } from '../utilities/cron-job-starter'; import { PermissionService } from './permission.service'; import { permissionActions } from '../enums/permissions/permission-actions-enum'; import Unit from '../dtos/units/unit.dto'; -import { buildInclusiveWhereQuery } from 'src/utilities/unit-filter-utilities'; +import { buildInclusiveWhereQuery } from '../utilities/unit-filter-utilities'; export type getListingsArgs = { skip: number; From 800a99761ddda6fc77eaed63cb41e5ed9096e094 Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Fri, 7 Jun 2024 12:26:25 -0500 Subject: [PATCH 08/12] fix: test fix seeds intake --- sites/public/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sites/public/package.json b/sites/public/package.json index ff3bb160f69..24df0b3eecf 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -32,7 +32,7 @@ "@bloom-housing/doorway-ui-components": "^1.0.0", "@bloom-housing/shared-helpers": "^7.7.1", "@bloom-housing/ui-components": "12.1.6", - "@bloom-housing/ui-seeds": "1.15.0", + "@bloom-housing/ui-seeds": "1.12.3", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", From 4b8c730f7ef3dadc3b20f226b67913c800aa2775 Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Wed, 12 Jun 2024 17:38:49 -0500 Subject: [PATCH 09/12] fix: seeds version and add padding --- sites/public/package.json | 2 +- .../src/components/listings/search/LandingSearch.module.scss | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sites/public/package.json b/sites/public/package.json index 24df0b3eecf..ff3bb160f69 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -32,7 +32,7 @@ "@bloom-housing/doorway-ui-components": "^1.0.0", "@bloom-housing/shared-helpers": "^7.7.1", "@bloom-housing/ui-components": "12.1.6", - "@bloom-housing/ui-seeds": "1.12.3", + "@bloom-housing/ui-seeds": "1.15.0", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/sites/public/src/components/listings/search/LandingSearch.module.scss b/sites/public/src/components/listings/search/LandingSearch.module.scss index 2e156e5257d..9ffb8d7cb98 100644 --- a/sites/public/src/components/listings/search/LandingSearch.module.scss +++ b/sites/public/src/components/listings/search/LandingSearch.module.scss @@ -12,6 +12,7 @@ @apply font-semibold; @apply text-sm; min-width: auto; + padding-bottom: var(--seeds-s2); @media (min-width: $screen-md) { @apply text-xl; From 4d27c15e50f64ca2f14cbf5a717b537dddb95b8d Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Wed, 12 Jun 2024 17:59:10 -0500 Subject: [PATCH 10/12] fix: latest seeds --- shared-helpers/package.json | 2 +- sites/public/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 123c65a4313..9bc52c530da 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -19,7 +19,7 @@ "dependencies": { "@bloom-housing/doorway-ui-components": "^1.0.0", "@bloom-housing/ui-components": "12.1.6", - "@bloom-housing/ui-seeds": "1.12.3", + "@bloom-housing/ui-seeds": "1.16.1", "axios-cookiejar-support": "4.0.6", "tough-cookie": "4.1.3" }, diff --git a/sites/public/package.json b/sites/public/package.json index ff3bb160f69..dae48a9727d 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -32,7 +32,7 @@ "@bloom-housing/doorway-ui-components": "^1.0.0", "@bloom-housing/shared-helpers": "^7.7.1", "@bloom-housing/ui-components": "12.1.6", - "@bloom-housing/ui-seeds": "1.15.0", + "@bloom-housing/ui-seeds": "1.16.1", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/yarn.lock b/yarn.lock index 476ca48c04a..529ccdae437 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3186,10 +3186,10 @@ react-tabs "^6.0.0" typescript "^4.4.2" -"@bloom-housing/ui-seeds@1.15.0": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@bloom-housing/ui-seeds/-/ui-seeds-1.15.0.tgz#033d17fe33d4fb18a3bbbb0cc9605f758d70b6fc" - integrity sha512-WrCObkbw2E6zoDnsf3q2OU5yxN9MrS+a8/mT6Bpb5J/jF896iyFMNkMzqZWqx24s6tERXdGe7rzKtwXzZl4nqA== +"@bloom-housing/ui-seeds@1.16.1": + version "1.16.1" + resolved "https://registry.yarnpkg.com/@bloom-housing/ui-seeds/-/ui-seeds-1.16.1.tgz#da293bc9c38c485df0d12f2a8c81120134bb57e4" + integrity sha512-BsNNw954cH3eeEF/qlcd6O/NjWWtgWVoKGAEO3JRMFGMmItVxBGOVg3Ye0Eyfs7bWXTxiw0ucDv/ZoMHRJbgmQ== dependencies: "@fortawesome/fontawesome-svg-core" "^6.3.0" "@fortawesome/react-fontawesome" "^0.2.0" From cd025a9bcb41c4000b90a181a7c01c728520cab8 Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Wed, 12 Jun 2024 18:16:12 -0500 Subject: [PATCH 11/12] fix: fix bedroom type --- sites/public/src/lib/listings/search.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sites/public/src/lib/listings/search.test.ts b/sites/public/src/lib/listings/search.test.ts index 254b6ec3da7..70d81a77059 100644 --- a/sites/public/src/lib/listings/search.test.ts +++ b/sites/public/src/lib/listings/search.test.ts @@ -35,9 +35,9 @@ describe("parse search string", () => { describe("build search string", () => { it("should build expected string", () => { const example: ListingSearchParams = { - bedrooms: "2", + bedrooms: ["2"], counties: ["county1", "county2"], - bathrooms: null, + bathrooms: [], minRent: null, monthlyRent: null, } From 9bb15b064f75b27ba249e40cb86b6d651c219971 Mon Sep 17 00:00:00 2001 From: Cade Wolcott Date: Mon, 17 Jun 2024 13:44:56 -0500 Subject: [PATCH 12/12] fix: latest seeds after merge --- shared-helpers/package.json | 2 +- sites/partners/package.json | 2 +- sites/public/package.json | 2 +- sites/public/src/components/listings/search/LandingSearch.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shared-helpers/package.json b/shared-helpers/package.json index b37dedfe65f..8ff9630a05d 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -19,7 +19,7 @@ "dependencies": { "@bloom-housing/doorway-ui-components": "^1.0.0", "@bloom-housing/ui-components": "12.1.6", - "@bloom-housing/ui-seeds": "1.14.0", + "@bloom-housing/ui-seeds": "1.16.1", "@heroicons/react": "^2.1.1", "axios-cookiejar-support": "4.0.6", "tough-cookie": "4.1.3" diff --git a/sites/partners/package.json b/sites/partners/package.json index d84a3550be4..4fc6a9fa863 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -32,7 +32,7 @@ "@bloom-housing/doorway-ui-components": "^1.0.0", "@bloom-housing/shared-helpers": "^7.7.1", "@bloom-housing/ui-components": "12.1.6", - "@bloom-housing/ui-seeds": "1.14.0", + "@bloom-housing/ui-seeds": "1.16.1", "@heroicons/react": "^2.1.1", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", diff --git a/sites/public/package.json b/sites/public/package.json index 1562ea882c0..cecb9e9a9cb 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -33,7 +33,7 @@ "@bloom-housing/doorway-ui-components": "^1.0.0", "@bloom-housing/shared-helpers": "^7.7.1", "@bloom-housing/ui-components": "12.1.6", - "@bloom-housing/ui-seeds": "1.14.0", + "@bloom-housing/ui-seeds": "1.16.1", "@heroicons/react": "^2.1.1", "@react-google-maps/api": "^2.18.1", "@sentry/nextjs": "^7.61.0", diff --git a/sites/public/src/components/listings/search/LandingSearch.tsx b/sites/public/src/components/listings/search/LandingSearch.tsx index 5e9c516ba37..6fc27400a61 100644 --- a/sites/public/src/components/listings/search/LandingSearch.tsx +++ b/sites/public/src/components/listings/search/LandingSearch.tsx @@ -23,7 +23,7 @@ type LandingSearchProps = { // TODO: Refactor LandingSearch to utilize react-hook-form. It is currently using a custom form object and custom valueSetters // which is mostly functional but fails to leverage UI-C's formatting, accessibility and any other future improvements to the // package. To expedite development and avoid excessive workarounds (ie. line 121), a full form refactor should be completed. -export function LandingSearch(props: LandingSearchProps) { +export const LandingSearch = (props: LandingSearchProps) => { // We hold a map of county label to county FormOption const countyLabelMap = {} const countyLabels = []