Skip to content

Commit 1dafeda

Browse files
Merge pull request #77 from talent-path-pipeline/3-authentication
3 authentication
2 parents 6979b14 + 317afd0 commit 1dafeda

File tree

15 files changed

+658
-183
lines changed

15 files changed

+658
-183
lines changed

netlify.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[[redirects]]
2+
from = "/*"
3+
to = "/index.html"
4+
status = 200
5+
6+
[context.production.environment]
7+
REACT_APP_SVR_API = "https://stonehaven-academy.herokuapp.com/api"
8+
9+
[context.branch-deploy.environment]
10+
REACT_APP_SVR_API = "https://stonehaven-server-staging.herokuapp.com/api"
11+
12+
[context.deploy-preview.environment]
13+
REACT_APP_SVR_API = "https://stonehaven-server-staging.herokuapp.com/api"

package-lock.json

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"axios": "^0.19.0",
1111
"dotenv": "^8.1.0",
1212
"isotope-layout": "^3.0.6",
13+
"jwt-decode": "^2.2.0",
1314
"node-sass": "^4.12.0",
1415
"prop-types": "^15.7.2",
1516
"react": "^16.8.6",
@@ -39,6 +40,7 @@
3940
]
4041
},
4142
"devDependencies": {
43+
"eslint-config-airbnb": "^18.0.1",
4244
"eslint-config-prettier": "^6.4.0"
4345
}
4446
}

src/App.js

