Skip to content
Open

Ex4 #39

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
86ee18b
new start
Kuper-S May 14, 2022
5e5280e
add html css and js to my project
Kuper-S May 19, 2022
fa9eef5
edit app file
Kuper-S May 19, 2022
acf4f31
change the css and html script
Kuper-S May 19, 2022
30a37e3
js changes
Kuper-S May 19, 2022
c7f6c24
change html href
Kuper-S May 19, 2022
67bad92
add sound
Kuper-S May 19, 2022
3ea4620
edit style
Kuper-S May 19, 2022
b852445
remove svg
Kuper-S May 19, 2022
d44d269
change card position
Kuper-S May 19, 2022
ed0e206
final changes
Kuper-S May 19, 2022
b3755b1
Merge branch 'monday-u-com:main' into main
Kuper-S May 21, 2022
6128f47
Change my index.html and add classes to app
Kuper-S May 26, 2022
f8f6531
change css style and add taskloader file
Kuper-S May 26, 2022
cabbcc2
add pokemon api
Kuper-S May 26, 2022
af70d3b
change html href file
Kuper-S May 26, 2022
bf60d42
add date function to utils and move all functions to taskloader
Kuper-S May 26, 2022
29e2142
app change logic
Kuper-S May 29, 2022
21c25d2
style change
Kuper-S May 30, 2022
4d4a6ae
app file change
Kuper-S May 30, 2022
ba03f0b
after my pc dead change my app
Kuper-S May 30, 2022
0b03b7d
Merge branch 'monday-u-com:main' into main
Kuper-S May 30, 2022
d68b647
add ex2 to ex3
Kuper-S May 31, 2022
6c99c71
add readme
Kuper-S May 31, 2022
ea89b39
fix pokemon clinet file
Kuper-S May 31, 2022
72f04d5
add index.js for cli app
Kuper-S Jun 2, 2022
e7e5424
add an done and todo text file ans list it
Kuper-S Jun 2, 2022
a64ed7d
last change before split branches
Kuper-S Jun 9, 2022
1e4470e
change the add function
Kuper-S Jun 9, 2022
5fc2343
change ex2
Kuper-S Jun 9, 2022
bb2812f
add ex4
Kuper-S Jun 12, 2022
23504fe
ex4 edit
Kuper-S Jun 12, 2022
700101a
start ex4
Kuper-S Jun 12, 2022
0c4dbe1
ex4 change client item
Kuper-S Jun 17, 2022
a7267bb
move itemManager to services and refactor main
Kuper-S Jun 17, 2022
0407846
add main
Kuper-S Jun 17, 2022
e55c8d6
refactor code and improve UI
Kuper-S Jun 17, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ This is going to be so much fun! Here are a few general details:

## Exercises list

