The Sweet As team have diversified! They are now selling a variety of organic foods, but have this time chosen to store their stock data in a database.
After cloning this repo
cd sweet-as-organics-api
npm install
npm run knex migrate:latest
npm run knex seed:run
npm run devCheck that everything is running on localhost:3000. The app should look quite familiar, but let's check out how it differs from Sweet As Beers.
This is the first time we've seen the full stack in play, redux included. With some features already complete, the code base is bigger than it has been in previous challenges.
Take some time to familiarise yourself with how it's all working together by exploring the ProductList component, specifically how the array of products is being retrieved from our API and stored in Redux. Feel free to step through it yourself if you're feeling confident/up for a challenge, or:
Try exploring these things:
- How the
productsgetting on toProductList's props. - How that
productsarray gets into the Redux store in the first place. What's happening inProductList'suseEffectmethod? - Check out that
fetchProductsaction creator. It returns a function rather than an object, which means it is an async action creator. It calls agetProducts()function. What does that function do? - On our server side, we have
/api/v1/productsGET route that uses a DB function - you could fire up a tool like Postman or Insomnia to see if this route works like you expect. - Follow the path back to the client side. How does the
productsdata get back to that asyncfetchProductsaction creator? What happens to the data then? - Open your Redux devtools, and as you refresh the Shop (ProductList) page, see how those dispatched actions update the store state. Can you confirm that understanding by taking a look at the reducers?
- What does setting the
waitingstate do in terms of UI? Using the timeline slider at the bottom of your Redux devtools is a good way to see how the UI is changing based on different actions. - Notice how both the
productsandwaitingreducers are watching for an action type ofFETCH_PRODUCTS_SUCCESS, so those two different parts of the Redux store state get updated from the one action!
Both the Shop and Cart pages are completed, with their data managed in the Redux store.
Your job will be to implement the functionality for the My Orders page. The React components and the database functions are already in place, so you'll be working with the stuff in the middle - Redux, API calls with Superagent and server side routes.
Once a user has their cart ready, they should be able to place their order with Sweet As Organics. Let's get the Place Order button working in Cart.
A potential approach could be:
- Create a new routes file (
server/routes/orders.js) and configure your server to use those routes with an/api/v1/ordersprefix. - Create a new POST route that uses the
addOrderfunction fromserver/db/orders.js.addOrderaccepts an order. It should have the same shape as thecartarray we have in Redux on our client side (i.e. you shouldn't need to reformat the cart data).- This route doesn't need to return anything, so it would make sense for your route to simply respond with a
201 (Created), and then returnnull.
- Test your route works as expected with a tool like Postman or Insomnia before continuing to the client side. Also browse your
dev.sqlite3file to ensure your order is being inserted. You should see rows added to both theordersandorders_productstables.
- Add a
client/api/orders.jsfile, and create apostOrderfunction that usessuperagentto make a POST request to the route you just made. (Remember it's going to need to send some order data.) - Create a
client/actions/orders.jsfile to hold your new action creators. Think about what you're going to need the new async action creator (perhaps call itplaceOrder) to do.- First, it should dispatch a pending action, so the user gets feedback that something is happening.
- Then use the
postOrderfunction fromclient/api/orders.jsto make the POST request. - We know our route only sends back a
201status, so we won't have any data to deal with when thepostOrderpromise resolves. - We should still dispatch a success action though, so our wait indicator stops spinning.
- A catch block is always a good idea ;)
- It's looking like the only part of the Redux store that cares about these
placeOrderactions is thewaitingstate - update thewaitingreducer so it sets the state totrueandfalseappropriately. - For the final piece of the puzzle, let's dispatch this
placeOrderaction from theCart.jsxPlace Order submit handler (remember to pass in thecart!). - Try it! Add something to your cart and place your order. Can you see the pending and success actions in your Redux devtools? Has your order been added to the database?
- Notice that even when placing the order occurs successfully, the cart doesn't empty. When a user starts shopping again, they would have to manually remove the previous order items from their cart, or end up with double ups! Perhaps the
cartreducer could also be watching for aPLACE_ORDER_SUCCESSaction? - It would also be great to redirect the user to the My Orders page once their order had been placed. However, we'd only want to redirect if the API call succeeds.
- The
dispatchfunction itself doesn't have a.then(), because it doesn't expect actions to be async. If we want to redirect after the order is placed, we'll need to do so inside the.then()in theplaceOrderaction creator. - You could pass
props.historyinto the action creator, along with thecart, and push onto it after you dispatch the success action.
- The
We've placed an order (WOO!)... but we need a way to see all the orders we've placed! This flow should be very similar to the fetchProducts for the Shop (ProductList) page.
You'll need:
- A new GET route in
server/routes/orders.jsthat usesdb.listOrders().- This db function returns an array of orders.
- Test your route works as you expect before moving on.
- A
getOrdersfunction inclient/api/orders.jsto make the API call to your new route. - A
fetchOrdersasync action creator, which dispatches pending and success actions, and calls thegetOrdersfunction. - A new
ordersreducer, which can watch for theFETCH_ORDERS_SUCCESSaction and set the store state to the newordersarray.- Be sure to import this new reducer into
client/reducers/index.jsand use it inside thecombineReducersso we get some orders showing up in our Redux store!
- Be sure to import this new reducer into
- Also make sure to update the
waitingreducer. - Dispatch your
fetchOrdersaction from auseEffecthook inOrderList.jsx. You'll need to import thedispatchhook and use it in the OrderList component. - Check your Redux devtools - can you see the orders?
OrderListis expecting to have anordersarray, but currently this is hardcoded to an empty array. You'll need to make use ofuseSelectorto get theordersfrom your Redux store into the component, and then we should have a snazzy list of orders displaying on the page!
Amazing! There are all of our orders - but currently they're all pending. Let's give our users a way to let Sweet As Organics know when they've received their order, or cancel their order if they make a mistake.
Sweet As Organics wants to keep track of all orders, even cancelled ones, so rather than deleting the order, we'll just change its status to cancelled. Likewise, we can change the status to completed once an order has been received.
You'll need:
- A PATCH route on your server side that uses
db.editOrderStatus(id: number, newStatus: string).editOrderStatusreturns the updated order, which you can respond with.- Test your route works as you expect before hitting it from the client side.
- A client side
patchOrderStatusfunction which makes the API call to that route, sending the new status and the id. - An
updateOrderStatusasync action creator, which dispatches pending and success actions.- The success action should have an
orderproperty, so you can update theordersarray inclient/reducers/orders.js. - Also make sure to update the
waitingreducer.
- The success action should have an
- Dispatch your
updateOrderStatusaction from thecancelOrderandcompleteOrderclick handlers inOrder.jsx. - Tip: use the strings
'cancelled'and'completed'for the new statuses to change the status symbol colour for the order - the CSS is already in place!
Give the Sweet As team some admin rights - add the ability to add, remove or update a product.
Write some tests! You've got the full stack available to you to test - write some that you feel you've had the least practice in.