Skip to content

Commit

Permalink
Merge branch 'main' into nancy_main
Browse files Browse the repository at this point in the history
  • Loading branch information
leafie8 committed Sep 27, 2023
2 parents 3c1cda8 + a150237 commit c68a388
Show file tree
Hide file tree
Showing 12 changed files with 1,833 additions and 28 deletions.
1 change: 1 addition & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
.env.development.local
.env.test.local
.env.production.local
.env

npm-debug.log*
yarn-debug.log*
Expand Down
1,388 changes: 1,388 additions & 0 deletions client/package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
"axios": "^1.3.4",
"date-fns": "^2.29.3",
"dotenv": "^16.0.3",
"firebase": "^10.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.10.1",
"react-router-dom": "^6.8.1",
"react-scripts": "5.0.1",
"react-secure-storage": "^1.3.0",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down Expand Up @@ -53,5 +56,8 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"prettier": "2.8.4"
},
"prettier": {
"singleQuote": true
}
}
29 changes: 22 additions & 7 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import '@fontsource/poppins';

import { CssBaseline, ThemeProvider } from '@mui/material';
import React, { Suspense } from 'react';
import React, { Suspense, useState } from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import secureLocalStorage from 'react-secure-storage';

import Loading from './components/Loading';
import AuthContext from './contexts/AuthContext';
import theme from './theme';

