Skip to content

Commit 82b8c51

Browse files
committed
Filter frontend
1 parent 337b9d4 commit 82b8c51

File tree

7 files changed

+449
-162
lines changed

7 files changed

+449
-162
lines changed
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Loading

frontend/src/components/ApartmentCard/HomePageApartmentCards.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ const ApartmentCards = ({
116116
<div className={container}>
117117
<div className={header}>
118118
<Typography className={titleText}>{title}</Typography>
119-
<Button className={viewMoreButton}>View More</Button>
119+
<Button className={viewMoreButton} disableRipple>
120+
View More
121+
</Button>
120122
</div>
121123
<div className={cardsContainer}>
122124
{data &&

frontend/src/components/ApartmentCard/LargeApartmentCard.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ import bedIcon from '../../assets/apartment-card-bedroom-icon.svg';
1616
import moneyIcon from '../../assets/apartment-card-money-icon.svg';
1717
import axios from 'axios';
1818
import { createAuthHeaders, getUser } from '../../utils/firebase';
19-
import { ApartmentWithId, ReviewWithId } from '../../../../common/types/db-types';
20-
import FavoriteIcon from '@material-ui/icons/Favorite';
19+
import { ApartmentWithId, DetailedRating, ReviewWithId } from '../../../../common/types/db-types';
2120
import { colors } from '../../colors';
2221
import HeartRating from '../utils/HeartRating';
2322
import ReviewHeader from '../Review/ReviewHeader';
@@ -211,6 +210,7 @@ const NewApartmentCard = ({
211210
const img = photos.length > 0 ? photos[0] : ApartmentImg;
212211
const isMobile = useMediaQuery('(max-width:600px)');
213212
const [reviewList, setReviewList] = useState<ReviewWithId[]>([]);
213+
const [aveRatingInfo, setAveRatingInfo] = useState<RatingInfo[]>([]);
214214
const sampleReview = reviewList.length === 0 ? '' : reviewList[0].reviewText;
215215
const [isSaved, setIsSaved] = useState(false);
216216
const [isHovered, setIsHovered] = useState(false);
@@ -286,14 +286,22 @@ const NewApartmentCard = ({
286286
});
287287
}, [id]);
288288

289-
const calculateAveRating = (): RatingInfo[] => {
289+
const calculateAveRating = (reviews: ReviewWithId[]): RatingInfo[] => {
290290
const features = ['location', 'maintenance', 'safety', 'conditions'];
291291
return features.map((feature) => {
292-
let rating = 4.5;
292+
let key = feature as keyof DetailedRating;
293+
let rating =
294+
reviews.reduce((sum, review) => sum + review.detailedRatings[key], 0) / reviews.length;
295+
293296
return { feature, rating };
294297
});
295298
};
296299

300+
// Set the average rating after calculating it from the data.
301+
useEffect(() => {
302+
setAveRatingInfo(calculateAveRating(reviewList));
303+
}, [reviewList]);
304+
297305
return (
298306
<Card
299307
className={isHovered ? redHighlight : root}
@@ -332,7 +340,7 @@ const NewApartmentCard = ({
332340
</div>
333341
</div>
334342
<div className={apartmentStatsContainer}>
335-
<ReviewHeader aveRatingInfo={calculateAveRating()} isAptCard={true} />
343+
<ReviewHeader aveRatingInfo={aveRatingInfo} isAptCard={true} />
336344
</div>
337345
<div className={sampleReviewContainer}>
338346
<img src={verticalLine} alt="vertical line" style={{ width: '4px', height: '80%' }} />

frontend/src/components/Search/Autocomplete.tsx

Lines changed: 73 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const defaultFilters: FilterState = {
3838
const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
3939
const [isMobile, setIsMobile] = useState<boolean>(false);
4040
const [filters, setFilters] = useState<FilterState>(defaultFilters);
41+
const [openFilter, setOpenFilter] = useState(false);
42+
4143
const useStyles = makeStyles((theme) => ({
4244
menuList: {
4345
position: 'absolute',
@@ -67,6 +69,20 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
6769
fontStyle: 'normal',
6870
fontWeight: 400,
6971
lineHeight: '28px',
72+
boxShadow: '0px 0px 4px 2px rgba(0, 0, 0, 0.05)',
73+
'& .MuiOutlinedInput-root': {
74+
'& fieldset': {
75+
borderColor: '#F9F9F9',
76+
borderWidth: '1px',
77+
},
78+
'&:hover fieldset': {
79+
borderColor: '#E5E5E5',
80+
},
81+
'&.Mui-focused fieldset': {
82+
borderColor: '#E5E5E5',
83+
borderWidth: '1px',
84+
},
85+
},
7086
},
7187
subText: {
7288
color: colors.gray2,
@@ -97,57 +113,37 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
97113
display: 'flex',
98114
alignItems: 'center',
99115
justifyContent: 'center',
100-
border: '1px solid red',
101-
},
102-
searchIconBackground: {
103-
backgroundColor: colors.red1,
104-
width: '50px',
105-
height: isMobile ? '35px' : '45px',
106-
position: 'absolute',
107-
right: '0',
108-
borderRadius: '0px 10px 10px 0px',
109-
display: 'flex',
110-
justifyContent: 'center',
111-
alignItems: 'center',
112-
cursor: 'pointer',
116+
gap: '8px',
113117
},
114-
filterIconBackground: {
115-
backgroundColor: colors.white,
116-
width: '40px',
117-
height: isMobile ? '35px' : '40px',
118-
position: 'absolute',
119-
right: '55px',
118+
iconBackground: {
119+
width: '34px',
120+
height: '34px',
120121
display: 'flex',
121122
justifyContent: 'center',
122123
alignItems: 'center',
123124
cursor: 'pointer',
125+
'&:hover': {
126+
backgroundColor: '#F9F9F9',
127+
scale: '1.02',
128+
},
124129
},
125-
searchLabelIcon: {
130+
searchMenuLabelIcon: {
126131
cursor: 'pointer',
127132
display: 'flex',
128133
alignItems: 'center',
129134
},
130135
field: {
131-
borderRadius: '10px',
132-
'&.Mui-focused': {
133-
'& .MuiOutlinedInput-notchedOutline': {
134-
border: `1px solid ${colors.red1}`,
135-
borderRadius: '10px',
136-
},
137-
},
136+
borderRadius: openFilter ? '10px 10px 0px 0px' : '10px',
138137
height: isMobile ? '35px' : '68px',
139138
padding: '24px',
140139
},
141140
}));
142141
const {
143142
searchBarRow,
144-
filterIconBackground,
143+
iconBackground,
145144
text,
146-
searchIcon,
147-
homeSearchIcon,
148145
iconContainer,
149-
searchIconBackground,
150-
searchLabelIcon,
146+
searchMenuLabelIcon,
151147
field,
152148
menuList,
153149
menuItem,
@@ -159,8 +155,7 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
159155
const [query, setQuery] = useState('');
160156
const [width, setWidth] = useState(inputRef.current?.offsetWidth);
161157
const [focus, setFocus] = useState(false);
162-
const [open, setOpen] = useState(false);
163-
const [openFilter, setOpenFilter] = useState(false);
158+
const [openMenu, setOpenMenu] = useState(false);
164159
const [options, setOptions] = useState<LandlordOrApartmentWithLabel[]>([]);
165160
const [selected, setSelected] = useState<LandlordOrApartmentWithLabel | null>(null);
166161
const history = useHistory();
@@ -175,39 +170,50 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
175170
function handleListKeyDown(event: React.KeyboardEvent) {
176171
event.preventDefault();
177172
if (event.key === 'Tab') {
178-
setOpen(false);
173+
setOpenMenu(false);
179174
}
180175
}
181176

177+
function checkIfSearchable() {
178+
return (
179+
query.trim() !== '' ||
180+
Object.values(filters).some((val) =>
181+
Array.isArray(val) ? val.length > 0 : val !== '' && val !== 0
182+
)
183+
);
184+
}
185+
182186
function textFieldHandleListKeyDown(event: React.KeyboardEvent) {
183187
if (event.key === 'ArrowDown') {
184188
setFocus(true);
185-
} else if (event.key === 'Enter') {
189+
} else if (event.key === 'Enter' && checkIfSearchable()) {
186190
setFocus(true);
187191
console.log('Current filter state:', filters);
188192
const filterParams = encodeURIComponent(JSON.stringify(filters));
189193
console.log('Encoded filter params:', filterParams);
190194
history.push(`/search?q=${query}&filters=${filterParams}`);
191195
setQuery('');
192-
setOpen(false);
196+
setOpenMenu(false);
193197
}
194198
}
195199

196200
const handleSearchIconClick = () => {
197-
console.log('Current filter state:', filters);
198-
const filterParams = encodeURIComponent(JSON.stringify(filters));
199-
console.log('Encoded filter params:', filterParams);
200-
history.push(`/search?q=${query}&filters=${filterParams}`);
201-
setQuery('');
201+
if (checkIfSearchable()) {
202+
console.log('Current filter state:', filters);
203+
const filterParams = encodeURIComponent(JSON.stringify(filters));
204+
console.log('Encoded filter params:', filterParams);
205+
history.push(`/search?q=${query}&filters=${filterParams}`);
206+
setQuery('');
207+
}
202208
};
203209

204210
const handleToggleFilter = () => {
205211
setOpenFilter(!openFilter);
206-
setOpen(false);
212+
setOpenMenu(false);
207213
};
208214

209215
const handleClickAway = () => {
210-
setOpen(false);
216+
setOpenMenu(false);
211217
setOpenFilter(false);
212218
};
213219

@@ -231,11 +237,11 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
231237
<div>
232238
<ClickAwayListener
233239
onClickAway={() => {
234-
setOpen(false);
240+
setOpenMenu(false);
235241
}}
236242
>
237243
<div>
238-
{open ? (
244+
{openMenu ? (
239245
<MenuList
240246
style={{ width: `${inputRef.current?.offsetWidth}px`, zIndex: 1 }}
241247
className={menuList}
@@ -258,12 +264,12 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
258264
<MenuItem
259265
button={true}
260266
key={index}
261-
onClick={() => setOpen(false)}
267+
onClick={() => setOpenMenu(false)}
262268
className={menuItem}
263269
style={index === options.length - 1 ? { borderBottom: 'none' } : {}}
264270
>
265271
<Grid container spacing={2} alignItems="center">
266-
<Grid item className={searchLabelIcon}>
272+
<Grid item className={searchMenuLabelIcon}>
267273
<img
268274
src={label === 'LANDLORD' ? searchLandlordIcon : searchPropertyIcon}
269275
alt="search icon"
@@ -293,11 +299,11 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
293299
};
294300
useEffect(() => {
295301
if (query === '') {
296-
setOpen(false);
302+
setOpenMenu(false);
297303
} else if (selected === null) {
298-
setOpen(true);
304+
setOpenMenu(true);
299305
} else {
300-
setOpen(false);
306+
setOpenMenu(false);
301307
}
302308
}, [query, selected]);
303309

@@ -343,24 +349,25 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
343349
<div className={iconContainer}>
344350
<IconButton
345351
onClick={(e) => {
346-
e.stopPropagation(); // Prevent click from triggering ClickAwayListener
352+
e.stopPropagation();
347353
handleToggleFilter();
348354
}}
355+
className={iconBackground}
349356
disableRipple
350357
>
351-
<img src={filterIcon} alt={'filter-icon'} />
358+
<img src={filterIcon} alt={'filter-icon'} style={{ width: '32px', height: '32px' }} />
352359
</IconButton>
353360
<IconButton
354361
onClick={(e) => {
355-
e.stopPropagation(); // Prevent click from triggering ClickAwayListener
356-
handleToggleFilter();
362+
e.stopPropagation();
363+
handleSearchIconClick();
357364
}}
358-
className={filterIconBackground}
365+
className={iconBackground}
359366
disableRipple
360367
>
361-
<img src={SearchIcon} alt="search icon" />
368+
<img src={SearchIcon} alt="search icon" style={{ width: '34px', height: '34px' }} />
362369
</IconButton>
363-
{loading ? <CircularProgress color="inherit" size={20} /> : null}
370+
{/* {loading ? <CircularProgress color="inherit" size={20} /> : null} */}
364371
</div>
365372
),
366373
startAdornment: <></>,
@@ -374,7 +381,7 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
374381
endAdornment: (
375382
<React.Fragment>
376383
{loading ? <CircularProgress color="inherit" size={20} /> : null}
377-
<div className={searchIconBackground} onClick={handleSearchIconClick}>
384+
<div className={iconBackground} onClick={handleSearchIconClick}>
378385
<img src={SearchIcon} alt="search icon" />
379386
</div>
380387
</React.Fragment>
@@ -397,9 +404,8 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
397404
className={text}
398405
variant="outlined"
399406
style={{
400-
borderRadius: '10px',
407+
borderRadius: openFilter ? '10px 10px 0px 0px' : '10px',
401408
width: !isMobile ? '100%' : '98%',
402-
border: '1px solid red',
403409
}}
404410
onKeyDown={textFieldHandleListKeyDown}
405411
onChange={(event) => {
@@ -412,7 +418,12 @@ const Autocomplete = ({ drawerOpen }: Props): ReactElement => {
412418
/>
413419
</div>
414420
<Menu />
415-
<FilterSection filters={filters} onChange={handleFilterChange} open={openFilter} />
421+
<FilterSection
422+
filters={filters}
423+
onChange={handleFilterChange}
424+
open={openFilter}
425+
handleSearch={handleSearchIconClick}
426+
/>
416427
</div>
417428
</ClickAwayListener>
418429
</div>

0 commit comments

Comments
 (0)