7 days
- React
- Webpack
- Ajax
- JavaScript (ES6)
- Python
- PostgreSQL
- Flask
- HTML5
- Bulma (CSS framework)
- SCSS
- GitHub
This was a sole-coding project and the final project for the software engineering immersive course.
The brief was to:
- Create an application with a Python/Flask/PostgreSQL backend and a React front-end utilising Webpack.
- The application had to include APIs to enable a user to register, login and add content.
The site is deployed to Heroku at http://upcyclestore.herokuapp.com.
I had decided early on in my project planning that I wanted to build a store. After researching for inspiration, I thought it would be fun to combine the creativity of giving old things a new lease of life with a shop. Upcycle Store was born.
I realised that to build a complete ecommerce platform was a challenging task in just 7 days. I decided that my MVP was a platform which allowed a user to register, list their items and also simulate buying items through a cart.
The project started with an entity relationship diagram (ERD) and I used Sketch and Zeplin to wireframe and design.
The site has a Python/PostgreSQL backend and a React front end. Based on the ERD and wireframes I built the data models and used Flask to provide the API framework. Before building the front end I tested the API endpoints using Insomnia.
The shopping cart items had a one-to-many relationship with both the user and the listings. This caused a number of issues with recursion, but this was resolved by excluding data within some endpoints.
Endpoints were built out for:
- Login and registration (POST)
- Categories (POST)- fixed number from a seeds file.
- Listings - (GET, POST, DELETE)
- Cart items - (GET, POST, DELETE)
The endpoints for the cart items were more complex as I needed to retrieve all items for a user:
@router.route('/cart', methods=['GET'])
@db_session
@secure_route
def get_cart():
schema = CartItemSchema(many=True)
usercart = CartItem.select(lambda user: user.user == g.current_user)
return schema.dumps(usercart)
In addition. To simulate a 'checkout', I implemented an endpoint which removes all cart items for a user:
@router.route('/cart_checkout', methods=['DELETE'])
@db_session
@secure_route
def clear_cart():
delete(item for item in CartItem if item.user == g.current_user)
return '', 204
Whilst this is a simple form submission to the listing POST endpoint, I needed to store the categories as an array of ids. This is achieved by setting state for the categories and the rest of the form separately, and spreading this data in to one state before submission:
// HANDLES STATE CHANGE FOR FORM INPUT EXCEPT CATEGORIES ======
handleChange(e){
const data = { ...this.state.data, [e.target.name]: e.target.value }
this.setState({ data })
}
// HANDLES THE CATEGORY MULTISELECT ============================
handleCategorySelect(e){
const categoryIds = Array.from(new Set([
...this.state.data.category_ids,
parseInt(e.target.value)
]))
const data = { ...this.state.data, category_ids: categoryIds }
this.setState({ data })
}
The browse page includes filter buttons. These are sorted first:
sortedCategories() {
return this.state.categories.sort((a, b) => {
if (a.name === b.name) return 0
return a.name < b.name ? -1 : 1
})
}
And a filter function is used to toggle the content:
filteredListings() {
const filter = this.state.filters.name
if (this.state.filters.name === 'All') return this.sortedListings()
return this.sortedListings().filter(category => {
return category.categories.some(category => category.name === filter)
})
}
<section className="columns is-multiline">
{this.filteredListings().map(listing =>
<div key={listing.id} className="listing-wrapper column is-one-quarter">
<ListingCard {...listing} />
</div>
)}
</section>
- PayPal integration
- Search
- Add comments to listings
- Update endpoint for a listing