├── db
└── db.json
├── index.html
└── src
├── css
└── style.css
└── js
└── index.js
/db/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "todos": [
3 | {
4 | "title": "buy milk",
5 | "completed": false,
6 | "id": 1
7 | },
8 | {
9 | "title": "buy sugar",
10 | "completed": false,
11 | "id": 2
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | HW21-js-event-loop
9 |
10 |
11 |
12 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/css/style.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
7 | body {
8 | height: 100vh;
9 | font: 22px 'Helvetica Neue', Helvetica, Arial, sans-serif;
10 | line-height: 1.2em;
11 | background: linear-gradient(to right, #4e65d4 0%, #293dcb 19%, #4246a5 60%, #505ac2 100%);
12 | display: flex;
13 | align-items: center;
14 | justify-content: center;
15 | color: #fff;
16 | }
17 |
18 | ul {
19 | list-style-type: none;
20 | -webkit-padding-start: 0;
21 | }
22 |
23 | .btn {
24 | cursor: pointer;
25 | font-size: 15px;
26 | padding: 10px 20px;
27 | border-radius: 2em;
28 | background: none;
29 | border: 1px solid #fff;
30 | transition: 250ms ease-out;
31 | }
32 |
33 | .btn-danger:hover {
34 | color: #fff;
35 | background: #e74c3c;
36 | }
37 |
38 | .btn-success:hover {
39 | color: #fff;
40 | background: #05b437;
41 | }
42 |
43 | .btn:focus {
44 | color: #fff;
45 | outline: none;
46 | }
47 |
48 | .container {
49 | width: 400px;
50 | min-height: 500px;
51 | box-shadow: 0 20px 80px rgba(0,0,0,0.6);
52 | position: relative;
53 | border-radius: 1em;
54 | padding: 15px;
55 | display: flex;
56 | flex-direction: column;
57 | justify-content: space-between;
58 | }
59 |
60 | .header {
61 | display: flex;
62 | justify-content: space-around;
63 | }
64 | .header h2 {
65 | margin: 0;
66 | }
67 |
68 | .task-count {
69 | align-self: center;
70 | }
71 |
72 | .todo-list:first-of-type {
73 | border-top: 1px solid #f5f4f4;
74 | }
75 |
76 | .list-item-view {
77 | padding-top: 12px;
78 | padding-bottom: 12px;
79 | display: flex;
80 | align-items: center;
81 | justify-content: space-between;
82 | }
83 |
84 | .form {
85 | margin-bottom: 10px;
86 | display: flex;
87 | justify-content: center;
88 | }
89 |
90 | .input-text {
91 | height: 40px;
92 | font-size: 15px;
93 | border: 1px solid #dce4ec;
94 | width: 100%;
95 | margin-right: 15px;
96 | border-radius: 2em;
97 | padding: 5px 10px;
98 | transition: border 250ms ease-out;
99 | }
100 |
101 | .input-text:focus {
102 | border: 1px solid;
103 | outline: none;
104 | }
105 |
106 |
107 | .completed {
108 | text-decoration: line-through;
109 | }
110 |
111 | input[type="checkbox"] {
112 | display: none;
113 | }
114 |
115 | .list-item-view label:hover {
116 | cursor: pointer;
117 | }
118 | .list-item-view label {
119 | color: #e6ff01;
120 | }
121 | .list-item-view label.completed {
122 | color: #00f204;
123 | }
124 |
125 | #todos {
126 | max-height: 500px;
127 | overflow: auto;
128 | }
129 |
--------------------------------------------------------------------------------
/src/js/index.js:
--------------------------------------------------------------------------------
1 | const resource = 'http://localhost:3000'
2 | const todosSection = document.getElementById('todos')
3 | const formAddTask = document.getElementById('addTask')
4 | let todos = []
5 |
6 | function deleteTask(id) {
7 | return fetch(`${resource}/todos/${id}`, {
8 | method: 'DELETE'
9 | })
10 | .then(res => res.json())
11 | }
12 |
13 | function addTask(task) {
14 | return fetch(`${resource}/todos`, {
15 | method: 'POST',
16 | headers: {
17 | 'Content-Type': 'application/json',
18 | },
19 | body: JSON.stringify(task)
20 | })
21 | .then(res => res.json())
22 | }
23 |
24 | function changeTask(task) {
25 | return fetch(`${resource}/todos/${task.id}`, {
26 | method: 'PUT',
27 | headers: {
28 | 'Content-Type': 'application/json',
29 | },
30 | body: JSON.stringify(task)
31 | })
32 | .then(res => res.json())
33 | }
34 |
35 | function draw(whereDraw) {
36 | let todosContainer = ''
37 | todos.forEach(todo => {
38 | todosContainer += templateTodo(todo)
39 | })
40 | whereDraw.innerHTML = todosContainer
41 | }
42 | function templateTodo(data) {
43 | return `
44 |
45 |
48 |
49 |
50 | `
51 | }
52 |
53 | fetch(`${resource}/todos`)
54 | .then(res => res.json())
55 | .then((res) => {
56 | todos = res
57 | draw(todosSection)
58 | })
59 |
60 | function handlerDeleteTask(event) {
61 | const parentEl = event.target.closest('.todo-list')
62 | const id = parentEl.getAttribute('data-id')
63 | deleteTask(id)
64 | .then(() => {
65 | parentEl.remove()
66 | })
67 | }
68 |
69 | function handlerMarkedTask(event) {
70 | const parentEl = event.target.closest('.todo-list')
71 | const id = parentEl.getAttribute('data-id')
72 | const status = parentEl.getAttribute('data-completed')
73 | const title = parentEl.getAttribute('data-title')
74 | let newStatus = !Boolean(+status)
75 | changeTask({
76 | completed: newStatus,
77 | id,
78 | title
79 | })
80 | .then(() => {
81 | parentEl.setAttribute('data-completed', newStatus ? '1' : '0')
82 | const label = event.target.closest('label')
83 | if (newStatus) {
84 | label.className = 'completed'
85 | } else {
86 | label.className = ''
87 | }
88 | })
89 | }
90 |
91 |
92 | formAddTask.addEventListener('submit', (event) => handlerAddTask(event))
93 |
94 | function handlerAddTask(event) {
95 | event.preventDefault()
96 | addTask({
97 | title: event.target[0].value,
98 | completed: false
99 | })
100 | .then(res => {
101 | const el = templateTodo(res)
102 | todosSection.innerHTML += el
103 | event.target[0].value = ''
104 | })
105 | }
106 |
107 | todosSection.addEventListener('click', (event) => {
108 | switch (event.target.tagName) {
109 | case 'LABEL':
110 | handlerMarkedTask(event)
111 | break;
112 | case 'BUTTON':
113 | handlerDeleteTask(event)
114 | break;
115 | }
116 | })
117 |
118 |
--------------------------------------------------------------------------------