Skip to content
This repository was archived by the owner on Aug 5, 2022. It is now read-only.

Commit 320178f

Browse files
arnostpleskotdannytce
authored andcommitted
Week 5 (#10)
* Pagination + 404 * Link to detail * Get Products hook * Romoved products store * Product detail woth hooks * Fetch products in cart * Padding removed from paggination * Remove @flow * Naive exception handling * Inline exports * Routes params changed to query, changed imports of actions * Reordered imports * Extract routes into constants * Fix for resolve conditions passed into useApi hook
1 parent 8781930 commit 320178f

File tree

23 files changed

+291
-202
lines changed

23 files changed

+291
-202
lines changed

.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = {
55
'prettier',
66
'prettier/react',
77
],
8+
plugins: ['react-hooks'],
89
root: true,
910
env: {
1011
browser: true,
@@ -28,5 +29,7 @@ module.exports = {
2829
'no-shadow': [2, { allow: ['name'] }],
2930
// let's enforce this approach a bit
3031
'import/no-default-export': 1,
32+
'react-hooks/rules-of-hooks': 'error',
33+
'react-hooks/exhaustive-deps': 'warn',
3134
},
3235
}

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@
3636
],
3737
"dependencies": {
3838
"formik": "^1.5.2",
39+
"qs": "^6.7.0",
40+
"ramda": "^0.26.1",
3941
"react": "^16.8.4",
4042
"react-dom": "^16.8.4",
4143
"react-redux": "^6.0.1",
44+
"react-router": "^5.0.0",
4245
"react-router-dom": "^5.0.0",
4346
"react-scripts": "2.1.8",
4447
"redux": "^4.0.1",
@@ -51,6 +54,7 @@
5154
"@strv/eslint-config-react": "^1.0.1",
5255
"@strv/stylelint-config-styled-components": "^1.0.0",
5356
"eslint-config-prettier": "^4.1.0",
57+
"eslint-plugin-react-hooks": "^1.6.0",
5458
"husky": "^1.3.1",
5559
"lint-staged": "^8.1.5",
5660
"prettier": "^1.16.4",

src/App.js

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import React, { Component } from 'react'
2-
import { Switch, Route } from 'react-router-dom'
1+
import React from 'react'
2+
import { Switch, Route, Redirect } from 'react-router-dom'
33
import { Provider } from 'react-redux'
44

55
import GlobalStyles from './globalStyles'
@@ -9,32 +9,36 @@ import { Cart } from './pages/Cart'
99
import { SignUp } from './pages/SignUp'
1010
import { LogIn } from './pages/LogIn'
1111
import { Account } from './pages/Account'
12+
import { NotFound } from './pages/NotFound'
1213
import { PrivateRoute } from './components/PrivateRoute'
1314
import { getCustomer } from './utils/customer'
1415
import { configureStore } from './store'
16+
import * as routes from './routes'
1517

1618
const store = configureStore({
1719
customer: getCustomer(),
1820
})
1921

20-
class App extends Component {
21-
render() {
22-
return (
23-
<Provider store={store}>
24-
<React.Fragment>
25-
<GlobalStyles />
26-
<Switch>
27-
<Route path="/" exact component={ProductList} />
28-
<Route path="/cart" component={Cart} />
29-
<Route path="/signup" component={SignUp} />
30-
<Route path="/login" component={LogIn} />
31-
<PrivateRoute path="/account" component={Account} />
32-
<Route path="/:productId" component={ProductDetail} />
33-
</Switch>
34-
</React.Fragment>
35-
</Provider>
36-
)
37-
}
38-
}
22+
const App = () => (
23+
<Provider store={store}>
24+
<React.Fragment>
25+
<GlobalStyles />
26+
<Switch>
27+
<Route
28+
path={routes.HOMEPAGE}
29+
exact
30+
render={() => <Redirect to={routes.PRODUCT_LIST} />}
31+
/>
32+
<Route path={routes.PRODUCT_LIST} exact component={ProductList} />
33+
<Route path={routes.PRODUCT_DETAIL} component={ProductDetail} />
34+
<Route path={routes.CART} component={Cart} />
35+
<Route path={routes.SIGN_UP} component={SignUp} />
36+
<Route path={routes.LOGIN} component={LogIn} />
37+
<PrivateRoute path={routes.ACCOUNT} component={Account} />
38+
<Route component={NotFound} />
39+
</Switch>
40+
</React.Fragment>
41+
</Provider>
42+
)
3943

40-
export default App
44+
export { App }

src/api/products/get-products.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
import qs from 'qs'
12
import { api } from '../api-client'
23
import { formatProduct } from './utils/format-product'
34

4-
export const getProducts = async () => {
5-
const { data, included } = await api('/api/skus?include=prices')
5+
export const getProducts = async urlQuery => {
6+
const { data, meta, included } = await api(
7+
`/api/skus?${qs.stringify({ include: 'prices', ...urlQuery })}`
8+
)
69

7-
return data.map(product => formatProduct(product, included))
10+
return {
11+
data: data.map(product => formatProduct(product, included)),
12+
meta,
13+
}
814
}

src/api/use-api.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useState, useEffect } from 'react'
2+
3+
const useApi = (fn, resolveCondition = []) => {
4+
const [data, setData] = useState(null)
5+
const [isLoading, setLoading] = useState(false)
6+
7+
if (!Array.isArray(resolveCondition)) {
8+
// eslint-disable-next-line no-console
9+
console.error('Passed resolve condition for useEffect hook is not an Array')
10+
}
11+
12+
const request = (...args) => {
13+
setLoading(true)
14+
fn(...args)
15+
.then(returnedData => setData(returnedData))
16+
// eslint-disable-next-line no-console
17+
.catch(console.error)
18+
.finally(() => setLoading(false))
19+
}
20+
21+
useEffect(request, resolveCondition)
22+
23+
return {
24+
request,
25+
data,
26+
isLoading,
27+
}
28+
}
29+
30+
export { useApi }

src/components/Layout/index.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { withRouter } from 'react-router-dom'
33
import { connect } from 'react-redux'
44

55
import * as customerActions from '../../store/customer/actions'
6+
import * as routes from '../../routes'
7+
68
import { removeToken } from '../../utils/token'
79
import { removeCustomer } from '../../utils/customer'
810
import { Wrapper, Header, HeaderSection, HeaderLink } from './styled'
@@ -12,7 +14,7 @@ class Layout extends Component {
1214
this.props.logout()
1315
removeToken()
1416
removeCustomer()
15-
this.props.history.push('/')
17+
this.props.history.push(routes.HOMEPAGE)
1618
}
1719

1820
render() {
@@ -22,21 +24,21 @@ class Layout extends Component {
2224
<Fragment>
2325
<Header>
2426
<HeaderSection>
25-
<HeaderLink to="/">All Products</HeaderLink>
27+
<HeaderLink to={routes.PRODUCT_LIST}>All Products</HeaderLink>
2628
</HeaderSection>
2729
<HeaderSection>
28-
<HeaderLink to="/cart">My Cart</HeaderLink>|
30+
<HeaderLink to={routes.CART}>My Cart</HeaderLink>|
2931
{isAuthenticated ? (
3032
<>
31-
<HeaderLink to="/account">My Account</HeaderLink>|
33+
<HeaderLink to={routes.ACCOUNT}>My Account</HeaderLink>|
3234
<HeaderLink as="button" onClick={this.handleLogout}>
3335
Logout
3436
</HeaderLink>
3537
</>
3638
) : (
3739
<>
38-
<HeaderLink to="/login">Log In</HeaderLink> |
39-
<HeaderLink to="/signup">Sign Up</HeaderLink>
40+
<HeaderLink to={routes.LOGIN}>Log In</HeaderLink> |
41+
<HeaderLink to={routes.SIGN_UP}>Sign Up</HeaderLink>
4042
</>
4143
)}
4244
</HeaderSection>

src/components/Pagination/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as React from 'react'
2+
import range from 'ramda/src/range'
3+
import map from 'ramda/src/map'
4+
import { Link } from 'react-router-dom'
5+
6+
import * as routes from '../../routes'
7+
8+
import { List, ListItem } from './styled'
9+
10+
const renderPaginationItem = number => (
11+
<ListItem key={number}>
12+
<Link to={`${routes.PRODUCT_LIST}?page=${number}`}>{number}</Link>
13+
</ListItem>
14+
)
15+
16+
const Pagination = ({ pages }) => (
17+
<List>{map(renderPaginationItem, range(1, pages + 1))}</List>
18+
)
19+
20+
export { Pagination }
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import styled from 'styled-components'
2+
3+
export const List = styled.ul`
4+
align-items: center;
5+
display: flex;
6+
flex-direction: row;
7+
justify-content: center;
8+
list-style: none;
9+
padding: 0;
10+
text-align: center;
11+
width: 100%;
12+
`
13+
14+
export const ListItem = styled.li`
15+
margin: 5px;
16+
`

src/components/PrivateRoute/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react'
22
import { Route, Redirect } from 'react-router-dom'
33
import { connect } from 'react-redux'
44

5+
import * as routes from '../../routes'
6+
57
const PrivateRouteComponent = ({
68
isAuthenticated,
79
component: Component,
@@ -17,7 +19,7 @@ const PrivateRouteComponent = ({
1719
return (
1820
<Redirect
1921
to={{
20-
pathname: '/login',
22+
pathname: routes.LOGIN,
2123
state: {
2224
from: routeProps.location.pathname,
2325
},

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import * as serviceWorker from './serviceWorker'
22

3-
import App from './App'
43
import React from 'react'
54
import ReactDOM from 'react-dom'
65
import { BrowserRouter as Router } from 'react-router-dom'
76

7+
import { App } from './App'
8+
89
const render = () => {
910
ReactDOM.render(
1011
<Router>

0 commit comments

Comments
 (0)