Skip to content

Commit

Permalink
add login, logout and private route functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
heytulsiprasad committed Sep 20, 2020
1 parent 1062221 commit 2d367ac
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 166 deletions.
129 changes: 129 additions & 0 deletions src/components/Profile/Dropdown.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, { useRef, useState, useEffect } from "react";
import styled from "styled-components";
import { withRouter } from "react-router-dom";
import PropTypes from "prop-types";

import Person from "../../assets/person.jpg";

const Container = styled.div`
position: relative;
user-select: none;
`;

const Topbar = styled.div`
display: flex;
align-items: center;
cursor: pointer;
> * + * {
margin-left: 1rem;
}
.person-profile {
height: 35px;
border-radius: 5px;
}
.person-title {
font-size: 1rem;
}
`;

const Overlay = styled.div`
position: absolute;
width: 120%;
right: 0;
margin-top: 25px;
padding: 8px;
border: 1px solid #e0e0e0;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.05);
border-radius: 12px;
/* Except the first one */
> * + * {
margin-top: 5px;
}
.option {
display: flex;
align-items: center;
padding: 10px 12px;
border-radius: 10px;
}
.option:hover {
background: #f2f2f2;
cursor: pointer;
}
.option-text {
margin-left: 10px;
font-size: 13px;
font-weight: normal;
}
`;

const Dropdown = ({ onLogout, history }) => {
const dropdownRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
const toggleDropdown = () => setIsOpen(!isOpen);

// Change state when clicked outside of dropdown
useEffect(() => {
if (isOpen) {
const handleClickOutside = (event) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target)
) {
setIsOpen(false);
}
};

// Add event listener
document.addEventListener("mousedown", handleClickOutside);

return () => {
// Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
}
}, [dropdownRef, isOpen]);

return (
<Container ref={dropdownRef} onClick={toggleDropdown}>
<Topbar>
<img
className="person-profile"
src={Person}
alt="person-looking-straight"
/>
<h1 className="person-title">Xanthe Neal</h1>
<span className="material-icons drop-icon">
{isOpen ? "arrow_drop_up" : "arrow_drop_down"}
</span>
</Topbar>
{isOpen && (
<Overlay>
<div className="profile option" onClick={() => history.push("/")}>
<span className="material-icons">account_circle</span>
<h2 className="option-text">My Profile</h2>
</div>
<div className="logout option" onClick={() => onLogout(history)}>
<span className="material-icons">login</span>
<h2 className="option-text">Logout</h2>
</div>
</Overlay>
)}
</Container>
);
};

Dropdown.propTypes = {
onLogout: PropTypes.func.isRequired,
history: PropTypes.shape({
push: PropTypes.func.isRequired,
}).isRequired,
};

export default withRouter(Dropdown);
133 changes: 15 additions & 118 deletions src/components/Profile/Navbar.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";

import Icon from "./../../assets/logo-talk.png";
import Person from "./../../assets/person.jpg";
import Dropdown from "./Dropdown";
import Icon from "../../assets/logo-talk.png";

