E-commerce website for a bicycle brand producing carbon fiber and titanium frames, selling them as well as fully assembled bicycles with high end components. (Full Stack project)
FABIKE - deployed project /temporary unavailable/
Target audience:
- Cycling enthusiast, semi- & professionals, who value high quality products for sports, who are very knowledgeable about the industry, and want to purchase a high end customizable frame or fully assembled bicycle for their sports or recreational cycling.
Main goal of this site:
- Build a full cycle e-shop to present the brand's products to potential buyers, and enable the latter with an easy way to choose a product, get all necessary info about the products on the web or additional info by contacting the website's owner, quickly and easily purchase one, or save it for buying later.
Organizational Goals:
- Present products in way to make a purchase easy
- Enable convenient & quick way of buying products via its e-shop
- Sell frames & bicycles
- Promote & provide educational support on how to service and take care of bicycles
- Strengthen the brand's image
User Goals:
- Browse through products & get all necessary info about them, and all related info (e.g., components, shipping)
- Quickly and easily purchase a product
- Get answers from the producers in case any
- Attend educational workshops
back to top
Create an e-shop which is easy to navigate, with a simple & convenient order-payment process. The website will be built on Python-Django, with PostgreSQL database, and Stripe payment system.
Website visitors:
-
First time website visitor:
- I want to get a clear idea what the website is about
- I want to be able to easily see where I can find various info (menu, sections, links, individual pages/windows) and easily navigate there
- Find appropriate section - bikes or frames, urban bikes or all road bikes, etc, and be able to see all the products in the section
- Choose an individual product, and get all necessary info: images, descriptions, technical parameters, price, delivery terms, etc.
- I want to be able to choose appropriate size, color and add the product to shopping cart
- I want to be able to easily navigate from the shopping cart back to products
- Then once I am done with products, I want to be able to easily find the shopping cart, and navigate there.
- I want to be able to go to checkout, see all the info about my purchase, be able to add billing/shipping details, add my card details, and pay with one-click.
- Also I want to be able to automatically (during checkout-payment process) add my details and automatically create an account (adding all my data, incl. shipping address).
- Once the payment is processed, I want to get a confirmation email about the payment, and the order, with all the order details. And be able to login into my account and see the order there.
- If I don’t purchase anything, I want to be able to create an account, add my details, and add products to my wish list.
- If I decide not to create an account, I want to have an option to send a message or ask questions about the products
-
Returning visitor (in addition to possibilities described above, except those specific to first time visitors):
- If I haven’t created a profile yet, to find easily where I can do that, and create one.
- I want to be able to login into my account, update my data if needed, and see my wish list if I created it.
- I want to be able to navigate across the website and get info necessary for me to decide about a purchase and purchase a product taking steps described above.
-
Website administrator:
- To have access to all the necessary data to manage products’ display on the web-site, customers’ accounts, and purchase histories, etc.
- To be able to easily login and navigate within the admin area of the website.
- To be able to easily add, modify or delete products listings, with all the necessary info – images, descriptions, technical parameters, etc.
back to top
Visitor vs Administrator:
-
Website's visitor can:
- Look through products (bikes, frames, repair workshops)
- Check details of an individual product
- Add a product to a wish list
- Add a product to shopping cart
- Go to checkout
- Order a product/Pay for it
- Create an account
- Update user’s details (email, password, phone, shipping address, etc)
- See one’s wish list
- See one’s order history
- See calendar of planned repair workshops
- Send a message via Contact Us form
-
Website's administrator can:
- Add/delete/update products (info, descriptions, parameters, prices, images)
- Manage users’ accounts
- See orders’ list (fulfilled, in progress, ordered)
- Add/update/delete events (repair workshops)
Logged-in vs unlogged:
-
Unlogged user can see/access:
- Products (all or individual)
- Events (repair workshops)
- Create an account form
- Add to cart
- Proceed to checkout
- Order/Pay a product (with or without registration)
- Warranty & Compensations
- Return & refund policy
-
Logged user can see/access:
- Everything as above
- Personal account (user’s details, order history, wish list)
- Administrator can access product management area (inaccessible to other logged in users)
back to top
The website will consist of the following pages/sections:
Unlogged users' view:
- HOME
- ABOUT
- BIKES
- URBAN BIKES
- ALL-ROAD BIKES
- ROAD BIKES
- FRAMES
- Individual PRODUCT pages
- EVNETS
- Individual EVENT pages
- SHOPPING CART
- CHECKOUT
Logged users' view: In addition to the pages mentioned above
- ACCOUNT
- ORDER HISTORY
- PRODUCT MANAGEMENT (for super-user)
For more details on what pages will contain check out the wireframes below.
back to top
Balsamiq wireframes software was used to create wireframes for this project.
Each wireframe below contains mobile, pad & laptop/desktop view:
- HOME
- SIGN UP/IN
- ABOUT
- BIKES
- Section URBAN BIKES
- FRAMES
- PRODUCT page
- SHOPPING CART
- CHECKOUT
- ACCOUNT
- ORDER HISTORY
- PRODUCT MANAGEMENT
- EVENTS
- EVENT page
back to top
Design choices:
- General guidelines are taken from the existing website www.fabike.it, and approved by the main designed of the company, Fabio Putzolu.
- The overall structure of existing pages(www.fabik.it) will be re-created in this project, but will be coded from scratch, and all the non-existing pages/features will be added during the development of the project.
Colors:
- All the colors & color combination will be derived from the 4 main colors used for the frames/bikes: black, blue, yellow, & red.
For the purpose of the project the following colors (matching or related) will be used:
Greys-Blacks
Blues
Yellows
Reds
Fonts:
- The original web-site is using DINPro font, Bold and Light. For the purpose of the project, i will use a very similar Google font - Noto Sans KR.
Images:
- All product(bikes, frames) & lifestyle photos were taken by the designer of the frames, Fabio Putzolu.
back to top
-
During development on a local machine standard sqlite3 database will be used, as it is installed with Django.
-
After deployment, PostgreSQL database will be used as, it is provided by Heroku where the website will be deployed.
The User model used is provided by Django as a part of defaults django.contrib.auth.models
.
Name | Database Key | Validation | Field Type |
---|---|---|---|
User | user | on_delete=models.CASCADE | OneToOneField 'User' |
Full Name | account_full_name | max_length=70 | CharField |
Phone number | account_phone_number | max_length=25 | CharField |
Address | account_address | max_length=120 | TextField, ForeignKey 'Address' |
Name | Database Key | Validation | Field Type |
---|---|---|---|
Address Line1 | account_address_line1 | max_length=120 | CharField |
Address Line2 | account_address_line2 | max_length=120, null=True, blank=True | CharField |
Town/City | account_town_or_city | max_length=70 | CharField |
County | account_county | max_length=50, null=True, blank=True | CharField |
Postcode | accountaccount_postcode | max_length=20 | CharField |
Country | account_country | blank_label='Country' | CountryField |
back to top
Name | Database Key | Validation | Field Type |
---|---|---|---|
Product Type | product_type | choices=PRODUCT_TYPE | CharField |
Product Group | product_group | choices=PRODUCT_GROUP | CharField |
Name | name | max_length=80 | CharField |
Frame Type | frame | max_length=80 | CharField |
Title | title | max_length=120 | TextField |
Fork | fork | max_length=80 | CharField |
Wheels | wheels | max_length=80, null=True, blank=True | CharField |
Tyres | tyres | max_length=80, null=True, blank=True | CharField |
Max Tyre Size | max_tyre_size | max_length=80, null=True, blank=True | CharField |
Crankset | crankset | max_length=80, null=True, blank=True | CharField |
Shift Levers | shift_levers | max_length=80, null=True, blank=True | CharField |
Derailleurs | derailleurs | max_length=80, null=True, blank=True | CharField |
Casette/Sprocket | casette_or_sprocket | max_length=80, null=True, blank=True | CharField |
Chain/Belt | chain_or_belt | max_length=80, null=True, blank=True | CharField |
Brakes | brakes | max_length=80, null=True, blank=True | CharField |
Handlebar | handlebar | max_length=80, null=True, blank=True | CharField |
Stem | stem | max_length=100, null=True, blank=True | CharField |
Saddle | saddle | max_length=80, null=True, blank=True | CharField |
Seatpost | seatpost | max_length=80, null=True, blank=True | CharField |
Seat Clamp | seat_clamp | max_length=80, null=True, blank=True | CharField |
Headset | headset | max_length=80, null=True, blank=True | CharField |
Seatpost Diameter | seatpost_diameter | max_length=20 | CharField |
Bottom Bracket Type | bottom_bracket | max_length=80 | CharField |
Dropouts | dropouts | max_length=120 | CharField |
Weigth | weight | max_digits=4, decimal_places=2 | DecimalField |
Weight Alloy | weight_alloy | max_digits=4, decimal_places=2 | DecimalField |
Weight Carbon | weight_carbon | max_digits=4, decimal_places=2 | DecimalField |
Price | price | max_digits=6, decimal_places=2 | DecimalField |
Price Alloy | price_alloy | max_digits=6, decimal_places=2 | DecimalField |
Price Carbon | price_carbon | max_digits=6, decimal_places=2 | DecimalField |
Price comment | price_comment | max_length=120 | CharField |
Image 1 | product_image01 | ImageField | |
Image 2 | product_image02 | null=True, blank=True | ImageField |
- Product Types/choices are defined within the Product model.
back to top
Name | Database Key | Validation | Field Type |
---|---|---|---|
Order Number | order_number | CharField | max_length=32, null=False, editable=False |
User | account | ForeignKey 'Account' | on_delete=models.SET_NULL, null=True, blank=True, related_name='orders' |
Full Name | account_full_name | max_length=70 | CharField |
Phone number | account_phone_number | max_length=25 | CharField |
Address | address | max_length=254 | TextField, ForeignKey 'Address' |
Town or City | town_or_city | max_length=80 | CharField |
Postcode | postcode | max_length=20 | CharField |
Country | country | blank_label='Country*' | CharField |
Purchase Date | purchase_date | auto_now_add=True | DateTimeField |
Order Total | order_total | max_digits=10, decimal_places=2, null=False, default=0 | DecimalField |
Delivery Cost | delivery_cost | max_digits=10, decimal_places=2, null=False, default=0 | DecimalField |
Final Total Total | final_total | max_digits=10, decimal_places=2, null=False, default=0 | DecimalField |
Stripe Pid | stripe_pid | max_length=254, null=False, blank=False, default='' | CharField |
Name | Database Key | Validation | Field Type |
---|---|---|---|
Order | order | null=False, blank=False, on_delete=models.CASCADE, related_name='orderitems' | ForeignKey 'Order' |
Product | product | null=False, blank=False, on_delete=models.PROTECT | ForeignKey 'Product' or 'Event' |
Quantity | quantity | null=False, blank=False, default=0 | IntegerField |
Color | color | choices=COLORS | CharField |
Size | size | choices=SIZES | CharField |
Components | alloy_or_carbon | choices=COMPONENTS | CharField |
Item Total | item_total | max_digits=6, decimal_places=2, null=False, blank=False, editable=False | DecimalField |
- Colors, Components & Sizes choices will be added via other means, product views, Javascript.
Data model for Products done at the beginning of the project didn't take into account some aspects of data manipulation, specifically creating a unique product record with a combination of model, color, size, components, etc. Based on the knowledge & experience I have obtained during the project's implementation, I'd plan a Product data model differently, i.e. each individual combination of a model, color, size, components would be given an individual sku code. This would simplify quite a number of processes within the following apps: cart, checkout.
back to top
Name | Database Key | Validation | Field Type |
---|---|---|---|
Title | title | max_length=80 | CharField |
Learning | Learning | max_length=254 | TextFeild |
Date | date | max_length=20 | DateTimeField |
Time | time | max_length=20 | DateTimeField |
Address | address | max_length=80 | CharField |
Town or City | town_or_city | max_length=80 | CharField |
Price | price | max_digits=3, decimal_places=2 | DecimalField |
Comment | Comment | max_length=120 | CharField |
back to top
Unlogged users' view:
- HOME
- Navbar with logo and nav links to:
- Products
- Events
- About
- Contact
- Search icon
- Account icon
- Sign Up link
- Sign In link
- Cart Icon
- Hero image slideshow
- Three secion of product with CTA button
- Components brands info section
- Footer with links/icons to:
- Bikes
- Frames
- Events
- About
- Warranty info
- Refunds and returns info
- Contact
- Navbar with logo and nav links to:
back to top
- ABOUT
- Info about the company
- Addresses
- CTS button to contact us
- BIKES
- Lists all three groups of products - Urban, All Road, Road
- Each group section has a clickable image/title
- Each product has a clickable image/title
- URBAN BIKES
- All the bikes from this product group
- Each product has a clickable image/title
back to top
- ALL-ROAD BIKES
- As above
- ROAD BIKES
- As above
- FRAMES
- Frames with clickable images/titles
back to top
- Individual PRODUCT pages
- Product image
- Name, frame, title
- Edit, Delete Buttons (superuser view)
- Color, size, components(only for bikes), price, Add-to-cart section
- Specifications section
- Geometry and sizes section
- Contact Us CTA button
- Section about customizable color parts
- Info about production location & delivery times
back to top
- EVENTS
- Lists all the events
- Each event has More Info button
- Individual EVENT pages
- Title
- Learning topics
- Date & time
- Location address / map link (if provided)
- Price
- Book CTA button
- Edit, Delete Buttons (superuser view)
- SHOPPING CART
- Info about added products
- Image, colors, size, components, quantity
- Update field to increase/decrease quantity
- Delete button
- Checkout button
- Back to Bikes Button
back to top
- CHECKOUT
- Summary of the order
- Image, colors, size, components, quantity
- Final total to pay
- Billing/Shipping info form
- Sign Up/Sign In link (if not logged)
- Save info to profile (if logged in)
- Card info field
- Complete Order Button
- Info about amount to be charged
- Back to Bikes Button
- CHECKOUT Success
- Summary of an order
- Order date and number
- Product descriptions
- Address to where it is sent
- Payment info
- Info about email to which a confirmation was sent
- Button Back to Bikes
- Summary of an order
back to top
Logged users' view: In addition to the pages mentioned above
- ACCOUNT
- Shipping info which can be updated
- ORDER HISTORY
- List of past order, ech of them can be open separately
- PRODUCT MANAGEMENT (for super-user)
- New Product & New Event in the Account menu
- Add Product/Add Event pages
- Each Product or Even has Edit/Delete buttons
- Edit Product/Event Pages
back to top
back to top
- Datetime picker for dates and time in adding/editing events
- Wishlist application
- More product pictures, macthing colors on color choice selected by a user
- HTML
- CSS
- Python
- Javascript
- Jinja
- Django
- Bootstrap
- Google Fonts
- FontAwesome
- JQuery
- Gunicorn - a Python WSGI HTTP Server to enable deployment to Heroku
- Psycopg2 - to enable the PostgreSQL database to function with Django
- Stripe - for card payment process
- Django Crispy Forms
- GitPod - IDE for developing this project
- Git - version control
- GitHub - storage of the project's repository
- PIP - for installation of necessary tools
- Heroku - to host the project
- AWS S3 Bucket - to store static and media files in production
- Boto3 for compatibility with AWS
- Travis - for integration testing
- Balsamiq - for wireframes
- ColosSpace - to create colour palette
- Autoprefixer - for adapting css to various browsers
- SQlite3 - for development
- PostgreSQL - for production
back to top
You will find all the info related to testing in Testing.md file
Gitpod was used as IDE to code this project. It was then committed and pushed to Github using the command line and deployed on Heroku PaaS platform, static and media files were uploaded to AWS S3. All versions and branches of the code are stored on Github repository.
Follow these steps to deploy the project to Heroku:
In HEROKU:
- Create an new app in Heroku dashboard, choose closest region to you
- Go to
Resources
& provision a new instance of PostgreSQL DB- Search for
Heroku Postgres
in ADDONS - Choose Development plan
- Search for
In IDE:
- Run the following commands to install
dj_database_url
&psycopg2-binary
:
- pip3 install dj_database_url
- pip3 install psycopg2-binary
- Update requirements.txt:
- pip3 freeze > requirements.txt
In settings.py:
- add
import dj_database_url
right afterimport os
- Change Database:
DATABASES = {
'default': dj_database_url.parse('....here goes a link for DB from Heroku Config Variables...')
}
In IDE:
- Run migrations:
- python3 manage.py showmigrations
- python3 manage.py migrate
- Load products & events data:
- python3 manage.py loaddata products
- python3 manage.py loaddata events
- Create a superuser:
- python3 manage.py createsuperuser
In settings.py:
- Change the Database settings as follows:
if 'DATABASE_URL' in os.environ:
DATABASES = {
'default': dj_database_url.parse(os.environ.get('DATABASE_URL from Heroku'))
}
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
back to top
In IDE:
- Install gunicorn:
- pip3 install gunicorn”
- Update requirements.txt:
- pip3 freeze > requirements.txt
In general project's directory:
- Create a Procfile
In IDE:
- Run the following command:
- web gunicorn django_name.wsgi:application
- To login into heroku run:
- heroku login -i
- Enter your email & password
- Run the following command:
- heroku config:set DISABLE_COLLECTSTATIC=1 --app fabike
In settings.py:
-
To ALLOWED_HOSTS add ['https://fabike.herokuapp.com', 'localhost']
-
Generate a new SCRET_KEY on Django Secret Key generator (find online)
In Heroku:
- Add it to
Settings > Config Vars
, - Make sure you have other necessesary keys are setup there:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- DATABASE_URL
- EMAIL_HOST_PASS
- EMAIL_HOST_USER
- SECRET_KEY
- STRIPE_PUBLIC_KEY
- STRIPE_SECRET_KEY
- STRIPE_WH_SECRET
- USE_AWS set to
True
In settings.py:
- Change SECRET_KEY to:
- SECRET_KEY = os.environ.get('SECRET_KEY', ' ')
In IDE:
- Commit changes to Github:
- git push origin master
- Push to Heroku:
- git push -u heroku master
Additionally In Heroku:
- You can setup automatic deployment to Heroku when you commit changes to Githu:
- Go to Deploy
- Set deployment method to GitHub
- Find Fabike and connect
- Enable Automatic Deploys
The deployment has been completed.
back to top
The static files and media files (product images) are hosted in the AWS S3 Bucket. To host them, you need to create an account in AWS and create your S3 basket with public access.
To be able to send emails with Gmail, you need to connect it to your Gmail account, setting up your email address in EMAIL_HOST_USER variable and your app password generated by your email provider in EMAIL_HOST_PASS variable.
To be able to run this project, the following tools have to be installed:
Besides, you need to have/create accounts at:
Follow these steps:
- Copy the repository by entering this in your IDE:
- git clone https://github.com/OlekSt/Fabike
- Create .env file and add it to .gitignore to protect your sensitive variables
- env. file example:
import os
os.environ["DEVELOPMENT"] = "True"
os.environ["SECRET_KEY"] = "<Your Secret key>"
os.environ["STRIPE_PUBLIC_KEY"] = "<Your Stripe Public key>"
os.environ["STRIPE_SECRET_KEY"] = "<Your Stripe Secret key>"
os.environ["STRIPE_WH_SECRET"] = "<Your Stripe WH_Secret key>"
In IDE:
- Install all the requirements:
- pip3 install -r requirements.txt
- Start the server:
- python3 manage.py runserver
- Migrate the models for the DB
- python3 manage.py makemigrations
- python3 manage.py migrate
- Load data using fixtures:
- python3 manage.py loaddata products
- python3 manage.py loaddata events
- Create a superuser for admin
- python3 manage.py createsuperuser
- Start the server:
- python3 manage.py runserver
Now the project should be active on localhost port 8000.
To access admin you need to add /admin
at the end of the url.
back to top
The website is created by Alexey Statsenko, using the media described below.
- All product(bikes, frames) & lifestyle photos were taken by the designer of the frames, Fabio Putzolu. All rights belong to FABIKE Design s.r.o.
- Code for search dropdown menu and search algorithm was copied from CI's Boutique Ado project, and modified according to the current project's needs. Thanks, Chris! ;-)
- HTML/CSS code for color radio buttons on bike.html was taken from https://uicookies.com/bootstrap-radio-button-styles/ and modified according to the needs of the project.
- HTML/CSS code for size and components selector was taken from https://freefrontend.com/css-radio-buttons/ and modified according to the needs of the project.
- Code for toasts was copied from CI's Boutique Ado project, and modified according to the current project's needs.
- Code for cart including calc_subtotal templatetags was copied from CI's Boutique Ado project, and modified according to the current project's needs.
- Code for checkout app was partly copied from CI's Boutique Ado project, and modified according to the current project's needs.
- Code for profile was partly copied from I's Boutique Ado project, and modified according to the current project's needs.
- Code for clear input widget was taken from Boutique Ado.
- I have added appropriate comments where needed. Disclaimer: I might have missed some place, but please consider the credits given to the Boutique Ado project, as I have used it for both inspiration, structure and soruce code for building my project.
- My mentor, Can Sucullu, for advice and help with planning and creating the website; checking the project and giving advice during the project's calls - checking the code, giving general advice.
- CI's tutors for help during the project.
- Chris Z. (ckz8780), for answering my questions about the projects, especially the checkout/webhooks part.