This project is an E-Commerce site for selling artisanal woodworking projects, such as chairs, tables, and bowls, as part of my Level 5 Diploma in Web Application Development.
This project is for educational purposes only. Do not attempt to place an order with real payment details. No purchases will be fulfilled.
- The Woodshed
For this project I have made use of a simple Kanban Board, which is now available as a feature of Github Projects - You can see it here: https://github.com/users/mikesealey/projects/3
I also implemented a project board for other taks not specifically related to user stories, but helpful for a to-do list to help keep myself focussed on the task. https://github.com/users/mikesealey/projects/4
- I want to easily log in/out So that to keep my details and orders safe at minimal inconvenience.
- I want view more specific details about a product So that I can make an informed decision about price, material, finish, etc
- I want to see a gallery/carousel of images depicting a particular product So that I can see physical details not covered by basic stats.
- I want to be able to view a list of products So that I can select some products to purchase.
- I want to be able to see products in a specific category So that I can more easily find products to fit my requirements
- I want to be able to add products to my wishlist So that I can save for later.
- I want to be able to add products from wishlist to basket, So that I can keep track of products that I like and buy them
- I want to see my basket total So that I can avoid going over-budget.
- I want to be able to recover my account/password So that I don't lose my account/order history/wishlist items.
- I want To easily register for an account So that I can save account/address details or past orders
- I want recive an email confirming successful sign-up So that I know other email confirmations wont go directly to junk.
The Woodshed welcomes the user in with a cosy, rustic vibe, to match the artisanal nature of the products sold.
Users can search through the entire catalogue of products with the "All Products" option in the navigation bar, or narrow in on a specific product type such as "Chairs".
Users can also search for specific teams in the search field at the top of the page, which will seach against the product name, the product material, and the product's finish. For example, searching "Live Edge" will show a Live Edge Coffee Table, and a Live Edge Incense Burner. Searching "Oak" will return 4 products made from oak, but this can be refined further by selecting to search the Trinkets category with the term "Oak".
Users can see a brief overview of the product in the Product Card, or click it's title to navigate to the Product Details see more details such as quantity in stock, material, finish, and a description of the item. In both the Product Card and the Product Details, the user will be prevented from adding the item to their basket if it is out of stock.
When a user adds an item to their basket, the stock is decremented accordingly, preventing two users from both adding the last remaining item to their basket and engaging in a race-to-checkout. However, this could lead to products being tied up and sales being missed if a user abandons items in their basket. To combat this, items left in a basket without checking out will be bumped back into the user's wishlist after a set time, and the stock level is replenished.
At the basket, the user will be able to see how much their basket total is, and if they meet the threshold to recieve free shipping (and prompted to add more items to qualify for free shipping). If a user realises they are close to the threshold, they may head back into the Products List to find a small item to increase the basket total, which will increase sales.
At the checkout, users can select from addresses saved against their profile, speeding up the checkout process. They can also copy the billing address to the shipping address with a single button, which will help make the process quicker and easier for the customer. When completing the purchase, a confirmation email is sent to the user.
The user can also see their past orders in My Profile, which gives a black & white, document/invoice style order summary including the subtotal (ex shipping) and total. My Profile will also let the user manage their saved addresses. Saved addresses can be used in the Checkout to speed up transactions.
My Profile also includes the Privacy Policy, which exists as a markdown .md file in the project repository. Should the owner or management of The Woodshed ever need to change or update this, they simply need to update or replace the privacy_policy.md file - this is then read with jQuery and the Marked library to convert to HTML elements and display on the page. The user can also download the file for safe keeping or closer inspection. https://marked.js.org/
Early on in the development of The Woodshed, I knew that I would like to make reusable components as much as possible. The Product Card shown in the wireframe is itself an HTML template.
This means that when passing an array of products, I can simply loop over them and pass in a this-product to a Product Card as follows
{% for product in products %}
<div class="col-12 col-sm-6 col-lg-4 px-5 pb-4">
{% include "components/product_card.html" with product=product %}
</div>
{% endfor %}This means that I can work on one product card and have the changes reflect everywhere the products are listed (Basket, Wishlist, all products, filtered products).
Later in the development of this project, through a discussion with my mentor, I found that I had to tackle some interesting design-decisions around how stock is handled, particularly at the batch/one-off end of the e-commerce scale.
The items being sold are frequently one of one, or one of only a handful of items made and listed for sale. Naturally, there are protections against adding an out-of-stock item to basket, but it's possible that two people could add the same one-of-a-kind item to their basket before either has checked out. They could then both proceed to checkout, and one customer could end up sorely disapointed.
I explored a few ways of handling this in order to try to strike a balance between making the sale and not giving a customer a bad experience. In the end, I opted to decrement the stock level when a user adds the item to their basket, but have an expiry on the item so that abandonned baskets don't tie up the stock.
When an item is added to the basket, a timestamp is also saved. In settings.py, the variable BASKET_RESERVATION_HOURS = 3 is used in conjunction with added_to_basket_at from basket/models.py to determine at what point an item has been abandonned in the basket. Heroku Scheduler will then run a job created in /basket/management/commands/expire_basket_items.py which replenishes the stock, removes it from the user's basket and places it into the user's wishlist instead.
A lot of the initial understanding of management commands came from this Geeks4Geeks tutorial https://www.geeksforgeeks.org/python/custom-django-management-commands/
I used Balsamiq Wireframes to mock up a plan for how I would like The Woodshed to look.
When designing a database structure, the focal point of the database for any E-Commerce site is always going to be the Product itself. This database is no different, with the Products table being a cornerstone of the project. Wishlist and Basket both behave very similarly - a user can add an item to their Wishlist and their Basket, which actually creates a row in the table of thier user-id and the product-id through a junction-table of wishlist-items and basket-items, which can be filtered by user-id.
One addition to the diagram that has been implemented is the timestamp added to the basket to recover items (and stock levels) from any abandonned baskets. When a customer adds an item to their basket, the stock level is decreased, but using the timestamp we can determine when a basket has been abandonned and move the items to the wishlist instead, freeing up the stock.
ERD can be found here: Download The Woodshed entity relationship diagram PDF
-
Django management commands tutorial here: https://www.geeksforgeeks.org/python/custom-django-management-commands/ (Used for scheduling expir)
-
Multi-field search with Q https://www.freecodecamp.org/news/what-is-q-in-django-and-why-its-super-useful Lets me use the seach bar to search name and/or material and/or finish
-
NetNinja Django Testing tutorial - https://www.youtube.com/watch?v=OfiCALrGE14&list=PL4cUxeGkcC9ic9O6xDW2d1qMp3rMOb0Nu
All of the product descriptions were generated by giving a simple prompt to ChatGPT like:
"Please help write a product description of a coffee table I made. I want a couple of paragraphs, no bullet points, just a nice description. Perhaps 250 words. A live-edge coffee table, planed from a cross section of a sycamore trunk at the point of bifurcation"
Which returned:
This handcrafted live-edge coffee table is a striking fusion of natural beauty and artisan craftsmanship. Created from a cross section of a sycamore trunk, it captures a rare moment in the tree’s growth—where the trunk naturally bifurcates into two sweeping branches. This unique point of division gives the tabletop a sculptural, organic silhouette, with flowing contours and grain patterns that ripple outward like the rings of time itself. The surface has been carefully planed to reveal the sycamore’s warm, pale hues, delicately accented by darker striations and intricate figuring that make every inch of the wood uniquely expressive. Retaining its natural live edge, the table preserves the raw elegance of the tree while offering a smooth, refined surface for everyday use. The craftsmanship honors the inherent character of the material rather than masking it, allowing nature’s artistry to take center stage. With its uncommon shape and rich texture, this coffee table becomes more than just a piece of furniture—it’s a conversation piece, a functional sculpture that carries the story of the tree it came from. Perfect for modern, rustic, or eclectic interiors, it adds warmth and authenticity to any living space, blending the timeless appeal of natural wood with a bold, contemporary design.
See the conversation here: https://chatgpt.com/share/68966b84-1954-8004-a7bd-d6e05ec7c20d
Using Chrome Developer tools, I have tested the main flows of my project, with Homepage and Profile performing well.
Though with multiple images to load on the Products list, and each bringing with it cookies from Cloudinary where the images are stored, I get flagged up for this in "Best Practices". Perhaps in the future I could look at building my own cloud hosting service, but for now that might be beyond the scope of this project.
Just to compare to another e-commerce site:
For testing accessability I'm using Web Accessibility Evaluation Tool. This catches 2 cases of "Low Contrast", though on closer inspection the contrast is checked before the page has properly loaded.
Side-by-side it is clear that Wave is catching an issue that is resolved when the page loads further. These two issues persist accross all pages, though they are the only two serious issues.

