├── .cache
└── 325c8f456729b912b0d2134054eb7448-dfeeb2271cc2857eb0a45a5003c8bbee
├── .gitignore
├── .storybook
└── config.js
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
└── src
├── App.css
├── App.js
├── App.test.js
├── apis
├── login.api.js
├── postit.api.js
└── signup.api.js
├── components
├── alert
│ ├── alert.css
│ ├── alert.js
│ ├── alert.stories.js
│ └── index.js
├── container
│ ├── container.css
│ ├── container.js
│ ├── container.stories.js
│ └── index.js
├── form
│ ├── button
│ │ ├── button.css
│ │ ├── button.js
│ │ ├── button.stories.js
│ │ └── index.js
│ ├── form.css
│ ├── form.js
│ ├── form.stories.js
│ ├── index.js
│ ├── input
│ │ ├── index.js
│ │ ├── input.css
│ │ ├── input.js
│ │ ├── input.stories.js
│ │ └── input2.js
│ ├── label
│ │ ├── index.js
│ │ ├── label.css
│ │ ├── label.js
│ │ └── label.stories.js
│ └── link
│ │ ├── index.js
│ │ ├── link.css
│ │ ├── link.js
│ │ └── link.stories.js
├── navbar
│ ├── index.js
│ ├── logo-reprograma.png
│ ├── menu
│ │ ├── index.js
│ │ ├── menu.css
│ │ ├── menu.js
│ │ └── menu.stories.js
│ ├── navbar.css
│ ├── navbar.js
│ └── navbar.stories.js
└── postit
│ ├── index.js
│ ├── postit.css
│ ├── postit.js
│ └── postit.stories.js
├── index.css
├── index.js
├── infra
├── api-config.js
└── local-storage.js
├── logo.svg
├── pages
├── home
│ ├── home.css
│ ├── home.js
│ ├── home.stories.js
│ └── index.js
├── login
│ ├── index.js
│ ├── login.js
│ └── login.stories.js
├── pageNotFound
│ ├── index.js
│ ├── notFound.jpeg
│ ├── pageNotFound.css
│ └── pageNotFound.js
└── signup
│ ├── index.js
│ ├── signup.js
│ └── signup.stories.js
└── serviceWorker.js
/.cache/325c8f456729b912b0d2134054eb7448-dfeeb2271cc2857eb0a45a5003c8bbee:
--------------------------------------------------------------------------------
1 | {"value":{"success":true,"data":{"latest":{"version":"4.0.0","info":{"plain":"- upgrade webpack & babel to latest\n- new addParameters and third argument to .add to pass data to addons\n- added the ability to theme storybook\n- improved ui for mobile devices\n- improved performance of addon-knobs"}}},"time":1542980441431},"type":"Object"}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
--------------------------------------------------------------------------------
/.storybook/config.js:
--------------------------------------------------------------------------------
1 | import { configure } from '@storybook/react';
2 |
3 | const req = require.context('../src', true, /\.stories\.js$/)
4 |
5 | function loadStories() {
6 | req.keys().forEach((filename) => req(filename))
7 | }
8 |
9 | configure(loadStories, module);
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Todo List React
2 |
3 | Guia para Front End Developers
4 |
5 | https://frontendmasters.com/books/front-end-handbook/2018/learning/internet.html
6 |
7 | Criar Projeto React do Zero
8 |
9 | https://reactjs.org/docs/create-a-new-react-app.html
10 |
11 | Links das Documentações:
12 |
13 |
14 | Lib de Componentes React
15 |
16 | https://material-ui.com/
17 |
18 | React Doc
19 |
20 | https://reactjs.org/
21 |
22 |
23 | EMS6 Resumo
24 |
25 | http://es6-features.org/#Constants
26 |
27 | MDN- DOc HTMl,CSS e Javascript
28 |
29 | https://developer.mozilla.org/pt-BR/docs/Web/JavaScript
30 |
31 | Babel
32 |
33 | https://babeljs.io/repl
34 |
35 | Storybook
36 |
37 | https://storybook.js.org/basics/guide-react/
38 |
39 | Axios Instance
40 |
41 | https://github.com/axios/axios
42 |
43 | React Router
44 |
45 | https://reacttraining.com/react-router/web/api/Link
46 |
47 | Deploy da API gratuitamente
48 |
49 | https://app.netlify.com/
50 |
51 | Deploy de APIS
52 |
53 | https://www.heroku.com/
54 |
55 | Resumo de Map,Filter e Reduce
56 |
57 | http://desenvolvimentoparaweb.com/javascript/map-filter-reduce-javascript/
58 |
59 |
60 | Documentação da API usada
61 |
62 | https://lehtodoapi.herokuapp.com/api-docs
63 |
64 |
65 | Livros e Cursos
66 |
67 | Clean Code - Livro
68 |
69 | https://www.amazon.com.br/C%C3%B3digo-limpo-Robert-C-Martin/dp/8576082675/ref=sr_1_1?ie=UTF8&qid=1542661633&sr=8-1&keywords=clean+codehttps://br.udacity.com/course/object-oriented-javascript--ud015
70 |
71 | Javascript Orientado a Objetos(curso gratuito)
72 |
73 | https://br.udacity.com/course/object-oriented-javascript--ud015
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^3.5.1",
7 | "@storybook/addon-actions": "^4.0.6",
8 | "@storybook/addon-links": "^4.0.6",
9 | "@storybook/addon-notes": "^4.0.6",
10 | "@storybook/addons": "^4.0.6",
11 | "axios": "^0.18.0",
12 | "react": "^16.6.1",
13 | "react-dom": "^16.6.1",
14 | "react-icons": "^3.2.2",
15 | "react-router": "^4.3.1",
16 | "react-router-dom": "^4.3.1",
17 | "react-scripts": "2.1.1",
18 | "storybook-react-router": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "storybook": "start-storybook -p 9001 -c .storybook",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": "react-app"
29 | },
30 | "browserslist": [
31 | ">0.2%",
32 | "not dead",
33 | "not ie <= 11",
34 | "not op_mini all"
35 | ],
36 | "devDependencies": {
37 | "@storybook/react": "^4.0.6"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reprograma/t6-react-todo-list/9c2da5e20c61c795014740138905bb58d08b7f10/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 40vmin;
8 | }
9 |
10 | .App-header {
11 | background-color: #282c34;
12 | min-height: 100vh;
13 | display: flex;
14 | flex-direction: column;
15 | align-items: center;
16 | justify-content: center;
17 | font-size: calc(10px + 2vmin);
18 | color: white;
19 | }
20 |
21 | .App-link {
22 | color: #61dafb;
23 | }
24 |
25 | @keyframes App-logo-spin {
26 | from {
27 | transform: rotate(0deg);
28 | }
29 | to {
30 | transform: rotate(360deg);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Route, Switch } from 'react-router-dom'
3 | import { withRouter } from 'react-router'
4 | import Login from './pages/login'
5 | import Signup from './pages/signup'
6 | import Home from './pages/home'
7 | import Navbar from './components/navbar'
8 | import PageNotFound from './pages/pageNotFound'
9 | import { startServer } from './infra/api-config'
10 | import Alert from './components/alert'
11 | import { getUser } from './infra/local-storage'
12 |
13 | class App extends Component {
14 | constructor(props){
15 | super(props)
16 | this.state = {
17 | openAlert : false,
18 | childrenAlert : ''
19 | }
20 | this.user = ''
21 | }
22 | showAlert= (message) =>{
23 | this.setState({
24 | openAlert : true,
25 | childrenAlert : message
26 | })
27 | }
28 | closeAlert = () =>{
29 | this.setState({
30 | openAlert : false,
31 | childrenAlert : ''
32 | })
33 | }
34 | componentDidMount() {
35 | startServer()
36 | //this.user = getUser()
37 | }
38 |
39 | render() {
40 | this.user = getUser()
41 | return (
42 |
43 |
44 |
45 |
46 | }/>
47 | } />
48 |
49 |
50 | {this.state.childrenAlert}
51 |
52 |
53 | )
54 | }
55 | }
56 |
57 | export default withRouter(App)
58 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/apis/login.api.js:
--------------------------------------------------------------------------------
1 | import api from '../infra/api-config'
2 |
3 | export function loginUser(user){
4 | const url = '/users/login'
5 |
6 | const data = {
7 | email : user.email,
8 | password: user.password
9 | }
10 |
11 | return api().post(url,data)
12 |
13 | }
--------------------------------------------------------------------------------
/src/apis/postit.api.js:
--------------------------------------------------------------------------------
1 | import api from '../infra/api-config'
2 |
3 | export function createPostit(postit){
4 | const url = '/todo'
5 |
6 | const data = {
7 | title : postit.title,
8 | desc : postit.text,
9 | color: postit.color
10 | }
11 |
12 | return api().post(url, data)
13 | }
14 | export function getPostitsApi(){
15 |
16 | const url = '/todo'
17 |
18 | return api().get(url)
19 | }
20 |
21 | export function deletePostit(idPostit){
22 | const url = `/todo/${idPostit}`
23 |
24 | return api().delete(url)
25 | }
26 |
27 | export function updatePostitApi(postit){
28 | const url = `/todo/${postit.id}`
29 |
30 | const data = {
31 | title : postit.title,
32 | desc : postit.text,
33 | color: postit.color
34 | }
35 |
36 | return api().put(url,data)
37 |
38 | }
--------------------------------------------------------------------------------
/src/apis/signup.api.js:
--------------------------------------------------------------------------------
1 | import api from '../infra/api-config'
2 |
3 | function signupUser(user){
4 | const url = '/users'
5 |
6 | const data = {
7 | name : user.name,
8 | email: user.email,
9 | phone: user.phone,
10 | password: user.password
11 | }
12 |
13 | return api().post(url,data)
14 |
15 | }
16 |
17 | export default signupUser
--------------------------------------------------------------------------------
/src/components/alert/alert.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reprograma/t6-react-todo-list/9c2da5e20c61c795014740138905bb58d08b7f10/src/components/alert/alert.css
--------------------------------------------------------------------------------
/src/components/alert/alert.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Dialog from '@material-ui/core/Dialog'
3 | import DialogTitle from '@material-ui/core/DialogTitle'
4 | import Button from '@material-ui/core/Button'
5 |
6 | const Alert = (props) => {
7 | return (
8 |
12 | )
13 | }
14 |
15 | export default Alert
--------------------------------------------------------------------------------
/src/components/alert/alert.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Alert from './index'
4 |
5 | storiesOf('Components/Alert', module)
6 | .add('default', () =>(
7 | Email e Senha não cadastrados!
8 | ))
9 |
--------------------------------------------------------------------------------
/src/components/alert/index.js:
--------------------------------------------------------------------------------
1 | import Alert from './alert'
2 |
3 | export default Alert
--------------------------------------------------------------------------------
/src/components/container/container.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 0.75rem 1rem 0;
3 | }
4 |
5 | @media (min-width: 768px) {
6 | .container {
7 | padding: 1.5rem 20% 0;
8 | }
9 | }
10 |
11 | @media (min-width: 992px) {
12 | .container {
13 | padding: 2.25rem 30% 0;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/components/container/container.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import './container.css'
3 |
4 | function Container(props){
5 | return (
6 |
7 | {props.children}
8 |
9 | )
10 | }
11 |
12 | export default Container
--------------------------------------------------------------------------------
/src/components/container/container.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Container from './index'
4 |
5 | storiesOf('Components/Container', module)
6 | .add('default', () =>(
7 |
8 | Children Here
9 |
10 | ))
--------------------------------------------------------------------------------
/src/components/container/index.js:
--------------------------------------------------------------------------------
1 | import Container from './container'
2 |
3 | export default Container
--------------------------------------------------------------------------------
/src/components/form/button/button.css:
--------------------------------------------------------------------------------
1 | .button {
2 | color: white;
3 | font-size: 1.3rem;
4 | font-weight: bold;
5 | width: 100%;
6 | padding: 0.8rem;
7 | margin-bottom: 1rem;
8 | background: #9351b4;
9 | border: 1px solid #9351b4;
10 | border-radius: 5px;
11 | }
12 |
13 | .button--disabled {
14 | color: gray;
15 | background: lightgrey;
16 | border-color: lightgrey;
17 | }
--------------------------------------------------------------------------------
/src/components/form/button/button.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import './button.css'
3 |
4 | function Button(props){
5 | let classes = 'button'
6 |
7 | if(props.disabled){
8 | classes += ' button--disabled'
9 | }
10 |
11 | return(
12 |
15 | )
16 | }
17 |
18 | export default Button
--------------------------------------------------------------------------------
/src/components/form/button/button.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Button from './index'
4 |
5 | storiesOf('Components/Form/Button', module)
6 | .add('default', () =>(
7 |
8 | ))
9 | .add('with disabled', () =>(
10 |
11 | ))
12 |
--------------------------------------------------------------------------------
/src/components/form/button/index.js:
--------------------------------------------------------------------------------
1 | import Button from './button'
2 |
3 | export default Button
--------------------------------------------------------------------------------
/src/components/form/form.css:
--------------------------------------------------------------------------------
1 | .form {
2 | font-family: monospace;
3 | }
4 | .form__title {
5 | color: #9351b4;
6 | }
7 | .form__text{
8 | font-size: 16px;
9 | }
--------------------------------------------------------------------------------
/src/components/form/form.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Button from './button'
3 | import Input from './input'
4 | import Label from './label'
5 | import Link from './link'
6 |
7 | import './form.css'
8 |
9 | function Form(props) {
10 | return (
11 |
16 | )
17 | }
18 |
19 | Form.Button = Button
20 | Form.Input = Input
21 | Form.Label = Label
22 | Form.Link = Link
23 |
24 | export default Form
--------------------------------------------------------------------------------
/src/components/form/form.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Form from './index'
4 |
5 |
6 | storiesOf('Components/Form', module)
7 | .add('default', () =>(
8 | Nome
10 |
11 |
12 | ))
13 |
--------------------------------------------------------------------------------
/src/components/form/index.js:
--------------------------------------------------------------------------------
1 | import Form from './form'
2 |
3 | export default Form
--------------------------------------------------------------------------------
/src/components/form/input/index.js:
--------------------------------------------------------------------------------
1 | import Input from './input'
2 |
3 | export default Input
--------------------------------------------------------------------------------
/src/components/form/input/input.css:
--------------------------------------------------------------------------------
1 | .input {
2 | font-size: 1rem;
3 | box-sizing: border-box;
4 | display: block;
5 | width: 100%;
6 | padding: 0.8rem;
7 | margin: 0 0 1rem;
8 | border: 1px solid black;
9 | border-radius: 5px;
10 | }
11 | .input:focus {
12 | outline: none;
13 | border-color: #9351b4;
14 | box-shadow: 0 0 10px #9351b4;
15 | }
16 |
17 | .input__erro {
18 | color: red;
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/form/input/input.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import './input.css'
3 |
4 | // function Input2(props){
5 | // return (
6 | //
12 | // )
13 | // }
14 |
15 | class Input extends Component {
16 | constructor(props){
17 | super(props)
18 | this.state={
19 | message : null
20 | }
21 | this.value = ''
22 | }
23 | getValue = () => {
24 | return this.value
25 | }
26 | hasError = () => {
27 | if(this.state.message === null || this.state.message !== ''){
28 | return true
29 | }else{
30 | return false
31 | }
32 | }
33 |
34 | handleChange = (e) => {
35 | this.value = e.target.value
36 | const regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@(([[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
37 | let message = ''
38 | if(this.props.required && this.value.trim() === ''){
39 | message = 'Campo Obrigatório'
40 | }else if(this.value && this.props.minLength && this.value.length < (this.props.minLength)){
41 | message = `Digite pelo menos ${this.props.minLength} caracteres`
42 | }else if(this.props.type==='email' && !regex.test(this.value)){
43 | message= 'Digite um email válido'
44 | }
45 | this.setState({ message : message },this.props.onChange)
46 |
47 | }
48 | render() {
49 | return (
50 |
51 |
60 | {this.state.message}
61 |
62 |
63 | )
64 | }
65 |
66 | }
67 |
68 | export default Input
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/components/form/input/input.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Input from './index'
4 |
5 | storiesOf('Components/Form/Input', module)
6 | .add('default', () =>(
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src/components/form/input/input2.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reprograma/t6-react-todo-list/9c2da5e20c61c795014740138905bb58d08b7f10/src/components/form/input/input2.js
--------------------------------------------------------------------------------
/src/components/form/label/index.js:
--------------------------------------------------------------------------------
1 | import Label from './label'
2 |
3 | export default Label
--------------------------------------------------------------------------------
/src/components/form/label/label.css:
--------------------------------------------------------------------------------
1 | .label {
2 | font-weight: bold;
3 | font-family: monospace;
4 | font-size: 18px;
5 | display: block;
6 | margin-bottom: 0.3rem;
7 | }
--------------------------------------------------------------------------------
/src/components/form/label/label.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import './label.css'
3 |
4 | function Label(props) {
5 | return (
6 |
9 | )
10 | }
11 |
12 | export default Label
--------------------------------------------------------------------------------
/src/components/form/label/label.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Label from './index'
4 |
5 | storiesOf('Components/Form/Label', module)
6 | .add('default', () =>(
7 |
8 | ))
9 |
--------------------------------------------------------------------------------
/src/components/form/link/index.js:
--------------------------------------------------------------------------------
1 | import Link from './link'
2 |
3 | export default Link
--------------------------------------------------------------------------------
/src/components/form/link/link.css:
--------------------------------------------------------------------------------
1 | .link {
2 | cursor: pointer;
3 | color: #9351b4;
4 | text-align: center;
5 | text-decoration: underline;
6 | display: block;
7 | }
--------------------------------------------------------------------------------
/src/components/form/link/link.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link as LinkRouter } from 'react-router-dom'
3 | import './link.css'
4 |
5 | function Link(props){
6 | return (
7 |
8 | {props.children}
9 |
10 | )
11 | }
12 |
13 | export default Link;
14 |
--------------------------------------------------------------------------------
/src/components/form/link/link.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import StoryRouter from 'storybook-react-router'
4 | import Link from './index'
5 |
6 |
7 | storiesOf('Components/Form/Link', module)
8 | .addDecorator(StoryRouter())
9 | .add('default', () =>(
10 | Link here
11 | ))
12 |
13 |
--------------------------------------------------------------------------------
/src/components/navbar/index.js:
--------------------------------------------------------------------------------
1 | import Navbar from './navbar'
2 |
3 | export default Navbar
--------------------------------------------------------------------------------
/src/components/navbar/logo-reprograma.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reprograma/t6-react-todo-list/9c2da5e20c61c795014740138905bb58d08b7f10/src/components/navbar/logo-reprograma.png
--------------------------------------------------------------------------------
/src/components/navbar/menu/index.js:
--------------------------------------------------------------------------------
1 | import Menu from './menu'
2 |
3 | export default Menu
--------------------------------------------------------------------------------
/src/components/navbar/menu/menu.css:
--------------------------------------------------------------------------------
1 | .menu__button{
2 | cursor: pointer;
3 | text-transform: uppercase;
4 | padding: 0.5rem;
5 | border: 1px solid #9351b4;
6 | border-radius: 5px;
7 | }
8 |
9 | .menu__button--open:after {
10 | content: "";
11 | position: fixed;
12 | top: 0;
13 | right: 0;
14 | height: 100vh;
15 | width: 100vw;
16 | background: rgba(0, 0, 0, 0.5);
17 | }
18 |
19 | .menu__options {
20 | list-style: none;
21 | color: white;
22 | font-weight: bold;
23 | font-size: 18px;
24 | font-family: monospace;
25 | text-transform: uppercase;
26 | position: fixed;
27 | top: 0;
28 | left: 0;
29 | height: 100vh;
30 | width: 80vw;
31 | padding: 2rem 1rem 0;
32 | margin: 0;
33 | background: #9351b4;
34 | transform: translateX(-100%);
35 | transition: transform 1s ease-in-out;
36 | }
37 |
38 | .menu__options--open {
39 | transform: translateX(0);
40 | }
41 |
42 | .menu__options--active {
43 | border-bottom: 2px solid white;
44 | }
45 |
46 | .menu__options li + li {
47 | margin-top: 1rem;
48 | }
49 |
50 | .menu__options li a {
51 | cursor: pointer;
52 | color: white;
53 | text-transform: uppercase;
54 | text-decoration: none;
55 | }
56 |
57 | @media (min-width: 992px) {
58 | .menu__button {
59 | display: none;
60 | }
61 |
62 | .menu__options {
63 | display: flex;
64 | align-items: center;
65 | position: initial;
66 | height: initial;
67 | width: initial;
68 | padding: initial;
69 | background: initial;
70 | transform: initial;
71 | transition: initial;
72 | }
73 |
74 | .menu__options--open {
75 | transform: initial;
76 | }
77 |
78 | .menu__options--active {
79 | border-color: #9351b4;
80 | }
81 |
82 | .menu__options li + li {
83 | margin-top: 0;
84 | margin-left: 1rem;
85 | }
86 |
87 | .menu__options li a {
88 | color: #9351b4;
89 | padding: 0.3rem 0;
90 | }
91 | }
--------------------------------------------------------------------------------
/src/components/navbar/menu/menu.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link,Redirect } from 'react-router-dom'
3 | import './menu.css'
4 |
5 |
6 | class Menu extends React.Component {
7 | constructor(props){
8 | super(props)
9 | this.state = { open : false}
10 | }
11 |
12 | handleOpenOrClose = () => {
13 | this.setState({ open : !this.state.open})
14 | }
15 | handleLoginOrLogout = (e) => {
16 | e.preventDefault()
17 | if(this.props.user){
18 | localStorage.clear()
19 | }
20 | this.props.history.push('/login')
21 | }
22 | render(){
23 | console.log('hello render')
24 | let classesOfButton = 'menu__button'
25 | let classesOfOptions = 'menu__options'
26 |
27 | if(this.state.open){
28 | classesOfButton += ' menu__button--open'
29 | classesOfOptions += ' menu__options--open'
30 | }
31 | return (
32 |
54 | )
55 |
56 | }
57 | }
58 |
59 | export default Menu
--------------------------------------------------------------------------------
/src/components/navbar/menu/menu.stories.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reprograma/t6-react-todo-list/9c2da5e20c61c795014740138905bb58d08b7f10/src/components/navbar/menu/menu.stories.js
--------------------------------------------------------------------------------
/src/components/navbar/navbar.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | display: flex;
3 | align-items: center;
4 | justify-content: space-between;
5 | flex: 0 60px;
6 | min-height: 60px;
7 | padding: 0 1rem;
8 | background: #ffffff;
9 | border-bottom: 1px solid #d4dadf;
10 | box-shadow: 0 1px 1px 0 #f0f1f4;
11 | }
12 |
13 | .navbar__logo {
14 | display: block;
15 | width: 200px;
16 | }
17 |
18 |
19 | @media (min-width: 992px) {
20 | .navbar {
21 | padding: 0 10%;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/components/navbar/navbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 | import Menu from './menu'
4 | import logo from './logo-reprograma.png'
5 | import './navbar.css'
6 |
7 | const Navbar = (props) => (
8 |
18 | )
19 |
20 | export default Navbar
--------------------------------------------------------------------------------
/src/components/navbar/navbar.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import StoryRouter from 'storybook-react-router'
4 | import Navbar from './index'
5 |
6 |
7 | storiesOf('Components/Navbar', module)
8 | .addDecorator(StoryRouter())
9 | .add('default', () =>(
10 |
11 | ))
12 |
13 |
--------------------------------------------------------------------------------
/src/components/postit/index.js:
--------------------------------------------------------------------------------
1 | import Postit from './postit'
2 |
3 | export default Postit
--------------------------------------------------------------------------------
/src/components/postit/postit.css:
--------------------------------------------------------------------------------
1 | .postit {
2 | font-size: 1rem;
3 | display: flex;
4 | flex-direction: row;
5 | box-sizing: border-box;
6 | width: 100%;
7 | padding: 1rem;
8 | margin: 0 0 1.5rem;
9 | background: #ECDDF3;
10 | border-radius: 2px;
11 | box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.14);
12 | }
13 |
14 | .postit__color {
15 | border-radius: 100%;
16 | background-color: transparent;
17 | border: none;
18 | width: 1.5rem;
19 | height: 1.5rem;
20 | /* float: right; */
21 | display: block;
22 | margin-left: auto;
23 | }
24 | .postit__title {
25 | font-weight: bold;
26 | }
27 |
28 | .postit__button-remove {
29 | cursor: pointer;
30 | margin-left: auto;
31 | float: right;
32 | }
33 |
34 | .postit__button-completed {
35 | cursor: pointer;
36 | text-transform: uppercase;
37 | margin-left: auto;
38 | }
39 |
40 | .postit__title,
41 | .postit__text,
42 | .postit__button-remove,
43 | .postit__button-completed {
44 | pointer-events: auto;
45 | font-size: inherit;
46 | padding: 0.5rem;
47 | border: none;
48 | background: inherit;
49 | }
50 |
51 | .postit__title:focus,
52 | .postit__text:focus,
53 | .postit__button-completed:focus {
54 | outline: none;
55 | }
56 |
57 | @media (min-width: 992px) {
58 | .postit {
59 | display: inline-flex;
60 | width: 30%;
61 | }
62 |
63 | .postit:nth-child(3n + 2) {
64 | margin-left: 5%;
65 | margin-right: 5%;
66 | }
67 | }
--------------------------------------------------------------------------------
/src/components/postit/postit.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Form from '../form'
3 | import { createPostit, deletePostit, updatePostitApi } from '../../apis/postit.api'
4 |
5 | import './postit.css'
6 |
7 | class Postit extends React.Component {
8 | constructor(props){
9 | super(props)
10 | this.state = {
11 | id: this.props.id ? this.props.id : null,
12 | title: this.props.title ? this.props.title : '',
13 | text: this.props.text ? this.props.text : '',
14 | editing : false,
15 | color : this.props.color ? this.props.color : '#ECDDF3'
16 | }
17 | }
18 | handlePostitClick = () => {
19 | console.log('handlePostitClick')
20 | this.setState({
21 | editing : true
22 | })
23 | }
24 | handlePostitRemove = (e) =>{
25 | console.log('handlePostitRemove')
26 | e.stopPropagation()
27 | const id = this.state.id
28 | deletePostit(id)
29 | .then((response) =>{
30 | console.log(response)
31 | this.props.updatePostits()
32 | })
33 | .catch((error)=>{
34 | console.log(error)
35 | })
36 |
37 | }
38 | handlePostitSubmit = (e) => {
39 | e.preventDefault()
40 |
41 | if(this.state.id){
42 | const postit = {
43 | title : this.state.title,
44 | text : this.state.text,
45 | id : this.state.id,
46 | color: this.state.color
47 | }
48 | updatePostitApi(postit)
49 | .then((reponse)=>{
50 | this.setState({
51 | editing : false
52 | })
53 | })
54 | .catch((error)=>{
55 | console.log(error)
56 | })
57 |
58 | }else{
59 |
60 | const postit = {
61 | title : this.state.title,
62 | text : this.state.text,
63 | color: this.state.color
64 | }
65 |
66 | createPostit(postit)
67 | .then((response) =>{
68 | console.log(this)
69 | this.props.updatePostits()
70 | this.setState({
71 | id : '',
72 | title : '',
73 | text : '',
74 | color: '#ECDDF3',
75 | editing: false
76 | })
77 | })
78 | .catch((error)=>{
79 | console.log(error)
80 | })
81 | }
82 |
83 | }
84 | setTitle = (e) => {
85 | const inputTitle = e.target.value
86 | console.log('evento ',e)
87 | this.setState({
88 | title : inputTitle
89 | })
90 | }
91 | setText = (event) => {
92 | const inputText = event.target.value
93 | this.setState({
94 | text : inputText
95 | })
96 | }
97 | setColor = (e) =>{
98 | this.setState({
99 | color : e.target.value
100 | })
101 | }
102 | render() {
103 | return (
104 |
105 |
106 |
140 |
141 | )
142 | }
143 | }
144 |
145 | export default Postit
--------------------------------------------------------------------------------
/src/components/postit/postit.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import Postit from './index'
4 |
5 |
6 | storiesOf('Components/Postit', module)
7 | .add('default', () =>(
8 |
9 | ))
10 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import './index.css'
4 | import App from './App'
5 | import { BrowserRouter } from 'react-router-dom'
6 |
7 | import * as serviceWorker from './serviceWorker'
8 |
9 | ReactDOM.render(
10 |
11 |
12 | ,
13 | document.getElementById('root'))
14 |
15 | // If you want your app to work offline and load faster, you can change
16 | // unregister() to register() below. Note this comes with some pitfalls.
17 | // Learn more about service workers: http://bit.ly/CRA-PWA
18 | serviceWorker.unregister()
19 |
--------------------------------------------------------------------------------
/src/infra/api-config.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { getUser } from './local-storage'
3 |
4 | // axios.create( {
5 | // baseUrl : 'https://lehtodoapi.herokuapp.com/',
6 | // timeout: 5000,
7 | // }).get('/ping')
8 | const config = {
9 | baseURL : 'https://lehtodoapi.herokuapp.com/',
10 | timeout: 5000,
11 | headers: ''
12 | }
13 |
14 | function api(){
15 | const user = getUser()
16 | if(user){
17 | config.headers = {
18 | user : user
19 | }
20 | }
21 | return axios.create(config)
22 | }
23 |
24 | export function startServer(){
25 | const url ='/ping'
26 |
27 | return api().get(url)
28 | }
29 |
30 | export default api
31 |
32 |
--------------------------------------------------------------------------------
/src/infra/local-storage.js:
--------------------------------------------------------------------------------
1 | export function getUser() {
2 | return localStorage.getItem('user')
3 | }
4 | export function setUser(user){
5 | localStorage.setItem('user',JSON.stringify(user))
6 | }
7 |
8 | export function clearUser(){
9 | localStorage.clear()
10 | }
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/pages/home/home.css:
--------------------------------------------------------------------------------
1 |
2 | .home__search {
3 | font-size: 1rem;
4 | box-sizing: border-box;
5 | display: block;
6 | width: 100%;
7 | padding: 0.8rem;
8 | margin: 0 0 1rem;
9 | margin-bottom: 10px;
10 | margin-top: 10px;
11 | border: 1px solid black;
12 | border-radius: 5px;
13 | }
14 | .home__search:focus {
15 | outline: none;
16 | border-color: #9351b4;
17 | box-shadow: 0 0 10px #9351b4;
18 | }
19 |
20 | @media (min-width: 992px) {
21 | .home__search {
22 | width: 50%;
23 | margin: 0 auto;
24 | margin-bottom: 10px;
25 | margin-top: 10px;
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/src/pages/home/home.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { getUser } from '../../infra/local-storage'
3 | import { Redirect } from 'react-router-dom'
4 | import Postit from '../../components/postit'
5 | import { getPostitsApi } from '../../apis/postit.api'
6 |
7 | import './home.css'
8 |
9 | class Home extends React.Component {
10 | constructor(){
11 | super()
12 | this.state = {
13 | postits : [],
14 | postitsFilters : []
15 | }
16 | }
17 | componentDidMount() {
18 | console.log('hello componentDidMount foi criado')
19 | this.getPostits()
20 | }
21 | componentWillUnmount() {
22 | console.log('hello componentWillUnmount morreu :(')
23 | }
24 | getPostits = () => {
25 | getPostitsApi()
26 | .then((response) => {
27 | console.log(response)
28 | this.setState({
29 | postits : response.data.todo.reverse()
30 | })
31 | })
32 | .catch((error)=>{
33 | console.log(error)
34 | })
35 | }
36 | onFilterPostit = (e) => {
37 | const value = e.target.value.toLowerCase()
38 | //const postitsFilter = []
39 | //for( var i = 0 ; i < this.state.postits.length; i++ ){
40 | // const title = this.state.postits[i].title
41 | // if(title.indexOf(value) !== -1) {
42 | // postitsFilter.push(this.state.postits[i])
43 | // }
44 | // }
45 | const postits = this.state.postits.filter((item)=>{
46 | return item.title.toLowerCase().indexOf(value) !== -1
47 | })
48 | this.setState({
49 | postitsFilters : postits
50 | })
51 | }
52 | render(){
53 | const user = this.props.user ? this.props.user : getUser()
54 |
55 | const postits = this.state.postitsFilters.length > 0
56 | ? this.state.postitsFilters
57 | : this.state.postits
58 | if(user){
59 | return (
60 |
61 |
62 |
63 |
64 | {postits.map((item,index)=>(
65 |
73 | ))}
74 |
75 |
76 |
77 | )
78 |
79 | }else{
80 | return
81 | }
82 | }
83 | }
84 | export default Home
--------------------------------------------------------------------------------
/src/pages/home/home.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import StoryRouter from 'storybook-react-router'
4 | import Home from './index'
5 | import Navbar from '../../components/navbar'
6 |
7 | storiesOf('Pages/Home', module)
8 | .addDecorator(StoryRouter())
9 | .add('default', () =>(
10 |
11 |
12 |
13 |
14 |
15 | ))
--------------------------------------------------------------------------------
/src/pages/home/index.js:
--------------------------------------------------------------------------------
1 | import Home from './home'
2 |
3 | export default Home
--------------------------------------------------------------------------------
/src/pages/login/index.js:
--------------------------------------------------------------------------------
1 | import Login from './login'
2 |
3 | export default Login
4 |
5 |
--------------------------------------------------------------------------------
/src/pages/login/login.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Form from '../../components/form'
3 | import Container from '../../components/container'
4 | import { setUser } from '../../infra/local-storage'
5 | import { loginUser } from '../../apis/login.api'
6 |
7 | class Login extends React.Component {
8 | constructor(props){
9 | super(props)
10 |
11 | this.state= { disabled : true }
12 | this.email = React.createRef()
13 | this.password = React.createRef()
14 | }
15 |
16 | onDisabledButton = () => {
17 |
18 | const inputEmail = this.email.current
19 | const inputPassword = this.password.current
20 |
21 | if(inputEmail.hasError() || inputPassword.hasError()){
22 | this.setState({ disabled : true})
23 | }else{
24 | this.setState({ disabled : false})
25 | }
26 | }
27 | handleSubmit = (e) =>{
28 | e.preventDefault()
29 | const inputEmail = this.email.current
30 | const inputPassword = this.password.current
31 | const user = {
32 | email : inputEmail.getValue(),
33 | password : inputPassword.getValue()
34 | }
35 |
36 | loginUser(user)
37 | .then((response) => {
38 | setUser({ email : user.email})
39 | this.props.history.push('/')
40 | })
41 | .catch((error)=> {
42 | console.log(error.response.data.msg)
43 | this.props.showAlert(error.response.data.msg)
44 | })
45 |
46 | }
47 | render(){
48 | return (
49 |
50 | Email
52 |
53 | Password
54 |
55 | Enviar
56 | Criar uma Conta
57 |
58 |
59 | )
60 | }
61 | }
62 |
63 |
64 |
65 | export default Login
--------------------------------------------------------------------------------
/src/pages/login/login.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import StoryRouter from 'storybook-react-router'
4 | import Login from './index'
5 |
6 | storiesOf('Pages/Login', module)
7 | .addDecorator(StoryRouter())
8 | .add('default', () =>(
9 |
10 | ))
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/pages/pageNotFound/index.js:
--------------------------------------------------------------------------------
1 | import PageNotFound from './pageNotFound'
2 |
3 | export default PageNotFound
--------------------------------------------------------------------------------
/src/pages/pageNotFound/notFound.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reprograma/t6-react-todo-list/9c2da5e20c61c795014740138905bb58d08b7f10/src/pages/pageNotFound/notFound.jpeg
--------------------------------------------------------------------------------
/src/pages/pageNotFound/pageNotFound.css:
--------------------------------------------------------------------------------
1 | .page-not-found {
2 | padding: 1.5rem 1rem 0;
3 | text-align: center;
4 | }
5 |
6 | @media (min-width: 992px) {
7 | .page-not-found {
8 | padding: 3rem 10% 0;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/pages/pageNotFound/pageNotFound.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import './pageNotFound.css'
3 | import NotFoundImage from './notFound.jpeg'
4 |
5 | function PageNotFound(){
6 | return (
7 |
8 |
Página não encontrada
9 |

10 |
11 | )
12 | }
13 |
14 | export default PageNotFound
--------------------------------------------------------------------------------
/src/pages/signup/index.js:
--------------------------------------------------------------------------------
1 | import Signup from './signup'
2 |
3 | export default Signup
--------------------------------------------------------------------------------
/src/pages/signup/signup.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Form from '../../components/form'
3 | import Container from '../../components/container'
4 | import signupUser from '../../apis/signup.api'
5 | import { setUser } from '../../infra/local-storage'
6 |
7 | // function Signup(){
8 | // return (
9 | //
10 | // Nome:
12 | //
13 | // Email:
14 | //
15 | // Telefone:
16 | //
17 | // Senha:
18 | //
19 | // Enviar
20 | // Fazer Login
21 | //
22 | //
23 | // )
24 | // }
25 |
26 | class Signup extends React.Component{
27 | constructor(props){
28 | super(props)
29 | this.state = { disabled : true }
30 | this.name = React.createRef()
31 | this.email = React.createRef()
32 | this.phone = React.createRef()
33 | this.password = React.createRef()
34 | }
35 | handleSubmit= (e) => {
36 |
37 | e.preventDefault()
38 |
39 | const inputName = this.name.current
40 | const inputEmail = this.email.current
41 | const inputPhone = this.phone.current
42 | const inputPassword = this.password.current
43 |
44 | const user = {
45 | name: inputName.getValue(),
46 | email: inputEmail.getValue(),
47 | phone: inputPhone.getValue(),
48 | password: inputPassword.getValue(),
49 | }
50 |
51 | signupUser(user)
52 | .then((response) => {
53 | setUser({ email : user.email})
54 | this.props.history.push('/')
55 | this.props.showAlert('Usuário Cadastrado com Sucesso')
56 | })
57 | .catch((error) => {
58 | console.log(error)
59 | })
60 |
61 | }
62 | onDisabledButton = () => {
63 | const inputName = this.name.current
64 | const inputEmail = this.email.current
65 | const inputPhone = this.phone.current
66 | const inputPassword = this.password.current
67 |
68 | if(inputName.hasError()
69 | || inputEmail.hasError()
70 | || inputPhone.hasError()
71 | || inputPassword.hasError()){
72 |
73 | this.setState({ disabled : true})
74 |
75 | }else{
76 | this.setState({ disabled : false})
77 | }
78 | }
79 | render(){
80 | return (
81 |
82 | Nome:
84 |
85 | Email:
86 |
87 | Telefone:
88 |
89 | Senha:
90 |
91 | Enviar
92 | Fazer Login
93 |
94 |
95 | )
96 | }
97 | }
98 |
99 | export default Signup
--------------------------------------------------------------------------------
/src/pages/signup/signup.stories.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import StoryRouter from 'storybook-react-router'
4 | import Signup from './index'
5 |
6 | storiesOf('Pages/Signup', module)
7 | .addDecorator(StoryRouter())
8 | .add('default', () =>(
9 |
10 | ))
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read http://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------