Lines changed: 108 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { Switch, Route, Redirect } from 'react-router-dom';
44
import DUMMY_DATA from './DUMMY_DATA';
5+
import { tokenServices, ProtectedRoute } from './utils';
56
import {
67
NavBar,
78
HomePage,
89
PathPage,
910
LessonPage,
1011
CatalogPage,
11-
// RegistrationPage,
12-
// DashboardPage,
12+
RegistrationPage,
13+
DashboardPage,
1314
SupportPage,
1415
AboutPage,
1516
ErrorPage,
@@ -27,54 +28,115 @@ const links = {
2728
dashboard: '/dashboard',
2829
};
2930

30-
function App() {
31-
const { courses } = DUMMY_DATA;
31+
class App extends React.Component {
32+
constructor(props) {
33+
super(props);
34+
this.state = {
35+
user: null,
36+
isAuthenticated: false,
37+
};
38+
}
3239

33-
return (
34-
<div id="start-page">
35-
<NavBar links={links} />
36-
<Switch>
37-
<Route exact path="/" component={HomePage} />
38-
<Route
39-
exact
40-
path={links.path}
41-
render={props => <PathPage {...props} path_data={DUMMY_DATA} />}
42-
/>
43-
<Route exact path={links.catalog} component={CatalogPage} />
44-
{/* <Route exact path={links.login} component={RegistrationPage} /> */}
45-
{/* <Route exact path={links.dashboard} component={DashboardPage} /> */}
46-
<Route exact path={links.support} component={SupportPage} />
47-
<Route exact path={links.about} component={AboutPage} />
40+
// componentWillMount = () => {
41+
// const { history, location } = this.props;
42+
// console.log(history);
43+
// console.log(location);
44+
// }
45+
46+
/**
47+
* get user data from database
48+
*/
49+
componentDidMount = () => {
50+
const user = tokenServices.getToken();
51+
if (user) {
52+
this.setState({ isAuthenticated: true, user });
53+
}
54+
};
55+
56+
handleLogin = () => {
57+
const user = tokenServices.getToken();
58+
if (user) {
59+
this.setState({ isAuthenticated: true, user });
60+
} else {
61+
this.setState({ isAuthenticated: null, user: null });
62+
}
63+
};
4864

49-
<Redirect exact from="/courses/:course" to="/courses/:course/0" />
50-
<Route
51-
path="/courses/:course/:order"
52-
render={props => {
53-
const courseObj = courses.find(
54-
course => course.slug === props.match.params.course,
55-
);
56-
const prevCourse = courses.find(course => course.order === courseObj.order - 1);
57-
const nextCourse = courses.find(course => course.order === courseObj.order + 1);
58-
const order = parseInt(props.match.params.order, 10);
59-
if (!courseObj || order >= courseObj.lessons.length) return <ErrorPage />;
60-
return (
61-
<LessonPage
62-
{...props}
63-
course_title={courseObj.title}
64-
lessons={courseObj.lessons}
65-
curr_lesson_num={order}
66-
base_path={courseObj.slug}
67-
prev_slug={prevCourse ? prevCourse.slug : undefined}
68-
next_slug={nextCourse ? nextCourse.slug : undefined}
65+
handleLogoff = () => {
66+
tokenServices.removeToken();
67+
this.setState({ isAuthenticated: false, user: null });
68+
};
6969

70-
/>
71-
);
72-
}}
70+
render() {
71+
const { courses } = DUMMY_DATA;
72+
const { user, isAuthenticated } = this.state;
73+
74+
return (
75+
<div id="start-page">
76+
<NavBar
77+
links={links}
78+
isAuthenticated={isAuthenticated}
79+
handleLogoff={this.handleLogoff}
7380
/>
74-
<Route component={ErrorPage} />
75-
</Switch>
76-
</div>
77-
);
81+
<Switch>
82+
<Route exact path="/" component={HomePage} />
83+
<Route
84+
exact
85+
path={links.path}
86+
render={props => <PathPage {...props} path_data={DUMMY_DATA} />}
87+
/>
88+
<Route exact path={links.catalog} component={CatalogPage} />
89+
{/* Login Protected Route */}
90+
<ProtectedRoute
91+
path={links.login}
92+
isAuthenticated={!isAuthenticated}
93+
redirectLink={links.dashboard}
94+
component={RegistrationPage}
95+
handleLogin={this.handleLogin}
96+
/>
97+
{/* Dashboard Protected Route */}
98+
<ProtectedRoute
99+
path={links.dashboard}
100+
isAuthenticated={isAuthenticated}
101+
redirectLink={links.login}
102+
component={DashboardPage}
103+
/>
104+
<Route exact path={links.support} component={SupportPage} />
105+
<Route exact path={links.about} component={AboutPage} />
106+
107+
<Redirect exact from="/courses/:course" to="/courses/:course/0" />
108+
<Route
109+
path="/courses/:course/:order"
110+
render={props => {
111+
const courseObj = courses.find(
112+
course => course.slug === props.match.params.course,
113+
);
114+
const prevCourse = courses.find(
115+
course => course.order === courseObj.order - 1,
116+
);
117+
const nextCourse = courses.find(
118+
course => course.order === courseObj.order + 1,
119+
);
120+
const order = parseInt(props.match.params.order, 10);
121+
if (!courseObj || order >= courseObj.lessons.length) return <ErrorPage />;
122+
return (
123+
<LessonPage
124+
{...props}
125+
course_title={courseObj.title}
126+
lessons={courseObj.lessons}
127+
curr_lesson_num={order}
128+
base_path={courseObj.slug}
129+
prev_slug={prevCourse ? prevCourse.slug : undefined}
130+
next_slug={nextCourse ? nextCourse.slug : undefined}
131+
/>
132+
);
133+
}}
134+
/>
135+
<Route component={ErrorPage} />
136+
</Switch>
137+
</div>
138+
);
139+
}
78140
}
79141

80142
App.propTypes = {

src/components/NavBar.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,17 @@ class NavBar extends React.Component {
1111

1212
this.state = {
1313
menuOpen: false,
14+
isAuthenticated: false,
1415
};
1516
}
1617

18+
componentDidUpdate(prevProps, prevState) {
19+
const { isAuthenticated } = this.props;
20+
if (isAuthenticated !== prevState.isAuthenticated) {
21+
this.setState({ isAuthenticated });
22+
}
23+
}
24+
1725
// This keeps your state in sync with the opening/closing of the menu
1826
// via the default means, e.g. clicking the X, pressing the ESC key etc.
1927
handleStateChange(state) {
@@ -61,14 +69,14 @@ class NavBar extends React.Component {
6169
<li>
6270
<NavLink to={links.catalog}>Catalog</NavLink>
6371
</li>
64-
{/* Commented out until implemented */}
6572
<li>
6673
<NavLink to={links.about}>About</NavLink>
6774
</li>
6875
<li><NavLink to={links.support}>Support</NavLink></li>
6976
<li><a onClick={() => this.closeMenu()} target="_blank" rel="noopener noreferrer" href="https://forms.gle/2YMiTeQ4iuZByx4ZA">Feedback</a></li>
70-
{/* <li><NavLink to={links.dashboard}>Dashboard</NavLink></li> */}
71-
{/* <li><NavLink to={links.login}>Login</NavLink></li> */}
77+
{this.state.isAuthenticated ? (<li><NavLink to={links.dashboard}>Dashboard</NavLink></li>) : null}
78+
{this.state.isAuthenticated ? (<li><NavLink onClick={()=>{this.props.handleLogoff();}} to="/">Log Off</NavLink></li>) : (
79+
<li><NavLink to={links.login}>Login</NavLink></li>)}
7280
</ul>
7381
<div className="mobile-nav">
7482
<Menu
@@ -88,14 +96,21 @@ class NavBar extends React.Component {
8896
<NavLink onClick={() => this.closeMenu()} to={links.about}>
8997
About
9098
</NavLink>
91-
<NavLink onClick={() => this.closeMenu()} to={links.login}>
92-
LogIn
93-
</NavLink>
94-
<NavLink onClick={() => this.closeMenu()} to={links.dashboard} />
9599
<NavLink onClick={() => this.closeMenu()} to={links.support}>
96100
Support
97101
</NavLink>
98102
<a onClick={() => this.closeMenu()} target="_blank" rel="noopener noreferrer" href="https://forms.gle/2YMiTeQ4iuZByx4ZA">Feedback</a>
103+
104+
{this.state.isAuthenticated ? (
105+
<NavLink onClick={() => this.closeMenu()} to={links.dashboard}>
106+
Dashboard
107+
</NavLink>
108+
) : (null
109+
)}
110+
{this.state.isAuthenticated ? (<NavLink onClick={()=>{ this.props.handleLogoff();}} to="/">Log Off</NavLink>) : (
111+
<NavLink to={links.login}>Login</NavLink>)}
112+
113+
99114
</Menu>
100115
</div>
101116
</div>
@@ -107,6 +122,7 @@ NavBar.propTypes = {
107122
links: PropTypes.shape({
108123
home: PropTypes.string,
109124
}).isRequired,
125+
isAuthenticated: PropTypes.bool,
110126
};
111127

112128
export default NavBar;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
/**
5+
* Dashboard navigation menu
6+
* @param {Object} props Passed props
7+
* @param {string} activeView Active navigation text
8+
* @param {Function} showOverview Display function handler
9+
* @param {Function} showSettings Display function handler
10+
* @param {Function} logout Logout function handler
11+
*/
12+
const DashboardMenu = ({ activeView, showOverview, showSettings, logout }) => (
13+
<div className="accountMenu">
14+
<h1 className="menuHeader">My Dashboard</h1>
15+
<ul>
16+
<li>
17+
{activeView === 'overview' ? (
18+
<span className="activeView">Overview</span>
19+
) : (
20+
<button type="button" className="linkButton" onClick={() => showOverview()}>
21+
Overview
22+
</button>
23+
)}
24+
</li>
25+
<li>
26+
{activeView === 'settings' ? (
27+
<span className="activeView">Account Settings</span>
28+
) : (
29+
<button type="button" className="linkButton" onClick={() => showSettings()}>
30+
Account Settings
31+
</button>
32+
)}
33+
</li>
34+
{/* <li>
35+
<button type="button" className="linkButton" onClick={() => logout()}>
36+
Logout
37+
</button>
38+
</li> */}
39+
</ul>
40+
</div>
41+
);
42+
43+
DashboardMenu.propTypes = {
44+
activeView: PropTypes.string.isRequired,
45+
showOverview: PropTypes.func.isRequired,
46+
showSettings: PropTypes.func.isRequired,
47+
logout: PropTypes.func,
48+
};
49+
50+
export default DashboardMenu;

0 commit comments

Comments
 (0)