├── 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 |
13 |
14 |
15 |

Tasks

16 |
17 |
18 |
    19 |
20 |
21 |
22 |
23 | 24 | 25 |
26 |
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 | --------------------------------------------------------------------------------