Last active
July 11, 2024 14:11
-
-
Save adrianhajdin/55e019f0ec6a48c0ca6d933d3cf0181c to your computer and use it in GitHub Desktop.
Build and Deploy a Modern Full Stack ECommerce Application with Stripe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html, | |
body, * { | |
padding: 0; | |
margin: 0; | |
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, | |
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; | |
box-sizing: border-box; | |
} | |
::-webkit-scrollbar { | |
width: 0px; | |
} | |
a { | |
color: inherit; | |
text-decoration: none; | |
} | |
.main-container{ | |
max-width: 1400px; | |
margin: auto; | |
width: 100%; | |
} | |
.layout{ | |
padding: 10px; | |
} | |
.navbar-container{ | |
display: flex; | |
justify-content: space-between; | |
margin: 6px 18px; | |
position: relative; | |
} | |
.marquee-text{ | |
font-size: 29px; | |
font-weight: 600; | |
margin: 60px 0px; | |
color: #f02d34; | |
} | |
.marquee { | |
position: relative; | |
height: 400px; | |
width: 100%; | |
overflow-x: hidden; | |
} | |
.track { | |
position: absolute; | |
white-space: nowrap; | |
will-change: transform; | |
animation: marquee 15s linear infinite; | |
width: 180%; | |
} | |
.track:hover { | |
animation-play-state: paused; | |
} | |
@keyframes marquee { | |
from { transform: translateX(0); } | |
to { transform: translateX(-50%); } | |
} | |
span.text-red { | |
-webkit-text-stroke: 1px #f02d34; | |
margin-left: 6px; | |
} | |
.logo{ | |
color: gray; | |
font-size: 18px; | |
} | |
.cart-icon{ | |
font-size: 25px; | |
color: gray; | |
cursor: pointer; | |
position: relative; | |
transition: transform .4s ease; | |
border: none; | |
background-color: transparent; | |
} | |
.cart-icon:hover{ | |
transform: scale(1.1,1.1); | |
} | |
.cart-item-qty{ | |
position: absolute; | |
right: -8px; | |
font-size: 12px; | |
color: #eee; | |
background-color: #f02d34; | |
width: 18px; | |
height: 18px; | |
border-radius: 50%; | |
text-align: center; | |
font-weight: 600; | |
} | |
.products-container{ | |
display: flex; | |
flex-wrap: wrap; | |
justify-content: center; | |
gap: 15px; | |
margin-top: 20px; | |
width: 100%; | |
} | |
.product-card{ | |
cursor: pointer; | |
transform: scale(1, 1); | |
transition: transform 0.5s ease; | |
color: #324d67; | |
} | |
.product-card:hover{ | |
transform: scale(1.1,1.1) | |
} | |
.product-image{ | |
border-radius: 15px; | |
background-color: #ebebeb; | |
transform: scale(1, 1); | |
transition: transform 0.5s ease; | |
} | |
.product-name{ | |
font-weight: 500; | |
} | |
.product-price{ | |
font-weight: 800; | |
margin-top: 6px; | |
color: black; | |
} | |
.hero-banner-container{ | |
padding: 100px 40px; | |
background-color: #dcdcdc; | |
border-radius: 15px; | |
position: relative; | |
height: 500px; | |
line-height: 0.9; | |
width: 100%; | |
} | |
.hero-banner-container .beats-solo{ | |
font-size: 20px; | |
} | |
.hero-banner-container button{ | |
border-radius: 15px; | |
padding: 10px 16px; | |
background-color: #f02d34; | |
color: white; | |
border: none; | |
margin-top: 40px; | |
font-size: 18px; | |
font-weight: 500; | |
cursor: pointer; | |
z-index:10000 !important; | |
} | |
.hero-banner-container h3{ | |
font-size: 4rem; | |
margin-top: 4px; | |
} | |
.hero-banner-container h1{ | |
color: white; | |
font-size: 10em; | |
margin-left: -20px; | |
text-transform: uppercase; | |
} | |
.hero-banner-image{ | |
position: absolute; | |
top: 0%; | |
right:20%; | |
width: 450px; | |
height: 450px; | |
} | |
.desc{ | |
position: absolute; | |
right: 10%; | |
bottom: 5%; | |
width: 300px; | |
line-height: 1.3; | |
display: flex; | |
flex-direction: column; | |
color: #324d67; | |
} | |
.desc p{ | |
color: #5f5f5f; | |
font-weight: 100; | |
text-align: end; | |
} | |
.desc h5{ | |
margin-bottom: 12px; | |
font-weight: 700; | |
font-size: 16px; | |
/* color: black; */ | |
align-self: flex-end; | |
} | |
.products-heading{ | |
text-align: center; | |
margin: 40px 0px; | |
color: #324d67; | |
} | |
.products-heading h2{ | |
font-size: 40px; | |
font-weight: 800; | |
} | |
.products-heading p{ | |
font-size: 16px; | |
font-weight: 200; | |
} | |
.footer-banner-container{ | |
padding: 100px 40px; | |
background-color: #f02d34; | |
border-radius: 15px; | |
position: relative; | |
height: 400px; | |
line-height: 1; | |
color: white; | |
width: 100%; | |
margin-top: 120px; | |
} | |
.banner-desc{ | |
display: flex ; | |
justify-content: space-between; | |
} | |
.banner-desc button{ | |
border-radius: 15px; | |
padding: 10px 16px; | |
background-color: white; | |
color: red; | |
border: none; | |
margin-top: 40px; | |
font-size: 18px; | |
font-weight: 500; | |
cursor: pointer; | |
} | |
.banner-desc .left h3{ | |
font-weight: 900; | |
font-size: 80px; | |
margin-left: 25px; | |
} | |
.banner-desc .left p{ | |
margin:18px; | |
} | |
.footer-banner-image{ | |
position: absolute; | |
/* top: -35%; | |
left: 8%; */ | |
top: -25%; | |
left: 25%; | |
} | |
.banner-desc .right{ | |
line-height: 1.4; | |
} | |
.banner-desc .right h3{ | |
font-weight: 800; | |
font-size: 60px; | |
} | |
.banner-desc .right p{ | |
font-size: 18px; | |
} | |
.banner-desc .right .company-desc{ | |
font-size: 14px; | |
font-weight: 300; | |
} | |
.cart-wrapper{ | |
width: 100vw; | |
background: rgba(0, 0, 0, 0.5); | |
position: fixed; | |
right: 0; | |
top: 0; | |
z-index: 100; | |
/* will-change: transform; */ | |
transition: all 1s ease-in-out; | |
} | |
.cart-container{ | |
height: 100vh; | |
width: 600px; | |
background-color: white; | |
float: right; | |
padding: 40px 10px; | |
position: relative; | |
} | |
.footer-container{ | |
color: #324d67; | |
text-align: center; | |
margin-top: 20px; | |
padding: 30px 10px; | |
font-weight: 700; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
gap: 10px; | |
justify-content: center; | |
} | |
.footer-container .icons{ | |
font-size: 30px; | |
display: flex; | |
gap: 10px; | |
} | |
.cart-heading{ | |
display: flex; | |
align-items: center; | |
font-size: 18px; | |
font-weight: 500; | |
cursor: pointer; | |
gap: 2px; | |
margin-left: 10px; | |
border: none; | |
background-color: transparent; | |
} | |
.cart-heading .heading{ | |
margin-left: 10px; | |
} | |
.cart-num-items{ | |
margin-left: 10px; | |
color: #f02d34; | |
} | |
.empty-cart{ | |
margin:40px; | |
text-align:center; | |
} | |
.empty-cart h3{ | |
font-weight: 600; | |
font-size: 20px; | |
} | |
.cancel{ | |
cursor: pointer; | |
} | |
.product-container{ | |
margin-top: 15px; | |
overflow: auto; | |
max-height: 70vh; | |
padding: 20px 10px; | |
} | |
.product{ | |
display: flex; | |
gap: 30px; | |
padding: 20px; | |
} | |
.product .cart-product-image{ | |
width:180px ; | |
height: 150px; | |
border-radius: 15px; | |
background-color: #ebebeb; | |
} | |
.item-desc .flex{ | |
display: flex; | |
justify-content: space-between; | |
width: 350px; | |
color: #324d67; | |
} | |
.item-desc .bottom{ | |
margin-top: 60px; | |
} | |
.flex h5{ | |
font-size: 24px; | |
} | |
.flex h4{ | |
font-size: 20px; | |
} | |
.total{ | |
display: flex; | |
justify-content: space-between; | |
} | |
.total h3{ | |
font-size: 22px; | |
} | |
.remove-item{ | |
font-size: 24px; | |
color: #f02d34; | |
cursor: pointer; | |
background: transparent; | |
border: none; | |
} | |
.cart-bottom{ | |
position: absolute; | |
bottom: 12px; | |
right: 5px; | |
width: 100%; | |
padding: 30px 65px; | |
} | |
.btn-container{ | |
width: 400px; | |
margin: auto; | |
} | |
.btn{ | |
width: 100%; | |
max-width: 400px; | |
padding: 10px 12px; | |
border-radius: 15px; | |
border: none; | |
font-size: 20px; | |
margin-top: 10px; | |
margin-top: 40px; | |
text-transform: uppercase; | |
background-color: #f02d34; | |
color: #fff; | |
cursor: pointer; | |
transform: scale(1, 1); | |
transition: transform 0.5s ease; | |
} | |
.btn:hover{ | |
transform: scale(1.1,1.1); | |
} | |
.product-detail-container{ | |
display: flex; | |
gap: 40px; | |
margin: 40px; | |
margin-top: 60px; | |
color: #324d67; | |
} | |
.product-detail-image{ | |
border-radius: 15px; | |
background-color: #ebebeb; | |
width: 400px; | |
height: 400px; | |
cursor: pointer; | |
transition: .3s ease-in-out; | |
} | |
.product-detail-image:hover{ | |
background-color: #f02d34; | |
} | |
.small-images-container{ | |
display: flex; | |
gap: 10px; | |
margin-top: 20px; | |
} | |
.small-image{ | |
border-radius: 8px; | |
background-color: #ebebeb; | |
width: 70px; | |
height: 70px; | |
cursor: pointer; | |
} | |
.selected-image{ | |
background-color:#f02d34; | |
} | |
.reviews{ | |
color: #f02d34; | |
margin-top: 10px; | |
display: flex; | |
gap: 5px; | |
align-items: center; | |
} | |
.product-detail-desc h4{ | |
margin-top: 10px; | |
} | |
.product-detail-desc p{ | |
margin-top: 10px; | |
} | |
.reviews p{ | |
color: #324d67; | |
margin-top: 0px; | |
} | |
.product-detail-desc .price{ | |
font-weight: 700 ; | |
font-size: 26px; | |
margin-top: 30px; | |
color:#f02d34; | |
} | |
.price .old-price, .product-price .old-price, .price .old-price{ | |
color: gray; | |
text-decoration: line-through; | |
} | |
.product-detail-desc .quantity{ | |
display: flex; | |
gap: 20px; | |
margin-top: 10px ; | |
align-items: center; | |
} | |
.product-detail-desc .buttons{ | |
display: flex; | |
gap: 30px; | |
} | |
.buttons .add-to-cart{ | |
padding: 10px 20px; | |
border: 1px solid #f02d34 ; | |
margin-top: 40px; | |
font-size: 18px; | |
font-weight: 500; | |
background-color: white; | |
color: #f02d34; | |
cursor: pointer; | |
width: 200px; | |
transform: scale(1, 1); | |
transition: transform 0.5s ease; | |
} | |
.buttons .add-to-cart:hover{ | |
transform:scale(1.1,1.1) | |
} | |
.buttons .buy-now{ | |
width: 200px; | |
padding: 10px 20px; | |
background-color: #f02d34; | |
color: white; | |
border: none; | |
margin-top: 40px; | |
font-size: 18px; | |
font-weight: 500; | |
cursor: pointer; | |
transform: scale(1, 1); | |
transition: transform 0.5s ease; | |
} | |
.buttons .buy-now:hover{ | |
transform:scale(1.1,1.1) | |
} | |
.quantity-desc{ | |
border: 1px solid gray; | |
padding: 6px; | |
} | |
.quantity-desc span{ | |
font-size: 16px; | |
padding: 6px 12px; | |
cursor: pointer; | |
} | |
.quantity-desc .minus{ | |
border-right: 1px solid gray; | |
color: #f02d34; | |
} | |
.quantity-desc .num{ | |
border-right: 1px solid gray; | |
font-size: 20px; | |
} | |
.quantity-desc .plus{ | |
color: rgb(49, 168, 49); | |
} | |
.maylike-products-wrapper{ | |
margin-top: 120px; | |
} | |
.maylike-products-wrapper h2{ | |
text-align: center; | |
margin: 50px; | |
color: #324d67; | |
font-size: 28px; | |
} | |
.maylike-products-container{ | |
display: flex; | |
justify-content: center; | |
gap: 15px; | |
margin-top: 20px; | |
} | |
.max-qty{ | |
font-weight: 500; | |
color: #f02d34; | |
} | |
.success-wrapper, .cancel-wrapper{ | |
background-color: white; | |
min-height: 60vh; | |
} | |
.success, .cancel{ | |
width: 1000px; | |
margin: auto; | |
margin-top: 160px; | |
background-color: #dcdcdc; | |
padding: 50px; | |
border-radius: 15px; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
flex-direction: column; | |
} | |
.success .icon { | |
color: green; | |
font-size: 40px; | |
} | |
.success h2{ | |
text-transform: capitalize; | |
margin-top: 15px 0px; | |
font-weight: 900; | |
font-size: 40px; | |
color:#324d67; | |
} | |
.success .email-msg{ | |
font-size: 16px; | |
font-weight: 600; | |
text-align: center; | |
} | |
.cancel p{ | |
font-size: 20px; | |
font-weight: 600; | |
} | |
.success .description{ | |
font-size: 16px; | |
font-weight: 600; | |
text-align: center; | |
margin: 10px; | |
margin-top: 30px; | |
} | |
.success .description .email{ | |
margin-left: 5px; | |
color: #f02d34; | |
} | |
.product-max-qty{ | |
margin-top: 10px; | |
} | |
@media screen and (max-width:800px) { | |
.hero-banner-container{ | |
height: 560px; | |
} | |
.hero-banner-image{ | |
width: 77%; | |
height: 62%; | |
top: -2%; | |
right: -6%; | |
} | |
.footer-banner-container{ | |
height: 560px; | |
margin-top: 80px; | |
} | |
.footer-banner-image{ | |
width: 77%; | |
left: 30%; | |
top: 6%; | |
height: 56% | |
} | |
.banner-desc .left h3{ | |
font-weight: 900; | |
font-size: 50px; | |
margin-left: 5px; | |
} | |
.banner-desc .left p{ | |
margin:18px; | |
} | |
.banner-desc .right h3{ | |
font-size: 45px; | |
} | |
.banner-desc .right p{ | |
font-size: 18px; | |
} | |
.banner-desc .right .company-desc{ | |
font-size: 14px; | |
} | |
.banner-desc{ | |
flex-wrap: wrap; | |
gap: 20px; | |
} | |
.hero-banner-container{ | |
line-height: 1.3; | |
} | |
.hero-banner-container h1{ | |
font-size: 50px; | |
} | |
.hero-banner-container h3{ | |
font-size: 40px; | |
} | |
.hero-banner-container button{ | |
margin-top: 90px; | |
z-index: 10000; | |
} | |
.desc{ | |
bottom: 60px; | |
} | |
.product-detail-container{ | |
flex-wrap: wrap; | |
} | |
.product-detail-container .product-detail-image{ | |
width: 350px; | |
height: 350px; | |
} | |
.cart-container{ | |
width: 415px; | |
padding: 4px; | |
} | |
.cart-heading{ | |
margin-top: 35px; | |
} | |
.product-container{ | |
margin-top: 10px; | |
} | |
.product{ | |
padding: 20px 5px; | |
} | |
.product .cart-product-image{ | |
width: 25%; | |
height: 25%; | |
} | |
.buttons .add-to-cart{ | |
width: 150px; | |
} | |
.buttons .buy-now{ | |
width: 150px; | |
} | |
.product-detail-container{ | |
margin: 20px; | |
} | |
.item-desc .flex{ | |
width: 200px; | |
} | |
.top{ | |
flex-wrap: wrap; | |
gap: 10px; | |
} | |
.item-desc .bottom{ | |
margin-top: 30px; | |
} | |
.flex h5{ | |
font-size: 16px; | |
color: #324d67; | |
} | |
.flex h4{ | |
font-size: 16px; | |
color: black; | |
} | |
.cart-bottom{ | |
padding: 30px; | |
} | |
.total h3{ | |
font-size: 20px; | |
} | |
.track { | |
animation: marquee 10s linear infinite; | |
width: 550%; | |
} | |
.success-wrapper, .cancel-wrapper{ | |
min-height: 69vh; | |
} | |
.success, .cancel { | |
width: 370px; | |
margin-top: 100px; | |
padding: 20px; | |
} | |
.success{ | |
height: 350px; | |
} | |
.success h2{ | |
font-size: 17px; | |
} | |
.btn-container{ | |
width: 300px; | |
margin: auto; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "sanity-ecommerce", | |
"version": "0.1.0", | |
"private": true, | |
"scripts": { | |
"dev": "next dev", | |
"build": "next build", | |
"start": "next start", | |
"lint": "next lint" | |
}, | |
"dependencies": { | |
"@sanity/client": "^3.2.0", | |
"@sanity/image-url": "^1.0.1", | |
"@stripe/stripe-js": "^1.25.0", | |
"canvas-confetti": "^1.5.1", | |
"next": "12.1.0", | |
"next-sanity-image": "^3.2.1", | |
"react": "17.0.2", | |
"react-dom": "17.0.2", | |
"react-hot-toast": "^2.2.0", | |
"react-icons": "^4.3.1", | |
"stripe": "^8.209.0" | |
}, | |
"devDependencies": { | |
"eslint": "^8.10.0", | |
"eslint-config-airbnb": "^19.0.4", | |
"eslint-config-next": "^12.1.0", | |
"eslint-plugin-import": "^2.25.4", | |
"eslint-plugin-jsx-a11y": "^6.5.1", | |
"eslint-plugin-react": "^7.29.3", | |
"eslint-plugin-react-hooks": "^4.3.0" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { createContext, useContext, useState, useEffect } from 'react'; | |
import { toast } from 'react-hot-toast'; | |
const Context = createContext(); | |
export const StateContext = ({ children }) => { | |
const getLocalStorage = (name) => { | |
if (typeof window !== 'undefined') { | |
const storage = localStorage.getItem(name); | |
if (storage) return JSON.parse(localStorage.getItem(name)); | |
if (name === 'cartItems') return []; | |
return 0; | |
} | |
}; | |
const [showCart, setShowCart] = useState(false); | |
const [cartItems, setCartItems] = useState(getLocalStorage('cartItems')); | |
const [totalPrice, setTotalPrice] = useState(getLocalStorage('totalPrice')); | |
const [totalQuantities, setTotalQuantities] = useState(getLocalStorage('totalQuantities')); | |
const [qty, setQty] = useState(1); | |
let findProduct; | |
let index; | |
useEffect(() => { | |
localStorage.setItem('cartItems', JSON.stringify(cartItems)); | |
localStorage.setItem('totalPrice', JSON.stringify(totalPrice)); | |
localStorage.setItem('totalQuantities', JSON.stringify(totalQuantities)); | |
}, [cartItems, totalPrice, totalQuantities]); | |
const onAdd = (product, quantity) => { | |
const checkProductInCart = cartItems.find( | |
(cartProduct) => cartProduct._id === product._id, | |
); | |
if (checkProductInCart) { | |
setTotalPrice(totalPrice + product.price * quantity); | |
setTotalQuantities(totalQuantities + quantity); | |
const updatedCartItems = cartItems.map((cartProduct) => { | |
if (cartProduct._id === product._id) { | |
return { ...cartProduct, quantity: cartProduct.quantity + quantity }; | |
} | |
return cartProduct; | |
}); | |
setCartItems(updatedCartItems); | |
toast.success(`${qty} ${product.name} added`); | |
} else { | |
setTotalPrice(totalPrice + product.price * quantity); | |
setTotalQuantities(totalQuantities + quantity); | |
// eslint-disable-next-line no-param-reassign | |
product.quantity = quantity; | |
setCartItems([...cartItems, { ...product }]); | |
toast.success(`${qty} ${product.name} added`); | |
} | |
}; | |
const onRemove = (product) => { | |
findProduct = cartItems.find((item) => item._id === product._id); | |
const tempCart = cartItems.filter((item) => item._id !== product._id); | |
setTotalPrice(totalPrice - findProduct.price * findProduct.quantity); | |
setTotalQuantities(totalQuantities - findProduct.quantity); | |
setCartItems(tempCart); | |
}; | |
const toggleCartItemQuantity = (id, value) => { | |
findProduct = cartItems.find((item) => item._id === id); | |
index = cartItems.findIndex((product) => product._id === id); | |
if (value === 'inc') { | |
findProduct.quantity += 1; | |
cartItems[index] = findProduct; | |
setTotalPrice(totalPrice + findProduct.price); | |
setTotalQuantities(totalQuantities + 1); | |
} | |
if (value === 'dec') { | |
if (findProduct.quantity > 1) { | |
findProduct.quantity -= 1; | |
cartItems[index] = findProduct; | |
setTotalPrice(totalPrice - findProduct.price); | |
setTotalQuantities(totalQuantities - 1); | |
} | |
} | |
}; | |
const incQty = () => { | |
setQty((oldQty) => { | |
const tempQty = oldQty + 1; | |
return tempQty; | |
}); | |
}; | |
const decQty = () => { | |
setQty((oldQty) => { | |
let tempQty = oldQty - 1; | |
if (tempQty < 1) { | |
tempQty = 1; | |
} | |
return tempQty; | |
}); | |
}; | |
return ( | |
<Context.Provider | |
// eslint-disable-next-line react/jsx-no-constructed-context-values | |
value={{ | |
onAdd, | |
onRemove, | |
cartItems, | |
totalPrice, | |
totalQuantities, | |
setShowCart, | |
setCartItems, | |
setTotalPrice, | |
setTotalQuantities, | |
showCart, | |
incQty, | |
decQty, | |
qty, | |
toggleCartItemQuantity, | |
}} | |
> | |
{children} | |
</Context.Provider> | |
); | |
}; | |
export const useStateContext = () => useContext(Context); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
can somebody share all the command for installing all the dependencies.