**Exercise 1** - [HTML, CSS, JS.](https://github.com/monday-u-com/monday-u-exercises/tree/main/src/ex1)
**Exercise 1** - [HTML, CSS, JS.](https://github.com/monday-u-com/monday-u-exercises/tree/master/src/ex1)

**Exercise 2** - [In depth JS, Async JS, MVC](https://github.com/monday-u-com/monday-u-exercises/tree/main/src/ex2)

**Exercise 3** - [Advance Node.js, CLI](https://github.com/monday-u-com/monday-u-exercises/tree/main/src/ex3)
**Exercise 2** - [In depth JS, Async JS, MVC](https://github.com/monday-u-com/monday-u-exercises/tree/master/src/ex2)

# Submitting your tasks
Create a pull request in your forked repository and send to your mentor when it's ready.
Expand Down
38 changes: 38 additions & 0 deletions ex4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Exercise 4 - Express.js

You know what they say about #4: you'll remember it *for*ever (ha)

## In this section you will practice

**Express** - One of the most popular server frameworks for node.js
**APIs** - We've already worked with an external API (the Pokemon one) - now we're going to build our own!
**CRUD** - Create, Read, Update, Delete - the four fundamental operations of any API. We aren't updating yet, but the principles are the same

## What you are going to build

So far our whole app has been 100% local, and if you refreshed the page - that's it, all your todos were gone. No persistency. So sad.

But that changes today! We're going to create a server that will handle all our todo items, _and_ our Pokemon fetching, and finally take our first step in becoming real #fullstack developers #hashtag

You can use your existing project (copy+paste then refactor), or use the boilerplate we've set up for you in this `ex4` directory

### The requirements:

- [ ] Create your express backend (include separate `dist` and `server` folders)
- [ ] Your `server.js` file should have all the express boilerplate and host your `dist` directory to any client that requests it (hint: you'll need to `.use` the `express.static` method)
- [ ] Create an `api.js` file that acts as the 'controller' of your backend, handling all the routes (endpoints)
- [ ] Create separate endpoints for (1) fetching all the todo items, (2) creating a new one, and (3) deleting an existing one (hint: don't forget `bodyParser`)
- [ ] Move the pokemon fetching code to the backend - use `axios` instead of `fetch` for your requests
- [ ] On app load (i.e. when a user enters the page) it should fetch all the todo items and render them

In terms of the front end, it will look the same as before:
![](../assets/hw-2.gif)

But now when you refresh the page **the data should still be there**

### Bonus

- [ ] Create a [middleware](https://expressjs.com/en/guide/using-middleware.html) that makes a log each time a user accesses any of the routes (you can just do a `console.log`)
- [ ] Handle server errors elegantly. Specifically, if anything goes wrong the user should see an error message (ideally, not an alert) with an explanation of what went wrong instead of crashing the page
- [ ] Add a loder/spinner to the page that indicates the client is waiting for an async operation (e.g. a call to the server) to finish
- [ ] Add simple caching to your server. If a user requests for the same pokemon ID three times in the same minute, for example, it should only make a request to the Pokemon API once. You can use a simple in-memory data structure for your cache
60 changes: 60 additions & 0 deletions ex4/dist/clients/itemClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class ItemClinet {
constructor() {}

async createItem(item) {
const response = await fetch("/item", {
method: "post",
body: JSON.stringify({ item }),
headers: { "Content-Type": "application/json" },
});
if (response.status == 201) {
return await response.json();
}
}

async fetchItems() {
const response = await fetch("/item", {
method: "GET",
headers: { "Content-Type": "application/json" },
});

if (response.status != 200) {
throw new Error(" Error fetching items");
}

const data = await response.json();

return data;
}

async deleteItem(itemId) {
try {
await fetch(`/item/${itemId}`, {
method: "delete",
headers: { "Content-Type": "application/json" },
});
} catch (err) {
throw new Error("failed to delete item");
}
}

async deleteAll()
{
try {
await fetch('/item' ,{
method: "delete",
headers: { "Content-Type": "application/json" },
});
} catch (err) {
throw new Error("failed to delete all items ");
}
}

}






export default new ItemClinet();
3 changes: 3 additions & 0 deletions ex4/dist/images/delete_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions ex4/dist/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Todo List</title>
<link rel="stylesheet" href="style.css" />
<script
src="https://kit.fontawesome.com/434f764404.js"
crossorigin="anonymous"
></script>
</head>

<body>
<section class="main-section">
<div class="app-container" id="app-container">
<h1 class="header">ToDo App</h1>
<h2>Let's work on some Todos</h2>
<span id="newtask" class="newTask">
<input id="taskInput" type="text" placeholder="Enter your Tasks" />
<button id="addButton" class="add">🔥
</button>
<div id="load-container"></div>
<button id="sortBtn">Alphabetical Sort</button>
<ul id="tasks"></ul>
<footer>
<span id="count" class="count"></span>
<button id="clearAllBtn">Clear 🆑</button>
</footer>
</span>
</div>
</section>
<footer class="footer">Kupers 2022</footer>
</body>
<script type="module" src="main.js"></script>
</html>
182 changes: 182 additions & 0 deletions ex4/dist/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import itemClient from "./clients/itemClient.js";

class Main {
constructor() {
this.input = document.getElementById("taskInput");
this.addButton = document.getElementById("addButton");
this.todoListH1Elem = document.querySelector("h1");
this.sortBtn = document.getElementById("sortBtn");
this.clearAllBtn = document.getElementById("clearAllBtn");
this.taskElement = document.getElementById("tasks");
this.counterElement = document.getElementById("count");
this.loader = document.querySelector("#load-container");
}
async init() {
const itemsArr = await itemClient.fetchItems();

if (itemsArr.length != 0) {
this.addItem(itemsArr);
}
this.whenPressEnter();
this.sortBtn.addEventListener("click", () => this.sortTaskByName());
this.clearAllBtn.addEventListener("click", () => this.deleteAllTasks());
this.addButton.addEventListener("click", async () => {
if (!this.input.value.trim()) {
const invalidMsg = "Cannot insert Empty Input";
this.addAlert(invalidMsg);
return;
}

try {
this.loader.classList.add("display");
const itemsArr = await itemClient.createItem(this.input.value);
this.loader.classList.remove("display");
this.input.value =""
this.addItem(itemsArr);
} catch (err) {
this.addItem([err], false);
}

});
}

async deleteAllTasks() {
await itemClient.deleteAll();
this.taskElement.classList.toggle("removed-item");
setTimeout(() => {

this.taskElement.innerHTML = "";
this.taskElement.classList.remove("removed-item");
this.countTasks();
}, 500);

}

addAlert(invalidMsg, miilis) {
this.addErorrAlert(invalidMsg, miilis);
}

addErorrAlert(invalidMsg, miilis = 4000) {
const div = document.createElement("div");
const i = document.createElement("i");
div.classList = "error-msg";
div.appendChild(i);
this.todoListH1Elem.appendChild(div);
div.innerText = invalidMsg;
setTimeout(() => {
div.remove();
i.remove();
}, miilis);
}

whenPressEnter() {
this.input.addEventListener("keypress", (event) => {
if (event.key === "Enter") {
event.preventDefault();
this.addButton.click();
}
});
}
countTasks() {
const tasksCounter = this.taskElement.childElementCount;
if (tasksCounter === 0) {
this.counterElement.style.visibility = "hidden";
this.clearAllBtn.style.visibility = "hidden";
}
if (tasksCounter > 0) {
this.counterElement.innerText = `${tasksCounter} Pending Tasks`;
this.counterElement.style.visibility = "visible";
this.clearAllBtn.style.visibility = "visible";
}
if (tasksCounter < 2) {
this.sortBtn.style.visibility = "hidden";
} else {
this.sortBtn.style.visibility = "visible";
}
}

sortTaskByName() {
const taksElements = [...this.taskElement.childNodes];
taksElements.sort((task1, task2) => {
const text1 = task1.querySelector("span").innerHTML;
const text2 = task2.querySelector("span").innerHTML;
return text1.toLowerCase().localeCompare(text2.toLowerCase());
});

taksElements.forEach((task) => task.remove());
taksElements.forEach((task) => this.taskElement.appendChild(task));
}

addItem(renderNewTask) {
for (const value of renderNewTask) {
this.renderItem(value);
}
this.countTasks();
}
renderItem(value) {
const taskListElem = document.createElement("li");
const inputText = document.createElement("span");
inputText.classList = "tasks_spans";
taskListElem.appendChild(inputText);
if (value.isPokemon) {
inputText.innerText = `Cool you got ${value.item}`;
const img = this.getPokemonImage(value);
taskListElem.appendChild(img);
} else {
inputText.innerText = value.item;
}
taskListElem.setAttribute("id", `${value.itemId}`);
taskListElem.classList = "new-item";
this.taskElement.appendChild(taskListElem);
this.createDeleteButton(taskListElem);
this.clickOnItem(taskListElem, inputText);
}

getPokemonImage(pokemonObj) {
const url = pokemonObj.imageUrl;
const img = document.createElement("img");
img.setAttribute("src", url);
return img;
}

async createDeleteBtn(taskListElem, deleteButton) {
deleteButton.addEventListener("click", async () => {
taskListElem.classList.toggle("removed-item");
const itemId = taskListElem.id;
await itemClient.deleteItem(itemId);
setTimeout(() => {
taskListElem.remove();
this.countTasks();
}, 400);
});
return;
}

clickOnItem(taskListElem, inputText) {
taskListElem.addEventListener(
"click",
(e) => {
if (e.target !== taskListElem) return;
alert(inputText.innerText);
taskListElem.classList.toggle("checked");
},
false
);
}

createDeleteButton(taskListElem) {
const deleteButton = document.createElement("button");
deleteButton.className = "delete";
deleteButton.innerHTML = "🗑️";
taskListElem.appendChild(deleteButton);
this.createDeleteBtn(taskListElem, deleteButton);
}

}
const main = new Main();

document.addEventListener("DOMContentLoaded", function () {
// you should create an `init` method in your class
// the method should add the event listener to your "add" button
main.init();
});
Loading