├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── src
├── components
│ ├── About.js
│ ├── Footer.js
│ ├── Tasks.js
│ ├── Button.js
│ ├── Task.js
│ ├── Header.js
│ └── AddTask.js
├── reportWebVitals.js
├── index.js
├── index.css
└── App.js
├── db.json
├── .gitignore
├── README.md
├── package.json
└── .eslintcache
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bradtraversy/react-crash-2021/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bradtraversy/react-crash-2021/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bradtraversy/react-crash-2021/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/components/About.js:
--------------------------------------------------------------------------------
1 | import { Link } from 'react-router-dom'
2 |
3 | const About = () => {
4 | return (
5 |
6 |
Version 1.0.0
7 | Go Back
8 |
9 | )
10 | }
11 |
12 | export default About
13 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import { Link } from 'react-router-dom'
2 |
3 | const Footer = () => {
4 | return (
5 |
9 | )
10 | }
11 |
12 | export default Footer
13 |
--------------------------------------------------------------------------------
/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "tasks": [
3 | {
4 | "id": 1,
5 | "text": "Doctors Appointment",
6 | "day": "Feb 5th at 2:30pm",
7 | "reminder": true
8 | },
9 | {
10 | "id": 2,
11 | "text": "Meeting at School",
12 | "day": "Feb 6th at 1:30pm",
13 | "reminder": true
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/src/components/Tasks.js:
--------------------------------------------------------------------------------
1 | import Task from './Task'
2 |
3 | const Tasks = ({ tasks, onDelete, onToggle }) => {
4 | return (
5 | <>
6 | {tasks.map((task, index) => (
7 |
8 | ))}
9 | >
10 | )
11 | }
12 |
13 | export default Tasks
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/components/Button.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | const Button = ({ color, text, onClick }) => {
4 | return (
5 |
12 | )
13 | }
14 |
15 | Button.defaultProps = {
16 | color: 'steelblue',
17 | }
18 |
19 | Button.propTypes = {
20 | text: PropTypes.string,
21 | color: PropTypes.string,
22 | onClick: PropTypes.func,
23 | }
24 |
25 | export default Button
26 |
--------------------------------------------------------------------------------
/src/components/Task.js:
--------------------------------------------------------------------------------
1 | import { FaTimes } from 'react-icons/fa'
2 |
3 | const Task = ({ task, onDelete, onToggle }) => {
4 | return (
5 | onToggle(task.id)}
8 | >
9 |
10 | {task.text}{' '}
11 | onDelete(task.id)}
14 | />
15 |
16 |
{task.day}
17 |
18 | )
19 | }
20 |
21 | export default Task
22 |
--------------------------------------------------------------------------------
/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 reportWebVitals from './reportWebVitals';
6 |
7 | ReactDOM.render(
8 |
9 |
10 | ,
11 | document.getElementById('root')
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Crash Course 2021 (Task Tracker App)
2 |
3 | This is the project from the [YouTube crash course](https://www.youtube.com/watch?v=w7ejDZ8SWv8). It includes the react ui as well as JSON-server for our mock backend
4 |
5 | ## Usage
6 |
7 | ### Install dependencies
8 |
9 | ```
10 | npm install
11 | ```
12 |
13 | ### Run React dev server (http://localhost:3000)
14 |
15 | ```
16 | npm start
17 | ```
18 |
19 | ### Run the JSON server (http://localhost:5000)
20 |
21 | ```
22 | npm run server
23 | ```
24 |
25 | ### To build for production
26 |
27 | ```
28 | npm run build
29 | ```
30 |
--------------------------------------------------------------------------------
/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 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 | Task Tracker
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { useLocation } from 'react-router-dom'
3 | import Button from './Button'
4 |
5 | const Header = ({ title, onAdd, showAdd }) => {
6 | const location = useLocation()
7 |
8 | return (
9 |
10 | {title}
11 | {location.pathname === '/' && (
12 |
17 | )}
18 |
19 | )
20 | }
21 |
22 | Header.defaultProps = {
23 | title: 'Task Tracker',
24 | }
25 |
26 | Header.propTypes = {
27 | title: PropTypes.string.isRequired,
28 | }
29 |
30 | // CSS in JS
31 | // const headingStyle = {
32 | // color: 'red',
33 | // backgroundColor: 'black',
34 | // }
35 |
36 | export default Header
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-task-tracker",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "json-server": "^0.16.3",
10 | "react": "^17.0.1",
11 | "react-dom": "^17.0.1",
12 | "react-icons": "^4.1.0",
13 | "react-router-dom": "^6.0.2",
14 | "react-scripts": "4.0.1",
15 | "web-vitals": "^0.2.4"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test",
21 | "eject": "react-scripts eject",
22 | "server": "json-server --watch db.json --port 5000"
23 | },
24 | "eslintConfig": {
25 | "extends": [
26 | "react-app",
27 | "react-app/jest"
28 | ]
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/AddTask.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 |
3 | const AddTask = ({ onAdd }) => {
4 | const [text, setText] = useState('')
5 | const [day, setDay] = useState('')
6 | const [reminder, setReminder] = useState(false)
7 |
8 | const onSubmit = (e) => {
9 | e.preventDefault()
10 |
11 | if (!text) {
12 | alert('Please add a task')
13 | return
14 | }
15 |
16 | onAdd({ text, day, reminder })
17 |
18 | setText('')
19 | setDay('')
20 | setReminder(false)
21 | }
22 |
23 | return (
24 |
55 | )
56 | }
57 |
58 | export default AddTask
59 |
--------------------------------------------------------------------------------
/.eslintcache:
--------------------------------------------------------------------------------
1 | [{"/Users/bradtraversy/Desktop/dev/react-crash-2021/src/App.js":"1","/Users/bradtraversy/Desktop/dev/react-crash-2021/src/components/AddTask.js":"2","/Users/bradtraversy/Desktop/dev/react-crash-2021/src/components/Task.js":"3","/Users/bradtraversy/Desktop/dev/react-crash-2021/src/components/TaskDetails.js":"4"},{"size":3112,"mtime":1638644487276,"results":"5","hashOfConfig":"6"},{"size":1347,"mtime":1638631205734,"results":"7","hashOfConfig":"6"},{"size":455,"mtime":1638644496012,"results":"8","hashOfConfig":"6"},{"size":1002,"mtime":1638633759781,"results":"9","hashOfConfig":"6"},{"filePath":"10","messages":"11","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"18ahndu",{"filePath":"12","messages":"13","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"14","messages":"15","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"16","messages":"17","errorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"/Users/bradtraversy/Desktop/dev/react-crash-2021/src/App.js",[],"/Users/bradtraversy/Desktop/dev/react-crash-2021/src/components/AddTask.js",[],"/Users/bradtraversy/Desktop/dev/react-crash-2021/src/components/Task.js",[],"/Users/bradtraversy/Desktop/dev/react-crash-2021/src/components/TaskDetails.js",["18","19","20"],{"ruleId":"21","severity":1,"message":"22","line":2,"column":21,"nodeType":"23","messageId":"24","endLine":2,"endColumn":29},{"ruleId":"21","severity":1,"message":"25","line":8,"column":10,"nodeType":"23","messageId":"24","endLine":8,"endColumn":15},{"ruleId":"21","severity":1,"message":"26","line":8,"column":17,"nodeType":"23","messageId":"24","endLine":8,"endColumn":25},"no-unused-vars","'Navigate' is defined but never used.","Identifier","unusedVar","'error' is assigned a value but never used.","'setError' is assigned a value but never used."]
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400&display=swap');
2 |
3 | * {
4 | box-sizing: border-box;
5 | margin: 0;
6 | padding: 0;
7 | }
8 |
9 | body {
10 | font-family: 'Poppins', sans-serif;
11 | }
12 |
13 | .container {
14 | max-width: 500px;
15 | margin: 30px auto;
16 | overflow: auto;
17 | min-height: 300px;
18 | border: 1px solid steelblue;
19 | padding: 30px;
20 | border-radius: 5px;
21 | }
22 |
23 | .header {
24 | display: flex;
25 | justify-content: space-between;
26 | align-items: center;
27 | margin-bottom: 20px;
28 | }
29 |
30 | .btn {
31 | display: inline-block;
32 | background: #000;
33 | color: #fff;
34 | border: none;
35 | padding: 10px 20px;
36 | margin: 5px;
37 | border-radius: 5px;
38 | cursor: pointer;
39 | text-decoration: none;
40 | font-size: 15px;
41 | font-family: inherit;
42 | }
43 |
44 | .btn:focus {
45 | outline: none;
46 | }
47 |
48 | .btn:active {
49 | transform: scale(0.98);
50 | }
51 |
52 | .btn-block {
53 | display: block;
54 | width: 100%;
55 | }
56 |
57 | .task {
58 | background: #f4f4f4;
59 | margin: 5px;
60 | padding: 10px 20px;
61 | cursor: pointer;
62 | }
63 |
64 | .task.reminder {
65 | border-left: 5px solid green;
66 | }
67 |
68 | .task h3 {
69 | display: flex;
70 | align-items: center;
71 | justify-content: space-between;
72 | }
73 |
74 | .add-form {
75 | margin-bottom: 40px;
76 | }
77 |
78 | .form-control {
79 | margin: 20px 0;
80 | }
81 |
82 | .form-control label {
83 | display: block;
84 | }
85 |
86 | .form-control input {
87 | width: 100%;
88 | height: 40px;
89 | margin: 5px;
90 | padding: 3px 7px;
91 | font-size: 17px;
92 | }
93 |
94 | .form-control-check {
95 | display: flex;
96 | align-items: center;
97 | justify-content: space-between;
98 | }
99 |
100 | .form-control-check label {
101 | flex: 1;
102 | }
103 |
104 | .form-control-check input {
105 | flex: 2;
106 | height: 20px;
107 | }
108 |
109 | footer {
110 | margin-top: 30px;
111 | text-align: center;
112 | }
113 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 | import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'
3 | import Header from './components/Header'
4 | import Footer from './components/Footer'
5 | import Tasks from './components/Tasks'
6 | import AddTask from './components/AddTask'
7 | import About from './components/About'
8 |
9 | const App = () => {
10 | const [showAddTask, setShowAddTask] = useState(false)
11 | const [tasks, setTasks] = useState([])
12 |
13 | useEffect(() => {
14 | const getTasks = async () => {
15 | const tasksFromServer = await fetchTasks()
16 | setTasks(tasksFromServer)
17 | }
18 |
19 | getTasks()
20 | }, [])
21 |
22 | // Fetch Tasks
23 | const fetchTasks = async () => {
24 | const res = await fetch('http://localhost:5000/tasks')
25 | const data = await res.json()
26 |
27 | return data
28 | }
29 |
30 | // Fetch Task
31 | const fetchTask = async (id) => {
32 | const res = await fetch(`http://localhost:5000/tasks/${id}`)
33 | const data = await res.json()
34 |
35 | return data
36 | }
37 |
38 | // Add Task
39 | const addTask = async (task) => {
40 | const res = await fetch('http://localhost:5000/tasks', {
41 | method: 'POST',
42 | headers: {
43 | 'Content-type': 'application/json',
44 | },
45 | body: JSON.stringify(task),
46 | })
47 |
48 | const data = await res.json()
49 |
50 | setTasks([...tasks, data])
51 |
52 | // const id = Math.floor(Math.random() * 10000) + 1
53 | // const newTask = { id, ...task }
54 | // setTasks([...tasks, newTask])
55 | }
56 |
57 | // Delete Task
58 | const deleteTask = async (id) => {
59 | const res = await fetch(`http://localhost:5000/tasks/${id}`, {
60 | method: 'DELETE',
61 | })
62 | //We should control the response status to decide if we will change the state or not.
63 | res.status === 200
64 | ? setTasks(tasks.filter((task) => task.id !== id))
65 | : alert('Error Deleting This Task')
66 | }
67 |
68 | // Toggle Reminder
69 | const toggleReminder = async (id) => {
70 | const taskToToggle = await fetchTask(id)
71 | const updTask = { ...taskToToggle, reminder: !taskToToggle.reminder }
72 |
73 | const res = await fetch(`http://localhost:5000/tasks/${id}`, {
74 | method: 'PUT',
75 | headers: {
76 | 'Content-type': 'application/json',
77 | },
78 | body: JSON.stringify(updTask),
79 | })
80 |
81 | const data = await res.json()
82 |
83 | setTasks(
84 | tasks.map((task) =>
85 | task.id === id ? { ...task, reminder: data.reminder } : task
86 | )
87 | )
88 | }
89 |
90 | return (
91 |
92 |
93 |
setShowAddTask(!showAddTask)}
95 | showAdd={showAddTask}
96 | />
97 |
98 |
102 | {showAddTask && }
103 | {tasks.length > 0 ? (
104 |
109 | ) : (
110 | 'No Tasks To Show'
111 | )}
112 | >
113 | }
114 | />
115 | } />
116 |
117 |
118 |
119 |
120 | )
121 | }
122 |
123 | export default App
124 |
--------------------------------------------------------------------------------