https://wave.webaim.org/report#/https://ci-pp4-the-woodshed-69e11d9176ff.herokuapp.com/
Running the tests on a Products List (All Products, Bowls, Chairs, Tables, Trinkets, Vases, Wishlist, Basket), there are the same two issues in the navigation, and a couple of alerts on heading level and 'reduntant alternative text' for users with screen readers. In this case, I feel like it's okay for that to be present and redundant, as the title itself is descriptive enough.
WAVE seems to have an issue with the alt-text provided for each image, but it's not a serious issue.

In order to validate the HTML of this project I have used https://validator.w3.org/nu. When validating, I have fed the URL of the deployed app. I had previously tried to validate individual files, but this causes errors when HTML components such as the Product Card does not have Head or Body tags, because it is only meant to be used as part of a larger page. Taking this approach actually makes validating the HTML easier, as I can validate multiple components at once.
-
Homepage - https://validator.w3.org/nu/?showsource=yes&doc=https%3A%2F%2Fci-pp4-the-woodshed-69e11d9176ff.herokuapp.com%2F - No errors or warnings to show

-
All products list - https://validator.w3.org/nu/?showsource=yes&doc=https%3A%2F%2Fci-pp4-the-woodshed-69e11d9176ff.herokuapp.com%2Fproducts%2Fproducts%2F - No errors or warnings to show

