Skip to content

Commit

Permalink
Frontend Auth (#13)
Browse files Browse the repository at this point in the history
* FEAT: basic structure of navbar

* FEAT: added login feature to view any page

* REMOVE: incorrent dependency location

* FEAT: add login connected to UNTESTED endpoint

* PAGE: added 404 Not Found Page

* LINT: added prop-types and styling

* REMOVE: deleted 404 page

* DEPENDENCY: added ag-grid

* STYLING: Updated Profile dropdown CSS

* STYLING: changed Login Page styling

* FEAT: added proper headers for ag-grid

* LINT: fixes some lint issues

* DOCUMENATION: added comments to components

* LINT: fixed documentation styling

* LINT: fixed prop-types

* PR FEEDBACK: resolves @alicesf2 comments

* PR FEEDBACK: resolves @mattwalo32 comments
  • Loading branch information
amit-sawhney authored and n3a9 committed May 23, 2021
1 parent 3c15641 commit 577b705
Show file tree
Hide file tree
Showing 20 changed files with 451 additions and 1,333 deletions.
3 changes: 3 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"homepage": "https://pineapple.lol/",
"license": "MIT",
"dependencies": {
"ag-grid-community": "^25.0.0",
"ag-grid-enterprise": "^25.0.0",
"ag-grid-react": "^25.0.0",
"axios": "^0.21.1",
"prop-types": "^15.7.2",
"react": "^16.13.1",
Expand Down
82 changes: 82 additions & 0 deletions client/src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useState, useEffect } from 'react';
import {
BrowserRouter as Router,
Route,
Switch,
Redirect,
} from 'react-router-dom';

import * as Routes from './routes';

import Home from './pages/Home';
import Member from './pages/Member';
import Login from './pages/Login';

import Navbar from './components/navbar/Navbar';

import {
endUserSession,
getUserAuth,
startUserSession,
} from './utils/apiWrapper';

function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);

useEffect(() => {
const userAuth = async () => {
const resp = await getUserAuth();
if (!resp.error) setIsAuthenticated(resp.data.result);
};
userAuth();
}, []);

const login = () => {
const startSession = async () => {
const resp = await startUserSession();
if (!resp.error) setIsAuthenticated(true);
};

// TODO: remove unnecessary line after backend is connected properly
setIsAuthenticated(true);

startSession();
};

const logout = () => {
const endSession = async () => {
const resp = await endUserSession();
if (!resp.error) setIsAuthenticated(false);
};

// TODO: remove unnecessary line after backend is connected properly
setIsAuthenticated(false);

endSession();
};

return (
<div className="App">
<Router>
<Navbar logout={logout} />
<Switch>
<Route exact path="/login">
{isAuthenticated ? (
<Redirect to={Routes.DEFAULT_ROUTE} />
) : (
<Login login={login} />
)}
</Route>
<Route path={Routes.DEFAULT_ROUTE} exact>
{isAuthenticated ? <Home /> : <Redirect to="/login" />}
</Route>
<Route path={Routes.MEMBER_PAGE_ROUTE} exact>
{isAuthenticated ? <Member /> : <Redirect to="/login" />}
</Route>
</Switch>
</Router>
</div>
);
}

export default App;
Binary file added client/src/assets/blank-profile-picture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/src/assets/google-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions client/src/components/Profile/Profile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import '../../css/Profile.css';
import blankProfilePicture from '../../assets/blank-profile-picture.png';
import * as Routes from '../../routes';
import { Redirect } from 'react-router-dom';

/**
* Displays the Profile icon in the navbar + dropdown components for logout and viewing profile
* @param {func} logout connects to backend to end a users current session and log them out.
*/
const Profile = ({ logout }) => {
const [redirectToMemberPage, setRedirectToMemberPage] = useState(false);

useEffect(() => {
setRedirectToMemberPage(false);
}, [redirectToMemberPage]);

const handleProfileRedirect = () => {
setRedirectToMemberPage(true);
};

return (
<div className="dropdown">
{redirectToMemberPage && <Redirect to={Routes.MEMBER_PAGE_ROUTE} />}
<img alt="Blank Profile" src={blankProfilePicture} className="avatar" />
<div className="dropdown-content">
<button
onClick={handleProfileRedirect}
type="button"
className="dropdown-item"
>
View Profile
</button>
<button type="button" className="dropdown-item" onClick={logout}>
Logout
</button>
</div>
</div>
);
};

Profile.propTypes = {
logout: PropTypes.func.isRequired,
};

export default Profile;
32 changes: 30 additions & 2 deletions client/src/components/navbar/Navbar.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
import React from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';

