Here is a link to the final project
This is a cocktail recipe site, built for bartenders, by bartenders. It's a site where users can sign in, find cocktails, add their own cocktails, and edit or delete old cocktail recipes. It is designed to be responsive on a wide range of devices, whilst also being easy to navigate through.
I wanted to create a website linked to a database, which allows users to login, add their own cocktails, or just search and find previously added cocktails. As an ex-bartender myself, this was something I always wanted but couldn't find online.
As a first time visitor to this site, a user should be able to :
- Easily navigate the site.
- Intuitively and easily understand what to do.
- Register for an account.
- Search for a specific cocktail.
- Browse through all cocktails.
- Get visual feedback when an action on the site is completed.
In addition to the First Time Visitor Goals, a Returning Visitor should be able to :
- Log in.
- Add new cocktails.
- Edit and delete the user's cocktails.
- See the cocktails the user has added.
- Be confident that the user's password is be stored securely.
- Navigate intuitively, with no need to use the browser's back button.
In addition to the First Time and Returning Visitor Goals, as an administrator of this site, an admin user should be able to:
- Be confident that a user can't to brute force their way into the restricted pages.
- Edit or delete any cocktail recipe.
- Add a new category.
- Edit or delete any category.
- Give or remove admin rights.
- This project has the goal of demonstrating my understanding of maintaining a database attached to a website, with full CRUD (Create, Read, Update and Delete) functionality.
- I decided on a very simple theme of purple and black for my color scheme.
- I didn't want to distract from the content of the site, hence choosing two main colors.
- These colors are very contrasting, which provides a striking visual style on the site.
- I have used 3 different shades of purple to provide some contrast on the site.
- All text is white, which allows the text to be easily visible on all backgrounds.
- I have used other colors on buttons only:
- I have used red only for the delete buttons, to convey the button is a destructive thing.
- I have used green only for the cancel button on the delete modals, to signify a return to safety for the user.
- I have used blue only for the edit buttons, to show that it's a creative thing.
- I have used a barcode-style font from Google Fonts, called Libre Barcode 128 Text, to form the logo. This has the fallback font of sans-serif.
- I have used a cursive font from Google Fonts, called Dancing Script, to link with the theme of an elegant cocktail bar. This has the fallback font of cursive.
- The basic font-family of Arial, Helvetica, sans-serif has been used for the rest of the text, so as to not distract the user from the content of the site. This also comes with the bonus that it's standard with HTML, so it should render on any browser and any device.
- The images used in this project are all pictures of cocktails, to match the recipes.
- Icons are used where suitable to allow users to easily understand at a glance what is intended.
- Unfortunately there isn't much control about what images users upload. If any inappropriate images are added, the any admin user has the ability to edit the image.
- Create, Edit and Delete a cocktail recipe.
- Create and Delete a profile.
- Create, Edit and Delete a category for admin users only.
- Edit admin rights and Delete a user for admin users only.
- Search for a specific cocktail.
- Confirm to delete modal.
- Auto-updating copyright year.
- A link to Drink Aware in the footer, for anyone struggling with alcohol use.
- Email verification before a user can add a cocktail.
- Enter user's password to delete user account.
- Create an 'Add to Favourites' so users can store their favourite cocktail recipes.
- Admin to approve cocktail recipe before it goes live on the site.
- No audio is used in this project.
The navigation bar changes depending on user status and screen size:
Nav Link | Logged Out | Logged In (User) | Logged In (Admin) |
---|---|---|---|
Logo (Homepage) | âś… | âś… | âś… |
Home | âś… | âś… | âś… |
Profile | ❌ | ✅ | ✅ |
Add Cocktail | ❌ | ✅ | ✅ |
Manage Categories | ❌ | ❌ | ✅ |
Manage Users | ❌ | ❌ | ✅ |
Log Out | ❌ | ✅ | ✅ |
Log In | ✅ | ❌ | ❌ |
Register | ✅ | ❌ | ❌ |
- Logged in
- Logged out
- An admin
- On small screen sizes
MongoDB was used to store data for this site in a database. The data has been set out in three collections, which are described below:
Users | |
---|---|
_id | ObjectId |
username | string |
password | string |
is_admin | boolean |
Recipes | ||
---|---|---|
_id | ObjectId | |
category_id | ObjectId | categories._id |
recipe_name | string | |
recipe_list | array | |
method | string | |
recipe_img | string | |
recipe_alt | string | |
user_id | ObjectId | users._id |
Categories | |
---|---|
_id | ObjectId |
category_name | string |
The Recipes collection references both the Categories collection and the Users collection, using the ObjectId from the relevant fields to ensure that they automatically update if changes are made.
GitPod was used as a virtual IDE workspace to build this site.
Git was used for version control by utilizing the Gitpod terminal to add and commit to Git and push to GitHub.
GitHub is used to store the code for this project after being pushed from Git.
Balsamiq was used to create the wireframes during the design process.
Am I Responsive Design was used to check the responsive design of the site, and to create the final site image.
Responsinator was used to help improve the responsive design on a variety of devices.
Shields.io was used to create the GitHub badges for the top of this README.md file.
Font Awesome was used on all pages to add the icons.
Google Fonts was used to select all the fonts on the site.
favicon.io used to create a site favicon.
Imgur was used to host images to add to each cocktail recipe.
MongoDB was used to store the contents of the database, and allow full CRUD functionality.
Flask-PyMongo was used to connect my Python / Flask app to MongoDB.
Heroku was used to deploy the live site initially.
Render was used to deploy the live site after submission.
Google DevTools was used to help find what code correlated to which feature.
Lighthouse was used to ensure that the code was as performant as possible, confirming to best practices, and SEO and Accessibility guidelines.
Flask was used to help create the templating for this site.
Materialize was used to create a beautiful, responsive website.
jQuery was used to make the DOM traversal easier within the JavaScript.
Jinja was used to auto-populate the site with the contents of the database.
RandomKeygen was used to generate a strong SECRET_KEY
.
Flask-paginate was used to add pagination to the homepage.
pip was used to install the required dependancies for this site.
dnspython was used to provide access to DNS.
- Python
- MongoDB account and database
- GitHub account
- Heroku account / Render account
MONGO_DBNAME - This is the name of the database you need to connect to in MongoDB.
MONGO_URI - This can be found on the MongoDB website by following these steps: * In the clusters tab of your database, click connect on the associated cluster. * Click 'Connect', then 'Connect your application'. * Copy the string, then substitute the password (from Database access NOT your MongoDB password) and change "myFirstDatabase" to your MONGO_DBNAME.
SECRET_KEY - This is a custom string set up to keep sessions secure. I recommend using a 'Fork Knox' level password generated by RandomKeygen.
This site was deployed to Heroku by following these steps:
- Heroku needs to be told what the requirements are for this project, so go into your GitPod terminal, and create files to explain the requirements by using the following commands:
pip3 freeze --local > requirements.txt
echo web: python run.py > Procfile
- Ensure there is no blank line after the contents of this file
- Push these changes to your repository.
- Ensure you have a .gitignore file in your repository, and if not, create one.
- Add
env.py
and__pycache__/
into your .gitignore file, and save the file. This is to avoid any sensitive information being added into your repository. - Create an env.py file, and add the following information to it, updating the '## x ##' values with your own values:
import os
os.environ.setdefault("IP", "0.0.0.0")
os.environ.setdefault("PORT", "5000")
os.environ.setdefault("SECRET_KEY", " ## YOUR SECRET_KEY ## ")
os.environ.setdefault("MONGO_URI", " ## YOUR MONGO_URI ## ")
os.environ.setdefault("MONGO_DB", " ## YOUR MONGO_DBNAME ## ")
- Login or sign up to Heroku.
- Select 'Create New App' in the top right of your dashboard.
- Choose a unique app name, and select the region closest to you, before clicking 'Create App'.
- Go to the 'Deploy' tab, find 'Deployment Method' and select 'GitHub'.
- Search to find your GitHub repository, and click 'Connect'. Don't enable automatic deployment yet, as this can cause errors.
- Go to the 'Settings' tab, find 'Config Vars', and click 'Reveal Config Vars'.
- Enter key value pairs that match those in your env.py file, displayed like this :
Key | Value |
---|---|
IP | 0.0.0.0 |
PORT | 5000 |
MONGO_DBNAME | ## YOUR DATABASE NAME ## |
MONGO_URI | ## YOUR MONGO_URI ## |
SECRET_KEY | ## YOUR SECRET_KEY ## |
- Go to the 'Deploy' tab, and click 'Enable Automatic Deployment'.
- In 'Manual Deploy', choose which branch you'd like to deploy from (I chose 'master' branch, this is also known as 'main').
- Click 'Deploy Branch' to deploy your app onto the Heroku servers.
- Once the app has finished building, click 'Open App' to open your site.
This site was deployed to Render by following these steps:
- Render needs to be told what the requirements are for this project, so go into your GitPod terminal, and create files to explain the requirements by using the following commands:
pip3 freeze --local > requirements.txt
echo web: python run.py > Procfile
- Ensure there is no blank line after the contents of this file
- Push these changes to your repository.
- Ensure you have a .gitignore file in your repository, and if not, create one.
- Add
env.py
and__pycache__/
into your .gitignore file, and save the file. This is to avoid any sensitive information being added into your repository. - Create an env.py file, and add the following information to it, updating the '## x ##' values with your own values:
import os
os.environ.setdefault("IP", "0.0.0.0")
os.environ.setdefault("PORT", "5000")
os.environ.setdefault("SECRET_KEY", " ## YOUR SECRET_KEY ## ")
os.environ.setdefault("MONGO_URI", " ## YOUR MONGO_URI ## ")
os.environ.setdefault("MONGO_DB", " ## YOUR MONGO_DBNAME ## ")
- Login or sign up to Render.
- Select 'New' in the top right of your dashboard, and select 'Web Service'.
- Search for your repo, and click 'Connect'.
- Enter a unique name for your web service (ideally the project name).
- Use the following settings:
Setting Name | Value |
---|---|
Root Directory | {blank} |
Environment | Python 3 |
Region | Frankfurt (EU Central) |
Branch | main |
- Set the 'Build Command' to
pip install -r requirements.txt
. - Set the 'Start Command' to
python app.py
. - Select the 'Free plan $0/month'.
- Click 'Advanced', then 'Add Secret File'.
- Copy and paste the content of your
env.py
file into the 'File Contents' section. - Set the 'Filename' to
env.py
, and click 'Save'. - Select whether you want the repo to auto-deploy or not.
- Click 'Create web service'.
- Wait for this to deploy.
- Click the link at the top of the page displaying your new URL to take you to the deployed site.
- Login or Sign Up to GitHub.
- On GitHub, go to Abibubble/ms3-the-bar-blade.
- In the top right, click "Fork".
- You will need to create an env.py file with your own values, and create a MongoDB database with the data keys and types as shown above.
- You will also need to install all of the project requirements. This can be done using the command
pip3 install -r requirements.txt
. - Type
python3 app.py
in your GitPod terminal to run your local site of this project.
- Log in to GitHub and locate the Repository for this site.
- Under the repository name, above the list of files, click "Code".
- Here you can either Clone or Download the repository.
- You should clone the repository using HTTPS, clicking on the icon to copy the link.
- Open Git Bash.
- Change the current working directory to the new location, where you want the cloned directory to be.
- Type
git clone
, and then paste the URL that was copied in Step 4. - Press Enter, and your local clone will be created.
- You will need to create an env.py file with your own values, and create a MongoDB database with the data keys and types as shown above.
- You will also need to install all of the project requirements. This can be done using the command
pip3 install -r requirements.txt
. - Type
python3 app.py
in your GitPod terminal to run your local site of this project.
For a more detailed version of these steps, go to the Github Docs page on this topic.
The W3C Markup Validator, W3C CSS Validator, JSHint were used to validate the project to ensure there were no syntax errors within the site.
-
W3C HTML Markup Validator
-
W3C CSS Validator
- Homepage
- Register
- Log In
- Unfortunately, as it's not possible to log in using the Validator, it was only possible to test the 3 pages available to a user who isn't logged in.
- There is only one error appearing, which is from the Materialize CSS file.
- There are several warnings, mostly from the Materialize CSS file, but also three related to my purple variables. As these variables are acceptable CSS syntax, I've considered them to be not an error.
-
JSHint
- JSHint
- There is one undefined variable, which is due to the jQuery
$
. - There are three unused variables, which are used in onclick functions in my HTML.
- PEP8 Online
- The navigation bar is clearly visible on all pages.
- The navigation bar is fixed to the top of the screen, so even when the user scrolls, they can still see the navigation bar.
- Everything is clearly laid out.
- All buttons describe what they're for in simple terms.
- Icons are used to help convey meaning.
- The register button is clear to see on the navigation bar.
- It only requires a username, a password, and a confirmation of that password to register an account.
- The search bar is displayed prominently on the homepage for all users, whether logged in or not.
- All cocktails are displayed on the homepage for all users to view.
- A flash message appears at the top of the screen when :
- Any item is added.
- Any item is edited.
- Any item is deleted.
- A user registers.
- A user logs in.
- A user logs out.
- The log in button is visible in the navigation bar.
- The log in process is intuitive and simple to use.
- The 'Add Cocktail' button is available on the navigation bar once you log in.
- Every input field on the Add Cocktail form is clearly defined.
- There is a helpful reminder about accessibility when adding an image to the site.
- If the user hasn't added any cocktails, their profile prompts them to add one, with a helpful button to direct them to the 'Add Cocktail' page.
- Every cocktail that a user has added is displayed on their profile.
- Each cocktail listing has its own edit and delete buttons.
- There is a link to add a cocktail on the navigation bar, which is available on every page.
- If the user hasn't added any cocktails, their profile instead prompts them to add one, with a helpful button to direct them to the 'Add Cocktail' page.
- Every cocktail that a user has added is displayed on their profile.
- Werkzeug's password hashing methods have been used to store all user's passwords in a secure and safe way.
- The navigation bar is constantly visible across the top of the site.
- This is either the full navigation bar, or the condensed burger icon menu bar on smaller screen sizes.
- Admin users are set with an is_admin toggle in the database, so that it doesn't rely on usernames.
- If a user without access rights tries to access a restricted page, it presents them with the 404 page, with a navigation bar at the top to take them back to the pages they're allowed to access.
- Admin users are set with an is_admin toggle in the database, so that it doesn't rely on usernames.
- If the user has the is_admin toggle set to true, then all recipes are displayed on their profile.
- This includes all recipes created by other users, with edit and delete buttons.
- This enabled the admin to edit or delete any recipe.
- Admin accounts have access to the 'Manage Categories' page.
- This allows them to add a new category.
- Admin accounts have access to the 'Manage Categories' page.
- This allows them to edit or delete any categories.
- Admin users are set with an is_admin toggle in the database, so that it doesn't rely on usernames.
- If the user has the is_admin toggle set to true, then they have access to the Manage Users page.
- From there, any user can be deleted, or have their admin rights switched on or off.
- The only user that cannot be edited is the main admin account, to ensure the site isn't left without an admin user by mistake.
Click here to view the full testing steps that were completed on every device and browser, and screenshots of testing.
- When I added the confirm deletion modal into categories.html, it wasn't taking the category I'd clicked on to delete, it was just deleting the first category in the list.
- I looked through my code on Google DevTools and saw that, as my delete button was part of a for loop that was populating categories, it was also duplicating the ID of the modal.
- This meant that when it came to deleting, it didn't know which one of those IDs I actually wanted to delete.
- I then added in the
category._id
to each modal ID, so I could have an individual ID for each category. - This solved the bug, and also fixed an issue that W3C HTML validator brought up about repeated IDs.
- When the user clicked the 'delete' or 'edit' buttons on their profile cocktails, the collapsible would expand as well as the button's action.
- I discovered that, as the buttons were inside the collapsible-header Materialize class, they were being treated as clickable too.
- I attempted to move them to the right of the headers, but this resulted in the buttons not connecting to their cocktail recipes.
- Instead, I moved them into the expanded section, so now the user can click on the header to expand, and depending on the contents of the recipe, can then decide to delete or edit.
- This makes more sense than where I had them before, as the user won't know if they want to edit or delete a recipe until they've looked at it.
- The favicon files were throwing up errors in the console, as shown below.
- I checked that the files I was including were correct, according to Favicon.io, which was where I got my favicon from.
- I then did a Google search and found this article on Medium which suggested a possible fix.
- I added
crossorigin="use-credentials"
to my link for the webmanifest file, which fixed the console errors.
- The user couldn't view the Homepage without being logged in - which also meant that they couldn't log in.
- In my navigation bar, I was using
{% if user["is_admin"] %}
to check if the current user has the is_admin toggle set to true. - However, if the user isn't logged in, there was no information about the user, causing
KeyError: 'user'
to show. - In my base.html, I changed the
{% if user["is_admin"] %}
to{% if user %}{% if user["is_admin"] %}
to check if the user was logged in first. - I then added a try/except block in my app.py file to try to find the logged in user, but if that wasn't found, to select all users instead.
- This fixed this bug.
- In my navigation bar, I was using
- When adding a cocktail, the ingredients add as expected, but if there's more than one additional ingredient, the ingredients won't remove properly.
- I used
console.log()
to ensure I was referencing the correct elements. - I discovered that the counter variable wasn't being referenced correctly from the
elRemove
variable. - I created a new variable to find the counter for the clicked remove button, and used that to reference.
- I then used Google DevTools to inspect my code and discovered that the remove button was being given a number from the counter that was one too high for the element it was meant to be referencing.
- I changed it to
let thisRemoveButton = removeButton.replaceAll("*", counter-1);
which fixed this bug.
- I used
- When a user who isn't logged in tries to search, it gives a
key error: user
.- I checked my app.py file and found that the search function was still using
user = mongo.db.users.find_one({"username": session["user"]})
. - I changed this to the same try/except block that the homepage uses.
- This fixed the bug.
- I checked my app.py file and found that the search function was still using
- None found, if any errors are found, please contact me via my GitHub (Abibubble) to get them fixed.
I tested my website using DevTools Lighthouse feature, and got these results:
- The main issue was the fact that images do not have explicit width and height. This was necessary, as there is no way of knowing what size images users will be submitting with their cocktails, and it would cause more issues with images being stretched or squashed.
- All images have alt text, including a request for users to add alt text for their own images so the site stays fully accessible.
- All icons have titles where text isn't otherwise present to explain their use.
- All tap targets are correctly sized, and aren't overlapping other content.
- All colors are WCAG AA compliant. The majority are AAA compliant, except for the Delete and Edit buttons.
- This was knocked down due to an issue in the console.
- The console error is due to the new SameSite cookie issue Google has introduced, which I wasn't able to find a fix for.
- The only thing causing issues with SEO was that not all links are crawlable.
- The links it's referring to are the pagination links, which I don't have control over due to my use of Flask-paginate to create the pagination for this site.
- Font Awesome: Library of icons used for social media and download links.
- Materialize: Throughout the site, to create a beautiful responsive site, without taking too much time.
- Code Institute: For the select form element validation.
- This article on Medium: For helping me fix bug #3.
- Flask-paginate: For the pagination.
- This GitHub repo from mozillazg: For help with making the pagination work on this site.
- All content was created by Abi Harrison.
- Screwdriver - Photo by Gerhard G. from Pixabay.
- Long Island Iced Tea - Photo by Robert Krajewski from Pixabay.
- Negroni - Photo by Isabella Mendes from Pexels.
- Godfather - Photo by Marta Dzedyshko from Pexels.
- Mojito - Photo by StockSnap from Pixabay.
- Sex on the beach - Photo by Geraud pfeiffer from Pexels.
- Bellini - Photo by Sabel Blanco from Pexels.
- Aperol - Photo by Kristina Paukshtite from Pexels.
- Shirley Temple - Photo by Tim Douglas from Pexels.
- Fuzzy Navel - Image by Sergej Cankov from Pixabay
- Whiskey Sour - Photo by Geraud pfeiffer from Pexels.
- London Lemonade - Photo by Geraud pfeiffer from Pexels.
- Blue Lagoon - Photo by Nerfee Mirandilla from Pexels.
- Favicon - Image by Clker-Free-Vector-Images from Pixabay.
- Any images added by users can't be credited here, unfortunately, although I send my warmest thanks to the sites the users have found the images on.
- Manni Silva for the Mobile and Tablet Safari screenshots in the TESTING.md file.
- My mentor, Antonio Rodriguez, at Code Institute, for continuous helpful feedback and support.
- Eve Crabb, for her support through my learning, for being a sounding board for bug fixes, and for being the best boss ever.
- The team at Code Institute, for teaching me the necessary skills to create this site.
- Manni Silva for taking screenshots for me on iOS, and for being my coding buddy.
- Conor Nye for his continuous support throughout my coding journey.