diff --git a/.vscode/settings.json b/.vscode/settings.json
index c3c81b8b..0c7ffd3b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,7 @@
+// This file contains settings set for the project workspace
{
+ // Sets the font size in the code editor to 42px
"editor.fontSize": 42,
+ // Set the font size in the terminal to 62px
"terminal.integrated.fontSize": 62
}
\ No newline at end of file
diff --git a/index.ejs b/index.ejs
new file mode 100644
index 00000000..2dc30ae8
--- /dev/null
+++ b/index.ejs
@@ -0,0 +1,62 @@
+<%# This file render the data from server %>
+ <%#Declare this is an HTML5 document%>
+ <%# Opens HTML document, set language to english %>
+
<%# Opens head section%>
+ <%#Sets character encoding to UTF-8 which supports all characters/symbols)%>
+
+ <%# Tells Internet Explorer to use the latest rendering engine%>
+
+ <%# Makes the page responsive on mobile devices%>
+
+ Document<%#Sets the browser tab title%>
+ <%#Links Font Awesome CSS library (for icons like the trash icon)%>
+
+ <%# %>
+
+<%#Closes head section %>
+<%# Closes body section %>
+
+<%#line 20 - line 29 are duplicate section %>
+
+
+
+
+
+
+ Document
+
+
+
Todo List:
<%#Set to do list as main heading with h1 size%>
+
<%#Use unordered list container for todo items%>
+ <%#Starts a JS for loop, Loops through the items array %>
+ <% for(let i=0; i < items.length; i++) {%>
+
<%#creates a list item for each todo%>
+ <%#Checks if current item is marked as completed%>
+ <% if(items[i].completed === true) {%>
+ <%#If completed, displays the todo text in a span with 'completed' class%>
+ <%= items[i].thing %>
+ <%#If NOT completed, displays the todo text in a regular span (no class)%>
+ <% }else{
+ %><%= items[i].thing %>
+ <% } %>
+ <%#Displays a trash can icon for deleting%>
+
+
<%#closes the list item%>
+ <% } %><%#closes the for loop%>
+
<%#Closes the unordered list%>
+ <%#Displays count of remaining todos with h2 %>
+
Left to do: <%= left %>
+ <%#Heading for the add todo form%>
+
Add A Todo:
+
+ <%#form that submit to /addTodo route with POST method%>
+ <%#closes the form%>
+ <%#Links external JavaScript file%>
+
+<%#Closes body and html tags%>
+
+
diff --git a/package.json b/package.json
index 276ae590..33cc5c3f 100644
--- a/package.json
+++ b/package.json
@@ -1,18 +1,18 @@
-{
- "name": "rap-name-api",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "scripts": {
+{ //Open JSON object
+ "name": "rap-name-api", //declares project name
+ "version": "1.0.0", //declares project version
+ "description": "", //describe what the project is generally
+ "main": "index.js", //the main file that runs once the package get required
+ "scripts": { //defines custom commands you can run with npm run
"test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "",
- "license": "ISC",
- "dependencies": {
- "cors": "^2.8.5",
- "dotenv": "^8.2.0",
- "ejs": "^3.1.6",
- "express": "^4.17.1",
- "mongodb": "^3.6.5"
- }
-}
+ },//close the scripts section
+ "author": "",// generally contain author name but empty here
+ "license": "ISC", //list license type is ISC which is permissive open-source license
+ "dependencies": { //lists packages the app needs to run
+ "cors": "^2.8.5", //allows your api to be accessed from different domain
+ "dotenv": "^8.2.0",//loads environment variable from .env file, to hide sensitive data
+ "ejs": "^3.1.6",//allow JS to be embed in HTML templates
+ "express": "^4.17.1", //web app framework to handle rounting, requests, responses
+ "mongodb": "^3.6.5" //connects app to MongoDB database, used to store and retrieve the to do items
+ }//closes dependencies section
+}//closes the entire JSON object
diff --git a/public/css/style.css b/public/css/style.css
index 0475253a..cb1d6843 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -1,7 +1,11 @@
-h1{
- color: red;
-}
-.completed{
- color: gray;
- text-decoration: line-through;
-}
\ No newline at end of file
+/* This file is an external stylesheet containing CSS code */
+
+h1{ /* Targets every h1 element */
+ color: red; /* Set the color of the text to red */
+} /* end of style declaration for the h1 selector */
+
+
+.completed{ /* Targets the .completed class */
+ color: gray; /* Set the color of the text to gray */
+ text-decoration: line-through; /* Apply a strikethrough effect to the text */
+} /* end of style declaration for the .completed class */
\ No newline at end of file
diff --git a/public/js/main.js b/public/js/main.js
index ff0eac39..95a4a64d 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -1,72 +1,95 @@
+// target the class .fa-trash
const deleteBtn = document.querySelectorAll('.fa-trash')
+// target the span elements within the element with the class .item
const item = document.querySelectorAll('.item span')
+// target the span with the class .completed within the element with the class .item
const itemCompleted = document.querySelectorAll('.item span.completed')
+// for each item in the li that has the trash can icon
Array.from(deleteBtn).forEach((element)=>{
- element.addEventListener('click', deleteItem)
-})
+ // attach an event listener for each li item when the trash icon is clicked
+ element.addEventListener('click', deleteItem) // invoke deleteItem(): delete item
+})
+// for each item with a class .item
Array.from(item).forEach((element)=>{
+ // attach an event listener to mark the item as complete
element.addEventListener('click', markComplete)
})
+// for each li item with a span of a class .completed
Array.from(itemCompleted).forEach((element)=>{
+ // attach an event listener to mark the item and uncomplete
element.addEventListener('click', markUnComplete)
})
+// this function will be called when the trash icon is clicked
async function deleteItem(){
+ // grab the item the user clicked on to delete
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()
+ // error handling, in case the item does not exist
+ try{
+ // initiate the request and wait until a response is received
+ const response = await fetch('deleteItem', { // target endpoint
+ method: 'delete', // http method delete, remove the item
+ // request header, the body of the data is formatted in json
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({ // the request body, property set to stringified JSON object
+ // create key-value pair
+ 'itemFromJS': itemText // itemText = the text of the item to be deleted
+ }) // end of JSON object
+ }) // end of fetch call
+ const data = await response.json() // store the response
+ console.log(data) // display the response to the console
+ location.reload() // refresh the web page
- }catch(err){
- console.log(err)
- }
-}
+ }catch(err){ // end of try, catch the error
+ console.log(err) // display the error in the console
+ } // end of catch block
+} // end of deleteItem function
-async function markComplete(){
+// this function will mark the item on the todo list as complete
+async function markComplete(){ // declare a function name markComplete
+ // grab the item from the list to mark as complete
const itemText = this.parentNode.childNodes[1].innerText
- try{
+ try{ // start of the error handling
+ // store the response from the request from the markComplete endpoint
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()
+ method: 'put', // http method put, to update the item
+ // request header, the body of the data is formatted in json
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({ // request body, data converted to json
+ 'itemFromJS': itemText // key-value pair, itemText = text of the li item
+ }) // end of json object
+ }) // end of fetch
+ const data = await response.json() // store the response to data
+ console.log(data) // display the data in console
+ location.reload() // refresh page
- }catch(err){
- console.log(err)
- }
-}
+ }catch(err){ // catch error, if occured
+ console.log(err) // display error in console
+ } // end of catch
+} // end of markComplete()
-async function markUnComplete(){
+// this function changes the item status to incomplete
+async function markUnComplete(){
+ // grabs the text of the item the user clicked on
const itemText = this.parentNode.childNodes[1].innerText
- try{
- const response = await fetch('markUnComplete', {
- method: 'put',
+ try{ // error handling
+ // start a fetch request for the endpoint markUnComplete
+ const response = await fetch('markUnComplete', {
+ method: 'put', // http method put, updates the status of the item
+ // request header, the body of the data is formatted in json
headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({
- 'itemFromJS': itemText
- })
- })
- const data = await response.json()
- console.log(data)
- location.reload()
+ body: JSON.stringify({ // request body, data converted to json
+ 'itemFromJS': itemText // key-value pair, itemText = text of the li item
+ }) // end of json object
+ }) // end of fetch request
+ const data = await response.json() // store response to json in a data variable
+ console.log(data) // display the data in console
+ location.reload() // refresh page
- }catch(err){
- console.log(err)
- }
-}
\ No newline at end of file
+ }catch(err){ // handle error, if any
+ console.log(err) // display the error in console
+ } // end of catch block
+} // end of markUnComplete function
\ No newline at end of file
diff --git a/server.js b/server.js
index 58b53e2f..f5b9eff1 100644
--- a/server.js
+++ b/server.js
@@ -1,29 +1,49 @@
-const express = require('express')
-const app = express()
+const express = require('express')//imports express framework
+const app = express() //creates express application instance
+//import MongoClient from MongoDB package
const MongoClient = require('mongodb').MongoClient
+//Sets the port number the server will run
const PORT = 2121
+//loads environment variables from .env file
require('dotenv').config()
-
-let db,
- dbConnectionStr = process.env.DB_STRING,
+//Declares these variables:
+let db, //db will hold the database connection
+ //dbConnectionStr = MongoDB connection string from .env file
+ dbConnectionStr = process.env.DB_STRING,
+ //dbName = name of the database('todo')
dbName = 'todo'
-
+//connect MongoDB using the connection string and uses new MongoDB connection
+//engine, returns a promise
MongoClient.connect(dbConnectionStr, { useUnifiedTopology: true })
+//when the connection succeeds, run this function below.
.then(client => {
+ //print out success message to console
console.log(`Connected to ${dbName} Database`)
+ //sets the db variable to 'todo'database
db = client.db(dbName)
- })
+ })//closes the .then()block
-app.set('view engine', 'ejs')
-app.use(express.static('public'))
-app.use(express.urlencoded({ extended: true }))
-app.use(express.json())
-
+app.set('view engine', 'ejs')//tells express to use EJS as templating engine
+app.use(express.static('public'))//serves static files(CSS/JS) from the public folder
+app.use(express.urlencoded({ extended: true }))//Middleware to parse form data from POST requests
+app.use(express.json())//Middleware to parse JSON data from requests
+/*
+defines GET route for homepage(/)
+async() is the function can use await for asynchronous operations
+including two parameters:
+request-> incoming request object;
+response-> object to send response back to client*/
app.get('/',async (request, response)=>{
+ //get all the documents from 'todos' collection, find them and list them in an array
+ //await-> waits for database operation to complete
const todoItems = await db.collection('todos').find().toArray()
+ //count the amount of incompleted items, stored the amount in itemsLeft
const itemsLeft = await db.collection('todos').countDocuments({completed: false})
+ //Render index.ejs template,
+ //passes array of all todosItems and the amount of incompleted todos
+ //to the template
response.render('index.ejs', { items: todoItems, left: itemsLeft })
// db.collection('todos').find().toArray()
// .then(data => {
@@ -33,61 +53,89 @@ app.get('/',async (request, response)=>{
// })
// })
// .catch(error => console.error(error))
-})
+ //commented out line 48-line55:
+ /* The original code was using .then() instead of async/await
+ async/await is cleaner and easier to read so modern code prefer this method
+ */
+}) //closes the Get route
+
+//Define Post route at /addTodo
+//Triggered when the form in the EJS file is submitted
app.post('/addTodo', (request, response) => {
+ //insert a new document to todos collections
+ //create objects:
+ //declare thing = the todo text from form input name is todoItem
+ //todoIteam condition is incompleted if completed = false
db.collection('todos').insertOne({thing: request.body.todoItem, completed: false})
+ //when insert succeeds, runs this function
.then(result => {
+ //print out success message to console
console.log('Todo Added')
+ //redirects user back to homepage, this refreshes the page to show
+ //the new todo
response.redirect('/')
})
- .catch(error => console.error(error))
-})
+ .catch(error => console.error(error))//if error occurs, print it out to console
+}) //closes the route function
+//Define PUT route at /markComplete
+//Called from frontend JS(main.js) when user clicks a todo
app.put('/markComplete', (request, response) => {
+ //Updates one document in 'todos' collection
+ //first parameter declares filter to find the todo from frontend
+ //second parameter declares update conditions
db.collection('todos').updateOne({thing: request.body.itemFromJS},{
- $set: {
- completed: true
+ $set: { //MongoDB operator to set field value
+ completed: true //set completed field to be true
}
- },{
- sort: {_id: -1},
- upsert: false
+ },{ //third parameter is options object
+ sort: {_id: -1}, //update according to descending order
+ upsert: false //dont create new item if no match found
})
- .then(result => {
- console.log('Marked Complete')
- response.json('Marked Complete')
+ .then(result => {//when update succeeds
+ console.log('Marked Complete')//print out to console
+ response.json('Marked Complete')//send JSON response back to frontend
})
- .catch(error => console.error(error))
+ .catch(error => console.error(error))//catches and print out errors
-})
+})//closes the put route
+//declares PUT rounte at /markUnComplete
app.put('/markUnComplete', (request, response) => {
+ //first parameter declares filter to find todos from frontend main.js
db.collection('todos').updateOne({thing: request.body.itemFromJS},{
- $set: {
- completed: false
+ //seconde parameter declares update conditions
+ $set: { //MongoDB operator to set field value
+ completed: false //set completed filed to be false
}
- },{
- sort: {_id: -1},
- upsert: false
+ },{ //third parameter is option object
+ sort: {_id: -1},//update the newest incompleted item by descending order
+ upsert: false //dont create new item if no match found
})
- .then(result => {
- console.log('Marked Complete')
- response.json('Marked Complete')
+ .then(result => {//if update succeeds
+ console.log('Marked Complete')// BUG: here should print out Marked Incompleted to console
+ response.json('Marked Complete')//BUG: here should response Marked Incompleted to JSON
})
- .catch(error => console.error(error))
+ .catch(error => console.error(error))//catch and print out error if any
-})
+})//closed the put route
+//define delete route at /deleteItem,
+//Called from frontend when user clicks trash icon
app.delete('/deleteItem', (request, response) => {
+ //Deletes ONE document from 'todos' collection
+ //first parameter is to filter the item match thing defination
db.collection('todos').deleteOne({thing: request.body.itemFromJS})
- .then(result => {
- console.log('Todo Deleted')
- response.json('Todo Deleted')
+ .then(result => {//if update succeeds
+ console.log('Todo Deleted')//print out 'Todo Deleted' to console
+ response.json('Todo Deleted')//response 'Todo Deleted' to frontend
})
- .catch(error => console.error(error))
+ .catch(error => console.error(error))//catch and print out error if any
-})
+}) //closes the delete route
+//listen from env file or port 2121
app.listen(process.env.PORT || PORT, ()=>{
- console.log(`Server running on port ${PORT}`)
-})
\ No newline at end of file
+ console.log(`Server running on port ${PORT}`)//print out message if server starts
+})//closes the listen function
\ No newline at end of file