const NavbarContainer = styled.nav`
grid-column: 2 / 5;
Expand Down Expand Up @@ -33,122 +34,18 @@ const Logo = styled(Link)`
}
`;

const Dropdown = styled.div`
position: relative;
user-select: none;
`;

const Topbar = styled.div`
display: flex;
align-items: center;
cursor: pointer;
> * + * {
margin-left: 1rem;
}
.person-profile {
height: 35px;
border-radius: 5px;
}
.person-title {
font-size: 1rem;
}
`;

const Overlay = styled.div`
position: absolute;
width: 120%;
right: 0;
margin-top: 25px;
padding: 8px;
border: 1px solid #e0e0e0;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.05);
border-radius: 12px;
/* Except the first one */
> * + * {
margin-top: 5px;
}
.option {
display: flex;
align-items: center;
padding: 10px 12px;
border-radius: 10px;
}
.option:hover {
background: #f2f2f2;
cursor: pointer;
}
.option-text {
margin-left: 10px;
font-size: 13px;
font-weight: normal;
}
`;

const Navbar = () => {
const topbarRef = React.useRef(null);
const [isOpen, setIsOpen] = React.useState(false);
const toggleDropdown = () => setIsOpen(!isOpen);

// Change state when clicked outside of dropdown
React.useEffect(() => {
if (isOpen) {
const handleClickOutside = (event) => {
if (topbarRef.current && !topbarRef.current.contains(event.target)) {
console.log("Hit");
setIsOpen(false);
}
};

// Add event listener
document.addEventListener("mousedown", handleClickOutside);

return () => {
// Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
}
}, [topbarRef, isOpen]);

return (
<NavbarContainer>
<Logo to="/dash">
<img src={Icon} alt="product-logo" />
<h1>Talk.to</h1>
</Logo>
<Dropdown>
<Topbar ref={topbarRef} onClick={toggleDropdown}>
<img
className="person-profile"
src={Person}
alt="person-looking-straight"
/>
<h1 className="person-title">Xanthe Neal</h1>
<span className="material-icons drop-icon">
{isOpen ? "arrow_drop_up" : "arrow_drop_down"}
</span>
</Topbar>
{isOpen && (
<Overlay>
<div className="profile option">
<span className="material-icons">account_circle</span>
<h2 className="option-text">My Profile</h2>
</div>
<div className="logout option">
<span className="material-icons">login</span>
<h2 className="option-text">Logout</h2>
</div>
</Overlay>
)}
</Dropdown>
</NavbarContainer>
);
const Navbar = ({ onLogout }) => (
<NavbarContainer>
<Logo to="/">
<img src={Icon} alt="product-logo" />
<h1>Talk.to</h1>
</Logo>
<Dropdown onLogout={onLogout} />
</NavbarContainer>
);

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

export default Navbar;
9 changes: 6 additions & 3 deletions src/containers/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Navbar from "../components/Profile/Navbar";
import Titlebar from "../components/Profile/Titlebar";
import Infobox from "../components/Profile/Infobox";

import { fetchUserProfile } from "../redux/actions/authActions";
import { fetchUserProfile, logoutUser } from "../redux/actions/authActions";

class Dashboard extends React.Component {
componentDidMount() {
Expand All @@ -16,9 +16,11 @@ class Dashboard extends React.Component {
}

render() {
const { logoutUser } = this.props;

return (
<Layout>
<Navbar />
<Navbar onLogout={logoutUser} />
<Titlebar />
<Infobox />
</Layout>
Expand All @@ -28,6 +30,7 @@ class Dashboard extends React.Component {

Dashboard.propTypes = {
fetchUserProfile: PropTypes.func.isRequired,
logoutUser: PropTypes.func.isRequired,
};

export default connect(null, { fetchUserProfile })(Dashboard);
export default connect(null, { fetchUserProfile, logoutUser })(Dashboard);
11 changes: 1 addition & 10 deletions src/containers/LoginContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ class LoginContainer extends React.Component {
}

static getDerivedStateFromProps(props, state) {
// If logged in redirect to dashboard
if (props.auth.isAuthenticated) props.history.push("/dashboard");

// Check and fill the errors
if (props.errors) return { ...state, errors: props.errors };
}
Expand All @@ -51,15 +48,11 @@ class LoginContainer extends React.Component {
password: this.state.password,
};

this.props.loginUser(userInput);
this.props.loginUser(userInput, this.props.history);
};

componentDidMount() {
this.props.clearErrors();

if (this.props.auth.isAuthenticated) {
this.props.history.push("/dashboard");
}
}

render() {
Expand Down Expand Up @@ -90,12 +83,10 @@ class LoginContainer extends React.Component {

LoginContainer.propTypes = {
loginUser: PropTypes.func.isRequired,
auth: PropTypes.object,
errors: PropTypes.object,
};

const mapStateToProps = (state) => ({
auth: state.auth,
errors: state.errors,
});

Expand Down
11 changes: 1 addition & 10 deletions src/containers/SignupContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ class SignupContainer extends React.Component {
}

static getDerivedStateFromProps(props, state) {
// If authenticated, pass on to /dashboard route
if (props.auth.isAuthenticated) props.history.push("/dashboard");

// Add errors to local state
if (props.errors) return { ...state, errors: props.errors };
}
Expand All @@ -51,15 +48,11 @@ class SignupContainer extends React.Component {
password: this.state.password,
};

this.props.registerUser(userInput);
this.props.registerUser(userInput, this.props.history);
};

componentDidMount() {
this.props.clearErrors();

if (this.props.auth.isAuthenticated) {
this.props.history.push("/dashboard");
}
}

render() {
Expand Down Expand Up @@ -91,12 +84,10 @@ class SignupContainer extends React.Component {
SignupContainer.propTypes = {
registerUser: PropTypes.func.isRequired,
clearErrors: PropTypes.func.isRequired,
auth: PropTypes.object,
errors: PropTypes.object,
};

const mapStateToProps = (state) => ({
auth: state.auth,
errors: state.errors,
});

Expand Down
Loading

0 comments on commit 2d367ac

Please sign in to comment.