Skip to content

Commit 345d20b

Browse files
committed
Initial Commit
1 parent 4a8f1b6 commit 345d20b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+13582
-4416
lines changed

.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["next/babel"]
3+
}

.eslintrc.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"extends": "next/core-web-vitals"
2+
"extends": ["next/babel", "next/core-web-vitals"]
33
}

.gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
22

33
# dependencies
4-
/node_modules
4+
node_modules
55
/.pnp
66
.pnp.js
77

@@ -26,7 +26,7 @@ yarn-error.log*
2626
.pnpm-debug.log*
2727

2828
# local env files
29-
.env*.local
29+
.env
3030

3131
# vercel
3232
.vercel

components/Cart.jsx

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import React, { useRef } from 'react';
2+
import Link from 'next/link';
3+
import { AiOutlineMinus, AiOutlinePlus, AiOutlineLeft, AiOutlineShopping } from 'react-icons/ai';
4+
import { TiDeleteOutline } from 'react-icons/ti';
5+
import toast from 'react-hot-toast';
6+
7+
import { useStateContext } from '../context/StateContext';
8+
import { urlFor } from '../lib/client';
9+
import getStripe from '../lib/getStripe';
10+
11+
const Cart = () => {
12+
const cartRef = useRef();
13+
const { totalPrice, totalQuantities, cartItems, setShowCart, toggleCartItemQuanitity, onRemove } = useStateContext();
14+
15+
const handleCheckout = async () => {
16+
const stripe = await getStripe()
17+
// getStripe() is responsible for initiating a payemnt request with Stripe
18+
19+
const response = await fetch('/api/stripe', {
20+
method: 'POST',
21+
headers: {
22+
'Content-Type': 'application/json',
23+
},
24+
body: JSON.stringify(cartItems),
25+
});
26+
27+
if(response.statusCode === 500) return;
28+
29+
const data = await response.json();
30+
31+
toast.loading('Redirecting...');
32+
33+
stripe.redirectToCheckout({ sessionId: data.id });
34+
35+
}
36+
37+
38+
return (
39+
<div className="cart-wrapper" ref={cartRef}>
40+
<div className="cart-container">
41+
<button
42+
type="button"
43+
className="cart-heading"
44+
onClick={() => setShowCart(false)}>
45+
<AiOutlineLeft />
46+
<span className="heading">Your Cart</span>
47+
<span className="cart-num-items">({totalQuantities} items)</span>
48+
</button>
49+
50+
{cartItems.length < 1 && (
51+
<div className="empty-cart">
52+
<AiOutlineShopping size={150} />
53+
<h3>Your shopping bag is empty</h3>
54+
<Link href="/">
55+
<button
56+
type="button"
57+
onClick={() => setShowCart(false)}
58+
className="btn"
59+
>
60+
Continue Shopping
61+
</button>
62+
</Link>
63+
</div>
64+
)}
65+
66+
<div className="product-container">
67+
{cartItems.length >= 1 && cartItems.map((item) => (
68+
<div className="product" key={item._id}>
69+
<img src={urlFor(item?.image[0])} className="cart-product-image" />
70+
<div className="item-desc">
71+
<div className="flex top">
72+
<h5>{item.name}</h5>
73+
<h4>${item.price}</h4>
74+
</div>
75+
<div className="flex bottom">
76+
<div>
77+
<p className="quantity-desc">
78+
<span className="minus" onClick={() => toggleCartItemQuanitity(item._id, 'dec') }>
79+
<AiOutlineMinus />
80+
</span>
81+
<span className="num">{item.quantity}</span>
82+
<span className="plus" onClick={() => toggleCartItemQuanitity(item._id, 'inc') }><AiOutlinePlus /></span>
83+
</p>
84+
</div>
85+
<button
86+
type="button"
87+
className="remove-item"
88+
onClick={() => onRemove(item)}
89+
>
90+
<TiDeleteOutline />
91+
</button>
92+
</div>
93+
</div>
94+
</div>
95+
))}
96+
</div>
97+
{cartItems.length >= 1 && (
98+
<div className="cart-bottom">
99+
<div className="total">
100+
<h3>Subtotal:</h3>
101+
<h3>${totalPrice}</h3>
102+
</div>
103+
<div className="btn-container">
104+
<button type="button" className="btn" onClick={handleCheckout}>
105+
CHECKOUT
106+
</button>
107+
</div>
108+
</div>
109+
)}
110+
</div>
111+
</div>
112+
)
113+
}
114+
115+
export default Cart

components/Footer.jsx

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
import { AiFillInstagram, AiOutlineTwitter} from 'react-icons/ai';
3+
4+
const Footer = () => {
5+
return (
6+
<div className="footer-container">
7+
<p>2022 NullCandy &copy; All rights reserved</p>
8+
<p className="icons">
9+
<AiFillInstagram />
10+
<AiOutlineTwitter />
11+
</p>
12+
</div>
13+
)
14+
}
15+
16+
export default Footer

