The Delphin Lifesaving Club e-shop was designed, built and deployed by Rebecca Tracey-Timoney as the fourth and final Milestone Project to be completed for the duel Software Development Diploma from The Code Institute and UCD. The purpose of this online shop is to provide a virtual shopping environment for club members (future and current) to browse and purchase Delphin products, including classes and club apparel. The website provides a smooth and uncomplicated online shopping experience for users, with its simplistic and intuitive design.
- UX
- Information Architecture
- Features
- Issues and Bugs
- Technologies Used
- Testing β Testing.md
- Deployment
- Credits
- Acknowledgements
The User is looking to:
- Securely purchase available Club course credits
- Securely purchase club apparel
- Contact the Delphin Team
- Learn about the club (for prospective members)
- Navigate through the online store with ease
The Developer is looking to:
- Provide and professional and trustworthy webshop, helping users to meet their goals.
- Reach a wider audience of prospective members through online intergration, making it easier to get involved.
- Provide a seamless process, allowing swimmers to pre-purchase lesson credits, prior to registration, avoiding unnecessary waiting times on location.
- Showcase the variety of lessons that Delphin provides.
- Showcase the Club's apparel collection
- Demonstrate their proficiency in a variety of software development skills, using newly learned languages and libraries as well as a document database system.
- Deploy a project they are proud of, and excited to have, on their portfolio.
As a user, I want to:
- View all products, to purchase my desired items.
- Filter through categories, to only see relevant products.
- Use a search query, to find a specific product or product type.
- Add items to my shopping bag, to begin the order process.
- Receive visual feedback that my item has been added to the bag, to confirm my selection.
- Select a course on a specific day, to suit my needs.
- Select apparel in a specific size, to suit my needs.
- View contact information for the club, to communicate with team.
- Connect with the club on one of their social apps, to communicate with the team.
- Get a breakdown of classes offered, to find one that suits my needs.
- Navigate to a page that could help me with my enquiry, to answer my question.
As a new visitor, I want to:
- Create an account, to unlock registered user features.
As a returning visitor, I want to:
- Easily log into my existing account, to unlock registered user features.
- View my previous orders, to keep a record of my transaction.
- Save default information, to save time for future orders.
- Edit default information, to update any necessary fields.
- View shopping bag to get an overview of products I wish to order.
- Remove products from my bag, to suit my needs.
- Update a product's quantity, to suit my needs.
- Get real-time feedback on available stock, to make purchase decisions.
- Proceed to a secure checkout, to make a purchase.
- Have clear visual feedback of the order process, to understand all steps of the process.
- Be able to edit my bag at all times, to allow change of mind.
- Receive a summary of my order via email to confirm that my transaction has been process.
In order to design and create the application, the developer distinguished the required functionality of the site and how it would answer the user stories, as described above, using the Five Development Planes:
1. Strategy
Broken into three categories, the website will focus on the following target audiences:
-
Roles:
- New Swimmers
- Current Swimmers
- Parents/Guardians of Swimmers
-
Demographic:
- Aged 16+ (to make a purchase)
- Dublin (or neighbouring counties) resident
- Active/Sporty
- Swimming/Lifesaving Orientated
-
Psychographics:
- Personality & Attitudes:
- Sporty
- Outgoing
- Team Orientated
- Values:
- Friendship
- Community
- Activeness
- Lifestyles:
- Sporty
- Team Player
- Outgoing
- Personality & Attitudes:
The website needs to enable the user to:
- Create an account or log in to an existing one
- Purchase a course
- Purchase Club Apparel
- View past purchases
- Edit/Save Delivery Information
- Find out more about the club:
- Academy Programme
- General Questions
- Get in contact:
- Email(mailto)
- Phone
- Contact Form
The website needs to enable the client to:
- Display all available course
- Provide a virtual shop for Club Apparel
- Provide club information:
- Academy Programme
- General Questions
- Provide a point of contact:
- Email(mailto)
- Phone
- Contact Form
With these goals in mind, a strategy table was created to determine the trade-off between importance and viability with the following results:
2. Scope
A scope was defined to identify what needed to be done in order to align features with the strategy previously defined. This was broken into two categories:
-
Content Requirements
- The user will be looking for:
- Product Information:
- Name
- Description
- Image
- Size (where applicable)
- Day (where applicable)
- Contact Information:
- Email(mailto)
- Phone Number
- Contact Form
- Academy Programme
- General Questions
- Create/Login to Profile
- Save default information
- View Previous Orders
- Thematic Imagery and Typography
- Club logo and colours
- Typography matching club aesthetic
- Product Information:
- The user will be looking for:
-
Functionality Requirements
- The user will be able to:
- Search Products
- Courses:
- By Age
- Apparel
- By Type
- Courses:
- Make Transactions
- Sign Up / Login to profile
- Edit Profile information
- View Previous orders
- View shopping bag
- Interact with shopping bag
- Add to bag
- Review bag contents
- Update bag contents (increase quantity)
- Remove from bag
- Read the Academy Programme
- Find answers to frequent questions
- Search Products
- The user will be able to:
3. Structure
The website's navigation was organized in order to ensure that users could navigate through the site with ease and efficiency, with the following results:
4. Skeleton
Wireframe mockups were created in a Figma Workspace with providing a positive user experience in mind:
Post Mock-Up Design Changes While the developer relied heavily on these Wireframes in order to maintain the desired design, there are several differences between the Mockups and the final product:
- Primarily, the majority of the page content was changed to better suit the club and the information it wanted to display.
- The addtion of an
Order Review
page was needed in order to provide a better user checkout story. - The
All Product
page filters were necessarily divided intoApparel
andCourses
page. The initial design was to have one page for all products but this became too messy for UI reasons. - The
Profile
page was split into two pages for a better user experience. Dividing the information update section and the order history provided a much needed and less 'busier' experience. - As stated in the TESTING file, earlier testing stages altered the category buttons on the
Home
,Shopping Bag
andSearch
pages, providing a wider variety of categories for the user to better refine their search.
5. Surface
-
Colour Scheme
-
The chosen colour scheme was specifically selected to match the club colours. They are representitive of Lifesaving equipment used, such as flags and lifebuoys.
-
The red colour is specific to Lifeguarding and is used to highlight anything Lifeguarding related.
-
The Blus is an accent colour used in the club logo and beyond for colour contrast.
-
-
Typography
-
The primary font chosen is Lato. A humanist sans-serif typeface, Lato is semi-rounded with a structured, but friendly warmth.
-
The Secondary font (accent font) chosen is Londrina Solid. A newfolk typeface, with a rough, handwritten feel, for versatile screen display.
-
The secondary font is an updated version of the font used in the club's typography for logo and official letters. The font is paired well with the primary font, in order provide a minimally contrasting font combination serving as a practical and professional typeface with a hint of playfulness. This highlights the club's character for being professional, yet friendly, setting the tone for new and unfamiliar vistiors.
-
-
Imagery
-
The imagery used was created by the developer using the application Procreate in order to create a consistency of the elements while maintaining the look and feel of the application.
-
The product imagery used digital renderings and, where permitted, the developer edited them to display the Club imagery and colour scheme. A complete list of credits can be found in a separate file here.
-
- During development, a single-database was setup using SQLite as this is included and did not require any further installation to support.
- Upon deployment, Heroku Postgres was used, as this is an add-on service provided by Heroku.
model diagram created using DrawSQL
A full list of the data models used on the site are below:
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
User | user | OneToOneField(User) | on_delete=models.CASCADE |
Full Name | default_full_name | Charfield | max_length=50, null=True, blank=True |
Phone Number | default_phone_number | Charfield | max_length=20, null=True, blank=True |
Street Address 1 | default_street_address1 | Charfield | max_length=80, null=True, blank=True |
Street Address 2 | default_street_address2 | Charfield | max_length=80, null=True, blank=True |
Town Or City | default_town_or_city | Charfield | max_length=40, null=True, blank=True |
County | default_county | Charfield | max_length=80, null=True, blank=True |
Postcode | default_postcode | Charfield | max_length=20, null=True, blank=True |
Country | default_country | CountryField | blank_label='Country', null=True, blank=True |
A custom User model was created using Django AllAuth to ensure secure functionalty of user registration and authentication.
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
User | user | OneToOneField(User) | on_delete=models.CASCADE |
Full Name | default_full_name | Charfield | max_length=50, null=True, blank=True |
Phone Number | default_phone_number | Charfield | max_length=20, null=True, blank=True |
Street Address 1 | default_street_address1 | Charfield | max_length=80, null=True, blank=True |
Street Address 2 | default_street_address2 | Charfield | max_length=80, null=True, blank=True |
Town Or City | default_town_or_city | Charfield | max_length=40, null=True, blank=True |
County | default_county | Charfield | max_length=80, null=True, blank=True |
Postcode | default_postcode | Charfield | max_length=20, null=True, blank=True |
Country | default_country | CountryField | blank_label='Country', null=True, blank=True |
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
Programmable Name | name | charfield | max_length=254 |
Friendly Name | friendly_name | Charfield | max_length=254 |
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
Programmable Name | product_option | charfield | max_length=255 |
Friendly Name | option_name | Charfield | max_length=255, default='Monday' |
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
Product Name | name | charfield | max_length=254 |
Description | description | Textfield | default='test' |
Price | price | Decimalfield | max_digits=6, decimal_places=2 |
Category | category | ForeignKey(Category) | null=True, blank=True, on_delete=models.SET_NULL |
Product Select | product_select | ManyToManyField(ProductOption) | through='ProductSelect', related_name='product_options', blank=True |
Course Check | is_course | Boolean | default=False, null=True, blank=True |
Apparel Check | is_apparel | Boolean | default=False, null=True, blank=True |
Course Information | course_info | TestField | default='Test', null=True, blank=True |
Course Age Range | course_age | CharField | max_length=255, default='10 years' |
Image | image | Imagefield | null=True, blank=True |
(
course_info
andcourse_age
were used to create unique modals for each course page, providing users with further information on each course, avoidin page over-crowding or unecessary linking to other pages.)
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
Selected Product Option | product_select | ForeignKey(ProductOption) | on_delete=models.CASCADE |
Selected Product | product | ForeignKey(Product) | on_delete=models.CASCADE, related_name='product_options' |
Stock Count | stock_count | IntegerField | default=30 |
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
Order Number | order_number | Charfield | max_length=32, null=False, editable=False |
User | user_profile | ForeignKey(User) | null=True, blank=True, on_delete=models.SET_NULL, r elated='orders' |
Full Name | full_name | Charfield | max_length=50, null=False, blank=False |
Email Address | EmailField | max_length=254, null=False, blank=False |
|
Phone Number | phone_number | Charfield | max_length=20, null=False, blank=False |
Street Address 1 | street_address1 | Charfield | max_length=80, null=False, blank=False |
Street Address 2 | street_address2 | Charfield | max_length=80, null=True, blank=True |
Town Or City | town_or_city | Charfield | max_length=40, null=False, blank=False |
County | county | Charfield | max_length=80, null=False, blank=False |
Postcode | postcode | Charfield | max_length=20, null=False, blank=False |
Country | country | CountryField | blank_label='Country*', null=False, blank=False |
Order Date | date | DateField | auto_now_add=True |
Delivery Cost | delivery_cost | DecimalField | max_digits=6, decimal_places=2, null=False, default=0 |
Order Total | order_total | DecimalField | max_digits=10, decimal_places=2, null=False, default=0 |
Grand Total | grand_total | DecimalField | max_digits=10, decimal_places=2, null=False, default=0 |
Original Bag | original_bag | TextField | null=False, blank=False, default='' |
Stripe Payment Intent ID | stripe_pid | CharField | max_length=254, null=False, blank=False, default='' |
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
Order | order | ForeignKey(Order) | null=False, blank=False, on_delete=models.CASCADE, related_name='lineitems' |
Product | product | ForeignKey(Product) | null=False, blank=False, on_delete=models.CASCADE |
Selected Product Option | product_select | charfield | max_length=10, null=False, blank=False |
Quantity | quantity | IntegerField | null=False, blank=False, default=0 |
Lineitem Total | lineitem_total | DecimalField | max_digits=6, decimal_places=2, null=False, blank=False, editable=False |
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
Programmable Name | name | charfield | max_length=254 |
Friendly Name | friendly_name | Charfield | max_length=254 |
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
Programme Name | name | charfield | max_length=254 |
Description | description | Textfield | |
Age Range | age | charfield | max_length=254 |
Swim Category | swim_category | ForeignKey(SwimCategory) | null=True, blank=True, on_delete=models.SET_NULL |
Name | Database Key | Field Type | Type Validation |
---|---|---|---|
Question | question | charfield | max_length=254 |
Answer | answer | TextField |
Each page of the websire features a consistently responsive and intuitive naviational system:
- A navbar is conventionally placed on the top of each page, excluding the checkout pages, containing easily identifiable and accessible navigational links with the club logo that redirects users to the home page.
- The navbar utilises dropdown menus to provide a clean design, with each link appropriately categorised.
- An
active
class provides feedback to users on their current location, through distinguishable differences on active links and their respective category title. - If the user is in session, an additional dropdown menu provides users with account functionality.
- On mobile and tablet devices, the navigation menu collapses into a conventionally positioned hamburger menu, with all navigation links inside.
- The shopping bag is centrally placed on mobile and tablet devices for quick and convenient access to the user's bag.
- Toasts are displayed under the navbar, providing users with real-time feedback on their actions.
- The footer contains a convenient quick-links area for users to navigate to their preferred location with east
- The links are divided into appropriate categories, allowing users to see the variety of pages the website has. Notably, they also provide a means to categories the Apparel and Courses available, allowing users to find their desired products quicker.
- These links are displayed in three columns on larger devices, two columns and medium and single columns on small.
- The footer also provides the Club social links and copyright information.
- Link to Club Facebook page
- Mailto link to club emails
- Link to Clubs Whatsapp correspondence number
-
A banner title is provided on each page to help users to quickly determine their location
-
Jinja was used to extend the base.html page, allowing for the utmost consistency and preservation of functionality across all pages. The extended block elements created the same basic layout for each page:
<nav> <!-- Navigational content --> </nav> {% block messages %} <!-- Appropriate toast messages --> {% endblock %} {% block content %} <!-- Content unique to each page --> {% endblock %} <footer> <!-- Footer content --> </footer>
Feature | Description |
---|---|
Hero Image | The hero image depicts a pool, clearly defining the website's affiliation and purpose for users (for club interaction) |
Club Statement and Logo | The club statement and logo are overlayed on the hero image, introducing the club. |
Duel Links | Links are displayed to provide users quick-link access to product categories. Both images and buttons redirect users to their respective categories. |
Feature | Description |
---|---|
Breadcrumbs | Page breadcrumbs allow users to backtrack to the Home page |
Category Filters | Category filters are in place to allow users to refine their search. On mobile and tablet devices this is in a dropdown menu, on larger devices buttons are used. |
Product Links | All products are displayed on this page, allowing users to select their preference. The product cards provide users with a link from the product image and name to the product details page. |
Pagination | Pagination displays 12 products per page, avoiding unneccary scrolling. |
Feature | Description |
---|---|
Breadcrumbs | Allows users to return to the All Products or Home page. |
Sub-Category Filters | Allows users to refine their search to a particular product type (e.g. Apparel type or Course age range) |
Product Links | The product cards provide users with a link from the product image and name to the product details page. |
Course Information Modal | Specific to the Courses page, an information modal is displayed on click (represented by an i icon), providing information on purchasing a course. |
Feature | Description |
---|---|
Input field | Allowing users to enter their search query, with an appended search button. |
Category Buttons | Category filters provide users with a convenient quick-link to the categories, in lieu of searching for something. Both images and buttons redirect users to their respective categories. |
Category Filters | Category filters are in place to allow users to refine their search, directing to their prefered product type page. |
Dynamic feedback | When a user enters a term, there is dynamic text that displays the number of results returned and their search term. If there are no results, conditional text explains this to the user. |
Reset Button | Clicking the Search title will reset the search bar. |
Feature | Description |
---|---|
Breadcrumbs | Allows users to return the category page for that product (Apparel or Courses) or Home page. |
Dropdown Select Menu | Allows users to refine their product selection (sizes of Apparel and times for Courses). Information is provided for stock availabilty for all product selections. |
Quantity | An input with appending and prepending buttons allows users to alter their quantity selection. This feature is dynamically designed to become inactive if the quantity count is 1 or at the product's stock availabilty. |
Back Button | Returns users back to the shop page, for convenience. |
Add to Bag button | Allows users to add their selection to their shopping bag |
Feature | Description |
---|---|
Input fields | Allows users to enter their username, or email, and password |
Radio button | Allows users to chose to remember their login details for convenience (allauth functionality) |
Reset Password link | Redirecting users to reset their password (allauth functionality) |
Register button | call-to-action button directing users to signup page |
Feature | Description |
---|---|
Input fields | Allows users to enter their information to register an account |
Signup button | Allows users to create their account (allauth functionality) |
Feature | Description |
---|---|
Call to action links | Links directing users to external pages for clarity purposes. Opens page on new tab. |
Feature | Description |
---|---|
Accordion | An accordion is used for each individual class, with the class name and age range displayed. Clicking the level opens the card and displays information pertaining to that level. |
Hide/Show Buttons | A Hide All and Show All button are used to provide users with the capacity to open all cards or close all cards, for convienience. |
Call to action links | Links directing users to external pages open on new tab, while internal pages are redirect users. |
Feature | Description |
---|---|
Accordion | An accordion is used for each question, with the question displayed. Clicking the question opens the card and displays the answer. |
Hide/Show Buttons | A Hide All and Show All button are used to provide users with the capacity to open all cards or close all cards, for convienience. |
Call to action links | Internal links redirecting users to useful pages within the site. |
Feature | Description |
---|---|
Embeded Google Maps | A Google maps embed was used to provide users with an interactive map, with the pin dropped on the pool location. (Map reference) |
Phone Link | An anchor link was used for the Membership Officers phone number to provide a convenient quick-link. |
Mailto action | Three emails are provided using the club email address, with the difference in the subject line to highlight the message for the appropriate officer (for convienience) in order to help the user to send the message quicker. |
Contact Form | Used for users to conveniently send a message to our Marketing team. Validated using jQuery Validation. |
Shopping Bag Page
Feature | Description |
---|---|
Breadcrumbs | Page breadcrumbs allow users to backtrack to the Home page |
Duel Links | Links are displayed when user's shopping bag is empty to provide quick-link access to product categories. Both images and buttons redirect users to their respective categories. |
Keep Shopping button | Link redirects users to the All Products page. Available when at all stages (when bag is both empty and containing items. |
Remove button | Placed at the top right of each product, allowing users to quickly remove the product from the shopping bag. |
Quantity | An input with appending and prepending buttons allows users to alter their quantity selection. This feature is dynamically designed to become inactive if the quantity count is 1 or at the product's stock availabilty. |
Update button | Updates the bag with the users inputed quantity. |
Checkout button | Directs users to the first page of the checkout process. |
Order Review Page
Feature | Description |
---|---|
No navbar or footer | This conventionally removed, to minimalist checkout interface , preventing users from breaking the checkout process. (Source) |
Logo Link | Logo at the top of the page redirects users to the shopping bag for convienience |
Heads-up display | This shows the users the steps in the checkout process, giving them feedback on where they are and where they are going. |
Return buttton | Redirects users back to their shopping bag for editing. |
Checkout button | Directs users to the next page of the checkout process. |
Order Details Page
Feature | Description |
---|---|
No navbar or footer | This conventionally removed, to minimalist checkout interface , preventing users from breaking the checkout process. (Source) |
Heads-up display | This shows the users the steps in the checkout process, giving them feedback on where they are and where they are going. Clicking the first number will return them to to the previous page |
Input fields | Allowing users enter their delivery information |
Checkbox button | Allowing users to chose to save their information to their profile |
Order Summary | Providing clear feedback on bag contents. On large screens, this is displayed on the right side of the screen. On mobile and tablet devices, this is displayed in a collapsable menu |
Edit buttton | Redirects users back to their shopping bag for editing. |
Complete Order button | Completes the checkout process. |
Order Complete Page
Feature | Description |
---|---|
Internal links | Subtle links providing users with quick-link access to FAQs and Contact pages, for convienience. |
Product Buttons | Products ordered are displayed in a table (along with other order information, such as price and quantity) and act as buttons to the product detail page, for convienience. |
User Profile Page
Feature | Description |
---|---|
Call to action Buttons | Displayed on the left side of the screen on larger devices and at the end of the page on mobile and tablet devices. Provide users with actions to take on their account, such as change password or emails, as well as logout and redirection to Past Orders page. |
Input fields | Allows users to add or edit their default information to provide them with a faster checkout experience. |
Update button | Saves new user information with toast used as feedback. |
Past Orders Page
Feature | Description |
---|---|
Call to action Buttons | Displayed on the left side of the screen on larger devices and at the end of the page on mobile and tablet devices. Provide users with actions to take on their account, such as change password or emails, as well as logout and redirection to Profile page. |
Call to action buttons | User's order history is displayed on the right side of the screen on larger devices and at the top (single column) on smaller screens. It provides a snapshop of order information, including date of purchase, number of items, order total and a call-to-action View button allowing them to view the Order Complete page with appropriate conditional text to let them know this is a past order. |
-
SwapShop Page
- As children tend to grow out of their clothing quickly, a service we are looking into providing is a 'SwapShop' allowing swimmers to trade club gear (excluding swimwear) with others, passing it along to to others when they grow out of it.
- The devloper looked into implementing this, researching how to set up a virtual noticeboard, but it proved to require a far greater skillset than their current abilities.
-
QR Codes
- Another feature that was looked into was providing unique QR codes with every class purchase.
- The club is currently looking into implementing this into their registration process, to avoid paperwork methods, and are only in the design stages.
- The project time constraint was the deciding factor as the devloper needed to finish the design of the scanning process and learn how to implement it in order to use it.
-
Swimmer Profiles
- As the club moves away from paperwork methods, integrating into a more technologically minded system (for GDPR reasons), we would want to expand this webshop into providing a 'Swimmer Profile', allowing swimmers to log in to see their progress week-by-week.
- Although looked into, the current skillset discouraged the developer to research this feature any further as they did not think they could do it the justice the feature deserves.
The developer encountered a number of issues during the development of the website, with the noteworthy ones listed below, along with solutions or ideas to implement in the future.
Pagination
In implementing the pagination feature (allowing only 12 products to be displayed on the page at any given time), the developer encountered an issue wherby, when applying pagination on the filtered pages, All Apparel, All Courses and Search, the feature would reset the filters set (e.g. the product categories) from the second page, meaning, that instead of the refined search the user had selected, all products would be displayed. This was not practical and rendered the category filtering system useless, so the devloper had to omit the pagination on these pages. The work around was to provide category filtering buttons at the top of the page, so that the user did not have to scroll through all products, but could instead just display the sub-categories they desired.
Users App
The original design of the User model was called the Profile
model. This was a fatal error on the developers part as it clashed with the integrated profile authorisation from allauth
. As a result, the developer had to revert two commits and re-create the profile app as the new users
app to restore functionality.
Google Maps Embed
The developers original design called for a Google Maps API to be used on the contacts page, giving users the ability to view the pool location with ease and clarity. On researching the methods of implementation, they discovered there were issues in using the API, the primary problem being authorisation when deploying on Heroku. From various sources, it was clear that additional variables were necessary in order to create a functioning map. It was decided, in order to avoid these additional steps, that an embedding of the map would be used instead. Sourced from Google Map Generator.
Quantity Counter
The biggest issue the developer faced was implementing the stock_count
functionality. The feature was in the original designs, and the developer heavily researched how to create it, using ManyToMany
relationships amongst their models. Through plenty of trial and error, and with the incredible patience of their mentor, the developer was able to achieve functionality. An issue arose in the implementation with a clash between the stock_count
and quantity
fields, where the user could select more than the product's current stock and put it in their bag. The developer was able to fix this by adapting a sourced solution from Stack Overflow.
- Git
- Git was used for version control to commit to Git and push to Heroku.
- GitHub
- GitHub was used to store the project repository, after pushing.
- Heroku
- Heroku was used in order to deploy the website.
- Django
- Django was used as the web framework for the application.
- AWS
- The Amazon Web Service s3 Bucket was used to store static and media files in the production database.
- Stripe
- Stripe was used to handle user payments on the webshop.
- Figma
- Figma was used to create the wireframes during the design phase of the project.
- Am I Responsive?
- Am I Responsive was used in order to validate the responsiveness of the design throughout the process, and to generate mockup imagery to be used.
- Procreate
- Procreate was used to create and edit images as well as using the colour picker tool to ensure consistency throughout.
- Font Awesome
- Font Awesome was used in conjunction with Iconify for icons used throughout the website.
- Bootstrap
- Bootstrap was used to implement the responsiveness of the site, using bootstrap classes.
- jQuery
- jQuery was used to simplify the JavaScript code used.
- Google Fonts
- Google fonts was used to import the fonts "Indie Flower" and "Work Sans" into the style.css file. These fonts were used throughout the project.
- jQuery Validation
- jQuery Validation was used to simplify form validation for the Contact Form.
- SweetAlert2
- SweetAlert2 was used to customise the Contact Form success message for UX
- Jinja
- Jinja templating language was used to simplify and display backend data in html.
- SQLite
- SQLite was used as a single-file database during development.
- Heroku Postgres
- Heroku Postgres was used for production database, provided by Heroku.
- Django AllAuth
- AllAuth was the primary method for user authentication, registration and account management.
- Django Crispy Forms
- Crispy Forms was used to style Django Forms.
- Pillow
- Pillow was used to aid in image processing on the database.
- Django Countries
- Django countries was used to ensure correct country codes were used in user forms.
Testing information can be found in a separate testing file
To further develop this project, a clone can be made using the following steps:
Deployment Instructions assume you are working on a MacOS and may differ on other operating systems. Please check documentation specific for your operating system
To run the project on your own IDE, ensure you have the following:
-
An IDE (such as Visual Studio Code or GitPod)
-
Python (this project uses version 3.6)
-
PIP for package installation
-
Git for project version control
-
Stripe Account for payment functionality
A Local Clone of the repository can be made in two ways:
1. Forking the Repository:
By forking the GitHub Repository we make a copy of the original repository on our GitHub account to view and/or make changes without affecting the original repository by using the following steps:
- Log into GitHub or create an account.
- Locate the GitHub Repository.
- At the top of the repository, on the right side of the page, select "Fork".
- You should now have a copy of the original repository in your GitHub account.
2. Creating a Clone:
How to run this project locally:
- Install the GitPod Browser Extension for Chrome.
- After installation, restart the browser.
- Log into GitHub or create an account.
- Locate the GitHub Repository.
- Click the green "GitPod" button in the top right corner of the repository. This will trigger a new gitPod workspace to be created from the code in github where you can work locally.
How to run this project within a local IDE, such as VSCode:
-
Log into GitHub or create an account.
-
Locate the GitHub Repository.
-
Under the repository name, click "Clone or download".
-
In the Clone with HTTPs section, copy the clone URL for the repository.
-
In your local IDE open the terminal.
-
Change the current working directory to the location where you want the cloned directory to be made.
-
Type 'git clone', and then paste the URL you copied in Step 3.
git clone https://github.com/USERNAME/REPOSITORY
-
Press Enter. Your local clone will be created.
(Further reading and troubleshooting on cloning a repository from GitHub here)
Once a local clone is created, follow the below steps to deploy locally:
-
Install all project requirments using the below command in the CLI terminal:
pip3 install -r requirements.txt
-
Create a
.gitignore
file in the project's root directory. -
Create an
env.py
file in the root directory. -
Add
env.py
to the.gitignore
file. -
Within the
env.py
file, enter the project's environment variables:import os os.environ.setdefault("SECRET_KEY", <your_secret_key>) os.environ.setdefault("DEVELOPMENT", '1') os.environ.setdefault("STRIPE_PUBLIC_KEY", <your_key>) os.environ.setdefault("STRIPE_SECRET_KEY", <your_key>) os.environ.setdefault("STRIPE_WH_SECRET", <your_key>)
-
A secret key can be generated using Django Secret Key Generator*
-
Stripe Public and Secret keys can be generated after Stripe account signup. Keys are found in 'Developers' Section, under 'API Keys'
-
Webhook key (WH) can be generated under 'Developers' section of your stripe Dashboard. Select 'Add endpoint' and enter:*
https://<yourhosturl>/checkout/wh/
- Select 'Receive All Events' and 'Add Endpoint' and view your 'signing secret'
-
-
Migrations will need to be made to create the local database and can be done using the following commands in the CLI terminal:
python3 manage.py makemigrations
python3 manage.py migrate
-
Next, you will need to import the fixtures folders found in the
Pages
andShop
apps. TheJSON
files contain all data needed to host the database and can be imported using the following commands in the CLI terminal:python3 manage.py loaddata swim_category
python3 manage.py loaddata swim_programme
python3 manage.py loaddata faq
python3 manage.py loaddata category
python3 manage.py loaddata product
python3 manage.py loaddata product_option
python3 manage.py loaddata product_select
- Ensure you import them in the correct order (as above), ensuring that the
ManyToMany
table is imported last, to avoid errors.
- Ensure you import them in the correct order (as above), ensuring that the
-
A
superuser
will needed to be created to gain access to the Django Admin Page and can be created using the following commands in the CLI terminal:python3 manage.py createsuperuser
-
Launch the project server using the below command in the CLI terminal:
python3 manage.py runserver
-
A server should be running locally on http://127.0.0.1:8000/. In running the server, a new SQLite3 database file (
db.sqlite3
) will be created in the root directory.
Once this is complete, the project should run within your local development environment.
The website requires back-end technology, including a server, application and database. It is because of this that the project was deployed on Heroku, a container-based cloud Platform as a Service. There are two ways to deploy on Heroku:
- Using the Heroku Command Line Interface
- Connect to GitHub Repository (the developer recommends this method)
Create Heroku App
- Log into Heroku or create an account.
- Select the
New
button on the top-right of the page, and chooseCreate New App
. Give your app a unique name (something that is the same, or similar to, your Django App) and set the region (in this instance: Europe). Then clickCreate App
. - Navigate to the
Deploy
tab on the dashboard and selectConnect to GitHub
. - Search for the repository name (ensuring it is spelled correctly). Once located, click
Connect
.
Create PostgreSQL Database
- On the Heroku dashboard, navigate to
Resources
section. - In the search bar, enter
postgres
and selectHeroku Postgres
. - For this project, the plan name os
Hobby Dev - Free
, but feel free to select your own. - Once a plan is selected,
Submit Order Form
.
Configure Heroku Variables
-
On the Heroku dashboard, navigate to
Settings
and locateReveal Config Vars
button to set the following necessary variables for the project (Note: You may not have all variable values yet, add the ones that you have available and add the others in due course):Key Value AWS_ACCESS_KEY_ID Your AWS Access Key
AWS_SECRET_ACCESS_KEY Your AWS Secret Access Key
DATABASE_URL Your Database URL
EMAIL_HOST_PASS Your Email Password
EMAIL_HOST_USER Your Email Address
SECRET_KEY Your Secret Key
STRIPE_PUBLIC_KEY Your Stripe Public Key
STRIPE_SECRET_KEY Your Stripe Secret Key
STRIPE_WH_SECRET Your Stripe WH Key
USE_AWS TRUE
Ensure all values are the same as the values set in your env.py
file -
Navigate back to the
Deploy
tab and scroll down toAutomatic Deploys
. -
Ensure that the
master
branch is selected, then selectEnable Automatic Deploys
.
Configure PostgreSQL Database
- In the project CLI, install the following dependancies to use Postgres:
pip3 install dj_database_url
pip3 install psycopg2_binary
pip3 install gunicorn
- Freeze new dependancies using
$ pip3 freeze > requirements.txt
- Create a
Procfile
file so that Heroku knows which file runs the app. In the terminal window, type the following command:Remove the blank line that may occur at the end of the Procfile to avoid any issues$ echo web: python app.py > Procfile
- Push the two files to the repository:
$ git add . $ git commit -m "Commit Message" $ git push
- In the project's
settings.py
file:- Add
import dj_database_url
to imports section (top) - Locate
DATABASES
constant variable within the file and replace it with the following:if "DATABASE_URL" in os.environ: DATABASES = {"default": dj_database_url.parse(os.environ ["DATABASE_URL"])} else: DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": BASE_DIR / "db.sqlite3", } }
- Add
- Backup current SQLite database by entering the following into the CLI:
./manage.py dumpdata --exclude auth.permission --exclude contenttypes > db.json
- Login into your Heroku CLI using:
heroku login -i
- Migrate models to the new Postgres database:
python manage.py makemigrations
python manage.py migrate
- A new
superuser
will needed to be created to gain access to the Django Admin Page and can be created using the following commands in the CLI terminal:python3 manage.py createsuperuser
- Reload the data into the new database using the folowing:
./manage.py loaddata db.json
- Temporarily disable
COLLECTSTATIC
to prevent Heroku from collecting static files until they have been set up on AWS:heroku config:set DISABLE_COLLECTSTATIC=1 --app <your app name>
--app
command is necessary if you have more than one heroku apps - Add Heroku to the
ALLOWED HOSTS
varible insettings.py
:ALLOWED_HOSTS = ['127.0.0.1', '<your app name>.herokuapp.com', 'localhost']
- Push these changes to Github:
$ git add . $ git commit -m "Commit Message" $ git push
- Set up push to Heroku:
heroku git:remote -a <your app name>
- Push to Heroku:
git push heroku main
Heroku will receive the pushed code from the GitHub repository and host the application with the required packages set out.
The deployed version can now be viewed by selecting View App
in the top-right of the page.
All Static and Media files in the production version of this site are hosted on an Amazon Web Services(AWS) S3 Bucket.
To create your own bucket,
- Log into Amazon AWS or create an account.
Create AWS S3 Bucket
- Navigate to the S3 Bucket and create a unique bucket for your application using the
Create Bucket
button:- Give your bucket a name (same or similar to Django and Heroku app names)
- Select the region closest to you
- Uncheck Block Public Access and tick to acknowledge that the bucket will be public
- Click
Create Bucket
- On your Bucket Dashboard, locate the
Properities
section and configure the following:Turn on Static Web Hosting
- In index and errror, enter
index.html
anderror.html
- Click
Save
- On your Bucket Dashboard, locate the
Permissions
section and configure the following:- In
CORS
:
[ { "AllowedHeaders": [ "Authorization" ], "AllowedMethods": [ "GET" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ]
- In
Bucket Policy
, clickGenerate Policy
and follow the below steps:- Click
Policy
>S3 Bucket Policy
- Add
*
to thePrincipal Field
(selects all principals) - Set
Action
toGet Object
- Paste in
ARN
from previous page - Click
Add Statement
- Click
Generate Policy
- Copy and paste new policy into
Bucket Policy
- Add
*/
to the end of theResources Key
- Click
Save
- Click
- In
Access Control List
set theList Objects Permission
toeveryone
- In
- Access
IAM
Dashboard fromSevices
dropdown list- Create an new user group
- Create a policy from the
Policy
tab- Select
Import Managed Policy
- Select
S3 Full Access Policy
- Select
- To override any existing content, in the
Resource
section, paste the following:[ arn:aws:s3:::<your-bucket-name>", "arn:aws:s3:::<your-bucket-name>/*" ]
- Continue to
Create Policy
- Attach new policy to the user group by locating the group and selecting
Attach Policy
next to your new policy. - Navigate to
Users
tab and selectAdd User
Create a name and ensure it hasProgrammatic access
. Add new user to the group. - Once the user is added, a
.csv
file will be provided.- Download this file and save these variables
- Add variables to your Heroku Config Variables.
- Navigate to your Bucket and manually input all files in the
media
folder. For further information on creating a bucket, head to the AWS Instructions page
Configure Bucket
- In the project CLI, install the following dependancies to use AWS:
pip3 install boto3
pip3 install django-storages
- Freeze new dependancies using
$ pip3 freeze > requirements.txt
- In the project's
settings.py
file, addstorages
to INSTALLED_APPS variable. - In order to link your new AWS Bucket to your project, paste:
if 'USE_AWS' in os.environ: # Cache control AWS_S3_OBJECT_PARAMETERS = { 'Expires': 'Thu, 31 Dec 2099 20:00:00 GMT', 'CacheControl': 'max-age=94608000', } # Bucket Config AWS_STORAGE_BUCKET_NAME = 'your_bucket_name' AWS_S3_REGION_NAME = 'your_region' AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com' # Static and media files STATICFILES_STORAGE = 'custom_storages.StaticStorage' STATICFILES_LOCATION = 'static' DEFAULT_FILE_STORAGE = 'custom_storages.MediaStorage' MEDIAFILES_LOCATION = 'media' # Override static and media URLs in production STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{STATICFILES_LOCATION}/' MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{MEDIAFILES_LOCATION}/'
- Create a
custom_storages.py
file in your project's root directory. Inside it, include the Static and Media Storage locations:from django.conf import settings from storages.backends.s3boto3 import S3Boto3Storage class StaticStorage(S3Boto3Storage): location = settings.STATICFILES_LOCATION class MediaStorage(S3Boto3Storage): location = settings.MEDIAFILES_LOCATION
- Delete
DISABLE_COLLECTSTATIC
variable from your Heroku Config Variables - Push these changes to Github:
$ git add . $ git commit -m "Commit Message" $ git push
The developer consulted multiple sites in order to better understand the code they needed to implement their deisgn.
The Code Institute Boutique Ado Mini Project was used as a reference point for the developer in the development of the core functionality of the website. The lessons included with the mini-project helped the developer to get a better understanding of each functionality and how to customise it to suit their project.
For code that was copied and edited, the developer made sure to reference this within the code. The following sites were used on a more regular basis:
The developer would like to thank the following:
- Their family and friends for the unrelenting support and encouragement they provided, most notably their very honest opinions during the testing phase.
- The Slack community for the support and inspiration they gave.
- Their mentor Seun for her patience and guidance throughout the process.