Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Dashboard Search #61

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function App(): JSX.Element {
<Route exact path="/dashboard">
<Dashboard />
</Route>
<Route exact path="/profile" component={ProfileDetails} />
<Route path="/profile/*" component={ProfileDetails} />
<Route path="*">
<Redirect to="/login" />
</Route>
Expand Down
108 changes: 108 additions & 0 deletions client/src/components/NavBar/BecomeSitter/BecomeSitter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import useStyles from './useStyles';
import { ChangeEvent, Fragment, useState } from 'react';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import FormControl from '@material-ui/core/FormControl';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import { Checkbox, FormGroup, TextField, Typography } from '@material-ui/core';
import Box from '@material-ui/core/Box';

const BecomeSitter = (): JSX.Element => {
const classes = useStyles();

const [state, setState] = useState({
mon: false,
tue: false,
wed: false,
thurs: false,
fri: false,
sat: false,
sun: false,
});

const [open, setOpen] = useState(false);
const [isSitter, setIsSitter] = useState(false);

const handleClickOpen = () => {
setOpen(true);
};

const handleClose = () => {
setOpen(false);
};

const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setState({
...state,
[event.target.name]: event.target.checked,
});
xyecs marked this conversation as resolved.
Show resolved Hide resolved
};

const handleSitter = (event: ChangeEvent<HTMLInputElement>) => {
setIsSitter(event.target.checked);
};

const handleSubmit = () => {
//TODO: Placeholder for editing profile info to switch between sitter and not a sitter
moffatethan marked this conversation as resolved.
Show resolved Hide resolved
console.log('hello');
xyecs marked this conversation as resolved.
Show resolved Hide resolved
setOpen(false);
};

return (
<Fragment>
<Button className={classes.appBarButtons} onClick={handleClickOpen}>
Become a sitter
</Button>
<Dialog fullWidth={true} maxWidth={'sm'} open={open} onClose={handleClose}>
<DialogTitle>Become a sitter</DialogTitle>
<DialogContent>
<DialogContentText>Please set your availability to become a sitter.</DialogContentText>
<Box
component="form"
onSubmit={handleSubmit}
display={'flex'}
flexDirection={'column'}
m={'auto'}
width={'fit-content'}
>
<FormControl>
<Box display={'flex'} mb={2} alignItems={'center'}>
<Typography className={classes.labels}>{isSitter ? `I'M AVAILABLE` : `I'M NOT AVAILABLE`}</Typography>
<Switch checked={isSitter} onChange={handleSitter} />
</Box>
<Box display={'flex'} mb={2} alignItems={'center'}>
<Typography className={classes.labels}>AVAILABILITY:</Typography>
<FormGroup>
<Box>
{Object.entries(state).map(([key, value], index) => (
<FormControlLabel
key={index}
control={<Checkbox checked={value} disabled={!isSitter} onChange={handleChange} name={key} />}
label={key}
/>
))}
</Box>
</FormGroup>
</Box>
<Box display={'flex'} mb={2} alignItems={'center'}>
<Typography className={classes.labels}>PRICE</Typography>
<TextField disabled={!isSitter} id="price" label="price" variant="outlined" />
</Box>
<Button type={'submit'}>Save</Button>
</FormControl>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Close</Button>
</DialogActions>
</Dialog>
</Fragment>
);
};

export default BecomeSitter;
18 changes: 18 additions & 0 deletions client/src/components/NavBar/BecomeSitter/useStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles((theme) => ({
labels: {
fontWeight: 'bold',
},
appBarButtons: {
fontFamily: 'Arial',
fontSize: 14,
padding: 5,
'&:hover': {
backgroundColor: 'rgba(0, 0, 0, 0.3);',
textDecoration: 'underline',
},
},
}));