components/FooterBanner.jsx

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import Link from 'next/link';
3+
4+
import { urlFor } from '../lib/client';
5+
6+
const FooterBanner = ({ footerBanner: { discount, largeText1, largeText2, saleTime, smallText, midText, desc, product, buttonText, image } }) => {
7+
return (
8+
<div className="footer-banner-container">
9+
<div className="banner-desc">
10+
<div className="left">
11+
<p>{discount}</p>
12+
<h3>{largeText1}</h3>
13+
<h3>{largeText2}</h3>
14+
<p>{saleTime}</p>
15+
</div>
16+
<div className="right">
17+
<p>{smallText}</p>
18+
<h3>{midText}</h3>
19+
<p>{desc}</p>
20+
<Link href={`/product/${product}`}>
21+
<button type="button">{buttonText}</button>
22+
</Link>
23+
</div>
24+
25+
<img
26+
src={urlFor(image)} className="footer-banner-image"
27+
height={'100%'}
28+
/>
29+
</div>
30+
</div>
31+
)
32+
}
33+
34+
export default FooterBanner

components/HeroBanner.jsx

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react';
2+
import Link from 'next/link';
3+
4+
import { urlFor } from '../lib/client';
5+
6+
const HeroBanner = ({ heroBanner }) => {
7+
return (
8+
<div className="hero-banner-container">
9+
<div>
10+
<p className="beats-solo">{heroBanner.smallText}</p>
11+
<h3>{heroBanner.midText}</h3>
12+
<h1>{heroBanner.largeText1}</h1>
13+
<img src={urlFor(heroBanner.image)} alt="headphones" className="hero-banner-image" />
14+
15+
<div>
16+
<Link href={`/product/${heroBanner.product}`}>
17+
<button type="button">{heroBanner.buttonText}</button>
18+
</Link>
19+
<div className="desc">
20+
<h5>Description</h5>
21+
<p>{heroBanner.desc}</p>
22+
</div>
23+
</div>
24+
</div>
25+
</div>
26+
)
27+
}
28+
29+
export default HeroBanner

components/Layout.jsx

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Head from 'next/head'
2+
import React from 'react'
3+
import Footer from './Footer'
4+
import Navbar from './Navbar'
5+
6+
const Layout = ({ children }) => {
7+
return (
8+
<div className='layout'>
9+
<Head>
10+
<title>NullCandy</title>
11+
</Head>
12+
<header>
13+
<Navbar />
14+
</header>
15+
<main className='main-container'>
16+
{children}
17+
</main>
18+
<footer>
19+
<Footer />
20+
</footer>
21+
</div>
22+
)
23+
}
24+
25+
export default Layout

components/Navbar.jsx

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React from 'react';
2+
import Link from 'next/link';
3+
import { AiOutlineShopping } from 'react-icons/ai'
4+
5+
import { Cart } from './';
6+
import { useStateContext} from '../context/StateContext';
7+
8+
const Navbar = () => {
9+
const { showCart, setShowCart, totalQuantities } = useStateContext();
10+
11+
return (
12+
<div className="navbar-container">
13+
<p className="logo">
14+
<Link href="/">NullCandy</Link>
15+
</p>
16+
17+
<button type="button" className="cart-icon" onClick={() => setShowCart(true)}>
18+
<AiOutlineShopping />
19+
<span className="cart-item-qty">{totalQuantities}</span>
20+
</button>
21+
22+
{showCart && <Cart />}
23+
</div>
24+
)
25+
}
26+
27+
export default Navbar

components/Product.jsx

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react'
2+
import Link from 'next/link'
3+
import {urlFor} from '../lib/client'
4+
5+
6+
const Product = ({ product: {image, name, price, slug } }) => {
7+
return (
8+
<div>
9+
<Link href={`/product/${slug.current}`}>
10+
<div className="product-card">
11+
<img
12+
src={urlFor(image && image[0])}
13+
width={250}
14+
height={250}
15+
className="product-image"
16+
/>
17+
<p className="product-name">{name}</p>
18+
<p className="product-price">${price}</p>
19+
</div>
20+
</Link>
21+
</div>
22+
)
23+
}
24+
25+
export default Product

components/index.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export { default as Footer } from './Footer';
2+
export { default as Layout } from './Layout';
3+
export { default as Navbar } from './Navbar';
4+
export { default as Product } from './Product';
5+
export { default as HeroBanner } from './HeroBanner';
6+
export { default as FooterBanner } from './FooterBanner';
7+
export { default as Cart } from './Cart';
8+

0 commit comments

Comments
 (0)