Skip to content

didadeeee/everyday-thoughts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Everyday Thoughts

App Description

Record everything about your favorite book, all in one place.

Access Everyday Thoughts Here

Everyday Thoughts is an app that demonstrates 2 data entities including the user, book and embedded data - thoughts.

To securely store the book information, authorisation and authentication are implemented throughout the app.

User Stories

As a user, I get to access my book records by logging in the Everyday Thoughts App.
As a user, I get to record my favorite quote, rating, genre and thought about my books.
As a user, I get to manage (update and delete) the information that I have recorded.

Wireframe

Login Page

login page

Initial Plan

initial plan

Thought Form

thought form

Model

model

CRUD

crud crud crud

Daily Plan

daily plan


Screenshots

Books

book index

Update View

update view

Technologies & Tools Used

  1. Node.js
  2. Express Framework
  3. Express Middleware
  4. MongoDB & Mongoose
  5. JavaScript
  6. EJS Partial Templates
  7. bcrypt, Validator
  8. Cyclic deployment
  9. nodemon, dotenv, gitignore
  10. Git & GitHub
  11. API

Next Steps

Future Plan

  1. Additional model entities such as thoughts for journaling purposes.
  2. Admin role to manage user database.
  3. Increased error handling.
  4. Clean input data.

Biggest Challenge

  • To manipulate the EJS view between Login & Logout
<% if (isLoggedIn) { %>
<li><a href="/users/logout">LOGOUT</a></li>
<%} else { %>
<li><a href="/users/newaccount">CREATE ACCOUNT</a></li>
<li><a href="/users/login">LOGIN</a></li>
<% } %>
  • To manipulate the EJS view for API quote
<%if(quote.author === "Anonymous"){ %>
<p class="dailyquote">"<%=quote.quote%>"</p>%>
<% } else { %>
<p class="dailyquote"><%=quote.author%>: "<%=quote.quote%>"</p>
<%};%>

homepage

  • Account Creation
async function create(req, res, next) {
  try {
    const password = await bcrypt.hash(req.body.password, saltRounds);
    const user = await User.create({
      name: req.body.name,
      email: req.body.email,
      password,
    });
    context = {
      isLoggedIn: false,
      msg: "Account Succesfully Created! Login to Begin your Everyday Thoughts!",
    };
    res.status(201).render("users/login", context);
  } catch (error) {
    if (error.code === 11000) {
      context = {
        errormsg: "There's a duplicate, try again?",
        isLoggedIn: false,
      };
      res.render("users/newaccount", context);
    }
    if (error.name === "ValidationError") {
      context = {
        errormsg: "Invalid Details. Try Again?",
        isLoggedIn: false,
      };
      res.render("users/newaccount", context);
    } else {
      return next(error);
    }
  }
}
  • Sign In
async function signIn(req, res, next) {
  const email = req.body.email;
  const password = req.body.password;
  const quote = await fetchData();
  const user = await User.findOne({ email }).exec();
  if (!user) {
    const context = { msg: "User does not exist", isLoggedIn: false };
    res.render("users/login", context);
    return;
  }
  bcrypt.compare(password, user.password, (err, result) => {
    if (result) {
      req.session.userId = user._id;
      req.session.isLoggedIn = true;
      req.session.quote = quote;
      res.render("index", req.session);
    } else {
      const context = { msg: "Incorrect Password", isLoggedIn: false };
      res.render("users/login", context);
    }
  });
}
  • Authorisation
const isAuth = async (req, res, next) => {
  if (req.session.userId) {
    const user = await User.findById(req.session.userId).exec();
    res.locals.user = user;
    isLoggedIn = true;
    next();
  } else {
    res.status(403).redirect("/users/newaccount");
  }
};
  • Delete an embedded data
async function deleteThought(req, res) {
  try {
    const bookId = req.params.bookId;
    const thoughtId = req.params.thoughtId;
    const book = await Book.findById(bookId);
    const foundThought = book.thoughts.find(
      (thought) => thought._id.toString() === thoughtId
    );
    foundThought.deleteOne(thoughtId);
    await book.save();
    res.redirect(`/books/${bookId}/edit/#thoughts`);
  } catch (err) {
    console.error(err);
    res.status(500).send("Server Error");
  }
}
  • API
function fetchData() {
  return fetch("https://api.goprogram.ai/inspiration").then((res) =>
    res.json()
  );
}

async function homePage(req, res) {
  const quote = await fetchData();
  if (req.session.isLoggedIn) {
    const isLoggedIn = true;
    res.render("index", { quote, isLoggedIn });
  } else {
    const isLoggedIn = false;
    res.render("index", { quote, isLoggedIn });
  }
}

Key Learnings

  • Initial planning on CRUD and Model Entity
  • Importance of MVC Approach
  • Different way of validation: HTML Input, Regular Expressions, Schema Validation
  • Naming (singular, plural, capital) matters A LOT
  • JavaScript Promises: Async & Await
  • Manipulate data using EJS
  • Error Handling
  • Opening and closing tags, to "/" or not to "/"
  • Debugging Skills
  • MongoDB & Mongoose Synthax
  • req.params/sessions/body & res.redirect/render/send
  • Managing embedded data as an array: push, find, deleteOne and save
  • The Importance of Authorisation
  • Password Hashing
  • User Testing

Q&A


Resources

Case studies: Journey | Penzu
Wireframe: Canva
Header and Footer Template: W3Schools
Social Icons: Font Awesome
Quote API: Inspiration
Validation: Validator
API Reference: Stack Overflow

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published