From acc14bbe706aaaaa175f528742296bf25f6c9ae0 Mon Sep 17 00:00:00 2001 From: slyncrafty <101752232+slyncrafty@users.noreply.github.com> Date: Mon, 16 Jun 2025 23:36:18 +0100 Subject: [PATCH 1/2] Add comments to to-do list. Added .vscode to gitignore file. Updated README.md with project details --- .gitignore | 3 +- README.md | 44 ++++++++++++++++++++++-- public/css/style.css | 6 ++-- public/js/main.js | 51 +++++++++++++++------------- server.js | 80 ++++++++++++++++++++++++-------------------- views/index.ejs | 11 ++++-- 6 files changed, 126 insertions(+), 69 deletions(-) diff --git a/.gitignore b/.gitignore index 1dcef2d9..a1cbec1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -.env \ No newline at end of file +.env +.vscode \ No newline at end of file diff --git a/README.md b/README.md index 5ab0326c..95240655 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,42 @@ -npm install -add DB_STRING to .env file \ No newline at end of file +# todo-list - Express App + +This is a simplified CRUD app for Todo List. +It provides basic functionalities to manage to-do list items including add, delete, and update the status as complete or incomplete. The to-do list items are on a database on MongoDB Atlas for easy access. + +## How It's Made: + +**Tech used:** HTML, CSS, JavaScript, Node, Express, MongoDB Atlas + +CRUD app created using Node, Express, MongoDB Atlas. The app functions include -- Create, Read, Update and Delete data that are on a MongoDB Atlas database using Node and Express. + +## How to Use: + +### Set up & Requirements to Play with it on your local machine + +🔑 Clone this repo: + +```clone git +git clone https://github.com/100devs/todo-list-express.git +``` + +🔑 To install requirements: + +``` +mkdir todo-list +cd todo-list +npm init +npm install express mongodb ejs dotenv --save +``` + +🔑 Create `.env` file and add `DB_STRING` that is a Connection String to your MongoDB. +For example, + +``` +echo "DB_STRING = mongodb+srv://:@cluster0.example.mongodb.net/?retryWrites=true&w=majority" > .env +``` + +**For more information, check out MongoDB Docs on [Connection Strings](https://www.mongodb.com/docs/manual/reference/connection-string/)** + +## Lessons Learned: + +Learned how to create a simple API that allows users to handle data on MongoDB via creating requests to Post, Get, Put, Delete via server and render results as HTML using EJS engine. diff --git a/public/css/style.css b/public/css/style.css index 0475253a..2e2cc7b8 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,7 +1,7 @@ h1{ - color: red; + color: red; /* set h1 text color to red */ } .completed{ - color: gray; - text-decoration: line-through; + color: gray; /* turn the text color gray for the completed item*/ + text-decoration: line-through; /* strike through the completed item */ } \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js index ff0eac39..7ebb28be 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,22 +1,23 @@ -const deleteBtn = document.querySelectorAll('.fa-trash') -const item = document.querySelectorAll('.item span') -const itemCompleted = document.querySelectorAll('.item span.completed') - +const deleteBtn = document.querySelectorAll('.fa-trash') // trash can icons +const item = document.querySelectorAll('.item span') // select todo items +const itemCompleted = document.querySelectorAll('.item span.completed') // select completed todo items +// add event listener to trash can icons next to todo items. Click to Delete the todo item Array.from(deleteBtn).forEach((element)=>{ element.addEventListener('click', deleteItem) }) - +// add event listener todo items. Click to toggle complete/incomplete Array.from(item).forEach((element)=>{ element.addEventListener('click', markComplete) }) - +// add event listener to completed todo items next to todo items. Click to toggle complete/incomplete Array.from(itemCompleted).forEach((element)=>{ element.addEventListener('click', markUnComplete) }) - +// Asynchronously delete an item async function deleteItem(){ - const itemText = this.parentNode.childNodes[1].innerText + const itemText = this.parentNode.childNodes[1].innerText // Get the text content of the item to be deleted, that is the second child of the parent node try{ + // Send a delete request to the server with the itemText in its body const response = await fetch('deleteItem', { method: 'delete', headers: {'Content-Type': 'application/json'}, @@ -24,18 +25,19 @@ async function deleteItem(){ 'itemFromJS': itemText }) }) - const data = await response.json() - console.log(data) - location.reload() + const data = await response.json() // Parse the JSON response + console.log(data) // Log the data + location.reload() // reload page to render deletion }catch(err){ - console.log(err) + console.log(err) // Log any errors } } - +// Asynchronously mark an item as completed async function markComplete(){ - const itemText = this.parentNode.childNodes[1].innerText + const itemText = this.parentNode.childNodes[1].innerText // Get the text content of the todo item to mark complete try{ + // Send a put/update request to the server to mark the item complete with its itemText in its body const response = await fetch('markComplete', { method: 'put', headers: {'Content-Type': 'application/json'}, @@ -43,18 +45,19 @@ async function markComplete(){ 'itemFromJS': itemText }) }) - const data = await response.json() - console.log(data) - location.reload() + const data = await response.json() // Parse the JSON response + console.log(data) // Log the data + location.reload() // reload page to render deletion }catch(err){ - console.log(err) + console.log(err) // Log any errors } } - +// Asynchronously mark an item as incomplete async function markUnComplete(){ - const itemText = this.parentNode.childNodes[1].innerText + const itemText = this.parentNode.childNodes[1].innerText // Get the text content of the todo item to unmark complete try{ + // Send a put/update request to the server to unmark the item complete with its itemText in its body const response = await fetch('markUnComplete', { method: 'put', headers: {'Content-Type': 'application/json'}, @@ -62,11 +65,11 @@ async function markUnComplete(){ 'itemFromJS': itemText }) }) - const data = await response.json() - console.log(data) - location.reload() + const data = await response.json() // Parse the JSON response + console.log(data) // Log the data + location.reload() // reload page to render deletion }catch(err){ - console.log(err) + console.log(err) // Log any errors } } \ No newline at end of file diff --git a/server.js b/server.js index 58b53e2f..18e09870 100644 --- a/server.js +++ b/server.js @@ -1,93 +1,101 @@ +// Load modules const express = require('express') const app = express() const MongoClient = require('mongodb').MongoClient const PORT = 2121 require('dotenv').config() - +// initialize db related variables let db, dbConnectionStr = process.env.DB_STRING, - dbName = 'todo' - + dbName = 'todos' +// MongoDB client setup 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()) +app.set('view engine', 'ejs') // set EJS as template engine +app.use(express.static('public')) // server static from public folder +app.use(express.urlencoded({ extended: true })) // Parse incoming requests with URL-encoded payloads +app.use(express.json()) // Parse incoming requests with JSON payloads +// CRUD - Read +// 1. GET data from MongoDB +// 2. Render the data in HTML with a template engine (ejs) 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() + // find all documents under the collection and return promise with the data + const todoItems = await db.collection('todos').find().toArray() // find all docs and put them in an array `data` + const itemsLeft = await db.collection('todos').countDocuments({completed: false}) // count documents with completed:false + response.render('index.ejs', { items: todoItems, left: itemsLeft }) // data array passed to template as key-value pair(named 'items') + // db.collection('todos').find().toArray() // .then(data => { // db.collection('todos').countDocuments({completed: false}) // .then(itemsLeft => { - // response.render('index.ejs', { items: data, left: itemsLeft }) + // response.render('index.ejs', { items: data, left: itemsLeft }) // }) // }) // .catch(error => console.error(error)) }) - +// CRUD - Create +// 1. form submission triggers POST request to server +// 2. refresh/get('/') with updated data from MongoDB app.post('/addTodo', (request, response) => { - db.collection('todos').insertOne({thing: request.body.todoItem, completed: false}) + db.collection('todos').insertOne({thing: request.body.todoItem, completed: false}) // find 'todos' collection and insert the object to database. toDoItem is the body of input from the forms. .then(result => { - console.log('Todo Added') - response.redirect('/') + console.log('Todo Added') // console log so we know this happened + response.redirect('/') // response to refresh the page -> trigger get('/') req }) - .catch(error => console.error(error)) + .catch(error => console.error(error)) // catch and log error }) - +// CRUD - Update +// 1. Update `completed` to true. app.put('/markComplete', (request, response) => { db.collection('todos').updateOne({thing: request.body.itemFromJS},{ $set: { - completed: true + completed: true // notice set `true` } },{ - sort: {_id: -1}, - upsert: false + sort: {_id: -1}, // descending order + upsert: false // upsert: insert a document if no documents can be found }) .then(result => { - console.log('Marked Complete') - response.json('Marked Complete') + console.log('Marked Complete') // console log so we know this happened + response.json('Marked Complete') // response to json }) - .catch(error => console.error(error)) + .catch(error => console.error(error)) // catch and log error }) - +// CRUD - Update +// 1. Update `completed` to false app.put('/markUnComplete', (request, response) => { db.collection('todos').updateOne({thing: request.body.itemFromJS},{ $set: { - completed: false + completed: false // notice set `false` } },{ - sort: {_id: -1}, - upsert: false + sort: {_id: -1}, // descending order + upsert: false // upsert: insert a document if no documents can be found }) .then(result => { - console.log('Marked Complete') - response.json('Marked Complete') + console.log('Marked Complete') // console log so we know this happened + response.json('Marked Complete') // response to json }) - .catch(error => console.error(error)) + .catch(error => console.error(error)) // catch and log error }) app.delete('/deleteItem', (request, response) => { db.collection('todos').deleteOne({thing: request.body.itemFromJS}) .then(result => { - console.log('Todo Deleted') - response.json('Todo Deleted') + console.log('Todo Deleted') // console log so we know this happened + response.json('Todo Deleted') // response to json }) - .catch(error => console.error(error)) + .catch(error => console.error(error)) // catch and log error }) app.listen(process.env.PORT || PORT, ()=>{ - console.log(`Server running on port ${PORT}`) + console.log(`Server running on port ${PORT}`) // so we know which server is running }) \ No newline at end of file diff --git a/views/index.ejs b/views/index.ejs index a26617ae..b908c882 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -20,7 +20,11 @@

Todo List: