Skip to content

Commit

Permalink
Private route, example data fetch static function
Browse files Browse the repository at this point in the history
  • Loading branch information
atulmy committed Nov 7, 2017
1 parent b8812f4 commit 0c354ad
Show file tree
Hide file tree
Showing 18 changed files with 325 additions and 16 deletions.
3 changes: 2 additions & 1 deletion code/web/src/client/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import { Route, Switch } from 'react-router-dom'
import { routes } from '../setup/routes'
import Layout from './common/Layout'
import NotFound from './common/NotFound'
import RoutePrivate from './user/RoutePrivate'

const App = (props) => (
<Layout>
<Switch>
{ Object.values(routes).map((route, index) => (
<Route { ...route } key={ index }/>
route.auth ? <RoutePrivate { ...route } key={ index }/> : <Route { ...route } key={ index }/>
)) }

<Route component={ NotFound } />
Expand Down
6 changes: 4 additions & 2 deletions code/web/src/client/components/common/header/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const Header = (props) => {
<MenuItem to={ home.women.path }>Women</MenuItem>

<MenuItem to={ home.howItWorks.path }>How It Works</MenuItem>

<MenuItem to={ home.whatsNew.path }>What's New</MenuItem>
</Menu>
</GridCell>

Expand All @@ -41,9 +43,9 @@ const Header = (props) => {
props.user.isAuthenticated
?
<Menu>
<MenuItem to={ user.signup.path }>Subscription</MenuItem>
<MenuItem to={ user.subscriptions.path }>Subscription</MenuItem>

<MenuItem to={ user.signup.path }>Profile</MenuItem>
<MenuItem to={ user.profile.path }>Profile</MenuItem>
</Menu>
:
<Menu>
Expand Down
6 changes: 3 additions & 3 deletions code/web/src/client/components/home/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { white } from '../ui/common/colors'
import { textLevel1 } from '../ui/common/shadows'

// App Imports
import user from '../../setup/routes/user'
import userRoutes from '../../setup/routes/user'

// Component
const Home = (props) => (
Expand All @@ -32,11 +32,11 @@ const Home = (props) => (
{
props.user.isAuthenticated
?
<Link to={ user.signup.path }>
<Link to={ userRoutes.subscriptions.path }>
<Button theme="secondary" style={ { marginTop: '1em' } }>Get Subscription</Button>
</Link>
:
<Link to={ user.signup.path }>
<Link to={ userRoutes.signup.path }>
<Button theme="secondary" style={ { marginTop: '1em' } }>Get Started</Button>
</Link>
}
Expand Down
2 changes: 1 addition & 1 deletion code/web/src/client/components/home/HowItWorks.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const HowItWorks = (props) => (
{
props.user.isAuthenticated
?
<Link to={ userRoutes.signup.path }>
<Link to={ userRoutes.subscriptions.path }>
<Button theme="primary" style={ { marginTop: '1em' } }>Subscribe <Icon size={ 1.2 } style={ { color: white } }>navigate_next</Icon></Button>
</Link>
:
Expand Down
2 changes: 1 addition & 1 deletion code/web/src/client/components/home/Men.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const Men = (props) => (
{
props.user.isAuthenticated
?
<Link to={ userRoutes.signup.path }>
<Link to={ userRoutes.subscriptions.path }>
<Button theme="secondary" style={ { marginTop: '1em' } }>Get Subscription</Button>
</Link>
:
Expand Down
95 changes: 95 additions & 0 deletions code/web/src/client/components/home/WhatsNew.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Imports
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Helmet } from 'react-helmet'
import { Link } from 'react-router-dom'

// UI Imports
import { Grid, GridCell } from '../ui/grid'
import { H3, H4 } from '../ui/typography'
import Button from '../ui/button'
import Icon from '../ui/icon'
import { textLevel1 } from '../ui/common/shadows'
import { white, grey, grey3 } from '../ui/common/colors'

// App Imports
import userRoutes from '../../setup/routes/user'
import { actionBlogsFetch } from './api/actions'

// Component
class WhatsNew extends Component {

static fetchData({store}) {
return store.dispatch(actionBlogsFetch())
}

componentDidMount() {
this.props.actionBlogsFetch()
}

refresh() {
this.props.actionBlogsFetch()
}

render() {
return (
<div>
{/* SEO */}
<Helmet>
<title>What's new - Crate</title>
</Helmet>

{/* Top title bar */}
<Grid gutter={false} style={{backgroundColor: grey}}>
<GridCell style={{padding: '2em', textAlign: 'center'}}>
<H3 font="secondary">What's new</H3>
</GridCell>
</Grid>

{
this.props.blogs.list.length > 0
?
this.props.blogs.list.map(blog => (
<p>{ blog.title }</p>
))
:
<p>Loading...</p>
}

{/* Bottom call to action bar */}
<Grid gutter={false} style={{backgroundColor: grey}}>
<GridCell style={{padding: '3em', textAlign: 'center'}}>
{
this.props.user.isAuthenticated
?
<Link to={userRoutes.subscriptions.path}>
<Button theme="primary" style={{marginTop: '1em'}}>Subscribe <Icon size={1.2} style={{color: white}}>navigate_next</Icon></Button>
</Link>
:
<Link to={userRoutes.signup.path}>
<Button theme="primary" style={{marginTop: '1em'}}>Start <Icon size={1.2} style={{color: white}}>navigate_next</Icon></Button>
</Link>
}
</GridCell>
</Grid>
</div>
)
}
}

// Component Properties
WhatsNew.propTypes = {
user: PropTypes.object.isRequired,
actionBlogsFetch: PropTypes.func.isRequired,
}

// Component State
function whatsNewState(state) {
return {
user: state.user,
blogs: state.blogs
}
}

export default connect(whatsNewState, { actionBlogsFetch })(WhatsNew)
2 changes: 1 addition & 1 deletion code/web/src/client/components/home/Women.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const Women = (props) => (
{
props.user.isAuthenticated
?
<Link to={ userRoutes.signup.path }>
<Link to={ userRoutes.subscriptions.path }>
<Button theme="secondary" style={ { marginTop: '1em' } }>Get Subscription</Button>
</Link>
:
Expand Down
73 changes: 73 additions & 0 deletions code/web/src/client/components/home/api/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Imports
import axios from 'axios'

export const ACTION_TYPE_BLOGS_FETCH = 'ACTION_TYPE_BLOGS_FETCH'
export const ACTION_TYPE_BLOGS_FETCHING = 'ACTION_TYPE_BLOGS_FETCHING'
export const ACTION_TYPE_BLOG_FETCH = 'ACTION_TYPE_BLOG_FETCH'
export const ACTION_TYPE_BLOG_FETCHING = 'ACTION_TYPE_BLOG_FETCHING'

export function actionBlogsFetch() {
return (dispatch, getState) => {
let state = getState()

if(state.blogs.list.length === 0) {
dispatch({
type: ACTION_TYPE_BLOGS_FETCHING
})

return axios.get('https://jsonplaceholder.typicode.com/posts')
.then((response) => {
if(response.status === 200) {
dispatch({
type: ACTION_TYPE_BLOGS_FETCH,
blogs: response.data
})
} else {
console.error(response)
}
})
.catch(function (error) {
console.error(error)
})
}
}
}

export function actionBlogFetch({ id }) {
return (dispatch) => {
dispatch({
type: ACTION_TYPE_BLOG_FETCHING
})

return axios.get(`https://jsonplaceholder.typicode.com/posts/${ id }`)
.then((response) => {
if(response.status === 200) {
dispatch({
type: ACTION_TYPE_BLOG_FETCH,
blog: response.data
})
} else {
console.error(response)
}
})
.catch(function (error) {
console.error(error)
})
}
}

export const actionBlogFetchIfNeeded = ({ id }) => {
return (dispatch, getState) => {
let state = getState()

if(typeof state.blog.details[id] === 'undefined') {
return dispatch(actionBlogFetch({ id }))
}
}
}

export const actionBlogAdd = (blog) => {
return (dispatch) => {
return axios.post(`https://jsonplaceholder.typicode.com/posts`, blog)
}
}
27 changes: 27 additions & 0 deletions code/web/src/client/components/home/api/state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// App Imports
import {
ACTION_TYPE_BLOGS_FETCH,
ACTION_TYPE_BLOGS_FETCHING
} from './actions'

export default (state = { list: [], error: false, loading: false }, action = {}) => {
switch (action.type) {

case ACTION_TYPE_BLOGS_FETCHING:
return Object.assign({}, state, {
list: [],
error: false,
loading: true
})

case ACTION_TYPE_BLOGS_FETCH:
return Object.assign({}, state, {
list: action.blogs,
error: action.error,
loading: false
})

default:
return state
}
}
1 change: 0 additions & 1 deletion code/web/src/client/components/user/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { white } from '../ui/common/colors'

// App Imports
import userRoutes from '../../setup/routes/user'
import homeRoutes from '../../setup/routes/home'
import { messageShow, messageHide } from '../common/api/actions'
import { login } from './api/actions'
import AuthCheck from './AuthCheck'
Expand Down
38 changes: 38 additions & 0 deletions code/web/src/client/components/user/Profile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Imports
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Helmet } from 'react-helmet'
import { Link } from 'react-router-dom'

// UI Imports
import { Grid, GridCell } from '../ui/grid'
import { H1, H4 } from '../ui/typography'
import Button from '../ui/button'
import { white } from '../ui/common/colors'
import { textLevel1 } from '../ui/common/shadows'

// App Imports
import user from '../../setup/routes/user'
import { logout } from './api/actions'

// Component
const Profile = (props) => (
<Grid>
{/* SEO */}
<Helmet>
<title>Profile - Crate</title>
</Helmet>

<GridCell>
<button onClick={ props.logout.bind(this) }>Logout</button>
</GridCell>
</Grid>
)

// Component Properties
Profile.propTypes = {
logout: PropTypes.func.isRequired
}

export default connect(null, { logout })(Profile)
29 changes: 29 additions & 0 deletions code/web/src/client/components/user/RoutePrivate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Imports
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Route, Redirect } from 'react-router-dom'
import { connect } from 'react-redux'

// App Imports
import userRoutes from '../../setup/routes/user'

// Component
class RoutePrivate extends Component {
render() {
const { isAuthenticated } = this.props.user

return ( isAuthenticated ? <Route { ...this.props } component={ this.props.component } /> : <Redirect to={ userRoutes.login.path } /> )
}
}

RoutePrivate.propTypes = {
user: PropTypes.object.isRequired,
}

function mapStateToProps(state) {
return {
user: state.user
}
}

export default connect(mapStateToProps, {})(RoutePrivate);
13 changes: 13 additions & 0 deletions code/web/src/client/components/user/Subscriptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Imports
import React from 'react'

// App Imports

// Component
const Subscription = () => (
<div>
<h1>Subscription</h1>
</div>
)

export default Subscription
Loading

0 comments on commit 0c354ad

Please sign in to comment.