Skip to content

Commit

Permalink
Manage cart product
Browse files Browse the repository at this point in the history
  • Loading branch information
hozayves committed Jul 16, 2024
1 parent f38f190 commit 5a58052
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import CategoriesPage from './pages/CategoriesPage';
import ResetPassword from './pages/ResetPassword';
import NewPassword from './pages/NewPassword';
import { ProductDetail } from './pages/product/ProductDetail';
import Cart from './components/cart/Cart';

const App = () => {
const { data, error, isLoading } = useGetProductsQuery();
Expand Down Expand Up @@ -70,6 +71,10 @@ const App = () => {
path: 'auth/success/:token',
element: <GoogleAuthSuccess />,
},
{
path: 'shoppingcart',
element: <Cart />
},
{
path: 'categories/:categoryId',
element: <CategoriesPage />,
Expand Down
66 changes: 66 additions & 0 deletions src/components/cart/Cart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useEffect, useState } from "react"
import Navbar from "../navbar/Navbar"
import CartItem from "./CartItem"
import { useGetCartsQuery } from "../../services/cartApi"
import { Link } from "react-router-dom"

const Cart: React.FC = () => {
const { data: carts, isLoading, isSuccess, isError, error } = useGetCartsQuery()
const [totalPrice, setTotalPrice] = useState(0)
const [items, setItems] = useState(0)

useEffect(() => {
if (isSuccess && carts) {
// Calculate total price
const calculatedTotalPrice = carts.cartProducts.reduce((total, cart) => {

Check failure on line 15 in src/components/cart/Cart.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Property 'cartProducts' does not exist on type 'Cart[]'.

Check failure on line 15 in src/components/cart/Cart.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Parameter 'total' implicitly has an 'any' type.

Check failure on line 15 in src/components/cart/Cart.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Parameter 'cart' implicitly has an 'any' type.
const pricePerItem = cart.sizes[0]?.price || 0;
const itemTotalPrice = pricePerItem * cart.quantity;
return total + itemTotalPrice;
}, 0);

// Update total price state
setTotalPrice(calculatedTotalPrice);

// Optionally calculate the number of items if needed
const totalItems = carts.cartProducts.length;

Check failure on line 25 in src/components/cart/Cart.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Property 'cartProducts' does not exist on type 'Cart[]'.
setItems(totalItems);
}
}, [isSuccess, carts]);

let content;
if (isLoading) {
content = <div>Loading</div>
} else if (isSuccess) {
console.log(carts)
const renderedCart = carts.cartProducts.map((cart, index) => (

Check failure on line 35 in src/components/cart/Cart.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Property 'cartProducts' does not exist on type 'Cart[]'.

Check failure on line 35 in src/components/cart/Cart.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Parameter 'cart' implicitly has an 'any' type.

Check failure on line 35 in src/components/cart/Cart.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Parameter 'index' implicitly has an 'any' type.
< CartItem key={index} {...cart} />
))
content = renderedCart
} else if (isError) {
content = <div>{error.toString()}</div>
}
return (
<div className="w-full min-h-screen overflow-x-hidden font-roboto flex flex-col 2xl:items-center">
<Navbar />
<div className="w-full px-3 pt-5 flex flex-col gap-2 md:p-4 xl:px-10 2xl:w-[1440px]">
<h1 className="leading-none font-semibold text-lg subpixel-antialiased tracking-wide">Shopping Cart ({items})</h1>
<p className="leading-none subpixel-antialiased text-xs tracking-wide font-light hidden md:block">Your cart contains {items} items and comes to a total of ${totalPrice}</p>
<div className="flex flex-col md:flex-row-reverse lg:gap-10 xl:gap-12">
<div className="flex flex-col gap-4 px-3 py-2 pt-4 md:flex-grow lg:w-72 lg:flex-grow-0 xl:flex-grow-0 xl:w-80">
<div className="flex justify-between text-sm font-medium">
<span className="md:font-semibold md:text-lg">Total:</span>
<span className="md:font-semibold md:text-lg">${totalPrice}</span>
</div>
<Link to="/checkoutbag"
className="leading-none bg-greenColor hover:bg-[#1a6461] rounded-full px-5 py-3 text-whiteColor font-medium text-xs transition-all delay-75 ease-in cursor-pointer text-center"
>Checkout</Link>
</div>
<div className="bg-[#f7f7f7] flex flex-col py-3 mt-2 md:flex-grow">
{content}
</div>
</div>
</div>
</div>
)
}
export default Cart
83 changes: 83 additions & 0 deletions src/components/cart/CartItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useEffect, useState } from 'react'
import { useDeleteCartMutation, useUpdateCartMutation } from '../../services/cartApi'
import { useAddProductToWishlistMutation } from '../../services/wishlistApi'

Check failure on line 3 in src/components/cart/CartItem.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

