Skip to content

Instantly share code, notes, and snippets.

@jaisonrobson
Created August 24, 2022 00:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaisonrobson/bbce72d8f406e0eeb2017ef6d6fe1e51 to your computer and use it in GitHub Desktop.
Save jaisonrobson/bbce72d8f406e0eeb2017ef6d6fe1e51 to your computer and use it in GitHub Desktop.
Modern Full Stack ECommerce Next.js Tutorial Solution for ShoppingCart
import React, { useState } from 'react'
import { AiOutlineMinus, AiOutlinePlus, AiFillStar, AiOutlineStar } from 'react-icons/ai'
import { client, urlFor } from '../../lib/client'
import { Product } from '../../components'
import { useShoppingCartContext } from '../../contexts/ShoppingCartContext'
const ProductDetails = ({
data: {
selectedProduct,
products,
} = {},
}) => {
const {
image,
name,
details,
price
} = selectedProduct
const [selectedImageIndex, setSelectedImageIndex] = useState(0)
const [quantity, setQuantity] = useState(1)
const increaseQuantity = () => setQuantity(prev => prev + 1)
const decreaseQuantity = () => setQuantity(prev => (prev-1 < 1) ? prev : prev - 1)
const { onAddCartItem } = useShoppingCartContext()
return (
<div>
<div className="product-detail-container">
<div>
<div className="image-container">
<img
className="product-detail-image"
src={urlFor(image && image[selectedImageIndex])}
/>
</div>
<div className="small-images-container">
{image?.map((item, index) => (
<img
className={
index === selectedImageIndex
? "small-image selected-image"
: "small-image"
}
src={urlFor(item)}
onMouseEnter={() => setSelectedImageIndex(index)}
key={item._id}
/>
))}
</div>
</div>
<div className="product-detail-desc">
<h1>{name}</h1>
<div className="reviews">
<div>
<AiFillStar />
<AiFillStar />
<AiFillStar />
<AiFillStar />
<AiOutlineStar />
</div>
<p>(20)</p>
</div>
<h4>Detalhes: </h4>
<p>{details}</p>
<p className="price">R${price}</p>
<div className="quantity">
<h3>Quantidade:</h3>
<p className="quantity-desc">
<span
className="minus"
onClick={decreaseQuantity}
>
<AiOutlineMinus />
</span>
<span
className="num"
onClick=""
>
{quantity}
</span>
<span
className="plus"
onClick={increaseQuantity}
>
<AiOutlinePlus />
</span>
</p>
</div>
<div className="buttons">
<button
className="add-to-cart"
type="button"
onClick={() => onAddCartItem(selectedProduct._id, quantity)}
>
Adicionar ao carrinho
</button>
<button
className="buy-now"
type="button"
onClick=""
>
Comprar agora
</button>
</div>
</div>
</div>
<div className="maylike-products-wrapper">
<h2>Itens relacionados</h2>
<div className="marquee">
<div className="maylike-products-container track">
{products.map((product) => (
<Product key={product._id} data={product} />
))}
</div>
</div>
</div>
</div>
)
}
export const getStaticPaths = async () => {
const productsPathNames = await client.fetch(
`*[_type == "product"] {
slug {
current
}
}`
)
const paths = productsPathNames.map((product) => ({
params: {
slug: product.slug.current
}
}))
return {
paths,
fallback: 'blocking'
}
}
export const getStaticProps = async ({
params: {
slug,
} = {}
}) => {
const selectedProduct = await client.fetch(
`*[_type == "product" && slug.current == "${slug}"][0]`
)
const products = await client.fetch(`*[_type == "product"]`)
return {
props: { data: { products, selectedProduct } },
}
}
export default ProductDetails
import React, { useRef } from 'react'
import Link from 'next/link'
import { AiOutlineMinus, AiOutlinePlus, AiOutlineLeft, AiOutlineShopping } from 'react-icons/ai'
import { TiDeleteOutline } from 'react-icons/ti'
import toast from 'react-hot-toast'
import { useShoppingCartContext } from '../contexts/ShoppingCartContext'
import { urlFor } from '../lib/client'
const Cart = () => {
const cartRef = useRef()
const {
totalPrice,
totalItemsQuantity,
cartItems,
onHideCart,
increaseCartItemQuantity,
decreaseCartItemQuantity,
onRemoveCartItem,
} = useShoppingCartContext()
return (
<div className="cart-wrapper" ref={cartRef}>
<div className="cart-container">
<button
className="cart-heading"
type="button"
onClick={() => onHideCart()}
>
<AiOutlineLeft />
<span className="heading">Seu Carrinho</span>
<span className="cart-num-items">({totalItemsQuantity} itens)</span>
</button>
{cartItems.length < 1 && (
<div className="empty-cart">
<AiOutlineShopping size={150} />
<h3>Seu carrinho está vazio</h3>
<Link href="/">
<button
className="btn"
type="button"
onClick={() => onHideCart()}
>
Continuar comprando
</button>
</Link>
</div>
)}
<div className="product-container">
{cartItems.length >= 1 && cartItems.map(item => (
<div className="product" key={item._id}>
<img
className="cart-product-image"
src={urlFor(item?.image[0])}
/>
<div className="item-desc">
<div className="flex top">
<h5>{item.name}</h5>
<h4>R${item.price}</h4>
</div>
<div className="flex bottom">
<div>
<p className="quantity-desc">
<span
className="minus"
onClick={() => decreaseCartItemQuantity(item._id)}
>
<AiOutlineMinus />
</span>
<span
className="num"
onClick=""
>
{item.quantity}
</span>
<span
className="plus"
onClick={() => increaseCartItemQuantity(item._id)}
>
<AiOutlinePlus />
</span>
</p>
</div>
<button
className="remove-item"
type="button"
onClick={() => onRemoveCartItem(item._id)}
>
<TiDeleteOutline />
</button>
</div>
</div>
</div>
))}
</div>
{cartItems.length >= 1 && (
<div className="cart-bottom">
<div className="total">
<h3>Subtotal:</h3>
<h3>R${totalPrice}</h3>
</div>
<div className="btn-container">
<button
className="btn"
type="button"
onClick=""
>
Prosseguir com pagamento
</button>
</div>
</div>
)}
</div>
</div>
)
}
export default Cart
import React from 'react'
import Link from 'next/link'
import { AiOutlineShopping } from 'react-icons/ai'
import { Cart } from './'
import { useShoppingCartContext } from '../contexts/ShoppingCartContext'
const Navbar = () => {
const { showCart, onShowCart, totalItemsQuantity } = useShoppingCartContext()
return (
<div className="navbar-container">
<p className="logo">
<Link href="/">Loja Online</Link>
</p>
<button
className='cart-icon'
type="button"
onClick={() => onShowCart()}
>
<AiOutlineShopping />
<span className='cart-item-qty'>{totalItemsQuantity}</span>
</button>
{showCart && <Cart />}
</div>
)
}
export default Navbar
//This file was called "StateContext.js", you need to make the due changes
//I didn't continue the code after the stripe part, so u might need to change the others files manually according to this
import React, { createContext, useContext, useState, useEffect } from 'react'
import _ from 'lodash'
import { toast } from 'react-hot-toast'
import { client } from '../lib/client'
const Context = createContext()
export const useShoppingCartContext = () => useContext(Context)
export const ShoppingCartContext = ({ children }) => {
const [showCart, setShowCart] = useState(false)
const [cartItems, setCartItems] = useState([])
const [totalPrice, setTotalPrice] = useState(0)
const [totalItemsQuantity, setTotalItemsQuantity] = useState(0)
useEffect(() => {
calculateCart()
}, [cartItems])
/* INTERNAL METHODS */
const calculateCart = () => {
calculateCartTotalPrice()
calculateCartTotalItemsQuantity()
}
const calculateCartTotalPrice = () =>
setTotalPrice(
_.reduce(
cartItems,
(result, item) => result += item.price * item.quantity,
0
)
)
const calculateCartTotalItemsQuantity = () =>
setTotalItemsQuantity(
_.reduce(
cartItems,
(result, item) => result += item.quantity,
0
)
)
const onChangeCartItemQuantity = async (productId, quantity) => {
const product = await client.fetch(`*[_type == "product" && _id == "${productId}"]`)
const cartItemSearch = _.find(cartItems, (item) => item._id == productId)
if (cartItemSearch) {
const newCartItems = _.map(cartItems, (item) => {
if (item._id == productId) {
const newQuantity = item.quantity + quantity
return {
...item,
quantity: newQuantity > 0 ? newQuantity : 1
}
}
return item
})
setCartItems(newCartItems)
}
else if (quantity > 0) {
const newCartItem = {
...product[0],
quantity,
}
setCartItems((prevItems) => [...prevItems, newCartItem])
}
}
/* EXTERNAL METHODS */
const increaseCartItemQuantity = (productId) => onChangeCartItemQuantity(productId, 1)
const decreaseCartItemQuantity = (productId) => onChangeCartItemQuantity(productId, -1)
const onAddCartItem = (productId, quantity) => onChangeCartItemQuantity(productId, quantity)
const onRemoveCartItem = (productId) => {
const newCartItems = _.filter(cartItems, item => item._id != productId)
setCartItems(newCartItems)
}
const onShowCart = () => setShowCart(true)
const onHideCart = () => setShowCart(false)
return (
<Context.Provider
value={{
showCart,
onShowCart,
onHideCart,
cartItems,
onAddCartItem,
onRemoveCartItem,
increaseCartItemQuantity,
decreaseCartItemQuantity,
totalPrice,
totalItemsQuantity,
}}
>
{children}
</Context.Provider>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment