Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
// Sets the font size for the main editor in VS Code
"editor.fontSize": 42,
// Sets the font size specifically for the integrated terminal
"terminal.integrated.fontSize": 62
}
3 changes: 3 additions & 0 deletions public/css/style.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/* All <h1> headings will have red text */
h1{
color: red;
}
/* Elements with the class "completed" will have gray text
and a line through them, typically used to show something is finished or crossed out */
.completed{
color: gray;
text-decoration: line-through;
Expand Down
29 changes: 26 additions & 3 deletions public/js/main.js
Original file line number Diff line number Diff line change
@@ -1,71 +1,94 @@
// Select all delete buttons (trash icons) on the page
const deleteBtn = document.querySelectorAll('.fa-trash')

// Select all <span> elements inside the li with class 'item'
const item = document.querySelectorAll('.item span')

// Select all <span> elements that are already marked as completed
const itemCompleted = document.querySelectorAll('.item span.completed')

// Add a click event listener to each delete button
Array.from(deleteBtn).forEach((element)=>{
element.addEventListener('click', deleteItem)
})

// Add a click event listener to each incomplete item marking it as completed
Array.from(item).forEach((element)=>{
element.addEventListener('click', markComplete)
})

// Add a click event listener to each completed item marking it as uncompleted
Array.from(itemCompleted).forEach((element)=>{
element.addEventListener('click', markUnComplete)
})

// Function to delete an item
async function deleteItem(){
// Get the text content of the task to be deleted
const itemText = this.parentNode.childNodes[1].innerText
try{
// Send a DELETE request to the server with the item text
const response = await fetch('deleteItem', {
method: 'delete',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
'itemFromJS': itemText
})
})
// Parse the JSON response from the server and store it in the variable `data`
const data = await response.json()
console.log(data)
// Reload the page to show updated list
location.reload()

// If an error occurs in the try block, catch it and log it to the console
}catch(err){
console.log(err)
}
}

// Function to mark an item as complete
async function markComplete(){
// Get the text content of the task to be updated
const itemText = this.parentNode.childNodes[1].innerText
try{
// Send a PUT request to mark item as complete
const response = await fetch('markComplete', {
method: 'put',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
'itemFromJS': itemText
})
})
// Parse the JSON response from the server and store it in the variable `data`
const data = await response.json()
console.log(data)
// Reload the page to show the updated list
location.reload()

// If an error occurs in the try block, catch it and log it to the console
}catch(err){
console.log(err)
}
}

// Function to mark an item as uncomplete (undo complete)
async function markUnComplete(){
// Get the text content of the task to be updated
const itemText = this.parentNode.childNodes[1].innerText
try{
// Send a PUT request to the server to mark the item as incomplete
const response = await fetch('markUnComplete', {
method: 'put',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
'itemFromJS': itemText
})
})
// Parse the JSON response from the server and store it in the variable `data`
const data = await response.json()
console.log(data)
// Reload the page to show the updated list
location.reload()

// If an error occurs in the try block, catch it and log it to the console
}catch(err){
console.log(err)
}
Expand Down
90 changes: 46 additions & 44 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
const express = require('express')
const app = express()
const MongoClient = require('mongodb').MongoClient
const PORT = 2121
require('dotenv').config()
const express = require('express') // Import Express library
const app = express() // Initialize Express app
const MongoClient = require('mongodb').MongoClient // Import MongoClient for MongoDB
const PORT = 2121 // Default port for server
require('dotenv').config() // Load environment variables from .env file


let db,
dbConnectionStr = process.env.DB_STRING,
dbName = 'todo'
dbConnectionStr = process.env.DB_STRING, // MongoDB connection string from environment
dbName = 'todo' // Name of the database

// Connect to MongoDB
MongoClient.connect(dbConnectionStr, { useUnifiedTopology: true })
.then(client => {
console.log(`Connected to ${dbName} Database`)
db = client.db(dbName)
db = client.db(dbName) // Assign the connected database to variable
})

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 the templating engine
app.use(express.static('public')) // Serve static files from 'public' folder
app.use(express.urlencoded({ extended: true })) // Parse URL-encoded bodies (from HTML forms)
app.use(express.json()) // Parse JSON bodies

