Skip to content
A full-stack web app designed to fast-track the weekly meal-planning process. Built using JS, Node.js, Express, and MongoDB.
JavaScript HTML CSS
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
config
controllers
middleware
models
public
uploads
views
.gitignore
Procfile
README.md
package-lock.json
package.json
seed.js
server.js

README.md

Honeydew

Log your recipes and start meal-planning now!

Project Proposal

A full-stack web app that enables users to more effectively plan their meals for the week.

Objective & Project Details

Honeydew is designed to fast-track the meal-planning process. The user starts by logging their favorite recipes, which are then presented in a recipe bank to select and place within their calendar week.

By selecting a recipe and placing it into a mealplan slot (e.g., Monday, dinner), the recipe becomes 'active' so that its ingredients are added to the user's grocery list. Once the user is done placing recipes into their mealplan, they can view a summary of their recipes and required ingredients by clicking the "See grocery list" button.

Lastly, the user can view all their recipes on the Profile screen. Recipes are organized by meal type (i.e., breakfast, lunch, dinner, dessert), along with their photos and expected prep & cook times. The user can click into each recipe card to view more recipe details such as serving size, prep instructions, and list of ingredients.

From a technical perspective, honeydew is a full-stack web application. It uses HTML/CSS/Javascript/jQuery/Bootstrap on the front-end, Express on the back-end, and MongoDB for its database. Honeydew was also designed for both desktop & mobile. On desktop, the week's mealplan is laid out like a standard calendar with the days stretching horizontally. On mobile, the mealplan calendar is inverted, displaying the calendar days vertically instead. Mobile functionality was enabled by Bootstrap 4 plus specific media queries.

Data Model

Honeydew data models include:

  • User: Object
    • name: String (e.g., 'Graeme')
    • email: String (e.g., 'graeme@erickson.com')
    • password: String
    • recipes: Array containing Recipe schema
    • mealPlan: Array of Strings
  • Recipe: Object
    • recipeName: String (e.g., Granola)
    • servingSize: Number (e.g., 2)
    • prepInstructions: String (e.g., Bake in oven at 400 degrees)
    • activeCount: Number (e.g., 1)
    • ingredients: Array containing Ingredient schema
  • Ingredient: Object
    • ingredientName: String (e.g., oats)
    • qty: String (e.g., 3.5)
    • measuringUnit: String (e.g., cups)

Third Party API

Honeydew integrates with Cloudinary for 3rd party image storage, allowing the user to upload their most delicious-looking photos to go along with their recipes. Honeydew both writes to and reads from Cloudinary for image upload & image loading.

Routes

URL Method Purpose
/ GET Render home screen (recipes & mealplan)
/auth/login GET Render login screen
/auth/login POST Accept login credentials
/auth/signup GET Render signup screen
/auth/signup POST Accept signup details
/api/recipes GET Get user's recipes
/api/recipes POST Create new recipe
/api/recipes PUT Update existing recipe (to increment/decrement active count)
/api/recipes DELETE Delete existing recipe
/grocerylist GET Get user's active recipes with related ingredients
/profile GET Get user's entire list of recipes
/profile/viewRecipe GET Get selected recipe to view recipe details
/profile/viewRecipe POST Post selected recipe ID to back-end
/profile/viewRecipe DELETE Delete selected recipe

Views

View Purpose
auth/login.ejs Display login form
auth/signup.ejs Display signup form
grocerylist.ejs Display summary of active (selected) recipes as well as related ingredients.
home.ejs Display homepage - recipe bank, add recipe button, mealplan calendar w/ active meals, and grocery list button.
layout.ejs Initialize html. Link to stylesheets and scripts. Contain references to partial views & body of other views.
partials/alerts.ejs Flash success or error alerts to user.
partials/header.ejs Contain logo and links to login/logout, signup & profile.
profile.ejs Display user's recipes, organized by mealtype (i.e., breakfast, lunch, dinner, dessert) and with links to view specific recipe details.
viewRecipe.ejs Display specific selected recipe details.

Selected Code Snippet

The most complicated section of code relates to handling when the user places a recipe into his/her mealplan. The code below tackles the three possible scenarios:

  1. No new recipe is selected, and the mealplan slot is not blank. Action: Decrement the existing recipe's active count by one, and clear the mealplan slot of the recipe name.
  2. A new recipe is selected, and the mealplan slot is blank. Action: Increment selected recipe's active count by one, and populate the mealplan slot with the recipe name.
  3. A new recipe is selected, and the mealplan slot is not blank. Action: Decrement the existing recipe's active count by one, increment the selected recipe's active count by one, and update the mealplan slot with the selected recipe name.