const withSuspense = (Component) => (
Expand All @@ -17,18 +19,31 @@ const HomePage = React.lazy(() => import('./pages/HomePage'));
const SearchResultsPage = React.lazy(() => import('./pages/SearchResultsPage'));
const AboutPage = React.lazy(() => import('./pages/AboutPage'));
const ErrorPage = React.lazy(() => import('./pages/ErrorPage'));
const TestPage = React.lazy(() => import('./pages/TestPage')); // temporary - to be deleted
const SignInPage = React.lazy(() => import('./pages/SignInPage'));

const App = () => {
const [authUser, setAuthUser] = useState(() => {
return secureLocalStorage.getItem('email')
? secureLocalStorage.getItem('email')
: null;
});

return (
<BrowserRouter>
<CssBaseline />
<ThemeProvider theme={theme}>
<Routes>
<Route path="/" element={withSuspense(HomePage)} />
<Route path="/search" element={withSuspense(SearchResultsPage)} />
<Route path="/about" element={withSuspense(AboutPage)} />
<Route path="*" element={withSuspense(ErrorPage)} />
</Routes>
<AuthContext.Provider value={[authUser, setAuthUser]}>
<Routes>
<Route path="/" element={withSuspense(HomePage)} />
<Route path="/search" element={withSuspense(SearchResultsPage)} />
<Route path="/about" element={withSuspense(AboutPage)} />
<Route path="/test" element={withSuspense(TestPage)} />
{/* temporary - to be deleted */}
<Route path="/signin" element={withSuspense(SignInPage)} />
<Route path="*" element={withSuspense(ErrorPage)} />
</Routes>
</AuthContext.Provider>
</ThemeProvider>
</BrowserRouter>
);
Expand Down
209 changes: 209 additions & 0 deletions client/src/components/AuthComponent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import LinkedInIcon from '@mui/icons-material/LinkedIn';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
import {
Box,
Button,
Card,
Checkbox,
Divider,
FormControl,
IconButton,
InputAdornment,
InputLabel,
Link,
OutlinedInput,
Stack,
TextField,
Typography,
} from '@mui/material';
import { signInWithPopup } from 'firebase/auth';
import React, { useContext, useState } from 'react';
import { FcGoogle } from 'react-icons/fc';
import { useNavigate } from 'react-router-dom';
import secureLocalStorage from 'react-secure-storage';

import AuthContext from '../contexts/AuthContext';
import { auth, provider } from '../utils/firebaseConfig';
import ErrorMessage from './ErrorMessage';

const AuthComponent = () => {
let navigate = useNavigate();

const [, setAuthUser] = useContext(AuthContext);
const [email, setEmail] = useState(() => {
return secureLocalStorage.getItem('email')
? secureLocalStorage.getItem('email')
: '';
});
const [password, setPassword] = useState(() => {
return secureLocalStorage.getItem('password')
? secureLocalStorage.getItem('password')
: '';
});
const [showPassword, setShowPassword] = useState(false);
const [rememberMe, setRememberMe] = useState(() => {
return secureLocalStorage.getItem('rememberMe')
? secureLocalStorage.getItem('rememberMe')
: false;
});
const [showErrorMessage, setShowErrorMessage] = useState('');
const MIN_PASSWORD_LENGTH = 6;

const handleEmail = (event) => {
setEmail(event.target.value);
setShowErrorMessage('');
};
const handlePassword = (event) => {
setPassword(event.target.value);
setShowErrorMessage('');
};
const handleCheckbox = (event) => {
setRememberMe(event.target.checked);
};
const handleClickShowPassword = () => setShowPassword((show) => !show);
const handleMouseDownPassword = (event) => {
event.preventDefault();
};
const handleSignIn = () => {
const validEmail =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!validEmail.test(email) || password.length < MIN_PASSWORD_LENGTH) {
setShowErrorMessage(
'Incorrect email or password. All passwords must be at least 6 characters.'
);
} else if (rememberMe) {
secureLocalStorage.setItem('email', email);
secureLocalStorage.setItem('password', password);
secureLocalStorage.setItem('rememberMe', rememberMe);
}
};
const googleSignUp = () => {
signInWithPopup(auth, provider)
.then((result) => {
setAuthUser(result.user.email);
// add code to store or check user in database
secureLocalStorage.setItem('email', result.user.email);
navigate('/search');
})
.catch(() => {
setShowErrorMessage(
'Something went wrong with Google sign up. Please try again.'
);
});
};
return (
<Card sx={{ mt: 5 }}>
<Stack direction="column" spacing={5}>
<TextField
fullWidth
id="outlined-basic"
label="Email Address"
variant="outlined"
value={email}
onChange={handleEmail}
/>
<Box margin={0}>
<FormControl fullWidth variant="outlined">
<InputLabel htmlFor="outlined-adornment-password">
Password
</InputLabel>
<OutlinedInput
id="outlined-adornment-password"
type={showPassword ? 'text' : 'password'}
onChange={handlePassword}
value={password}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
}
label="Password"
/>
</FormControl>
<Stack
mt={2}
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Stack direction="row" justifyContent="center" alignItems="center">
<Checkbox
checked={rememberMe}
onChange={handleCheckbox}
inputProps={{ 'aria-label': 'controlled' }}
size="small"
sx={{ paddingLeft: 0 }}
/>
<Typography variant="body2">Remember Me</Typography>
</Stack>
<Link target="_blank" underline="none">
<Typography
variant="body2"
fontWeight="600"
color="secondary.dark"
>
Forgot Password?
</Typography>
</Link>
</Stack>
{showErrorMessage ? (
<ErrorMessage message={showErrorMessage} />
) : null}
</Box>
<Button variant="rounded" color="primary" onClick={handleSignIn}>
<Typography variant="errorMessage" p={1.5}>
Sign In
</Typography>
</Button>
<Divider sx={{ fontSize: '0.9rem', color: 'text.main' }}>
or continue with
</Divider>
<Button
variant="social"
startIcon={
<Box
display="flex"
justifyContent="center"
alignItems="center"
marginRight="0.3rem"
padding={0}
>
<FcGoogle size={25} />
</Box>
}
onClick={googleSignUp}
>
Sign up with Google
</Button>
<Button
variant="social"
startIcon={
<LinkedInIcon
sx={{
transform: 'scale(1.2)',
color: 'icons.linkedin',
marginRight: '0.3rem',
}}
/>
}
>
LinkedIn
</Button>
<Typography variant="body2" textAlign="center">
New to Bobatalks?
<Link> Sign up here!</Link>
</Typography>
</Stack>
</Card>
);
};

export default AuthComponent;
20 changes: 20 additions & 0 deletions client/src/components/ErrorMessage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Box, Typography } from '@mui/material';
import React from 'react';

const ErrorMessage = ({ message }) => {
return (
<Box
alignItems="center"
bgcolor="error.light"
padding="1rem 1.5rem"
borderRadius="5px"
mt=".5rem"
>
<Typography variant="body2" fontWeight="600" color="error.main">
{message}
</Typography>
</Box>
);
};

export default ErrorMessage;
Loading

0 comments on commit c68a388

Please sign in to comment.