// GET route for home page
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 })
const todoItems = await db.collection('todos').find().toArray() // Fetch all todo items from 'todos' collection
const itemsLeft = await db.collection('todos').countDocuments({completed: false}) // Count how many items are not completed
response.render('index.ejs', { items: todoItems, left: itemsLeft }) // Render index.ejs with the todo items and remaining count

// Old promise-based approach (commented out)
// db.collection('todos').find().toArray()
// .then(data => {
// db.collection('todos').countDocuments({completed: false})
Expand All @@ -35,59 +38,58 @@ app.get('/',async (request, response)=>{
// .catch(error => console.error(error))
})

// POST route to add a new todo
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}) // Add a new todo to the database, initially not completed
.then(result => {
console.log('Todo Added')
response.redirect('/')
response.redirect('/') // Redirect back to home page
})
.catch(error => console.error(error))
.catch(error => console.error(error)) // Catch and log any errors
})

// PUT route to mark a todo as complete
app.put('/markComplete', (request, response) => {
db.collection('todos').updateOne({thing: request.body.itemFromJS},{
$set: {
completed: true
}
},{
sort: {_id: -1},
upsert: false
})
db.collection('todos').updateOne(
{ thing: request.body.itemFromJS }, // Find item by text
{ $set: { completed: true } }, // Set completed to true
{ sort: { _id: -1 }, upsert: false} // Update latest item, do not create if missing
)
.then(result => {
console.log('Marked Complete')
response.json('Marked Complete')
console.log('Marked Complete') // Log to the server console that the item was marked complete
response.json('Marked Complete') // Send a JSON response back to the client confirming the update
})
.catch(error => console.error(error))
.catch(error => console.error(error)) // Catch and log any errors

})

// PUT route to mark a todo as incomplete
app.put('/markUnComplete', (request, response) => {
db.collection('todos').updateOne({thing: request.body.itemFromJS},{
$set: {
completed: false
}
},{
sort: {_id: -1},
upsert: false
})
db.collection('todos').updateOne(
{ thing: request.body.itemFromJS }, // Find item by text
{ $set: { completed: false } }, // Set completed to false
{ sort: { _id: -1 }, upsert: false } // Update latest item, do not create if missing
)
.then(result => {
console.log('Marked Complete')
response.json('Marked Complete')
console.log('Marked Incomplete') //Log to the server console that the item was marked incomplete
response.json('Marked Incomplete') // Send a JSON response back to the client confirming the update
})
.catch(error => console.error(error))
.catch(error => console.error(error)) // Catch and log any errors

})

// DELETE route to remove a todo
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') //Log to the server console that a todo item was successfully deleted
response.json('Todo Deleted') // Respond with JSON message
})
.catch(error => console.error(error))
.catch(error => console.error(error)) // Catch and log any errors

})

// Start the server
app.listen(process.env.PORT || PORT, ()=>{
console.log(`Server running on port ${PORT}`)
})
45 changes: 22 additions & 23 deletions views/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,43 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<title>Todo List</title>
<!-- Link to Font Awesome for icons (trash can, etc.) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<!-- Link to your custom CSS file for styling the page -->
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- Main page heading -->
<h1>Todo List: </h1>
<!-- List of todo items -->
<ul class="todoItems">
<% for(let i=0; i < items.length; i++) {%>
<li class="item">
<% if(items[i].completed === true) {%>
<span class='completed'><%= items[i].thing %></span>
<% }else{ %>
<span><%= items[i].thing %></span>
<% } %>
<span class='fa fa-trash'></span>
</li>
<% } %>
<!-- Loop through all items passed from the server -->
<% for(let i=0; i < items.length; i++) {%>
<li class="item">
<!-- If the item is completed, add the 'completed' class for styling -->
<% if(items[i].completed === true) {%>
<span class='completed'><%= items[i].thing %></span>
<% }else{ %>
<span><%= items[i].thing %></span>
<% } %>
<!-- Trash icon for deleting this todo item -->
<span class='fa fa-trash'></span>
</li>
<% } %>
</ul>

<!-- Display the number of items left to do -->
<h2>Left to do: <%= left %></h2>

<!-- Form to add a new todo item -->
<h2>Add A Todo:</h2>

<form action="/addTodo" method="POST">
<input type="text" placeholder="Thing To Do" name="todoItem">
<input type="submit">
</form>


<!-- Link to main JavaScript file -->
<script src='js/main.js'></script>
</body>
</html>
</html>