-
Bowls - https://ci-pp4-the-woodshed-69e11d9176ff.herokuapp.com/products/products/?category=bowl - No errors or warnings to show

-
Chairs - https://ci-pp4-the-woodshed-69e11d9176ff.herokuapp.com/products/products/?category=chair - No errors or warnings to show

-
Tables - https://ci-pp4-the-woodshed-69e11d9176ff.herokuapp.com/products/products/?category=table - No errors or warnings to show

-
Trinkets - https://ci-pp4-the-woodshed-69e11d9176ff.herokuapp.com/products/products/?category=trinket - No errors or warnings to show

-
Vases - https://ci-pp4-the-woodshed-69e11d9176ff.herokuapp.com/products/products/?category=vase - No errors or warnings to show

-
Registration - https://validator.w3.org/nu/?showsource=yes&doc=https%3A%2F%2Fci-pp4-the-woodshed-69e11d9176ff.herokuapp.com%2Faccounts%2Fsignup%2F - Some false-positives on the baked-in Django AllAuth templates, but this isn't code I've written, it's simply part of Django itself.

-
Log In - https://validator.w3.org/nu/?showsource=yes&doc=https%3A%2F%2Fci-pp4-the-woodshed-69e11d9176ff.herokuapp.com%2Faccounts%2Flogin%2F - No errors or warnings to show

