Selling merchandise in the modern era requires digital solutions. For this project, you'll be tasked with designing and constructing an online student store for the College of Codepath. The application entails a frontend user interface for potential customers to peruse the goods, and a backend API to handle data management. The API will be built with Node and Express and the UI will be built with React.
Project Demo Part 2.1
Project Demo part 2.2
Project Demo Part 1.1
Project Demo Part 1.2
Project Demo Part 1.3
- Displays the following sections: header, banner, search, product grid, about, contact, and footer.
- On initial page load, display the products at the GET /store endpoint.
- User can click on the categories (Clothing, food, etc) to filter the product grid by type.
- User can search for products.
- User can click on a product in the grid to view additional product details. Navigation is via a React Router.
- User can click to expand the shopping cart in the left navigation.
- User can click the '+' button on a product cart to increment that product in the shopping cart.
- User can click the '-' button on a product cart to increment that product in the shopping cart.
- Shopping cart displays a table of products, quantities, subtotal, tax, and total.
- User can click in the top navigation bar to scroll to the relevant section.
- User sees a "not found" display when searching for a nonexistent product.
- Create an endpoint for fetching all orders in the database, and an endpoint for serving an individual order based on its id.
- Build a page in the UI that displays the list of all past orders and lets the user click on any individual order to take them to a more detailed page of the transaction.
- Allow users to use an input to filter orders by the email of the person who placed the order.
The following specifications were met on the Express backend and the React frontend.
App.jsx
- The core App component that contains the routes for the app and does the initial data fetching
- Renders a
BrowserRouter
component that contains aRoutes
component with the following routes:-
/
- Should render theHome.jsx
component -
/products/:productId
- should render theProductDetail
component -
*
- anything else should render theNotFound
component
-
- Renders the
Navbar
component on every route - Renders the
Sidebar
component on every route - Should create at least the following state variables:
-
products
- an array of product objects that is initially empty. -
isFetching
- a boolean value representing whether or not the App is currently fetching theproducts
from the API. -
error
- a variable used to display a message when something goes wrong with the API requests. -
isOpen
- a boolean value representing whether or not theSidebar.jsx
is in the open or closed state. -
shoppingCart
- should store state for the active user's shopping cart (items they want to purchase and the quantity of each item).- Use whatever data type works best here, but make sure the format the
shoppingCart
as an array before passing it to other components. - When passed down to other components as a prop, it should formatted as an array of objects.
- Each object in the array should have two fields:
- The
itemId
field should store theid
of the item being purchased. - The
quantity
field should store a number representing how many of that item the user is purchasing.
- The
- Use whatever data type works best here, but make sure the format the
-
checkoutForm
- the user's information that will be sent to the API when they checkout.
-
- Leverage the
useEffect
hook to ensure that when theApp.jsx
component is mounted to the screen...- It should make a
GET
request to the API's/store
endpoint with theaxios.get
method. - When the request completes successfully, it should store the
products
returned by the response in state. - If the request does not complete successfully, or there are no
products
found in the response, it should create an error message and store it in theerror
state variable.
- It should make a
- The
App.jsx
component should define handler functions to be passed as props to theHome
andProductDetail
components.- Define as many as are needed.
- At minimum, create these five handlers:
- The
handleOnToggle
function. When called...- It should toggle the open/closed state of the
Sidebar
.
- It should toggle the open/closed state of the
- The
handleAddItemToCart
function. When called...- It should accept a single argument -
productId
- It should add that product to the
shoppingCart
if it doesn't exist, and set its quantity to1
. - If it does exist, it should increase the quantity by
1
. - It should add the price of the product to the total price of the
shoppingCart
.
- It should accept a single argument -
- The
handleRemoveItemFromCart
function. When called...- It should accept a single argument -
productId
- It should decrease the quantity of the item in the
shoppingCart
by1
, but only if it already exists. - If it doesn't exist, the function should do nothing.
- If the new quantity is
0
, it should remove the item from theshoppingCart
- It should accept a single argument -
- The
handleOnCheckoutFormChange
function. When called...- It should receive two arguments:
-
name
- thename
attribute of the input being updated -
value
- the new value to set for that input
-
- It should update the
checkoutForm
object with the new value from the correct input(s)
- It should receive two arguments:
- The
handleOnSubmitCheckoutForm
function. When called...- It should submit the user's order to the API
- To submit the user's order, it should leverage the
axios.post
method to send aPOST
request to the/store
endpoint. - The body of that
POST
request should be an object with two fields:- The
user
field:- Should be an object containing
name
andemail
properties - Each property should be set to the correct value found in the
checkoutForm
- Should be an object containing
- The
shoppingCart
field:- Should contain the user's order formatted as an array of objects.
- Each object in the array should have two fields:
- The
itemId
field should store theid
of the item being purchased. - The
quantity
field should store a number representing how many of that item the user is purchasing.
- The
- Don't include the
total
price here, since we'll be calculating that on the backend. Remember to never trust the client!
- The
- The
Navbar.jsx
- Should render JSX that is wrapped by a
nav
element with aclassName
ofnavbar
- Should render the
Logo
component that links to the/
route when clicked
Logo.jsx
- Should render JSX that is wrapped by a
div
element with aclassName
oflogo
- Should use the
Link
component fromreact-router-dom
to link to the home route (/
) when clicked
Home.jsx
- Should render JSX that is wrapped by a
div
element with aclassName
ofhome
- Should accept at least the following props:
products
- an array of product objectshandleAddItemToCart
- handler function defined in theApp.jsx
componenthandleRemoveItemToCart
- handler function defined in theApp.jsx
component
- Should render the
Hero
component - Should render the
ProductGrid
component
Hero.jsx
- Should render JSX that is wrapped by a
div
element with aclassName
ofhero
- Should display an intro message inside an element with the
className
ofintro
. That message should contain the text"Welcome!"
somewhere within it. - Should render a hero image inside an
img
tag with theclassName
ofhero-img
.
ProductGrid.jsx
- Should render JSX that is wrapped by a
div
element with aclassName
ofproduct-grid
- Should accept at least the following props:
products
- an array of product objectshandleAddItemToCart
- handler function defined in theApp.jsx
componenthandleRemoveItemToCart
- handler function defined in theApp.jsx
component
- Should iterate over its
products
prop, rendering aProductCard
component for each one. Set theshowDescription
prop tofalse
for all of theProductCard
components rendered in theProductGrid
component.
ProductDetail.jsx
- Should render JSX that is wrapped by a
div
element with aclassName
ofproduct-detail
- Should accept at least the following props:
handleAddItemToCart
- handler function defined in theApp.jsx
componenthandleRemoveItemToCart
- handler function defined in theApp.jsx
component
- Should define at least a
product
state variable and updater - It should leverage the
useParams
hook fromreact-router-dom
to extract theproductId
param from the url. - When the component is mounted to the screen...
- It should make a
GET
request to the/store/:productId
endpoint with theaxios.get
method. - The
:productId
part of the request should be replaced with theproductId
pulled from the url. - When the initial request is loading, it should render an
h1
element with theclassName
ofloading
and contain the text"Loading..."
- It should store the
product
received by the request in state and then render theProductView
component. - If no
product
is found with thatid
, it should render theNotFound
component
- It should make a
ProductView.jsx
- Should render JSX that is wrapped by a
div
element with aclassName
ofproduct-view
- Should accept at least the following props:
product
- theproduct
object returned by the API requestproductId
- the id of the product extracted from the urlquantity
- the quantity for this product found in theshoppingCart
handleAddItemToCart
- handler functionhandleRemoveItemToCart
- handler function
- It should display an
h1
element with theclassName
ofproduct-id
that contains the text:Product #
followed by theproductId
prop - It should render a
ProductCard
component and pass it the props it needs. It should also set theshowDescription
prop totrue
for this product card.
ProductCard.jsx
- Should render JSX that is wrapped by a
div
element with aclassName
ofproduct-card
- Should accept at least the following props:
product
- a product objectproductId
- anumber
representing theid
of the productquantity
- the quantity for this product found in theshoppingCart
handleAddItemToCart
- handler functionhandleRemoveItemToCart
- handler functionshowDescription
- boolean
- Should render the
name
of the product inside an element with theclassName
ofproduct-name
- Should render the
price
of the product inside an element with theclassName
ofproduct-price
. The price should formatted so that it starts with a$
, and has at least one integer digit, along with exactly two decimal digits. Examples -$22.99
,$860.20
, and$0.50
- If the
showDescription
prop is set totrue
, it should render thedescription
of the product inside an element with theclassName
ofproduct-description
. - Should render an
img
element for the product:- The
img
element should have asrc
attribute to set to theimage
property of theproduct
prop. - The
img
element should be wrapped in aLink
component fromreact-router-dom
.- The
Link
element should have ato
prop so that when theimg
element is clicked on, it should navigate to the product detail route for that product using itsid
attribute. For example, a product with anid
of4
should create aLink
with itsto
prop set to/products/4
. - The
Link
that wraps theimg
element should be nested somewhere inside an element with theclassName
ofmedia
.
- The
- The
- Should render two
buttons
elements...- One button with a
className
ofadd
. When clicked, it should call thehandleAddItemToCart
function with theid
of theproduct
as its only argument. - One button with a
className
ofremove
. When clicked, it should call thehandleRemoveItemFromCart
function with theid
of theproduct
as its only argument.
- One button with a
- Should display the current quantity of items that the user has selected in their shopping cart. The quantity should be rendered inside an element with the
className
ofproduct-quantity
. If none of that particular item have been added to the shopping cart, it should render nothing there.
Sidebar.jsx
- Should render JSX that is wrapped by a
section
element with theclassName
ofsidebar
- Should accept at least the following props (and probably a few more):
isOpen
- boolean representing the open/closed state of the SidebarshoppingCart
- the active user's cart formatted as an array of objects withitemId
andquantity
keysproducts
- the array of products fetched from the APIcheckoutForm
- the form state for theCheckoutForm
componenthandleOnCheckoutFormChange
- handler function to update thecheckoutForm
objecthandleOnSubmitCheckoutForm
- handler function to submit the user's order to the APIhandleOnToggle
- handler function to toggle open/closedSidebar
state
- It should always render a
button
element with theclassName
oftoggle-button
. When that button is clicked it should change theisOpen
prop by calling thehandleOnToggle
prop. - When the sidebar is opened, it should display the
ShoppingCart
andCheckoutForm
components and should be wider than350px
. - When the sidebar is closed, it should only render the toggle button and shouldn't be wider than
150px
.
ShoppingCart.jsx
- Should render JSX that is wrapped by a
div
element with theclassName
ofshopping-cart
- Should accept at least the following props (and probably a few more):
isOpen
- boolean representing the open/closed state of the Sidebarproducts
- the array of products fetched from the APIshoppingCart
- the active user's cart formatted as an array of objects withitemId
andquantity
keys
- For every item in the
shoppingCart
:- It should display the
name
of the item in an element with theclassName
ofcart-product-name
. Remember that items in theshoppingCart
prop will only contain theitemId
andquantity
fields. Other props will have to be used to conver theitemId
field to theproduct
's name. - It should display the
quantity
of the item in an element with theclassName
ofcart-product-quantity
- It should display the
- It add up the cost of all items (make sure to use the quantity of the item requested), and render that amount rounded up to exactly 2 decimal places inside an element with the
className
ofsubtotal
. Make sure it is prefixed with a dollar sign ($)! - It should calculate the cost of taxes on that subtotal (using 8.75% as the tax rate), add that amount to the subtotal, and render the total cost rounded up to exactly 2 decimal places inside an element with the
className
oftotal-price
. Make sure it is prefixed with a dollar sign ($)! - If no items exist in the
shoppingCart
, it should render this message:"No items added to cart yet. Start shopping now!"
inside an element with theclassName
ofnotification
CheckoutForm.jsx
- Should render JSX that is wrapped by a
div
element with theclassName
ofcheckout-form
- Should accept at least the following props:
isOpen
- booleanshoppingCart
- the active user's cart formatted as an array of objects withitemId
andquantity
keyscheckoutForm
- the form state for theCheckoutForm
componenthandleOnCheckoutFormChange
- handler function to update thecheckoutForm
handleOnSubmitCheckoutForm
- handler function to submit the user's order to the API
- Should render two
input
elements, each with theclassName
ofcheckout-form-input
- The
checkoutForm
prop should supply the correct props needed to create the two controlled inputs:- The first input should have:
- the
type
prop set toemail
- the
name
prop set toemail
- the
placeholder
prop set to[email protected]
- the
value
prop set bycheckoutForm.email
. - a valid
onChange
prop that uses thehandleOnCheckoutFormChange
function to update thecheckoutForm
state
- the
- The second input should have:
- the
type
prop set totext
- the
name
prop set toname
- the
placeholder
prop set toStudent Name
- the
value
prop set bycheckoutForm.name
. - a valid
onChange
prop that uses thehandleOnCheckoutFormChange
function to update thecheckoutForm
state
- the
- The first input should have:
- The
- Should render a
button
element with theclassName
ofcheckout-button
.- It should contain the text
Checkout
. - When clicked, it should call the
handleOnSubmit
function.- If that request fails, the
CheckoutForm
component should display an error message inside an element with theclassName
oferror
. - If the
POST
request is successful...- The
CheckoutForm
component should display a success message that contains the text"Success!"
inside an element with theclassName
ofsuccess
. - The
shoppingCart
should be emptied - The
checkoutForm
should be reset to its default state.
- The
- If that request fails, the
- It should contain the text
Routes - The API should contain a route mounted at the /store
endpoint
- It should respond to
GET
requests to/store
with an array of all products in the store in this format:{ "products": products }
- It should respond to
GET
requests to/store/:productId
with a single product based on the product's id using this JSON format:{ "product": product }
- It should allow
POST
requests to the/store
endpoint:- The endpoint should create purchase orders for users and save them to the
db.json
file - The endpoint should accept a request body that contains
shoppingCart
anduser
fields.- The
shoppingCart
field should contain the user's order.- This should be an array of objects.
- Each object in the array should have two fields:
- The
item
field should store an object of the item being purchased - The
itemId
field should store theid
of the item being purchased - The
quantity
field should store a number representing how many of that item the user is purchasing.
- The
- The
user
field should contain the name and email of the person placing the order. - When either the
shoppingCart
oruser
fields are missing, it should throw a400
error. - If there are duplicate items in the
shoppingCart
, it should throw a400
error. - If either the
quantity
oritemId
field is missing for any of the items in theshoppingCart
, a400
error should be thrown. - When both are there, it should calculate the total cost of all the items (including quantities), add a
8.75%
tax to the total, and create a new purchase object containing 6 required fields and 1 optional field:- required:
-
id
- the newid
of the purchase should be equal to one more than the current number of existing purchases -
name
- the name of the user making the purchase -
email
- the email of the user making the purchase -
order
- theshoppingCart
value sent in thePOST
request -
total
- the calculated total of the order -
createdAt
- a string representation of the date and time when the order was placed
-
- optional:
-
receipt
- text describing the order (what might go on a receipt)
-
- required:
- It should then send a JSON response back to the client with the new purchase like so:
{ "purchase": purchase }
. The response should have a201
status code for a resource created action.
- The
- The endpoint should create purchase orders for users and save them to the
Server - Create an Express server
- Wire up the appropriate middleware and error handlers in the app.js file
- Create a single GET request handler at the / endpoint. It should respond to all GET requests with a JSON object and a 200 status code. The JSON response should contain a single key of ping that stores the string value: pong. For example: { "ping": "pong" }.
- Have a server.js file that starts the app by listening on port 3001.
Models - The API should use a Store model that handles the following
- List all products currently in the db.json file
- Fetch a single product by its id
- Create a purchase order
Upon completing the Express API you should have
- Developed static and dynamic routes with an Express router
- Use a model to handle core API-related logic and data management
- Define custom classes to communicate errors from API to client-side application
- Handle all data persistence through JSON files
- Did the topics discussed in your labs prepare you to complete the assignment? Be specific, which features in your weekly assignment did you feel unprepared to complete?
I think both of the labs this week were helpful as always but I thought the Gift Giver Lab was generally more helpful since it more closely mirrored what we were working on in our project. The practice with GET and POST requests was very useful and the Gift Giver Lab also taught me the typical way projects are structured to connect the backend/frontend. I thought it wasn't super clear the best way to actually test the new features we were implementing in the weekly assignment so it would have been nice if there was some added clarity in that sense.
- If you had more time, what would you have done differently? Would you have added additional features? Changed the way your project responded to a particular event, etc.
If I had more time I would've added some additional styling on the orders page as I don't think it looks as polished as the rest of my site. I also think it would be interesting to have a dropdown menu/radio buttons that allow the user to select what value to search by. Right now I just have search by email but it would be cool to also search by product in the cart, quantity, etc.
- Reflect on your project demo, what went well? Were there things that maybe didn't go as planned? Did you notice something that your peer did that you would like to try next time?
Overall I would say the project went pretty well and I was most pleased with how I was able to use the concepts I learned from implementing the product grid to help with implementing the orders page. Most of my peers this week all had similar features as we were mostly just working on the backend but one other feature that I think would have been interesting to complete would be changing the order that the products are displayed on the home screen based on some sort of ranking metric.
- React Router DOM
- Axios
Give a shout out to somebody from your cohort that especially helped you during your project. This can be a fellow peer, instructor, TA, mentor, etc.
Thank you to Vincent (our primary TA) for continuing to answer my endless questions. Thank you to Phineas and Yilika for also being incredibly helpful. Thank you also to Mohan and Sunil for their continued support and bigger picture understanding of my internship as a whole!