diff --git a/backend/src/services/units.ts b/backend/src/services/units.ts index f2a6810..1b0cff4 100644 --- a/backend/src/services/units.ts +++ b/backend/src/services/units.ts @@ -1,4 +1,4 @@ -import { UpdateQuery } from "mongoose"; +import { FilterQuery, UpdateQuery } from "mongoose"; import { Unit, UnitModel } from "@/models/units"; @@ -24,11 +24,23 @@ export type EditUnitBody = { dateAvailable: string } & Omit { const minPrice = filters.minPrice === "undefined" ? 0 : +(filters.minPrice ?? 0); const maxPrice = filters.maxPrice === "undefined" ? 100000 : +(filters.maxPrice ?? 100000); + const minSecurityDeposit = + filters.minSecurityDeposit === "undefined" ? 0 : +(filters.minSecurityDeposit ?? 0); + const maxSecurityDeposit = + filters.maxSecurityDeposit === "undefined" ? 100000 : +(filters.maxSecurityDeposit ?? 100000); + + const minApplicationFee = + filters.minApplicationFee === "undefined" ? 0 : +(filters.minApplicationFee ?? 0); + const maxApplicationFee = + filters.maxApplicationFee === "undefined" ? 100000 : +(filters.maxApplicationFee ?? 100000); + + const minSize = filters.minSize === "undefined" ? 0 : +(filters.minSize ?? 0); + const maxSize = filters.maxSize === "undefined" ? 100000 : +(filters.maxSize ?? 100000); + + const dateRegex = /^\d{4}-\d{2}-\d{2}$/; + const fromDate = dateRegex.test(filters.fromDate ?? "") ? filters.fromDate : "1900-01-01"; + const toDate = dateRegex.test(filters.toDate ?? "") ? filters.toDate : "2100-01-01"; + const avail = filters.availability ? (filters.availability === "Available" ? true : false) : true; const approved = filters.approved ? (filters.approved === "approved" ? true : false) : true; @@ -115,12 +144,78 @@ export const getUnits = async (filters: FilterParams) => { break; } - const units = await UnitModel.find({ + const accessibilityCheckboxMap = new Map([ + ["First Floor", "1st floor"], + ["> Second Floor", "2nd floor and above"], + ["Ramps", "Ramps up to unit"], + ["Stairs Only", "Stairs only"], + ["Elevators", "Elevators to unit"], + ]); + + const rentalCriteriaCheckboxMap = new Map([ + ["3rd Party Payment", "3rd party payment accepting"], + ["Credit Check Required", "Credit check required"], + ["Background Check Required", "Background check required"], + ["Program Letter Required", "Program letter required"], + ]); + + const additionalRulesCheckboxMap = new Map([ + ["Pets Allowed", "Pets allowed"], + ["Manager On Site", "Manager on site"], + ["Quiet Building", "Quiet Building"], + ["Visitor Policies", "Visitor Policies"], + ["Kid Friendly", "Kid friendly"], + ["Min-management Interaction", "Minimal-management interaction"], + ["High-management Interaction", "High-management interaction"], + ]); + + const hasHousingAuthority = filters.housingAuthority !== "Any"; + const hasAccessibility = !(filters.accessibility === undefined || filters.accessibility === "[]"); + const rentalCriteria = !(filters.rentalCriteria === undefined || filters.rentalCriteria === "[]"); + const additionalRules = !( + filters.additionalRules === undefined || filters.additionalRules === "[]" + ); + + const query: FilterQuery = { numBeds: { $gte: filters.beds ?? 1 }, numBaths: { $gte: filters.baths ?? 0.5 }, monthlyRent: { $gte: minPrice, $lte: maxPrice }, + securityDeposit: { $gte: minSecurityDeposit, $lte: maxSecurityDeposit }, + applicationFeeCost: { $gte: minApplicationFee, $lte: maxApplicationFee }, + sqft: { $gte: minSize, $lte: maxSize }, + dateAvailable: { $gte: fromDate, $lte: toDate }, approved, - }).sort(sortingCriteria); + }; + + if (hasHousingAuthority) { + query.housingAuthority = filters.housingAuthority ?? { $exists: true }; + } + + if (hasAccessibility) { + query.accessibility = { + $in: (JSON.parse(filters.accessibility ?? "[]") as string[]).map((str: string) => + accessibilityCheckboxMap.get(str), + ) as string[], + }; + } + + if (rentalCriteria) { + query.paymentRentingCriteria = { + $in: (JSON.parse(filters.rentalCriteria ?? "[]") as string[]).map((str: string) => + rentalCriteriaCheckboxMap.get(str), + ) as string[], + }; + } + + if (additionalRules) { + query.additionalRules = { + $in: (JSON.parse(filters.additionalRules ?? "[]") as string[]).map((str: string) => + additionalRulesCheckboxMap.get(str), + ) as string[], + }; + } + + const units = await UnitModel.find(query).sort(sortingCriteria); const filteredUnits = units.filter((unit: Unit) => { return addressRegex.test(unit.listingAddress) && unit.availableNow === avail; diff --git a/frontend/src/api/units.ts b/frontend/src/api/units.ts index 1e6639f..22099e7 100644 --- a/frontend/src/api/units.ts +++ b/frontend/src/api/units.ts @@ -43,11 +43,80 @@ export type Unit = { updatedAt: string; }; +export const AVAILABILITY_OPTIONS = ["Available", "Leased"]; +export type AvailableOptions = (typeof AVAILABILITY_OPTIONS)[number]; + +export const HOUSING_AUTHORITY_OPTIONS = ["Any", "LACDA", "HACLA"]; +export type HousingAuthorityOptions = (typeof HOUSING_AUTHORITY_OPTIONS)[number]; + +export const ACCESSIBILITY_OPTIONS = [ + "First Floor", + "> Second Floor", + "Stairs Only", + "Ramps", + "Elevators", +]; +export type AccessibilityOptions = (typeof ACCESSIBILITY_OPTIONS)[number]; + +export const RENTAL_CRITERIA_OPTIONS = [ + "3rd Party Payment", + "Credit Check Required", + "Background Check Required", + "Program Letter Required", +]; +export type RentalCriteriaOptions = (typeof RENTAL_CRITERIA_OPTIONS)[number]; + +export const ADDITIONAL_RULES_OPTIONS = [ + "Pets Allowed", + "Manager On Site", + "Quiet Building", + "Visitor Policies", + "Kid Friendly", + "Min-management Interaction", + "High-management Interaction", +]; +export type AdditionalRulesOptions = (typeof ADDITIONAL_RULES_OPTIONS)[number]; + export type FilterParams = { + search?: string; + availability?: AvailableOptions; + housingAuthority?: HousingAuthorityOptions; + accessibility?: AccessibilityOptions[]; + rentalCriteria?: RentalCriteriaOptions[]; + additionalRules?: AdditionalRulesOptions[]; + minPrice?: number; + maxPrice?: number; + minSecurityDeposit?: number; + maxSecurityDeposit?: number; + minApplicationFee?: number; + maxApplicationFee?: number; + minSize?: number; + maxSize?: number; + fromDate?: string; + toDate?: string; + beds?: number; + baths?: number; + sort?: string; + approved?: "pending" | "approved"; +}; + +export type GetUnitsParams = { search?: string; availability?: string; + housingAuthority?: string; + accessibility?: string; + rentalCriteria?: string; + additionalRules?: string; minPrice?: string; maxPrice?: string; + minSecurityDeposit?: string; + maxSecurityDeposit?: string; + minApplicationFee?: string; + maxApplicationFee?: string; + minSize?: string; + maxSize?: string; + fromDate?: string; + toDate?: string; beds?: string; baths?: string; sort?: string; @@ -64,7 +133,7 @@ export async function getUnit(id: string): Promise> { } } -export async function getUnits(params: FilterParams): Promise> { +export async function getUnits(params: GetUnitsParams): Promise> { try { const queryParams = new URLSearchParams(params); const url = `/units?${queryParams.toString()}`; diff --git a/frontend/src/components/DateFilter.tsx b/frontend/src/components/DateFilter.tsx index 7236e95..3e7ae7f 100644 --- a/frontend/src/components/DateFilter.tsx +++ b/frontend/src/components/DateFilter.tsx @@ -29,15 +29,42 @@ const FilterHeaderSpan = styled.span` font-size: 14px; `; -export const DateFilter = () => { +export type DateFilterValueType = { + from: string; + to: string; +}; + +export type DateFilterProps = { + title: string; + value: DateFilterValueType; + setValue(value: DateFilterValueType): void; +}; + +export const DateFilter = (props: DateFilterProps) => { return ( - + From - + { + props.setValue({ + from: e.target.value, + to: props.value.to, + }); + }} + /> To - + { + props.setValue({ + from: props.value.from, + to: e.target.value, + }); + }} + /> ); diff --git a/frontend/src/components/FilterDropdown.tsx b/frontend/src/components/FilterDropdown.tsx index c6a15d4..7f131ee 100644 --- a/frontend/src/components/FilterDropdown.tsx +++ b/frontend/src/components/FilterDropdown.tsx @@ -1,9 +1,8 @@ -import { useEffect, useState } from "react"; +import { useContext } from "react"; import styled from "styled-components"; -import { FilterParams } from "@/api/units"; -import { FilterText } from "@/components/FilterCommon"; import { SortDropDownComp } from "@/components/SortDropDown"; +import { FiltersContext } from "@/pages/Home"; const AllFiltersContainer = styled.div` display: flex; @@ -63,79 +62,13 @@ const SearchBarContainer = styled.div` box-shadow: 1px 1px 2px 0px rgba(188, 186, 183, 0.4); `; -const ResetIcon = styled.img` - height: 25px; - width: 25px; -`; - -const ResetFilterButton = styled.button` - background-color: transparent; - border-color: transparent; - cursor: pointer; -`; - -const ResetFilterText = styled(FilterText)` - color: #b64201; - font-weight: 500; - padding-top: 2px; -`; - -const ResetFilterRow = styled.div` - display: flex; - flex-direction: row; - justify-content: flex-start; - align-items: flex-center; - gap: 8px; -`; - -type FilterDropdownProps = { - value: FilterParams; - refreshUnits(filterParams: FilterParams): void; +export type FilterDropdownProps = { + searchText: string; + sortIndex: number; }; export const FilterDropdown = (props: FilterDropdownProps) => { - const [bedBathState, setBedBathState] = useState({ - beds: Number(props.value.beds ?? 1), - baths: Number(props.value.baths ?? 0.5), - }); - const [searchText, setSearchText] = useState(""); - const [sortIndex, setSortIndex] = useState(0); - const [availabilityState, setAvailabilityState] = useState({ - dropdownText: props.value.availability ?? "Available", - }); - const [priceState, setPriceState] = useState({ - minPrice: String(props.value.minPrice) === "undefined" ? -1 : Number(props.value.minPrice), - maxPrice: String(props.value.maxPrice) === "undefined" ? -1 : Number(props.value.maxPrice), - }); - - const applyFilters = () => { - const filters = { - search: searchText ?? "undefined", - beds: String(bedBathState.beds), - baths: String(bedBathState.baths), - sort: String(sortIndex), - availability: availabilityState.dropdownText, - minPrice: priceState.minPrice === -1 ? "undefined" : String(priceState.minPrice), - maxPrice: priceState.maxPrice === -1 ? "undefined" : String(priceState.maxPrice), - }; - - props.refreshUnits(filters); - }; - - useEffect(() => { - applyFilters(); - }, [sortIndex, searchText, priceState, bedBathState, availabilityState]); - - const resetFilters = () => { - setBedBathState({ beds: 1, baths: 0.5 }); - setSearchText(""); - setSortIndex(0); - setAvailabilityState({ dropdownText: "Available" }); - setPriceState({ - minPrice: -1, - maxPrice: -1, - }); - }; + const { filters, setFilters } = useContext(FiltersContext); return ( @@ -143,23 +76,21 @@ export const FilterDropdown = (props: FilterDropdownProps) => { { - setSearchText(event.target.value); + setFilters({ ...filters, search: event.target.value }); }} /> - + - - - - - Reset filters - - - + { + setFilters({ ...filters, sort: String(val) }); + }} + /> ); }; diff --git a/frontend/src/components/FilterPanel.tsx b/frontend/src/components/FilterPanel.tsx index 0d05560..751333b 100644 --- a/frontend/src/components/FilterPanel.tsx +++ b/frontend/src/components/FilterPanel.tsx @@ -1,12 +1,21 @@ -import { useState } from "react"; +import { useContext, useState } from "react"; import styled from "styled-components"; import { BedBathFilter } from "./BedBathFilter"; import { CheckboxFilter } from "./CheckboxFilter"; import { DateFilter } from "./DateFilter"; -import { MinMaxFitler } from "./MinMaxFilter"; +import { MinMaxFilter } from "./MinMaxFilter"; import { RadioButtonFilter } from "./RadioButtonFilter"; +import { + ACCESSIBILITY_OPTIONS, + ADDITIONAL_RULES_OPTIONS, + AVAILABILITY_OPTIONS, + HOUSING_AUTHORITY_OPTIONS, + RENTAL_CRITERIA_OPTIONS, +} from "@/api/units"; +import { FiltersContext } from "@/pages/Home"; + const PanelBackground = styled.div` min-width: 284px; background-color: #fff; @@ -85,84 +94,187 @@ const EndFilterGap = styled.div` min-height: 50px; `; -const AvailabilityOptions = ["Available", "Leased"]; - -const HousingAuthorityOptions = ["LACDA", "HACLA"]; - -const AccessibilityOptions = ["First Floor", "> Second Floor", "Stairs Only", "Ramps", "Elevators"]; - -const RentalCriteriaOptions = [ - "3rd Party Payment", - "Credit Check Required", - "Background Check Required", - "Program Letter Required", -]; - -const AdditionalRulesOptions = [ - "Pets Allowed", - "Manager On Site", - "Quiet Building", - "Visitor Policies", - "Kid Friendly", - "Min-management Interaction", - "High-management Interaction", -]; - -export const FitlerPanel = () => { - const [availabilityState, setAvailabilityState] = useState(0); - const [housingAuthorityState, setHousingAuthorityState] = useState(0); - const [accessibilityState, setAccessibilityState] = useState>(new Set()); - const [rentalCriteriaState, setRentalCriteriaState] = useState>(new Set()); - const [additionalRulesState, setAdditionalRulesState] = useState>(new Set()); +export const FilterPanel = () => { + const { filters, setFilters } = useContext(FiltersContext); + + const [availabilityState, setAvailabilityState] = useState( + filters.availability ? AVAILABILITY_OPTIONS.indexOf(filters.availability) : 0, + ); + const [housingAuthorityState, setHousingAuthorityState] = useState( + filters.housingAuthority ? HOUSING_AUTHORITY_OPTIONS.indexOf(filters.housingAuthority) : 0, + ); + const [accessibilityState, setAccessibilityState] = useState>( + new Set( + filters.accessibility + ? filters.accessibility.map((option) => ACCESSIBILITY_OPTIONS.indexOf(option)) + : [], + ), + ); + const [rentalCriteriaState, setRentalCriteriaState] = useState>( + new Set( + filters.rentalCriteria + ? filters.rentalCriteria.map((option) => RENTAL_CRITERIA_OPTIONS.indexOf(option)) + : [], + ), + ); + const [additionalRulesState, setAdditionalRulesState] = useState>( + new Set( + filters.additionalRules + ? filters.additionalRules.map((option) => ADDITIONAL_RULES_OPTIONS.indexOf(option)) + : [], + ), + ); const [bedBathState, setBedBathState] = useState({ - beds: 1, - baths: 0.5, + beds: filters.beds ?? 1, + baths: filters.baths ?? 0.5, + }); + const [priceState, setPriceState] = useState({ + min: filters.minPrice ?? 0, + max: filters.maxPrice ?? 10000, + }); + const [securityDepositState, setSecurityDepositState] = useState({ + min: filters.minSecurityDeposit ?? 0, + max: filters.maxSecurityDeposit ?? 10000, + }); + const [applicationFeeState, setApplicationFeeState] = useState({ + min: filters.minApplicationFee ?? 0, + max: filters.maxApplicationFee ?? 10000, + }); + const [sizeState, setSizeState] = useState({ + min: filters.minSize ?? 0, + max: filters.maxSize ?? 10000, }); + const [dateState, setDateState] = useState({ + from: filters.fromDate ?? "", + to: filters.toDate ?? "", + }); + + const applyFilters = () => { + const newFilters = { + availability: AVAILABILITY_OPTIONS[availabilityState], + housingAuthority: HOUSING_AUTHORITY_OPTIONS[housingAuthorityState], + accessibility: Array.from(accessibilityState).map((index) => ACCESSIBILITY_OPTIONS[index]), + + rentalCriteria: Array.from(rentalCriteriaState).map( + (index) => RENTAL_CRITERIA_OPTIONS[index], + ), + + additionalRules: Array.from(additionalRulesState).map( + (index) => ADDITIONAL_RULES_OPTIONS[index], + ), + beds: bedBathState.beds, + baths: bedBathState.baths, + minPrice: priceState.min, + maxPrice: priceState.max, + minSecurityDeposit: securityDepositState.min, + maxSecurityDeposit: securityDepositState.max, + minApplicationFee: applicationFeeState.min, + maxApplicationFee: applicationFeeState.max, + minSize: sizeState.min, + maxSize: sizeState.max, + fromDate: dateState.from, + toDate: dateState.to, + }; + + setFilters({ + ...newFilters, + approved: filters.approved, + sort: filters.sort ?? "", + search: filters.search ?? "", + }); + }; + + const resetFilters = () => { + setAvailabilityState(0); + setHousingAuthorityState(0); + setAccessibilityState(new Set()); + setRentalCriteriaState(new Set()); + setAdditionalRulesState(new Set()); + setBedBathState({ beds: 1, baths: 0.5 }); + setPriceState({ min: 0, max: 10000 }); + setSecurityDepositState({ min: 0, max: 10000 }); + setApplicationFeeState({ min: 0, max: 10000 }); + setSizeState({ min: 0, max: 10000 }); + setDateState({ from: "", to: "" }); + + setFilters({ + availability: "Available", + approved: "approved", + }); + }; return ( Filters - Clear All + Clear All - - - - + + + + - + - + Filter Icon Apply diff --git a/frontend/src/components/FilterRangeInput.tsx b/frontend/src/components/FilterRangeInput.tsx index d3912f5..3126ca7 100644 --- a/frontend/src/components/FilterRangeInput.tsx +++ b/frontend/src/components/FilterRangeInput.tsx @@ -9,10 +9,14 @@ const RangeRow = styled.div` const RangeInput = styled.input.attrs({ type: "text", - pattern: "[0-9]*", })` - width: 98px; + width: 100%; padding: 5px; + border: none; + + &:focus { + outline: none; + } `; const RangeInputTitle = styled.span` @@ -37,6 +41,38 @@ const RangeDivider = styled.span` padding-top: 15px; `; +const TextBoxContainer = styled.div` + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + padding-left: 5px; + padding-right: 5px; + + border-radius: 1.861px; + border: 0.465px solid #b4b4b4; +`; + +const TextboxPrefix = styled.span` + color: #000; + font-family: Montserrat; + font-size: 13.596px; + font-style: normal; + font-weight: 700; + line-height: 150%; /* 20.394px */ + letter-spacing: 0.272px; +`; + +const TextboxSuffix = styled.span` + color: #000; + font-family: Montserrat; + font-size: 11px; + font-style: normal; + font-weight: 500; + line-height: 150%; /* 16.5px */ + letter-spacing: 0.22px; +`; + export type FilterRangeInputType = "price" | "sqft"; export type FilterRangeInputValue = { @@ -52,17 +88,35 @@ export type FilterRangeInputProps = { setValue(val: FilterRangeInputValue): void; }; -export const FilterRangeInput = () => { +export const FilterRangeInput = (props: FilterRangeInputProps) => { + const changeHandler = (e: React.ChangeEvent) => { + const newValue = e.target.value; + if (/^\d*$/.test(newValue)) { + props.setValue({ + ...props.value, + [e.target.name]: newValue === "" ? 0 : parseInt(newValue), + }); + } + }; + return ( Min - + + {props.price === "price" && $} + + {props.price === "sqft" && sqft} + Max - + + {props.price === "price" && $} + + {props.price === "sqft" && sqft} + ); diff --git a/frontend/src/components/MinMaxFilter.tsx b/frontend/src/components/MinMaxFilter.tsx index c7b462b..aa3cd83 100644 --- a/frontend/src/components/MinMaxFilter.tsx +++ b/frontend/src/components/MinMaxFilter.tsx @@ -1,16 +1,16 @@ import { FilterContainer } from "./FilterCommon"; import { FilterHeader } from "./FilterHeader"; -import { FilterRangeInput } from "./FilterRangeInput"; +import { FilterRangeInput, FilterRangeInputProps } from "./FilterRangeInput"; -export type MinMaxFilterProps = { +export type MinMaxFilterProps = FilterRangeInputProps & { title: string; }; -export const MinMaxFitler = (props: MinMaxFilterProps) => { +export const MinMaxFilter = (props: MinMaxFilterProps) => { return ( - + ); }; diff --git a/frontend/src/components/SortDropDown.tsx b/frontend/src/components/SortDropDown.tsx index 29f2521..88f5702 100644 --- a/frontend/src/components/SortDropDown.tsx +++ b/frontend/src/components/SortDropDown.tsx @@ -8,6 +8,7 @@ import { DropDownPopup, DropdownIcon, FilterSubContainer, Sort } from "@/compone const SortDropDown = styled(DropDownPopup)` margin-top: 30px; padding-right: 70px; + z-index: 1; `; const SortRow = styled.div` diff --git a/frontend/src/components/UnitCard.tsx b/frontend/src/components/UnitCard.tsx index 79c907f..878c217 100644 --- a/frontend/src/components/UnitCard.tsx +++ b/frontend/src/components/UnitCard.tsx @@ -1,5 +1,5 @@ import { useContext, useEffect, useState } from "react"; -import { Link } from "react-router-dom"; +import { Link, useLocation } from "react-router-dom"; import styled from "styled-components"; import { Button } from "./Button"; @@ -211,6 +211,7 @@ type CardProps = { }; export const UnitCard = ({ unit, refreshUnits }: CardProps) => { + const { pathname } = useLocation(); const { filters } = useContext(FiltersContext); const [popup, setPopup] = useState(false); const dataContext = useContext(DataContext); @@ -275,7 +276,11 @@ export const UnitCard = ({ unit, refreshUnits }: CardProps) => { return ( <> - + diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 48402b9..131f69d 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -3,16 +3,19 @@ import { Helmet } from "react-helmet-async"; import { useLocation } from "react-router-dom"; import styled from "styled-components"; -import { FilterParams, Unit, getUnits } from "@/api/units"; +import { FilterParams, GetUnitsParams, Unit, getUnits } from "@/api/units"; import { FilterDropdown } from "@/components/FilterDropdown"; -import { FitlerPanel } from "@/components/FilterPanel"; +import { FilterPanel } from "@/components/FilterPanel"; import { NavBar } from "@/components/NavBar"; import { Page } from "@/components/Page"; import { UnitCardGrid } from "@/components/UnitCardGrid"; -export const FiltersContext = React.createContext({ - filters: {} as FilterParams, -}); +export type FilterContextType = { + filters: FilterParams; + setFilters: (filters: FilterParams) => void; +}; + +export const FiltersContext = React.createContext({} as FilterContextType); const HomePageLayout = styled.div` display: flex; @@ -37,7 +40,39 @@ export function Home() { ); const fetchUnits = (filterParams: FilterParams) => { - getUnits(filterParams) + let query: GetUnitsParams = { + sort: filterParams.sort, + approved: filterParams.approved, + search: filterParams.search, + // Filter Panel Filters + availability: filterParams.availability, + housingAuthority: filterParams.housingAuthority, + accessibility: filterParams.accessibility + ? JSON.stringify(Array.from(filterParams.accessibility)) + : undefined, + rentalCriteria: filterParams.rentalCriteria + ? JSON.stringify(Array.from(filterParams.rentalCriteria)) + : undefined, + additionalRules: filterParams.additionalRules + ? JSON.stringify(Array.from(filterParams.additionalRules)) + : undefined, + beds: filterParams.beds?.toString(), + baths: filterParams.baths?.toString(), + minPrice: filterParams.minPrice?.toString(), + maxPrice: filterParams.maxPrice?.toString(), + minSecurityDeposit: filterParams.minSecurityDeposit?.toString(), + maxSecurityDeposit: filterParams.maxSecurityDeposit?.toString(), + minApplicationFee: filterParams.minApplicationFee?.toString(), + maxApplicationFee: filterParams.maxApplicationFee?.toString(), + minSize: filterParams.minSize?.toString(), + maxSize: filterParams.maxSize?.toString(), + fromDate: filterParams.fromDate, + toDate: filterParams.toDate, + }; + + query = Object.fromEntries(Object.entries(query).filter(([_, value]) => value !== undefined)); + + getUnits(query) .then((response) => { if (response.success) { setUnits(response.data); @@ -51,32 +86,30 @@ export function Home() { }, [filters]); return ( - - - Home | USHS Housing Portal - - - - - -
- { - filterParams.approved = filters.approved; - setFilters(filterParams); - }} - > - { - const newFilters = { ...filters, approved }; - fetchUnits(newFilters); - setFilters(newFilters); - }} - /> -
-
-
+ + + + Home | USHS Housing Portal + + + + + +
+ + { + const newFilters = { ...filters, approved }; + setFilters(newFilters); + }} + /> +
+
+
+
); } diff --git a/frontend/src/pages/RenterCandidatePage.tsx b/frontend/src/pages/RenterCandidatePage.tsx index f38733b..bc53177 100644 --- a/frontend/src/pages/RenterCandidatePage.tsx +++ b/frontend/src/pages/RenterCandidatePage.tsx @@ -1,7 +1,7 @@ import { useContext, useEffect, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useParams } from "react-router"; -import { Link, useNavigate } from "react-router-dom"; +import { Link, useLocation, useNavigate } from "react-router-dom"; import styled from "styled-components"; import { Loading } from "./Loading"; @@ -275,6 +275,7 @@ const CustomAuthorityInput = styled.input` type ReferralQuery = Record>; export function RenterCandidatePage() { + const { pathname } = useLocation(); const navigate = useNavigate(); const [renterCandidate, setRenterCandidate] = useState(); const [renterReferrals, setRenterReferrals] = useState(); @@ -637,6 +638,7 @@ export function RenterCandidatePage() { {unit.listingAddress} , @@ -739,6 +741,7 @@ export function RenterCandidatePage() { {unit.listingAddress} , diff --git a/frontend/src/pages/UnitDetails.tsx b/frontend/src/pages/UnitDetails.tsx index ce61803..6a17c6a 100644 --- a/frontend/src/pages/UnitDetails.tsx +++ b/frontend/src/pages/UnitDetails.tsx @@ -333,8 +333,10 @@ const CarouselVideo = styled.video` padding: 0px 7.5px; `; +type UnitDetailsLocationState = { filters: FilterParams; prevPage: string }; + export function UnitDetails() { - const filters = useLocation().state as FilterParams; + const { filters, prevPage } = useLocation().state as UnitDetailsLocationState; const navigate = useNavigate(); const [loading, setLoading] = useState(true); const [unit, setUnit] = useState(); @@ -558,15 +560,7 @@ export function UnitDetails() {
- { - e.preventDefault(); - // go back relative to navigation history - navigate(-1); - }} - state={filters} - > +