These are the major features we’re looking for at each code review, in addition to your other progress.
If a route has a (*)
next to it, it means that it should require a logged in user to be present, if a route has a (**)
next to it, the logged in user should be the owner of the modified object. If a route has (*admin)
next to it, the logged in user must be an admin user (user.isAdmin === true
). Any (**)
route should also be accessible by any (*admin)
user.
Using the same notation above. If a user is authorized, show the component. If the user is not authorized, either (1) display an error message or (2) redirect the user to a different route/component (any component that would be appropriate)
- Create a
products
table definition with the following information:- id - serial; primary key
- name - not null
- description - not null
- price - not null
- imageURL - with a default value
- inStock - not null; default value false
- category - not null
- Create a
users
table definition with the following information:- id - serial; primary key
- firstName - not null
- lastName - not null
- email - unique; not null; must be a valid email format
- imageURL - with a default value
- username - unique; not null
- password - unique; not null
- "isAdmin" - not null; default value false
- Create an
orders
table definition with the following information:- id - serial; primary key
- status - default value 'created'. (can be created, cancelled, completed) - also optionally, processing
- NOTE: An order with an
orders.status = 'created'
is synonymous with a "cart"
- NOTE: An order with an
- "userId" - references users(id)
- "datePlaced" - date
- Create an
order_products
table definition with the following information:- id - serial; primary key
- "productId" - references products(id)
- "orderId" - references orders(id)
- price - not null
- quantity - not null; default value 0
-
getProductById
getProductById(id)
- return the product
-
getAllProducts
- select and return an array of all products
-
createProduct
createProduct(product)
- return the new product
-
GET /products
- Send back a list of all products in the database
-
GET /product/:productId
- Look up a product by id and send it back
- Write a component for a single product
- Display the single product component when the url matches
/product/:productId
- Add links to the navbar that can be used to navigate to the
/product/:productId
route - Write a component to display a list of all products (you might be able to reuse the single product component)
- Display the all-products component when the url matches
/products
- Add links to the navbar that can be used to navigate to the
/products
route
Congrats! You have completed your first vertical slice! Make sure to commit -m "feat(products): Add All Products and Single Products"
before moving on!
-
createUser
createUser({ username, password })
- make sure to hash the password before storing it to the database
-
getUser
getUser({ username, password })
- this should be able to verify the password against the hashed password
-
getAllUsers
getAllUsers()
- select all users. Return the user objects.
- do NOT return the passwords
-
getUserById
getUserById(id)
- select a user using the user's ID. Return the user object.
- do NOT return the password
-
getUserByUsername
getUserByUsername(username)
- select a user using the user's username. Return the user object.
-
POST /users/register
- Create a new user. Require
username
andpassword
, and hash password before saving user to DB. Require all passwords to be at least 8 characters long. - Throw errors for duplicate
username
, or password-too-short.
- Create a new user. Require
-
POST /users/login
- Log in the user. Require
username
andpassword
, and verify that plaintext login password matches the saved hashed password before returning a JSON Web Token. - Keep the
id
andusername
in the token.
- Log in the user. Require
-
GET /users/me
(*)
- Send back the logged-in user's data if a valid token is supplied in the header.
- Write a component for login
- Write a component for register
- Display the login/register components when the user is not logged in (either when url matches
/account/login
or/account/register
OR as a modal, or just at the top of the page). - Add links to the navbar that can be used to navigate to the
/account/login
or/account/register
components - Add a logout button that removes the token/user data from state and localstorage.
- Write a component for a single user's data
- Display the single user component when the url matches
/account
(*)
Nice! You have completed another vertical slice! Make sure to commit -m "feat(users): Login/Register"
before moving on!
-
getOrderById
getOrderById(id)
- return the order, include the order's products
-
getAllOrders
- select and return an array of orders, include their products
-
getOrdersByUser
getOrdersByUser({ username })
- select and return an array of orders made by user, include their products
-
getOrdersByProduct
getOrdersByProduct({ id })
- select and return an array of orders which have a specific
productId
in theirorder_products
join, include their products
-
getCartByUser
getCartByUser({ id })
orgetCartByUser(user)
- select one user's order (look up by
orders."userId"
) - ...an order that that has status = created
- return the order, include the order's products
-
createOrder
createOrder({ status, userId })
- create and return the new order
-
GET /orders
(*admin)
- Return a list of orders, include the products with them
-
GET /orders/cart
(*)
- Return the current user's order with
status='created'
(synonymous to a 'cart'). Use database adaptergetPendingOrderByUser
- Return the current user's order with
-
POST /orders
(*)
- Create a new order. Should initially be status = created.
-
GET /users/:userId/orders
(**)
- Get a list of orders for a particular user.
- Write a component for a single order's data
- Display the single order component when the url matches
/orders/:orderId
(**)
- Display the cart (using the single order component with the current user's in-progress order. Use the api call
GET /orders/cart
) when the url matches/cart
(*)
- Add "view cart" button to the navbar that can be used to navigate to the
/cart
route(*)
- Add Cart persistence
- for authenticated (logged in) users, using the database.
- for unauthenticated (guest) using localStorage.
- bonus: add ability to "merge" the localStorage cart with the database cart once a user logs in.
-
getOrderProductById
getOrderProductById(id)
- return the
order_products
-
addProductToOrder
addProductToOrder({ orderId, productId, price, quantity })
- if the
productId
is NOT on theorder
yet, create a neworder_products
- update the
order_products
quantity (add passed-in quantity to the currentorder_products
quantity) - update the
order_products
price - return the
order_products
-
updateOrderProduct
updateOrderProduct({ id, price, quantity })
- Find the order with
id
equal to the passed inid
- Update the
price
orquantity
as necessary
-
destroyOrderProduct
destroyOrderProduct(id)
- remove the single identified
order_products
from database
-
POST /orders/:orderId/products
(**)
- Add a single product to an order (using
order_products
). Prevent duplication on("orderId", "productId")
pair. If product already exists on order, increment quantity and update price.
- Add a single product to an order (using
-
PATCH /order_products/:orderProductId
(**)
- Update the quantity or price on the order product
-
DELETE /order_products/:orderProductId
(**)
- Remove a product from a order, use hard delete
- For each product NOT in cart
- Create add-to-cart button
- Up to you if you want this to increment previously-existing product quantity.
- Up to you if you want this to increment previously-existing product quantity.
- Create add-to-cart button
- For each product CURRENTLY in cart
- Create remove-from-cart button
- Create edit quantity drop-down
-
updateOrder
updateOrder({ id, status, userId })
- Find the order with
id
equal to the passed inid
- Don't update the order
id
, but do update thestatus
and/oruserId
, as necessary - Return the updated order
-
completeOrder
completeOrder({ id })
- Find the order with
id
equal to the passed inid
- Only update the
status
tocompleted
- Return the updated order
-
cancelOrder
cancelOrder(id)
- Update the order's status to
cancelled
-
PATCH /orders/:orderId
(**)
- Update an order, notably change status
-
DELETE /orders/:orderId
(**)
- Update the order's status to
cancelled
- Update the order's status to
- Write a component to display a checkout experience
- Display user data (perhaps reusing the single-user component)
- Display cart data (perhaps reusing the single-order component)
- Create a "Complete Order" button
- Updates the order status to completed
- Credit Card integration is in a future tier
- Display a success message, confirming the order status is now completed.
- Create a "Cancel Order" button
- Updates the order status to cancelled
- Display a success message, confirming the order is cancelled.
- Optionally, redirect user to another route (home?)
- Display the checkout component when the url matches
/cart/checkout
(*)
- Integrate Stripe
- For client side, use Stripe's prebuilt Checkout form, ideally with the "Custom" strategy. We recommend react-stripe-checkout in this case. Build a custom form and communicate with Stripe & your server via Stripe.js.
- For server side, use the
stripe
npm library (API docs here, tutorial here) to accept tokens from your front-end app and send charges via the Stripe API.
-
destroyProduct
destroyProduct({ id })
- hard delete a product.
- make sure to delete all the
order_products
whose product is the one being deleted. - make sure the
orders
for theorder_products
being deleted do not have a status = completed
-
updateProduct
updateProduct(product)
- don't try to update the
id
- do update the other fields (name, description, etc)
- return the updated product
-
updateUser
updateUser(user)
- don't try to update the
id
- do update the other fields (name, email, "isAdmin" etc)
- return the updated user
-
GET /users
(*admin)
- Send back all users
-
POST /products
(*admin)
Only admins can create a new product -
DELETE /products/:productId
(*admin)
Only admins can delete a product -
PATCH /products/:productId
(*admin)
Only admins can update a product -
GET /products/:productId/orders
(*admin)
Get a list of all orders which have that product in them -
PATCH /users/:userId
(*admin)
Only admins can update a user
- Admin - Users
- Write a component to display a list of all users
- Display the all-users component when the url matches
/users
(*admin)
- Add links to the navbar that can be used to navigate to the
/users
component(*admin)
- Display the all-users component when the url matches
- Write a component to display a single user
- Add a form to update the user in the component (notably, add ability to change "isAdmin" on any user)
- Display the single-user component when the url matches
/users/:userId
(*admin)
- Make a username clickable in the users list that can be used to navigate to the
/users/:userId
component(*admin)
- Write a form component to add a single user
- Display the add-user component when the url matches
/users/add
(*admin)
- Display the add-user component when the url matches
- Write a component to display a list of all users
- Admin - Orders - Display the multiple orders component (already written) when the url matches
/orders
, this time showing ALL users' orders(*admin)
- Admin - Products
- Write and display a component to create a new product
- Write and display a component to edit a product
- Add "delete product" button to products list
- Create a
reviews
table definition with the following information:- id - serial; primary key
- title - not null
- content - not null - must be at least x characters and no longer than y characters
- stars - integer not null (0 through 5)
- "userId" - references users(id)
- "productId" - references products(id)
- Ability to create/update/delete reviews for authenticated users
- For each product, in list view
- Display number ofreviews
- Display average stars
- For each product, in single view
- Display list of reviews, each showing title, content, user, stars
- Loading messages for all fetches
- Paginate product data
- Logged In Users - Orders
- Write a component to display all orders pertaining to the user (order history)
- Display the all-orders component when the url matches
/orders
(*)
- Email Confirmation
- Filter products with a search field
- Admin
- Trigger password reset for user