├── .babelrc ├── .eslintrc ├── .flowconfig ├── .gitignore ├── README.md ├── components ├── Container.js ├── Footer.js ├── Header.js ├── Page.js ├── UserThumb.js └── index.js ├── layouts ├── Posts.js ├── Users.js └── index.js ├── package.json ├── pages ├── _document.js ├── index.js └── posts.js ├── utils ├── api.js ├── api.test.js ├── capitalizefl.js ├── capitalizefl.test.js ├── index.js ├── initials.js ├── initials.test.js └── types.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "next/babel" 4 | ], 5 | "plugins": [ 6 | "transform-export-extensions" 7 | ], 8 | "env": { 9 | "test": { 10 | "presets": ["es2015", "next/babel"] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "jest": true 7 | }, 8 | "extends": [ 9 | "airbnb", 10 | "plugin:flowtype/recommended" 11 | ], 12 | "plugins": [ 13 | "flowtype" 14 | ], 15 | "rules": { 16 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 17 | "react/require-default-props": [0], 18 | "import/first": [0], 19 | "no-unused-expressions": [0], 20 | "arrow-parens": [0], 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renatorib/next-jph/706b44bee21529cf49a8fd2e44b2b01b0286f957/.flowconfig -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | ### What? 6 | Next JPH is an example app made with [JsonPlaceholder](http://jsonplaceholder.typicode.com/) api, using [Next.js](http://github.com/zeit/next.js), [styled-components](http://github.com/styled-components/styled-components), [flow](http://github.com/facebook/flow) and [jest](http://github.com/facebook/jest). 7 | 8 | *"JPH" is an acronym to **J**son**P**lace**h**older.* 9 | 10 | **See the app running at `now`**: http://jph.now.sh 11 | 12 | ### Why? 13 | This is a project I've made to learn more about the used stack. 14 | Also, is a good reference to newcommers in these technologies. 15 | 16 | Feel free to contribute if you find any mistake I've made, or if you want to improve the application. 17 | 18 | 19 | ## Development 20 | First of all, install dependencies: 21 | ```sh 22 | yarn 23 | ``` 24 | 25 | Run development server: 26 | ```sh 27 | yarn dev 28 | ``` 29 | Then open http://localhost:3000 30 | 31 | ## Production Deployment 32 | Next.js is production-ready framework with *server-side rendering* and *code-splitting* built-in. 33 | 34 | Just run these commands: 35 | ```sh 36 | yarn build 37 | yarn start 38 | ``` 39 | -------------------------------------------------------------------------------- /components/Container.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export default styled.div` 4 | width: 100%; 5 | max-width: 580px; 6 | padding: 0 10px; 7 | margin: 0 auto; 8 | box-sizing: border-box; 9 | `; 10 | -------------------------------------------------------------------------------- /components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | export default () => ( 5 | 6 | 7 | Next.js JsonPlaceholder sample app by  8 | 9 | @renatorib 10 | 11 | 12 | 13 | ); 14 | 15 | const Wrapper = styled.div` 16 | margin-top: 30px; 17 | border-top: 1px solid #EEEEEE; 18 | `; 19 | 20 | const CenterMessage = styled.div` 21 | padding: 30px 0; 22 | text-align: center; 23 | color: #BBBBBB; 24 | font-size: 12px; 25 | 26 | a { 27 | color: #8888AA; 28 | text-decoration: none; 29 | } 30 | `; 31 | -------------------------------------------------------------------------------- /components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import Link from 'next/link'; 4 | import { Container } from '.'; 5 | 6 | export default () => ( 7 | 8 | 9 | 10 | 11 | 12 | Next JPH 13 | 14 | 15 | Github 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | 23 | const Wrapper = styled.div` 24 | height: 50px; 25 | `; 26 | 27 | const Fixed = styled.div` 28 | width: 100%; 29 | height: 50px; 30 | line-height: 50px; 31 | border-bottom: 1px solid #EEE; 32 | background: white; 33 | position: fixed; 34 | z-index: 99; 35 | `; 36 | 37 | const Flex = styled.div` 38 | display: flex; 39 | align-items: center; 40 | justify-content: space-between; 41 | `; 42 | 43 | const Logo = styled.div` 44 | a { 45 | color: #888; 46 | font-weight: 300; 47 | letter-spacing: 1px; 48 | font-size: 22px; 49 | text-decoration: none; 50 | transition: all 1s ease; 51 | 52 | &:hover { 53 | color: #9b59b6; 54 | } 55 | } 56 | `; 57 | 58 | const Links = styled.div` 59 | a { 60 | color: #AAAAFF; 61 | transition: all 200ms ease; 62 | text-decoration: none; 63 | font-weight: 400; 64 | 65 | &:hover { 66 | opacity: 0.4; 67 | } 68 | 69 | &:not(:last-child) { 70 | margin-right: 10px; 71 | } 72 | } 73 | `; 74 | -------------------------------------------------------------------------------- /components/Page.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import Head from 'next/head'; 5 | import NProgress from 'nprogress'; 6 | import Router from 'next/router'; 7 | 8 | Router.onRouteChangeStart = () => NProgress.start(); 9 | Router.onRouteChangeComplete = () => NProgress.done(); 10 | Router.onRouteChangeError = () => NProgress.done(); 11 | 12 | type Props = { 13 | children?: any, 14 | title?: string, 15 | }; 16 | 17 | const Page = ({ children, title, ...restProps }: Props) => ( 18 |
19 | 20 | {title || 'Placeholder'} 21 | 22 | {children} 23 |
24 | ); 25 | 26 | export default Page; 27 | -------------------------------------------------------------------------------- /components/UserThumb.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import styled from 'styled-components'; 5 | import { prop } from 'styled-tools'; 6 | import { initials } from '../utils'; 7 | 8 | type Props = { 9 | name: string, 10 | }; 11 | 12 | export default ({ name, ...restProps }: Props) => ( 13 | 14 | {initials(name)} 15 | 16 | ); 17 | 18 | const Wrapper = styled.div` 19 | width: ${prop('size', 50)}px; 20 | height: ${prop('size', 50)}px; 21 | background: ${prop('color', '#aaaaaa')}; 22 | color: white; 23 | border-radius: ${prop('size', 50)}px; 24 | font-size: 20px; 25 | line-height: ${prop('size', 50)}px; 26 | text-align: center; 27 | font-weight: 400; 28 | `; 29 | -------------------------------------------------------------------------------- /components/index.js: -------------------------------------------------------------------------------- 1 | export Container from './Container'; 2 | export Footer from './Footer'; 3 | export Header from './Header'; 4 | export Page from './Page'; 5 | export UserThumb from './UserThumb'; 6 | -------------------------------------------------------------------------------- /layouts/Posts.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Post, User } from '../utils/types'; 4 | import React from 'react'; 5 | import styled from 'styled-components'; 6 | import { Header, Footer, Container } from '../components'; 7 | import { capitalizefl } from '../utils'; 8 | 9 | type Props = { 10 | posts: Post[], 11 | user: User, 12 | }; 13 | 14 | export default ({ posts, user }: Props) => ( 15 | 16 |
17 | 18 | 19 | <span>posts from</span> 20 | <h1>{user.name}</h1> 21 | 22 | 23 | {posts && posts.map(post => ( 24 | 25 |

{capitalizefl(post.title)}.

26 |

{capitalizefl(post.body)}.

27 |
28 | ))} 29 |
30 |