// PUT route to update user's recipe & mealplan
router.put('/', isLoggedIn, (req,res) => {
  let scenario = req.body.scenario;

  // scenario: no new recipe is selected, and mealplan slot is not blank - UPDATE PREVIOUS RECIPE AND CLEAR MEAL PLAN SLOT
  switch (scenario) {
    case 'clear-mealplan-slot':
      db.User.findById(res.locals.currentUser.id, function(err, user) {
        if (err) { console.log("Error finding user in db", err); };
        // decrement cleared recipe's active count
        for (let i = 0; i < user.recipes.length; i++) {
          if (user.recipes[i].recipeName === req.body.selectedMealPlanSlotExistingRecipe) {
            user.recipes[i].activeCount > 0 ? user.recipes[i].activeCount -= 1 : user.recipes[i].activeCount = 0;
          }
        }
        // update mealplan slot to a blank value
        user.mealPlan.splice(req.body.selectedMealPlanSlotId,1,"");
        user.save();
        res.render('home');
      })
      break;
    // scenario: new recipe is selected, and mealplan slot is blank - UPDATE SELECTED RECIPE AND POPULATE MEAL PLAN SLOT
    case 'populate-mealplan-slot':
      db.User.findById(res.locals.currentUser.id, function(err, user) {
        if (err) { console.log("Error finding user in db", err); };
        // increment selected recipe's active count
        for (let i = 0; i < user.recipes.length; i++) {
          if (user.recipes[i].recipeName === req.body.selectedRecipeName) {
            user.recipes[i].activeCount += 1;
          }
        }
        // update mealplan slot to selected recipe
        user.mealPlan.splice(req.body.selectedMealPlanSlotId,1,req.body.selectedRecipeName);
        user.save();
        res.render('home');
      })
      break;
    // scenario: new recipe is selected, and mealplan slot is not blank - UPDATE PREVIOUS AND SELECTED RECIPE, AND REPLACE MEAL PLAN SLOT
    case 'replace-mealplan-slot':
      db.User.findById(res.locals.currentUser.id, function(err, user) {
        if (err) { console.log("Error finding user in db", err); };
        // decrement previous recipe's active count, and increment selected recipe's active count
        for (let i = 0; i < user.recipes.length; i++) {
          if (user.recipes[i].recipeName === req.body.selectedMealPlanSlotExistingRecipe) {
            user.recipes[i].activeCount > 0 ? user.recipes[i].activeCount -= 1 : user.recipes[i].activeCount = 0;
          }
          if (user.recipes[i].recipeName === req.body.selectedRecipeName) {
            user.recipes[i].activeCount += 1;
          }
        }
        // update mealplan slot to selected recipe
        user.mealPlan.splice(req.body.selectedMealPlanSlotId,1,req.body.selectedRecipeName);
        user.save();
        res.render('home');
      })
      break;
  }
})

Tools & Technologies

  • HTML
  • CSS
  • Javascript
  • jQuery
  • Bootstrap 4: nav, grid, buttons, button groups, popovers, modals
  • MongoDB
  • User Auth
  • Npm modules: bcrypt, body-parser, cloudinary, connect-flash, dotenv, ejs, express, express-ejs-layouts, express-session, mongoose, morgan, multer, passport, passport-local, path
  • FontAwesome
  • Animate.css
  • Roboto Google Font
  • Github
  • Heroku
  • Trello

Screenshots

Signup screen

honeydew_screenshot_signup

Login screen

honeydew_screenshot_login

Home screen - recipes & mealplan

honeydew_screenshot_home

Add new recipe

honeydew_screenshot_addrecipe

Upload photo with recipe

honeydew_screenshot_addrecipe_photoupload

Profile screen

honeydew_screenshot_profile

Grocery list

honeydew_screenshot_grocerylist

Signup screen - mobile

honeydew_screenshot_signup_mobile

Login screen - mobile

honeydew_screenshot_login_mobile

Home screen - mobile

honeydew_screenshot_home_mobile

If I had more time...

I would implement the following additional features:

  • Drag & drop recipes to mealplan.
  • Smarter, more summarized grocery list.
  • A more efficient mechanism for adding recipes.
You can’t perform that action at this time.