'useAddProductToWishlistMutation' is declared but its value is never read.
interface CartItem {
id: string,
name: string,
image: string,
price: number,
sizes: [],
quantity: number
}
const CartItem: React.FC<CartItem> = ({ id, name, image, sizes, quantity }) => {
const [itemTotalPrice, setItemTotalPrice] = useState(sizes[0].price * quantity)

Check failure on line 13 in src/components/cart/CartItem.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Object is possibly 'undefined'.

Check failure on line 13 in src/components/cart/CartItem.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Tuple type '[]' of length '0' has no element at index '0'.
const [updateCart] = useUpdateCartMutation()
const [deleteCart, { isLoading }] = useDeleteCartMutation()
const currentPrice = sizes[0].price;

useEffect(() => {
setItemTotalPrice(currentPrice * quantity);
}, [quantity, currentPrice]);

const addQty = (quantity, productId, sizeId) => {
const newQuantity = quantity + 1;
updateCart({ id, updatedCart: { productId, quantity: newQuantity, sizeId } })
}
const removeQty = (quantity, productId, sizeId) => {
const newQuantity = quantity - 1;
if (newQuantity > 0) {
updateCart({ id, updatedCart: { productId, quantity: newQuantity, sizeId } })
}
}
return (
<>
<div className="flex gap-1 border-b border-b-grayColor py-2 px-3">
<div className="h-36 max-w-[98px] min-w-[100px]">
<img src={image} alt="" className="w-full h-36" />
</div>
<div className="flex flex-col gap-2 ml-3 w-full">
<div className="flex justify-between font-semibold">
<h3 className="leading-5 w-2/3 text-wrap capitalize text-sm break-words font-medium tracking-normal outline-none
md:text-lg">{name}</h3>
<span className="text-xs md:text-base">${itemTotalPrice}</span>
</div>
<div className="">
<div className="flex flex-col gap-2 md:gap-1">
<div className="flex flex-col gap-1 font-light mt-1 md:gap-1">
<label htmlFor="size" className="leading-none text-xs opacity-70">Size</label>
<div id="size" className="border border-greenColor rounded-full h-9 p-[6px] text-sm bg-whiteColor w-4/5 font-medium outline-none md:w-1/2 justify-start flex items-center leading-none px-3">
{sizes[0].size !== null ? sizes[0].size : 'One'}
</div>
</div>
<div className="flex flex-col font-light gap-1 mt-2">
<label htmlFor="quantity" className="leading-none text-xs opacity-70">Quantity</label>
<div>
<button
onClick={() => removeQty(quantity, id, sizes[0].id)}
className="border w-10 h-9 text-greenColor rounded-l-full bg-whiteColor cursor-pointer text-center"
>-</button>
<input type="text" value={quantity} className="h-9 w-9 border-t border-t-greenColor border-b text-center outline-none" />
<button
onClick={() => addQty(quantity, id, sizes[0].id)}
className="border w-10 h-9 text-greenColor rounded-r-full bg-whiteColor cursor-pointer text-center"
>+</button>
</div>
</div>
<div className="flex justify-end gap-2 mt-2 flex-col sm:flex-row">
{/* <button
onClick={() => addProductToWishlist(sizes[0].id)}
className="border border-greenColor rounded-full px-3 py-1 text-xs font-medium cursor-pointer"
>Save For Later</button> */}
<button
onClick={() => deleteCart({ productId: id, sizeId: sizes[0].id })}
className="border border-greenColor rounded-full px-3 py-1 text-xs font-medium cursor-pointer"
>Remove</button>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default CartItem
46 changes: 44 additions & 2 deletions src/services/cartApi.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,50 @@
import { mavericksApi } from '.';
import { ICart } from '../utils/schemas';
import { DeleteCartQueryParams, ICart } from '../utils/schemas';



export const cartApi = mavericksApi.injectEndpoints({
endpoints: builder => ({
getCarts: builder.query<Cart[], void>({
query: () => '/cart',
providesTags: ["Carts"]
}),
updateCart: builder.mutation({
query: ({ id, updatedCart }) => ({
url: `/cart/${id}`,
method: 'PATCH',
body: updatedCart
}),
invalidatesTags: ["Carts"],
// async onQueryStarted({ id, updatedCart }, { dispatch, queryFulfilled }) {
// const patchResult = dispatch(
// mavericksApi.util.updateQueryData('getCarts', undefined, draft => {
// console.log(draft)
// const cart = draft.find(cart => cart.id === id)
// if (cart) {
// const size = cart.sizes.find(size => size.id === updatedCart.sizeId)
// if (size) {
// cart.quantity = updatedCart.quantity;
// size.price = updatedCart.price
// }
// }
// })
// );
// try {
// await queryFulfilled
// } catch (error) {
// patchResult.undo()
// }
// }
}),
deleteCart: builder.mutation<void, DeleteCartQueryParams>({
query: ({ productId, sizeId }) => ({
url: "cart/delete",
method: 'DELETE',
body: { productId, sizeId }
}),
invalidatesTags: ["Carts"]
}),
addProductToCart: builder.mutation({
query: (cart: ICart) => ({
url: 'cart',
Expand All @@ -14,4 +56,4 @@ export const cartApi = mavericksApi.injectEndpoints({
overrideExisting: false,
});

export const { useAddProductToCartMutation } = cartApi;
export const { useGetCartsQuery, useDeleteCartMutation, useUpdateCartMutation, useAddProductToCartMutation } = cartApi;
2 changes: 1 addition & 1 deletion src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const mavericksApi = createApi({
return headers;
},
}),
tagTypes: ['Notifications'],
tagTypes: ['Notifications', 'Carts'],

endpoints: () => ({}),
});
1 change: 1 addition & 0 deletions src/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export interface ICart {
sizeId: string | null | undefined;
quantity?: number;
}
export type DeleteCartQueryParams = Pick<ICart, 'productId' | 'sizeId'>

export interface IError extends Error {
data: any;
Expand Down

0 comments on commit 5a58052

Please sign in to comment.