const Navbar = () => {
return <div></div>;
import '../../css/Navbar.css';

import Profile from '../Profile/Profile';

import * as Routes from '../../routes';

/**
* Navbar display to view user sesion and React-route-dom navigation
* @param {func} logout connects to backend to end a users current session and log them out.
*/
const Navbar = ({ logout }) => {
return (
<nav className="nav">
<h2 id="nav-title">
<Link className="nav-link" to={Routes.DEFAULT_ROUTE}>
Member Database
</Link>
</h2>
<div className="profile-item">
<h2 id="welcome-text">Hello, User!</h2>
<Profile logout={logout} />
</div>
</nav>
);
};

Navbar.propTypes = {
logout: PropTypes.func.isRequired,
};

export default Navbar;
64 changes: 64 additions & 0 deletions client/src/components/table/Table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { useState } from 'react';
import { AgGridReact } from 'ag-grid-react';

import '../../css/Table.css';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';

const Table = () => {
const [columnDefs] = useState([
{ headerName: 'Name', field: 'name', width: 100, sortable: true },
{ headerName: 'Email', field: 'email', width: 180, sortable: true },
{ headerName: 'Phone #', field: 'phone', width: 160, sortable: true },
{ headerName: 'NetID', field: 'netid', width: 120, sortable: true },
{ headerName: 'UIN', field: 'uin', width: 120, sortable: true },
{ headerName: 'Major', field: 'major', width: 120, sortable: true },
{
headerName: 'Birth Date',
field: 'birthdate',
width: 120,
sortable: true,
},
{ headerName: 'Github', field: 'github', width: 140, sortable: true },
{ headerName: 'Snapchat', field: 'snapchat', width: 140, sortable: true },
{ headerName: 'Instagram', field: 'instagram', width: 140, sortable: true },
{ headerName: 'Grad Sem/Yr', field: 'grad', width: 190, sortable: true },
{
headerName: 'Generation',
field: 'generation',
width: 120,
sortable: true,
},
{ headerName: 'Location', field: 'location', width: 120, sortable: true },
{ headerName: 'Role', field: 'role', width: 160, sortable: true },
{ headerName: 'Level', field: 'level', width: 120, sortable: true },
{ headerName: 'Status', field: 'status', width: 120, sortable: true },
]);
const [rowData] = useState([
{
name: 'Amit',
email: '[email protected]',
phone: '(309) 838-5466',
netid: 'sawhney4',
uin: 659426199,
major: 'CS + Math',
birthdate: '02/14/2002',
github: 'amit-sawhney',
snapchat: 'amit_sawhney02',
instagram: 'amit__02',
grad: 'Spring 2024 (Freshman)',
generation: 'Fall 2020',
location: 'On-campus',
role: 'Software Developer',
level: 'Member',
status: 'Active',
},
]);
return (
<div className="ag-theme-alpine table-wrapper">
<AgGridReact columnDefs={columnDefs} rowData={rowData}></AgGridReact>
</div>
);
};

export default Table;
13 changes: 1 addition & 12 deletions client/src/css/Home.css
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
@import url('https://fonts.googleapis.com/css2?family=Inter&display=swap');

h1,
p {
html {
font-family: 'Inter', sans-serif;
margin: 0 auto;
text-align: center;
}

h1 {
padding-top: 15vh;
}

p {
padding-top: 2em;
}
43 changes: 43 additions & 0 deletions client/src/css/Login.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.login-wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: white;
display: flex;
}

.login-card {
padding: 5rem;
margin: auto;
box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 2px, rgba(0, 0, 0, 0.07) 0px 2px 4px, rgba(0, 0, 0, 0.07) 0px 4px 8px, rgba(0, 0, 0, 0.07) 0px 8px 16px, rgba(0, 0, 0, 0.07) 0px 16px 32px, rgba(0, 0, 0, 0.07) 0px 32px 64px;
box-shadow: rgba(0, 0, 0, 0.56) 0px 22px 70px 4px;
}

.login-btn {
background-color: #dd4b39;
color: white;
font-weight: 500;
font-family: inherit;
font-size: 18px;
padding: 3px;
border: none;
display: flex;
align-items: center;
width: 100%;
}

.google-icon {
width: 28px;
height: 28px;
background-color: white;
padding: 5px;
border-radius: 5rem;
margin: 2px 5px 2px 5px;
}

.login-btn:hover {
cursor: pointer;
background-color: #ED7465;
}
24 changes: 24 additions & 0 deletions client/src/css/Navbar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.nav {
display: flex;
}

.nav #nav-title {
text-align: left;
padding: 1rem 0rem 0rem 3rem;
flex-grow: 1;
}

.nav .nav-link {
text-decoration: none;
color: black;
}

.nav .profile-item {
display: flex;
align-items: center;
padding: 1rem 3rem 0rem 0rem;
}

.nav .profile-item #welcome-text {
margin-right: 1rem;
}
49 changes: 49 additions & 0 deletions client/src/css/Profile.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.dropdown {
float: left;
overflow: hidden;
}

.dropdown:hover {
cursor: pointer;
}

.dropdown .avatar {
vertical-align: middle;
width: 50px;
height: 50px;
border-radius: 50%;
}

.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
right: 40px;
padding-top: 10px;
}

.dropdown-item {
float: none;
display: block;
padding: 12px 28px 12px 28px;
width: 100%;
border: none;
color: black;
text-decoration: none;
text-align: left;
font-family: 'Inter', sans-serif;
font-size: 16px;
background-color: white;
}

.dropdown-content .dropdown-item:hover {
background-color: #ddd;
cursor: pointer;
}

.dropdown:hover .dropdown-content {
display: block;
}
5 changes: 5 additions & 0 deletions client/src/css/Table.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.table-wrapper {
height: 70vh;
width: 95%;
margin: auto;
}
Loading

0 comments on commit 577b705

Please sign in to comment.