export default useStyles;
15 changes: 6 additions & 9 deletions client/src/components/NavBar/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@ import useStyles from './useStyles';
import { AppBar, Button } from '@material-ui/core';
import LogoHeader from '../LogoHeader/LogoHeader';
import AuthMenu from '../AuthMenu/AuthMenu';
import { User } from '../../interface/User';
import { Link as RouterLink } from 'react-router-dom';
import { Link as MUILink } from '@material-ui/core';
import AuthHeader from '../AuthHeader/AuthHeader';
import { useAuth } from '../../context/useAuthContext';

interface Props {
loggedIn: boolean;
}
import BecomeSitter from './BecomeSitter/BecomeSitter';

const NavBar = (): JSX.Element => {
const classes = useStyles();
Expand All @@ -30,13 +26,14 @@ const NavBar = (): JSX.Element => {
return (
<Box display={'flex'} alignItems={'center'} justifyContent={'space-evenly'}>
{/* TODO: Refactor links to Array.map, going to leave it for now and wait and see
info coming in from profiles as we may need to make changes to Model*/}
info coming in from profiles as we may need to make changes to Model
<MUILink component={RouterLink} to={"/"}>
<Button className={classes.appBarButtons}>Become a sitter</Button>
</MUILink>*/}
moffatethan marked this conversation as resolved.
Show resolved Hide resolved
<BecomeSitter />
<MUILink component={RouterLink} to={'/'}>
<Button className={classes.appBarButtons}>Notifications</Button>
</MUILink>
<MUILink component={RouterLink} to={'/'}>
<Button className={classes.appBarButtons}>Messages</Button>
</MUILink>
<MUILink component={RouterLink} to={'/'}>
<Button className={classes.appBarButtons}>My Jobs</Button>
</MUILink>
Expand Down
47 changes: 47 additions & 0 deletions client/src/components/ProfileCard/ProfileCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Box from '@material-ui/core/Box';
import useStyles from './useStyles';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import picture from '../../Images/d9fc84a0d1d545d77e78aaad39c20c11d3355074.png';
import { Profile } from '../../interface/Profile';
import { Link } from 'react-router-dom';

interface Props {
profile: Profile;
}

const ProfileCard = ({ profile }: Props): JSX.Element => {
const classes = useStyles();
const { _id: id, firstName, lastName, description, location, hourlyRate } = profile;

return (
<Card
component={Link}
to={`/profile/${id}`}
elevation={8}
className={classes.profileCard}
sx={{ minWidth: 245, maxWidth: 245, minHeight: 350, maxHeight: 350 }}
>
<CardContent>
<Box height={310} display={'flex'} flexDirection={'column'}>
<Box flexGrow={1}>
<img className={classes.picture} src={picture} />
<Typography gutterBottom variant="h5" component="div">
{`${firstName} ${lastName}`}
</Typography>
<Typography variant="body2" color="text.secondary">
{description}
</Typography>
</Box>
<Box display={'flex'} p={1} mt={5} justifyContent={'space-evenly'} className={classes.infoWrapper}>
<Typography>{location}</Typography>
<Typography>{`${hourlyRate}/hr`}</Typography>
</Box>
</Box>
</CardContent>
</Card>
);
};

export default ProfileCard;
21 changes: 21 additions & 0 deletions client/src/components/ProfileCard/useStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles((theme) => ({
profileCard: {
textDecoration: 'none',
overflow: 'hidden',
'&:hover': {
overflow: 'auto',
boxShadow: theme.shadows[23],
},
},
picture: {
borderRadius: '50%',
width: 150,
},
infoWrapper: {
borderTop: '1px solid grey',
},
}));

export default useStyles;
20 changes: 20 additions & 0 deletions client/src/helpers/APICalls/getAllProfiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { FetchOptions } from '../../interface/FetchOptions';
import { AllProfilesApiData } from '../../interface/Profile';

const getAllProfiles = async (
searchValue: string | '',
startValue: Date | null,
endValue: Date | null,
): Promise<AllProfilesApiData> => {
const fetchOptions: FetchOptions = {
method: 'GET',
credentials: 'include',
};
return await fetch(`/profile/profiles?search=${searchValue}&start=${startValue}&end=${endValue}`, fetchOptions)
.then((res) => res.json())
.catch(() => ({
error: { message: 'Unable to connect to server. Please try again' },
}));
};

export default getAllProfiles;
8 changes: 8 additions & 0 deletions client/src/interface/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ export interface User {
username: string;
}

export interface Profile {
firstName: string;
lastName: string;
email: string;
description: string;
location: string;
}

export interface SearchUsersApiData {
users?: User[];
error?: { message: string };
Expand Down
63 changes: 54 additions & 9 deletions client/src/pages/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,44 @@ import CssBaseline from '@material-ui/core/CssBaseline';
import CircularProgress from '@material-ui/core/CircularProgress';
import useStyles from './useStyles';
import { useAuth } from '../../context/useAuthContext';
import { useSocket } from '../../context/useSocketContext';
import { useHistory } from 'react-router-dom';
import ChatSideBanner from '../../components/ChatSideBanner/ChatSideBanner';
import { useEffect } from 'react';
import { ChangeEvent, useEffect, useState } from 'react';
import { Box, Typography } from '@material-ui/core';
import ProfileCard from '../../components/ProfileCard/ProfileCard';
import getAllProfiles from '../../helpers/APICalls/getAllProfiles';
import { Profile } from '../../interface/Profile';
import DashboardSearch from './DashboardSearch/DashboardSearch';

export default function Dashboard(): JSX.Element {
const classes = useStyles();
const [profiles, setProfiles] = useState<Profile[]>([]);
const [searchValue, setSearchValue] = useState<string | ''>('');
const [startValue, setStartValue] = useState<Date | null>(new Date());
const [endValue, setEndValue] = useState<Date | null>(null);

const { loggedInUser } = useAuth();
const { initSocket } = useSocket();

const history = useHistory();

useEffect(() => {
initSocket();
}, [initSocket]);
getAllProfiles(searchValue, startValue, endValue).then((data) => {
setProfiles(data.profiles);
});
}, [searchValue, startValue, endValue]);

const handleStartDateChange = (newValue: Date | null) => {
setStartValue(newValue);
};

const handleEndDateChange = (newValue: Date | null) => {
setEndValue(newValue);
};

const handleSearchChange = (event: ChangeEvent<HTMLInputElement>) => {
setSearchValue(event.target.value);
};

if (loggedInUser === undefined) return <CircularProgress />;

if (!loggedInUser) {
history.push('/login');
// loading for a split seconds until history.push works
Expand All @@ -30,8 +50,33 @@ export default function Dashboard(): JSX.Element {
return (
<Grid container component="main" className={`${classes.root} ${classes.dashboard}`}>
<CssBaseline />
<Grid item className={classes.drawerWrapper}>
<ChatSideBanner loggedInUser={loggedInUser} />
<Grid item className={classes.searchWrapper}>
<Typography className={classes.searchText}>Search for Users</Typography>
<DashboardSearch
handleSearchChange={handleSearchChange}
handleStartDateChange={handleStartDateChange}
handleEndDateChange={handleEndDateChange}
startValue={startValue}
endValue={endValue}
/>
</Grid>

{/**TODO:
*
* Currently using mock profiles, need to change to actual list of profiles from api call
***/}
moffatethan marked this conversation as resolved.
Show resolved Hide resolved
<Grid container className={classes.listingsWrapper}>
{profiles.length ? (
Array.from(profiles).map((_, index) => (
<Grid item xs={4} key={index} className={classes.listingItem}>
<Box py={2} display={'flex'} justifyContent={'center'}>
<ProfileCard profile={profiles[index]}></ProfileCard>
</Box>
</Grid>
))
) : (
<Box m={'auto'}>No users found.</Box>
)}
</Grid>
</Grid>
);
Expand Down
Loading