Skip to content

Updated the app to get it working and added styles #178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
.vscode/*
.vscode/*
.env
259 changes: 235 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,255 @@
# Introduction
<!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 -->

A Simple ToDo App is built using the MVC Architecture, we have also implemented "authorization" so folx can sign up, customize & personalize the app
<a name="readme-top"></a>

---
<!-- PROJECT LOGO -->
<br />
<div align="center">
<a href="LINK"><!-- TODO: add when hosted -->
<img src="./public/img/boatLogLogo.png" alt="Logo" width="80" />
</a>

> Be sure to add that lovely star 😀 and fork it for your own copy
<h3 align="center">ToDo List</h3>

---
<p align="center">
Website to keep track of all your todos.
<br />
<!-- TODO: add when hosted -->
<!-- <a href="LINK">View Site</a> -->
</p>

# Objectives
<!-- TODO: add when hosted -->
<!-- <p>
<sup>Deployment status:</sup>
<a href="https://app.netlify.com/sites/tahuyarivervalleywaterdistrict/deploys?branch=main">
<img src="https://api.netlify.com/api/v1/badges/e29f3a1f-bdbd-466e-93c1-9f84e3d7f4fc/deploy-status" alt="Deployment Status" />
</a>
</p> -->
</div>

- It's a beginner level app created to understand how MVC concept and logins are added
<!-- TABLE OF CONTENTS -->
<details>
<summary>Table of Contents</summary>
<ol>
<li>
<a href="#about-the-project">About The Project</a>
<ul>
<li><a href="#built-with">Built With</a></li>
</ul>
</li>
<li><a href="#usage">Usage</a></li>
<li><a href="#optimizations">Optimizations</a></li>
<li><a href="#lessons-learned">Lessons Learned</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#acknowledgments">Acknowledgments</a></li>
</ol>
</details>

---
<!-- ABOUT THE PROJECT -->

# Who is this for?
## About The Project

- It's for beginners & intermediates with little more experience, to help understand the various aspects of building a node app with some complex features
<p align="center">
<a href="LINK"><!-- TODO: add when hosted -->
<img src='./public/img/screenshot.png' alt='ToDo Screen Shot' />
</a>
</p>

---
Created using the MVC architecture, this todo list application allows users to sign up, log in, and create, read, update, and delete their very own todos!

# Packages/Dependencies used
<p align="right">(<a href="#readme-top">back to top</a>)</p>

bcrypt, connect-mongo, dotenv, ejs, express, express-flash, express-session, mongodb, mongoose, morgan, nodemon, passport, passport-local, validator
### Built With

---
- ![HTML5](https://img.shields.io/badge/HTML5-%23E34F26.svg?style=flat&logo=html5&logoColor=white)
- ![EJS](https://img.shields.io/badge/EJS-%23323330.svg?style=flat&logo=ejs&logoColor=23B4CA65)
- ![CSS3](https://img.shields.io/badge/CSS-%23663399.svg?style=flat&logo=css&logoColor=white)
- ![JavaScript](https://img.shields.io/badge/JavaScript-%23323330.svg?style=flat&logo=javascript&logoColor=%23F7DF1E)
- ![MongoDB](https://img.shields.io/badge/MongoDB-%2347A248.svg?style=flat&logo=mongodb&logoColor=white)
- ![Express](https://img.shields.io/badge/Express-%23000000.svg?style=flat&logo=express&logoColor=white)
- ![Node](https://img.shields.io/badge/Node-%235FA04E.svg?style=flat&logo=nodedotjs&logoColor=white)
- ![Passport](https://img.shields.io/badge/passport-%2334E27A.svg?style=flat&logo=passport&logoColor=white)

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- USAGE -->

## Usage

Getting started is as easy as 1-2-3:

1. Visit the [homepage](LINK)<!-- TODO: add when hosted -->
1. Click `signup`
- Create your account by signing up using your email and creating a password
1. Start adding things to do!

# Install all the dependencies or node packages used for development via Terminal
<p align="right">(<a href="#readme-top">back to top</a>)</p>

`npm install`
<!-- OPTIMIZATIONS -->

---
## Optimizations

# Things to add
This project can be improved by:

- [ ] Adding strategies to allow users to sign up and sign in using a provider
- [x] ~~Hiding the todos paper container when the user has finished all todos~~
- [ ] Showing the active page on the navbar
- [ ] Streamlining CSS
- [x] ~~Moving the passing of the user from `server.js` to its own middleware~~
- [ ] Fix how error messages show up for the user when incorrectly filling out forms

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- LESSONS LEARNED -->

## Lessons Learned

1. **MVC Architecture**

1. **Separation of Concerns**

- Create a `.env` file and add the following as `key: value`
- PORT: 2121 (can be any port example: 3000)
- DB_STRING: `your database URI`
---

Have fun testing and improving it! 😎
- Learned to keep business logic in controllers instead of writing logic inside routes or views.
- Improved code readability by ensuring models only define data structure and controllers handle CRUD operations.
- Understood that views (EJS templates) should remain lightweight, mainly responsible for rendering dynamic content.

1. **Model Layer – Structuring Mongoose Models Efficiently**

- Improved schema design by defining fields, default values, and validation in Mongoose models.
- Learned how to structure models for authentication (User model) vs. application data (Todo model).
- Used Mongoose methods & statics for reusable database queries (e.g., finding todos by user ID).

1. **Controller Layer – Keeping Business Logic Organized**

- Understood that controllers should handle requests & responses rather than routes doing everything.
- Improved error handling by wrapping database calls in `try/catch` blocks.
- Used `async/await` effectively for database interactions instead of nesting `.then()` calls.

Example of cleaner controller logic:

```js
exports.createTodo = async (req, res) => {
try {
await Todo.create({
todo: req.body.todoItem,
completed: false,
user: req.user.id,
});
res.redirect('/todos');
} catch (err) {
console.error(err);
res.status(500).send('Server Error');
}
};
```

1. **View Layer – Dynamic Rendering with EJS**

- Leveraged EJS loops (<% todos.forEach( el => { %>) to dynamically render lists.
- Used partials (`head.ejs`, `navbar.ejs`, etc.) for reusable components, reducing duplicate code.
- Improved UI by conditionally rendering elements based on authentication state (if user is logged in, show Logout button).

1. **Route Layer – Keeping Routes Clean & Modular**

- Moved route handlers to controllers for better organization.
- Used Express middleware (`ensureAuthenticated`) to protect routes properly.
- Applied RESTful route naming conventions (e.g., `GET /todos` for fetching, `POST /todos` for creating, etc.).

Example of refactored routes using a controller:

```js
const express = require('express');
const router = express.Router();
const todosController = require('../controllers/todos');
const { ensureAuthenticated } = require('../middleware/auth');

router.get('/', ensureAuthenticated, todosController.getTodos);
router.post(
'/createTodo',
ensureAuthenticated,
todosController.createTodo
);
router.delete(
'/deleteTodo/:id',
ensureAuthenticated,
todosController.deleteTodo
);

module.exports = router;
```

1. **Middleware – Improving Code Reusability**

- Used middleware to centralize authentication logic instead of checking in every route.
- Understood the middleware execution order (e.g., authentication runs before protected routes).

Example:

```js
module.exports = {
ensureAuthenticated: (req, res, next) => {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
},
};
```

1. **Benefits of the MVC Pattern**
- _Scalability_ – Adding new features is easier because of the clear separation of concerns.
- _Maintainability_ – Debugging is simpler since logic is divided into models, views, and controllers.
- _Code Reusability_ – Middleware, controllers, and models can be reused without duplication.

1. **Full-Stack Authentication Workflow**

- Gained experience with Passport.js for local authentication (session-based).
- Understood the flow of user registration, login, logout, and session management.
- Debugged authentication issues, such as incorrect session persistence or middleware order affecting login states.

1. **EJS & Express Backend Refinements**

- Improved how EJS templates dynamically render content based on user authentication.
- Used partials (`head.ejs`, `navbar.ejs`, `footer.ejs`) for better reusability.
- Ensured better form handling and validation for user inputs.

1. **Dynamic Frontend & Styling Enhancements**

- Styled the todo list dynamically so that `.paperContainer` and `.paperContent` resize properly based on the number of todos.
- Added scrolling behavior when the list grows too long, ensuring a clean UI.
- Improved CSS organization with flexbox, grid, and responsive design principles.

1. **RESTful API & Express Routes**

- Optimized how CRUD operations interact with the database.
- Learned how routes are structured for authentication vs. protected user actions.
- Debugged issues with route protection middleware (`ensureAuthenticated`).

1. **MongoDB & Mongoose Optimizations**

- Refactored Mongoose models to update from v6 to v8 and for better schema validation and default values.
- Improved error handling in database operations to prevent crashes.
- Understood `async/await` vs. promises when querying MongoDB.

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- CONTACT -->

## Contact

Danielle Andrews - [@DrAcula_codes](https://twitter.com/DrAcula_codes 'Twitter/X') - [daniellerandrews](https://www.linkedin.com/in/daniellerandrews 'LinkedIn') - [email protected]

<!-- TODO: add when hosted -->
<!-- Project Link: [LINK](LINK) -->

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- ACKNOWLEDGMENTS -->

## Acknowledgments

A special thanks to these resources used in the project!

- 100devs for creating the project; this is forked from the repo and updated/upgraded and styled
- ChatGPT for generating the logo

<p align="right">(<a href="#readme-top">back to top</a>)</p>
2 changes: 0 additions & 2 deletions config/.env

This file was deleted.

20 changes: 7 additions & 13 deletions config/database.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
const mongoose = require('mongoose')
const mongoose = require('mongoose');

const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.DB_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true
})

console.log(`MongoDB Connected: ${conn.connection.host}`)
const conn = await mongoose.connect(process.env.DB_STRING);
console.log(`MongoDB Connected: ${conn.connection.host}`);
} catch (err) {
console.error(err)
process.exit(1)
console.error(err);
process.exit(1);
}
}
};

module.exports = connectDB
module.exports = connectDB;
76 changes: 49 additions & 27 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,55 @@
const LocalStrategy = require('passport-local').Strategy
const mongoose = require('mongoose')
const User = require('../models/User')
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const User = require('../models/User');

module.exports = function (passport) {
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
User.findOne({ email: email.toLowerCase() }, (err, user) => {
if (err) { return done(err) }
if (!user) {
return done(null, false, { msg: `Email ${email} not found.` })
}
if (!user.password) {
return done(null, false, { msg: 'Your account was registered using a sign-in provider. To enable password login, sign in using a provider, and then set a password under your user profile.' })
}
user.comparePassword(password, (err, isMatch) => {
if (err) { return done(err) }
if (isMatch) {
return done(null, user)
passport.use(
new LocalStrategy(
{ usernameField: 'email' },
async (email, password, done) => {
try {
const user = await User.findOne({
email: email.toLowerCase(),
});

if (!user) {
return done(null, false, {
msg: `Email ${email} not found.`,
});
}

if (!user.password) {
return done(null, false, {
msg: 'Your account was registered using a sign-in provider. To enable password login, sign in using your provider, and then set a password under your user profile.',
});
}

const isMatch = await user.comparePassword(password);

if (isMatch) {
return done(null, user);
} else {
return done(null, false, {
msg: 'Invalid email or password.',
});
}
} catch (err) {
return done(err);
}
return done(null, false, { msg: 'Invalid email or password.' })
})
})
}))

}
)
);

passport.serializeUser((user, done) => {
done(null, user.id)
})
done(null, user.id);
});

passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => done(err, user))
})
}
passport.deserializeUser(async (id, done) => {
try {
const user = await User.findById(id);
done(null, user);
} catch (err) {
done(err, null);
}
});
};
Loading