Skip to content

Commit

Permalink
build(filter): auto hide(#17)
Browse files Browse the repository at this point in the history
Enabling the filter button & unselect of the years item from the year filter
  • Loading branch information
WeiViv authored Oct 18, 2024
1 parent 21f723c commit 070577d
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 91 deletions.
30 changes: 27 additions & 3 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,39 @@ import './App.css';
const AppContent = ({ currentPage, setCurrentPage }) => {
const location = useLocation();
const isEditProfilePage = location.pathname === '/edit-profile';
const isHomePage = location.pathname === '/'; // Check if we are on the HomePage

const [isFilterOpen, setIsFilterOpen] = useState(false); // State for the filter visibility

const handleFilterToggle = (forceClose = null) => {
if (forceClose === false) {
setIsFilterOpen(false); // Force close the filter
} else {
setIsFilterOpen((prev) => !prev); // Toggle the filter
}
};

return (
<ThemeProvider theme={theme}>
{!isEditProfilePage && <Header />}
{!isEditProfilePage && (
<Header
onFilterToggle={handleFilterToggle} // Pass filter toggle handler
isFilterOpen={isFilterOpen} // Pass filter open state
showFilter={isHomePage} // Only show filter button on HomePage
/>
)}
<div className="container">
<Routes>
<Route path="/" element={<HomePage />} />
<Route
path="/"
element={
<HomePage
isFilterOpen={isFilterOpen}
handleFilterToggle={handleFilterToggle} // Pass the filter logic to HomePage
/>
}
/>
<Route path="groups" element={<GroupsPage />} />
{/* <Route path="messages" element={<div>TBD</div>} /> */}
<Route path="profile/:id" element={<ProfilePage />} />
<Route path="edit-profile" element={<EditProfile />} />
<Route path="time-preferences" element={<TimePreferencesPage />} />
Expand Down
58 changes: 18 additions & 40 deletions src/components/Home/StudentFilter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,7 @@ import React from 'react';

import useCourses from '@data/useCourses';
import useMajors from '@data/useMajors';
import {
Box,
Autocomplete,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Chip,
OutlinedInput,
} from '@mui/material';
import { Box, Autocomplete, TextField } from '@mui/material';

export default function StudentFilter({
selectedMajors,
Expand Down Expand Up @@ -50,40 +40,28 @@ export default function StudentFilter({
renderInput={(params) => (
<TextField {...params} label="Filter by Course(s)" variant="outlined" />
)}
filterOptions={(options, { inputValue }) => {
// Convert inputValue to lowercase for case-insensitive matching
const lowercasedInput = inputValue.toLowerCase();
// filterOptions={(options, { inputValue }) => {
// // Convert inputValue to lowercase for case-insensitive matching
// const lowercasedInput = inputValue.toLowerCase();

// Only include options that start with the input
return options.filter((option) => option.toLowerCase().startsWith(lowercasedInput));
}}
// // Only include options that start with the input
// return options.filter((option) => option.toLowerCase().startsWith(lowercasedInput));
// }}
sx={{ flex: 1, mb: 2, minWidth: 200, maxWidth: '100%' }}
freeSolo
/>

{/* 3. Year filter */}
<FormControl sx={{ flex: 1, mb: 2, minWidth: 200, maxWidth: '100%' }}>
<InputLabel>Filter by Year(s)</InputLabel>
<Select
multiple
value={selectedYears}
onChange={(event) => setSelectedYears(event.target.value)}
input={<OutlinedInput label="Filter by Year(s)" />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
>
{['Freshman', 'Sophomore', 'Junior', 'Senior', 'Master', 'PhD'].map((year) => (
<MenuItem key={year} value={year}>
{year}
</MenuItem>
))}
</Select>
</FormControl>
<Autocomplete
multiple
options={['Freshman', 'Sophomore', 'Junior', 'Senior', 'Master', 'PhD']}
getOptionLabel={(option) => option}
value={selectedYears}
onChange={(event, newValue) => setSelectedYears(newValue)}
renderInput={(params) => (
<TextField {...params} label="Filter by Year(s)" variant="outlined" />
)}
sx={{ flex: 1, mb: 2, minWidth: 200, maxWidth: '100%' }}
/>
</Box>
);
}
29 changes: 18 additions & 11 deletions src/components/Home/StudentList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,25 @@ export default function StudentList({
selectedYears,
}) {
const studentData = useStudentData();
const filteredStudentData = studentData?.filter((profile) => {
const profileCourses = profile.listOfCourses || []; // Ensure profile has courses
// eslint-disable-next-line max-len
const normalizedSelectedCourses = selectedCourses.map((course) => course.toLowerCase()); // Normalize selected courses to lowercase

const filteredStudentData = studentData?.filter(
(profile) =>
profile.uid !== userProfile.uid &&
!matchedUserUids.has(profile.uid) &&
profile.major !== '' &&
profile.year !== '' &&
(selectedMajors.length === 0 || selectedMajors.includes(profile.major)) &&
(selectedCourses.length === 0 ||
profile.courses?.some((course) => selectedCourses.includes(course))) &&
(selectedYears.length === 0 || selectedYears.includes(profile.year)),
);
// Ensure filtering conditions for majors, courses, and years
return (
profile.uid !== userProfile.uid && // Exclude current user
!matchedUserUids.has(profile.uid) && // Exclude already matched users
profile.major !== '' && // Ensure major is defined
profile.year !== '' && // Ensure year is defined
(selectedMajors.length === 0 || selectedMajors.includes(profile.major)) && // Filter by major
(selectedCourses.length === 0 || // Filter by courses (case-insensitive match)
profileCourses.some((course) =>
normalizedSelectedCourses.includes(course.toLowerCase()),
)) &&
(selectedYears.length === 0 || selectedYears.includes(profile.year)) // Filter by year
);
});

const {
currentData: studentsToDisplay,
Expand Down
82 changes: 57 additions & 25 deletions src/components/HomePage.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,49 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';

import { useAuthState } from '@auth/useAuthState';
import StudentFilter from '@components/Home/StudentFilter';
import StudentList from '@components/Home/StudentList';
import useUserProfile from '@data/useUserProfile';
import { Box, CircularProgress, Typography } from '@mui/material';
import { useNavigate } from 'react-router-dom';

export default function HomePage() {
import StudentFilter from './Home/StudentFilter';

export default function HomePage({ isFilterOpen, handleFilterToggle }) {
// Receive props from AppContent
const [user] = useAuthState();
const { userProfile, requestedUsers, setRequestedUsers, matchedUserUids, loading } =
useUserProfile(user, true);
const [selectedMajors, setSelectedMajors] = useState([]);
const [selectedCourses, setSelectedCourses] = useState([]);
const [selectedYears, setSelectedYears] = useState([]);
const filterRef = useRef(null); // Reference to the filter component
const navigate = useNavigate();

// Redirect user to edit-profile if the profile is incomplete
// Close the filter if clicked outside
const handleClickOutside = (event) => {
// Check if the click is inside the filter or inside the dropdown (Autocomplete or Select)
if (
filterRef.current &&
!filterRef.current.contains(event.target) &&
!document.querySelector('.MuiAutocomplete-popper')?.contains(event.target) && // For Autocomplete dropdowns
!document.querySelector('.MuiPopover-root')?.contains(event.target) // For Select dropdowns
) {
handleFilterToggle(false); // Close the filter only when clicked outside
}
};

useEffect(() => {
if (isFilterOpen) {
document.addEventListener('mousedown', handleClickOutside); // or use 'pointerdown' instead of 'mousedown'
} else {
document.removeEventListener('mousedown', handleClickOutside);
}

return () => {
document.removeEventListener('mousedown', handleClickOutside); // Cleanup the listener
};
}, [isFilterOpen]);

useEffect(() => {
if (userProfile && (!userProfile.major || !userProfile.year || !userProfile.phoneNumber)) {
navigate('/edit-profile');
Expand All @@ -41,33 +68,38 @@ export default function HomePage() {
);
}

// Render the StudentFilter and StudentList only if the userProfile is complete
if (!userProfile || !userProfile.major || !userProfile.year || !userProfile.phoneNumber) {
return null;
}

return (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Box
sx={{
position: 'sticky',
top: 60,
zIndex: 10,
backgroundColor: 'white',
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)',
padding: 1,
marginBottom: 2,
}}
>
<StudentFilter
selectedMajors={selectedMajors}
setSelectedMajors={setSelectedMajors}
selectedCourses={selectedCourses}
setSelectedCourses={setSelectedCourses}
selectedYears={selectedYears}
setSelectedYears={setSelectedYears}
/>
</Box>
{/* Render the filter section only if it's open */}
{isFilterOpen && (
<Box
ref={filterRef}
sx={{
position: 'absolute',
top: 60,
left: 0,
zIndex: 10,
backgroundColor: 'white',
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)',
width: '100%',
padding: 1,
marginBottom: 2,
}}
>
<StudentFilter
selectedMajors={selectedMajors}
setSelectedMajors={setSelectedMajors}
selectedCourses={selectedCourses}
setSelectedCourses={setSelectedCourses}
selectedYears={selectedYears}
setSelectedYears={setSelectedYears}
/>
</Box>
)}
<StudentList
userProfile={userProfile}
requestedUsers={requestedUsers}
Expand Down
33 changes: 21 additions & 12 deletions src/components/common/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import React from 'react';

import { useAuthNavigation } from '@auth/useAuthNavigation';
import { ArrowBack } from '@mui/icons-material';
import FilterAltIcon from '@mui/icons-material/FilterAlt';
import { AppBar, Toolbar, Typography, IconButton, Avatar, Button, Box } from '@mui/material';
import { useTheme } from '@mui/material/styles';
import { useNavigate, useLocation } from 'react-router-dom';

export default function Header() {
export default function Header({ onFilterToggle, isFilterOpen, showFilter }) {
const { user, handleProfileClick, signInAndCheckFirstTimeUser } = useAuthNavigation();
const theme = useTheme();
const navigate = useNavigate();
Expand All @@ -19,7 +20,6 @@ export default function Header() {
location.pathname === '/messages';

const handleBackButtonClick = () => {
// If we came from edit-profile or a similar page, navigate home instead of going back.
if (location.state?.fromEditProfile) {
navigate('/'); // Redirect to the home page
} else if (window.history.length > 2) {
Expand All @@ -32,16 +32,26 @@ export default function Header() {
return (
<AppBar position="sticky" sx={{ backgroundColor: theme.palette.primary.light, color: '#000' }}>
<Toolbar sx={{ position: 'relative', justifyContent: 'space-between' }}>
{/* Left side: Back button or placeholder */}
{!isRootPage ? (
<IconButton edge="start" color="inherit" onClick={handleBackButtonClick}>
<ArrowBack />
</IconButton>
) : (
<Box sx={{ width: '48px' }} />
)}
<Box sx={{ display: 'flex', alignItems: 'center' }}>
{showFilter && (
<IconButton
edge="start"
color="inherit"
onClick={onFilterToggle}
sx={{ marginRight: 1 }}
>
<FilterAltIcon color={isFilterOpen ? 'secondary' : 'inherit'} />
</IconButton>
)}
{!isRootPage ? (
<IconButton edge="start" color="inherit" onClick={handleBackButtonClick}>
<ArrowBack />
</IconButton>
) : (
<Box sx={{ width: '48px' }} /> // Placeholder if no back button is needed
)}
</Box>

{/* Centered text */}
<Typography
variant="h6"
sx={{
Expand All @@ -55,7 +65,6 @@ export default function Header() {
StudyBuddy
</Typography>

{/* Right side: Sign In button or user avatar */}
{!isProfilePage ? (
user ? (
<IconButton edge="end" color="inherit" onClick={handleProfileClick}>
Expand Down

0 comments on commit 070577d

Please sign in to comment.