Unfortunately, this validation tool can't log in for me, so it's not possible to pass a URL for logged-in features. This must be tested by copy-pasting the page-source into the text-input.
- My Profile - No errors or warnings to show
- My Wishlist - No errors or warnings to show
- My Basket - No errors or warnings to show
I fed all JavaScript in this project into https://jshint.com/ to confirm that it is valid and clean JS. It mostly complained about missing (uneccesary) semicolons, but occasionally got tripped up by jQuery.
Validating addresses.js
Validating basket.js
Validating filters.js
Validating gallery.js
Validating navigation.js
Validating privacy_policy.js
Validating stripe.js
Validating toast.js
A Code Institue reccomended using Flake8 - a VSCode extension for python that helps with code validation and linting. I worked through the project one app at a time to bring all code up to standard and remove unused imports.
Steps:
- Navigate to the homepage
- Click "Register"
- Provide email, username, and password (twice)
- Click "Sign up"
Status: Pass - Toast notification lets the user know
I want recive an email confirming successful sign-up So that I know other email confirmations wont go directly to junk.
Steps:
- Upon successfully completing the registration, find the "Confirm your account" email that gets set to the user's email address
- Open the URL in the email
- Account is verified - user is redirected to Log In page
Status - Pass - user can now log in
Steps:
- With a verified account, visit the site and click "Log In"
- While logged in, visit My Profile
- Log out
Status - Pass
I want to be able to recover my account/password So that I don't lose my account/order history/wishlist items.
Steps:
- Visit the login page
- Click "Forgot Password?"
- Provide the email of a registered account
- A password reset email will be sent to that account
- Click the link provided to reset the password
- Log in with the new credentials
Status - pass
Steps:
- Visit The Woodshed
- View a Products List (All Products, Bowls, Chairs, Tables, Trinkets, Vases)
I want view more specific details about a product So that I can make an informed decision about price, material, finish, etc
Steps:
- Visit The Woodshed and view a Products List (All Products, Bowls, Chairs, Tables, Trinkets, Vases)
- Click "See More Details" on a Product Card
Status - Pass - More product details including price, material, finish, and a description are available
I want to see a gallery/carousel of images depicting a particular product So that I can see physical details not covered by basic stats.
Steps:
- Visit The Woodshed and view a Products List (All Products, Bowls, Chairs, Tables, Trinkets, Vases)
- Click "See More Details" on a Product Card
- Click the images to the side of the main image to scroll the gallery in that direction or
- Click the main image to scroll the gallery to the right
Status - Pass
I want to be able to see products in a specific category So that I can more easily find products to fit my requirements
Steps:
- Visit The Woodshed
- Click one of the following options from the navigation bar - Bowls, Chairs, Tables, Trinkets, Vases
Status - Pass
Steps:
- Visit the Woodshed as a logged in user and view a Products List (All Products, Bowls, Chairs, Tables, Trinkets, Vases)
- Decide on a product to add to the wishlist and click "Add to Wishlist" Or
- Click "See more details"
- Click "Add to Wishlist"
I want to be able to add products from wishlist to basket, So that I can keep track of products that I like and buy them
Steps:
- Visit the Woodshed as a logged in user and view a Products List (All Products, Bowls, Chairs, Tables, Trinkets, Vases)
- Decide on a product to add to the basket and click "Add to Basket" Or
- Click "See more details"
- Click "Add to Basket" Or
- Visit the Woodshed as a logged in user with an item already saved in the wishlist
- Navigate to Wishlist
- Click "add to basket" on a product
Status - Pass
- Visit the Woodshed as a logged in user and view a Products List (All Products, Bowls, Chairs, Tables, Trinkets, Vases)
- Decide on a product to add to the basket and click "Add to Basket"
- Navigate to the Basket and scroll to the end of the list of products in the basket to the basket summary
Status - Pass
Steps:
- As a logged-in user, add items to the basket (notice their stock levels)
- Do not check out. Return 3 hours and 10 minutes later (3 hour timeout, plus job runs every 10 minutes)
- Item will have been bumped to wishlist, stock level will have been replenished.
Status - pass
Stripe provides sample/dummy card numbers for testing. Using the provided card numbers, along with any future date for expiry, and any 3-digits for CVC will yield the specified result as a happy-path. https://docs.stripe.com/testing - a pass-status is awarded when the expected progression (successful payment) or error message
| Type | Card Number | Displays error to user | Status |
|---|---|---|---|
| Happy Path - Proceed | 4242424242424242 | Proceed to /basket/success. | Pass |
| Generic Decline | 4000000000000002 | Your card has been declined. | Pass |
| Insufficient funds decline | 4000000000009995 | Your card has been declined. | Pass |
| Expired card decline | 4000000000000069 | Your card has expired. Try a different card. | Pass |
| Processing error decline | 4000000000000119 | An error occurred while processing your card. Try again. | Pass |
I've written some basic unit tests in Django for both Wishlist and Basket. The structure of them is such that the tests are effectively the same across both models. In each case, the test runs to check if the user is logged in (only logged in users can have wishlists or baskets), adds a product to the wishlist/basket, adds multiple producst to the wishlist/basket, and then removes a product from the wishlist/basket.
These tests can be run locally with python3 manage.py test --verbosity=3
Through warning thrown during Code Validation it is clear that this project is unlikely to be fully compatable with any pre-ES6 browsers, though as ES6 was introduced a decade ago, I'm happy for this to be where the line is drawn. I've tested thoroughly on Chrome, Firefox, and Safari. The only issue I encountered was a simple styling fix, pushed in commit 64e976a0a1cb24d75bb82b6cebd458156b93b0f6 caused by slightly different handling of HTML elements causing a styling misalignment.
You may wish to clone this repo to work on it yourself.
-
At the top of the page above the file-list, click the "Code" button
-
Copy the URL
https://github.com/mikesealey/ci_pp4.git -
On the command line, navigate to where you want to clone this repository
-
run the following command
git clone https://github.com/mikesealey/ci_pp4.git -
cdinto the folder, and then runnpm ito install all dependancies
You may also wish to fork this repo to develop it further for your own needs
- In the top bar, click "Fork"
- Choose the github account or organisation where you want to fork the repository
- Github will now create a copy (or fork) of the repository in your account.
Background Image: https://www.pexels.com/photo/focused-artisan-making-hole-on-plank-with-hammer-and-chisel-5974283/
Icon Image: https://iconduck.com/icons/157332/hammer
Initially, I had hoped to find more images on sites like pexls, but it proved harder than expected to find images where the woodwork was the subject of the photo, and there were multiple angles of the woodwork. My mentor suggested using AI to generate the images, though this became problematic when, frequently, the image generator would never quite be able to show the same item each time. There would frequently be differences in the product between each image, so much that it looked like a slightly different product.
This meant that I had to use my own, real-life photos of my own, real-life creations, despite being a very inexperienced woodworker, and an even less experienced photographer.
| Product | Image(s) Source |
|---|---|
| Small Pine Vase | Mike Sealey |
| Live Edge Coffee Table | Mike Sealey |
| Large Wooden Vase | ChatGPT |
| White Oak Pestle & Mortar | Mike Sealey |
| Live edge Incense Burner | Mike Sealey |
| 6" Bowl | Mike Sealey |
| Openback Oak Dining Chair | ChatGPT |
| Spindleback Pine Chair | ChatGPT |
In the future it would be very useful to build some back-office spaces like admin panels that could collate information on sales-over-time, or apply discounts or special-offers on products. It would also be useful to make any changes or updates to the Privacy Policy - some of this work has been done already with the way the Privacy Policy is stored and fetched, simply uploading a new file
It would a great addition to add a page or feature that allows users to contact the site owner about comissioning a custom piece. The most basic version of this could simply be a form that the user fills out. This could go further to have some discussion and perhaps make a final offer that both parties review/accept before The Woodshop undertake creating it.
Adding customer reviews attatched to products would be a great way to interest other users, as well as get some feedback on the products sold
Effectively another arm of the marketing department, a blog could be used to boast about products that are still being made, perhaps with some teaser photos.





















