Skip to content

Commit 275cb65

Browse files
authored
Fix/remove client check (#322)
* add nextjs example with root layout (this looks like dog shit rn) * adjust conditions for rendering with persistence * add "use client" directive to react modules (doesn't actually work tho) * update lib version
1 parent 92b7371 commit 275cb65

25 files changed

+1415
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "next/core-web-vitals"
3+
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
27+
# local env files
28+
.env*.local
29+
30+
# vercel
31+
.vercel
32+
33+
# typescript
34+
*.tsbuildinfo
35+
next-env.d.ts

examples/nextjs-root-layout/README.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
# or
10+
yarn dev
11+
# or
12+
pnpm dev
13+
```
14+
15+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16+
17+
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
18+
19+
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
20+
21+
## Learn More
22+
23+
To learn more about Next.js, take a look at the following resources:
24+
25+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27+
28+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29+
30+
## Deploy on Vercel
31+
32+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33+
34+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"paths": {
4+
"@/*": ["./src/*"]
5+
}
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {}
3+
4+
module.exports = nextConfig
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "nextjs-root-layout",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev",
7+
"build": "next build",
8+
"start": "next start",
9+
"lint": "next lint"
10+
},
11+
"dependencies": {
12+
"autoprefixer": "10.4.14",
13+
"eslint": "8.40.0",
14+
"eslint-config-next": "13.4.1",
15+
"next": "13.4.1",
16+
"postcss": "8.4.23",
17+
"react": "18.2.0",
18+
"react-dom": "18.2.0",
19+
"tailwindcss": "3.3.2",
20+
"use-shopping-cart": "workspace:^3.1.5"
21+
}
22+
}
23+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useShoppingCart } from "use-shopping-cart";
2+
import { formatCurrencyString } from "use-shopping-cart";
3+
import Image from "next/image";
4+
5+
export default function CartItem({ item }) {
6+
const { name, emoji, quantity, price } = item;
7+
const { removeItem } = useShoppingCart();
8+
9+
const removeItemFromCart = () => {
10+
removeItem(item.id);
11+
};
12+
13+
return (
14+
<div className="flex items-center gap-4 mb-3">
15+
<p className="text-4xl">{emoji}</p>
16+
<div>
17+
{name} <span className="text-xs">({quantity})</span>
18+
</div>
19+
<div className="ml-auto">
20+
{formatCurrencyString({ value: price, currency: "GBP" })}
21+
</div>
22+
<button
23+
onClick={() => removeItemFromCart()}
24+
className="hover:bg-emerald-50 transition-colors rounded-full duration-500 p-1"
25+
>
26+
<Image alt="delete icon" src="./trash.svg" width={20} height={20} />
27+
</button>
28+
</div>
29+
);
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { useState } from "react";
2+
import { useShoppingCart } from "use-shopping-cart";
3+
4+
export default function CheckoutButton() {
5+
const [status, setStatus] = useState("idle");
6+
const { redirectToCheckout, cartCount, totalPrice } = useShoppingCart();
7+
8+
async function handleClick(event) {
9+
event.preventDefault();
10+
if (cartCount > 0) {
11+
setStatus("loading");
12+
try {
13+
const result = await redirectToCheckout();
14+
if (result?.error) {
15+
console.error(result);
16+
setStatus("redirect-error");
17+
}
18+
} catch (error) {
19+
console.error(error);
20+
setStatus("redirect-error");
21+
}
22+
} else {
23+
setStatus("no-items");
24+
}
25+
}
26+
27+
return (
28+
<article className="mt-3 flex flex-col">
29+
<div className="text-red-700 text-xs mb-3 h-5 text-center">
30+
{totalPrice && totalPrice < 30
31+
? "You must have at least £0.30 in your basket"
32+
: cartCount && cartCount > 20
33+
? "You cannot have more than 20 items"
34+
: status === "redirect-error"
35+
? "Unable to redirect to Stripe checkout page"
36+
: status === "no-items"
37+
? "Please add some items to your cart"
38+
: null}
39+
</div>
40+
<button
41+
onClick={handleClick}
42+
className="bg-emerald-50 hover:bg-emerald-500 hover:text-white transition-colors duration-500 text-emerald-500 py-3 px-5 rounded-md w-100 disabled:bg-slate-300 disabled:cursor-not-allowed disabled:text-white"
43+
disabled={
44+
(totalPrice && totalPrice < 30) ||
45+
(cartCount && cartCount > 20) ||
46+
status == "no-items"
47+
? true
48+
: false
49+
}
50+
>
51+
{status !== "loading" ? "Proceed to checkout" : "Loading..."}
52+
</button>
53+
</article>
54+
);
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use client'
2+
3+
import Head from 'next/head'
4+
import NavBar from './NavBar'
5+
6+
export default function Layout({ children }) {
7+
return (
8+
<>
9+
<Head>
10+
<title>fresh</title>
11+
<meta
12+
name="description"
13+
content="A simple website to show how to use use-shopping-cart"
14+
/>
15+
<meta name="viewport" content="width=device-width, initial-scale=1" />
16+
<link rel="icon" href="/favicon.ico" />
17+
</Head>
18+
<NavBar />
19+
<main className="bg-[#f8f7f5] min-h-[calc(100vh-76px)] px-10 py-8">
20+
<div className="container md:mx-auto md:max-w-[850px]">{children}</div>
21+
</main>
22+
</>
23+
)
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use client'
2+
3+
import { useShoppingCart } from 'use-shopping-cart'
4+
import Image from 'next/image'
5+
import Link from 'next/link'
6+
import ShoppingCart from './ShoppingCart'
7+
8+
export default function NavBar() {
9+
const { handleCartClick, cartCount } = useShoppingCart()
10+
return (
11+
<nav className="py-5 px-12 flex justify-between">
12+
<Link href="/">
13+
<p className="bg-white text-3xl font-bold underline underline-offset-4 decoration-wavy decoration-2 decoration-emerald-500">
14+
fresh
15+
</p>
16+
</Link>
17+
<button className="relative" onClick={() => handleCartClick()}>
18+
<Image
19+
src="./cart.svg"
20+
width={40}
21+
height={40}
22+
alt="shopping cart icon"
23+
/>
24+
<div className="rounded-full flex justify-center items-center bg-emerald-500 text-xs text-white absolute w-6 h-5 bottom-6 -right-1">
25+
{cartCount}
26+
</div>
27+
</button>
28+
<ShoppingCart />
29+
</nav>
30+
)
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { formatCurrencyString } from 'use-shopping-cart'
5+
import { useShoppingCart } from 'use-shopping-cart'
6+
7+
export default function Product({ product }) {
8+
const { addItem } = useShoppingCart()
9+
const { name, price, emoji } = product
10+
const [quantity, setQuantity] = useState(1)
11+
12+
const decreaseQuantity = () => {
13+
if (quantity > 1) {
14+
setQuantity(quantity - 1)
15+
}
16+
}
17+
18+
const increaseQuantity = () => {
19+
setQuantity(quantity + 1)
20+
}
21+
22+
const addToCart = () => {
23+
addItem(product, { count: quantity })
24+
setQuantity(1)
25+
}
26+
27+
return (
28+
<article className="flex flex-col gap-3 bg-white p-8 rounded-xl shadow-md text-center mb-6">
29+
<div className="text-8xl cursor-default">{emoji}</div>
30+
<div className="text-lg">{name}</div>
31+
<div className="text-2xl font-semibold mt-auto">
32+
{formatCurrencyString({ value: price, currency: 'GBP' })}
33+
</div>
34+
<div className="flex justify-around items-center mt-4 mb-2 ">
35+
<button
36+
onClick={decreaseQuantity}
37+
className="hover:text-emerald-500 hover:bg-emerald-50 w-8 h-8 rounded-full transition-colors duration-500"
38+
>
39+
-
40+
</button>
41+
<span className="w-10 text-center rounded-md mx-3">{quantity}</span>
42+
<button
43+
onClick={increaseQuantity}
44+
className="hover:text-emerald-500 hover:bg-emerald-50 w-8 h-8 rounded-full transition-colors duration-500"
45+
>
46+
+
47+
</button>
48+
</div>
49+
<button
50+
onClick={() => addToCart()}
51+
className="bg-emerald-50 hover:bg-emerald-500 hover:text-white transition-colors duration-500 text-emerald-500 rounded-md px-5 py-2"
52+
>
53+
Add to cart
54+
</button>
55+
</article>
56+
)
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useShoppingCart } from "use-shopping-cart";
2+
import CartItem from "./CartItem";
3+
import CheckoutButton from "./CheckoutButton";
4+
5+
export default function ShoppingCart() {
6+
const { shouldDisplayCart, cartCount, cartDetails } = useShoppingCart();
7+
return (
8+
<div
9+
className={`bg-white flex flex-col absolute right-3 md:right-9 top-14 w-80 py-4 px-4 shadow-[0_5px_15px_0_rgba(0,0,0,.15)] rounded-md transition-opacity duration-500 ${
10+
shouldDisplayCart ? "opacity-100" : "opacity-0"
11+
}`}
12+
>
13+
{cartCount && cartCount > 0 ? (
14+
<>
15+
{Object.values(cartDetails ?? {}).map((entry) => (
16+
<CartItem key={entry.id} item={entry} />
17+
))}
18+
<CheckoutButton />
19+
</>
20+
) : (
21+
<div className="p-5">You have no items in your cart</div>
22+
)}
23+
</div>
24+
);
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use client'
2+
3+
import React from 'react'
4+
import { CartProvider as USCProvider } from 'use-shopping-cart'
5+
6+
function CartProvider({ children }) {
7+
return (
8+
<USCProvider
9+
mode="checkout-session"
10+
stripe={'test'}
11+
currency={'USD'}
12+
successUrl={'https://example.com/success'}
13+
cancelUrl={'https://example.com/cancel'}
14+
allowedCountries={['US', 'GB', 'CA']}
15+
billingAddressCollection={true}
16+
>
17+
{children}
18+
</USCProvider>
19+
)
20+
}
21+
22+
export default CartProvider
23+

0 commit comments

Comments
 (0)