diff --git a/.vscode/settings.json b/.vscode/settings.json index c3c81b8b..e61e1490 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { - "editor.fontSize": 42, - "terminal.integrated.fontSize": 62 + "editor.fontSize": 16, + "terminal.integrated.fontSize": 15 } \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css index 0475253a..bb1f891b 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,7 +1,11 @@ -h1{ - color: red; +/* sets the color of the h1 selector to red */ +h1 { + color: red; +} + +/* sets the color of any elements that have the class .completed to gray + and applies a line-through to the same elements */ +.completed { + color: gray; + text-decoration: line-through; } -.completed{ - color: gray; - text-decoration: line-through; -} \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js index ff0eac39..4bc18a55 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,72 +1,111 @@ -const deleteBtn = document.querySelectorAll('.fa-trash') -const item = document.querySelectorAll('.item span') -const itemCompleted = document.querySelectorAll('.item span.completed') +// selects all elements that have the class .fa-trash +const deleteBtn = document.querySelectorAll(".fa-trash"); +// selects all span elements with the class of .item and saves them in an item NodeList +const item = document.querySelectorAll(".item span"); +// selects all span elements with a class of .completed and a class of .item +const itemCompleted = document.querySelectorAll(".item span.completed"); -Array.from(deleteBtn).forEach((element)=>{ - element.addEventListener('click', deleteItem) -}) +// add a click event listener and call deleteItem fnc to each delete button in the deleteBtn NodeList +Array.from(deleteBtn).forEach((element) => { + element.addEventListener("click", deleteItem); +}); +// // add a click event listener and call markComplete fnc to each item in the item NodeList +Array.from(item).forEach((element) => { + element.addEventListener("click", markComplete); +}); -Array.from(item).forEach((element)=>{ - element.addEventListener('click', markComplete) -}) +// // add a click event listener and call markComplete fnc to each completed item in the itemCompleted NodeList +Array.from(itemCompleted).forEach((element) => { + element.addEventListener("click", markUnComplete); +}); -Array.from(itemCompleted).forEach((element)=>{ - element.addEventListener('click', markUnComplete) -}) +// declare an async deleteItem function +async function deleteItem() { + // declare a constant that retrieves the inner text of a li + const itemText = this.parentNode.childNodes[1].innerText; + // try syntax + try { + // sends a delete request to the server using await fetch() + const response = await fetch("deleteItem", { + // specifies the delete http request + method: "delete", + // the requests headers indicates the request body is JSON + headers: { "Content-Type": "application/json" }, + // converts the javascript into a JSON string + body: JSON.stringify({ + // sends the text of the li to the server + itemFromJS: itemText, + }), + }); + // saves the response from the server into data and logs it to the console + const data = await response.json(); + console.log(data); -async function deleteItem(){ - const itemText = this.parentNode.childNodes[1].innerText - try{ - const response = await fetch('deleteItem', { - method: 'delete', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ - 'itemFromJS': itemText - }) - }) - const data = await response.json() - console.log(data) - location.reload() - - }catch(err){ - console.log(err) - } + // after the fetch request is complete it refreshes the page with reload() + location.reload(); + // catch syntax + } catch (err) { + console.log(err); + } } -async function markComplete(){ - const itemText = this.parentNode.childNodes[1].innerText - try{ - const response = await fetch('markComplete', { - method: 'put', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ - 'itemFromJS': itemText - }) - }) - const data = await response.json() - console.log(data) - location.reload() - - }catch(err){ - console.log(err) - } +// declares an async function that marks a todo completed +async function markComplete() { + // var that retrieves the text from a li + const itemText = this.parentNode.childNodes[1].innerText; + // try syntax + try { + // sends an HTTP request to the markComplete endpoint and + // waits for it to complete before moving to the next line + const response = await fetch("markComplete", { + // tells the server it's a PUT method + method: "put", + // sets the request headers to indicate that the request body contains JSON + headers: { "Content-Type": "application/json" }, + // converts the javascript into a JSON string + body: JSON.stringify({ + // sends the text of the li to the server + itemFromJS: itemText, + }), + }); + // converts the response from the server into a JavaScript object and saves the data to data + const data = await response.json(); + // log the data + console.log(data); + // refreshes the page + location.reload(); + // catch syntax + } catch (err) { + // logs the error + console.log(err); + } } -async function markUnComplete(){ - const itemText = this.parentNode.childNodes[1].innerText - try{ - const response = await fetch('markUnComplete', { - method: 'put', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ - 'itemFromJS': itemText - }) - }) - const data = await response.json() - console.log(data) - location.reload() - - }catch(err){ - console.log(err) - } -} \ No newline at end of file +// declares an async function called markUnComplete +async function markUnComplete() { + // constant that grabs the inner text of a li + const itemText = this.parentNode.childNodes[1].innerText; + // try syntax + try { + // sends an HTTP request to the markUnComplete endpoint + const response = await fetch("markUnComplete", { + // tells the server it's a PUT request + method: "put", + // sets the request headers to indicate that the req body contains JSON + headers: { "Content-Type": "application/json" }, + // converts the JavaScript object into an JSON + body: JSON.stringify({ + // sends the text to be deleted from the li to the server + itemFromJS: itemText, + }), + }); + // converts the response from the server into a JSON and saves it to data + const data = await response.json(); + console.log(data); + // refreshes the page + location.reload(); + // catch syntax + } catch (err) { + console.log(err); + } +} diff --git a/server.js b/server.js index 58b53e2f..fbad7994 100644 --- a/server.js +++ b/server.js @@ -1,93 +1,160 @@ -const express = require('express') -const app = express() -const MongoClient = require('mongodb').MongoClient -const PORT = 2121 -require('dotenv').config() - +// Commenting line by line +// in order to set up the server, we bring express +const express = require("express"); +// we set the application(app) to express +const app = express(); +// imports the MongoClient class from the mongodb package +const MongoClient = require("mongodb").MongoClient; +// declares a port constant with a value of 2121 +const PORT = 2121; +// loads the dotenv package and the config() method in order to read environment variables +require("dotenv").config(); +// declares the database var, the connection string and renames the db to 'todo' let db, - dbConnectionStr = process.env.DB_STRING, - dbName = 'todo' + dbConnectionStr = process.env.DB_STRING, + dbName = "todo"; -MongoClient.connect(dbConnectionStr, { useUnifiedTopology: true }) - .then(client => { - console.log(`Connected to ${dbName} Database`) - db = client.db(dbName) - }) - -app.set('view engine', 'ejs') -app.use(express.static('public')) -app.use(express.urlencoded({ extended: true })) -app.use(express.json()) +// establish the database connection with the help of MongoClient +MongoClient.connect(dbConnectionStr, { useUnifiedTopology: true }).then( + (client) => { + // console logs the connection to the db + console.log(`Connected to ${dbName} Database`); + db = client.db(dbName); + } +); +// middleware to configure express to use EJS as the template engine for rendering html +app.set("view engine", "ejs"); +// tells express to serve all files in the public directory without needing a route +app.use(express.static("public")); +// middleware that parses data from HTML forms +app.use(express.urlencoded({ extended: true })); +// middleware in Express used to parse json data and convert into javascript objects +app.use(express.json()); -app.get('/',async (request, response)=>{ - const todoItems = await db.collection('todos').find().toArray() - const itemsLeft = await db.collection('todos').countDocuments({completed: false}) - response.render('index.ejs', { items: todoItems, left: itemsLeft }) - // db.collection('todos').find().toArray() - // .then(data => { - // db.collection('todos').countDocuments({completed: false}) - // .then(itemsLeft => { - // response.render('index.ejs', { items: data, left: itemsLeft }) - // }) - // }) - // .catch(error => console.error(error)) -}) +// read route using get() method +app.get("/", async (request, response) => { + // try catch syntax + try { + // creates an array of todos from the db + const todoItems = await db.collection("todos").find().toArray(); + // array of to dos not completed in the db + const itemsLeft = await db + .collection("todos") + .countDocuments({ completed: false }); + // render in the UI through the ejs file am object with todo items and uncompleted items + response.render("index.ejs", { items: todoItems, left: itemsLeft }); + // catch syntax + } catch (err) { + // in case of an error, console log the error + console.error(err); + } +}); -app.post('/addTodo', (request, response) => { - db.collection('todos').insertOne({thing: request.body.todoItem, completed: false}) - .then(result => { - console.log('Todo Added') - response.redirect('/') - }) - .catch(error => console.error(error)) -}) +// create a POST route at '/addTodo' endpoint +app.post("/addTodo", async (request, response) => { + try { + // add a todo item in the todos collection in our db with insertOne(); + // it inserts an object with 2 properties: thing and completed + await db + .collection("todos") + .insertOne({ thing: request.body.todoItem, completed: false }); + // console log + console.log("Todo Added"); + // after adding a todo, redirect to main page ('/') + response.redirect("/"); + // catch syntax with cl error + } catch (err) { + console.error(err); + } +}); -app.put('/markComplete', (request, response) => { - db.collection('todos').updateOne({thing: request.body.itemFromJS},{ +// defines a PUT route at "/markComplete" endpoint +app.put("/markComplete", async (request, response) => { + // try syntax + try { + // goes to todos collection in the db and uses updateOne() method + // to update a completed item that matches the criteria + await db.collection("todos").updateOne( + // finds the todo item with a thing property = request.body.itemFromJS + { thing: request.body.itemFromJS }, + // updates the found todo by setting completed: true + { + // $set is a MongoDB operator used to update fields $set: { - completed: true - } - },{ - sort: {_id: -1}, - upsert: false - }) - .then(result => { - console.log('Marked Complete') - response.json('Marked Complete') - }) - .catch(error => console.error(error)) + completed: true, + }, + }, + // if multiple items match, update the most recently added + { + sort: { _id: -1 }, + // ensures that no new document is created if no match is found + upsert: false, + } + ); -}) + // logs confirmation of item marked as complete + console.log("Marked Complete"); + // responds with JSON to inform the frontend of success + response.json("Marked Complete"); + // logs any potential errors + } catch (err) { + console.error(err); + } +}); -app.put('/markUnComplete', (request, response) => { - db.collection('todos').updateOne({thing: request.body.itemFromJS},{ +// defines a PUT request at "/markUnComplete" +app.put("/markUnComplete", async (request, response) => { + // try syntax to catch errors + try { + // find todos collection in the db and use updateOne() + // to update any items that match the criteria + await db.collection("todos").updateOne( + // update all items that match the property thing = request.body.itemFromJS + { thing: request.body.itemFromJS }, + { + // MOngoDB operator used tu update fields $set: { - completed: false - } - },{ - sort: {_id: -1}, - upsert: false - }) - .then(result => { - console.log('Marked Complete') - response.json('Marked Complete') - }) - .catch(error => console.error(error)) + completed: false, + }, + }, + // if multiple items match, update the most recently added + { + sort: { _id: -1 }, + upsert: false, + } + ); -}) + // logs to confirm that an item was marked uncomplete + console.log("Marked Uncomplete"); + // responds with a JSON to inform frontend of success + response.json("Marked Uncomplete"); + // catch syntax and cl of any errors + } catch (err) { + console.error(err); + } +}); -app.delete('/deleteItem', (request, response) => { - db.collection('todos').deleteOne({thing: request.body.itemFromJS}) - .then(result => { - console.log('Todo Deleted') - response.json('Todo Deleted') - }) - .catch(error => console.error(error)) +// defines a DELETE route at "/deleteItem" endpoint +app.delete("/deleteItem", async (request, response) => { + // try syntax in order to catch any errors + try { + // go to todos collection in our db and use deleteOne() method + // find the todo item that matches the property thing = request.body.itemFromJS + await db.collection("todos").deleteOne({ thing: request.body.itemFromJS }); -}) + // logs confirmation of item being deleted + console.log("Todo Deleted"); + // responds with a JSON to inform the frontend of success + response.json("Todo Deleted"); + // catch syntax and cl of any errors + } catch (err) { + console.error(err); + } +}); -app.listen(process.env.PORT || PORT, ()=>{ - console.log(`Server running on port ${PORT}`) -}) \ No newline at end of file +// stars the server on the specified port and cl on what port is the server running +app.listen(process.env.PORT || PORT, () => { + console.log(`Server running on port ${PORT}`); +}); diff --git a/views/index.ejs b/views/index.ejs index a26617ae..f9bb4a18 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -1,47 +1,60 @@ -
- - - + + + +