Skip to content

Commit

Permalink
Fix/remove client check (#322)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
dayhaysoos committed May 9, 2023
1 parent 92b7371 commit 275cb65
Show file tree
Hide file tree
Showing 25 changed files with 1,415 additions and 18 deletions.
3 changes: 3 additions & 0 deletions examples/nextjs-root-layout/.eslintrc.json
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
35 changes: 35 additions & 0 deletions examples/nextjs-root-layout/.gitignore
@@ -0,0 +1,35 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
34 changes: 34 additions & 0 deletions examples/nextjs-root-layout/README.md
@@ -0,0 +1,34 @@
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).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

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.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
7 changes: 7 additions & 0 deletions examples/nextjs-root-layout/jsconfig.json
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
4 changes: 4 additions & 0 deletions examples/nextjs-root-layout/next.config.js
@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}

module.exports = nextConfig
23 changes: 23 additions & 0 deletions examples/nextjs-root-layout/package.json
@@ -0,0 +1,23 @@
{
"name": "nextjs-root-layout",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"autoprefixer": "10.4.14",
"eslint": "8.40.0",
"eslint-config-next": "13.4.1",
"next": "13.4.1",
"postcss": "8.4.23",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.2",
"use-shopping-cart": "workspace:^3.1.5"
}
}

6 changes: 6 additions & 0 deletions examples/nextjs-root-layout/postcss.config.js
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
30 changes: 30 additions & 0 deletions examples/nextjs-root-layout/src/app/components/CartItem.js
@@ -0,0 +1,30 @@
import { useShoppingCart } from "use-shopping-cart";
import { formatCurrencyString } from "use-shopping-cart";
import Image from "next/image";

export default function CartItem({ item }) {
const { name, emoji, quantity, price } = item;
const { removeItem } = useShoppingCart();

const removeItemFromCart = () => {
removeItem(item.id);
};

return (
<div className="flex items-center gap-4 mb-3">
<p className="text-4xl">{emoji}</p>
<div>
{name} <span className="text-xs">({quantity})</span>
</div>
<div className="ml-auto">
{formatCurrencyString({ value: price, currency: "GBP" })}
</div>
<button
onClick={() => removeItemFromCart()}
className="hover:bg-emerald-50 transition-colors rounded-full duration-500 p-1"
>
<Image alt="delete icon" src="./trash.svg" width={20} height={20} />
</button>
</div>
);
}
55 changes: 55 additions & 0 deletions examples/nextjs-root-layout/src/app/components/CheckoutButton.js
@@ -0,0 +1,55 @@
import { useState } from "react";
import { useShoppingCart } from "use-shopping-cart";

export default function CheckoutButton() {
const [status, setStatus] = useState("idle");
const { redirectToCheckout, cartCount, totalPrice } = useShoppingCart();

async function handleClick(event) {
event.preventDefault();
if (cartCount > 0) {
setStatus("loading");
try {
const result = await redirectToCheckout();
if (result?.error) {
console.error(result);
setStatus("redirect-error");
}
} catch (error) {
console.error(error);
setStatus("redirect-error");
}
} else {
setStatus("no-items");
}
}

return (
<article className="mt-3 flex flex-col">
<div className="text-red-700 text-xs mb-3 h-5 text-center">
{totalPrice && totalPrice < 30
? "You must have at least £0.30 in your basket"
: cartCount && cartCount > 20
? "You cannot have more than 20 items"
: status === "redirect-error"
? "Unable to redirect to Stripe checkout page"
: status === "no-items"
? "Please add some items to your cart"
: null}
</div>
<button
onClick={handleClick}
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"
disabled={
(totalPrice && totalPrice < 30) ||
(cartCount && cartCount > 20) ||
status == "no-items"
? true
: false
}
>
{status !== "loading" ? "Proceed to checkout" : "Loading..."}
</button>
</article>
);
}
24 changes: 24 additions & 0 deletions examples/nextjs-root-layout/src/app/components/Layout.js
@@ -0,0 +1,24 @@
'use client'

import Head from 'next/head'
import NavBar from './NavBar'

export default function Layout({ children }) {
return (
<>
<Head>
<title>fresh</title>
<meta
name="description"
content="A simple website to show how to use use-shopping-cart"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<NavBar />
<main className="bg-[#f8f7f5] min-h-[calc(100vh-76px)] px-10 py-8">
<div className="container md:mx-auto md:max-w-[850px]">{children}</div>
</main>
</>
)
}
31 changes: 31 additions & 0 deletions examples/nextjs-root-layout/src/app/components/NavBar.js
@@ -0,0 +1,31 @@
'use client'

import { useShoppingCart } from 'use-shopping-cart'
import Image from 'next/image'
import Link from 'next/link'
import ShoppingCart from './ShoppingCart'

export default function NavBar() {
const { handleCartClick, cartCount } = useShoppingCart()
return (
<nav className="py-5 px-12 flex justify-between">
<Link href="/">
<p className="bg-white text-3xl font-bold underline underline-offset-4 decoration-wavy decoration-2 decoration-emerald-500">
fresh
</p>
</Link>
<button className="relative" onClick={() => handleCartClick()}>
<Image
src="./cart.svg"
width={40}
height={40}
alt="shopping cart icon"
/>
<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">
{cartCount}
</div>
</button>
<ShoppingCart />
</nav>
)
}
57 changes: 57 additions & 0 deletions examples/nextjs-root-layout/src/app/components/Product.js
@@ -0,0 +1,57 @@
'use client'

import { useState } from 'react'
import { formatCurrencyString } from 'use-shopping-cart'
import { useShoppingCart } from 'use-shopping-cart'

export default function Product({ product }) {
const { addItem } = useShoppingCart()
const { name, price, emoji } = product
const [quantity, setQuantity] = useState(1)

const decreaseQuantity = () => {
if (quantity > 1) {
setQuantity(quantity - 1)
}
}

const increaseQuantity = () => {
setQuantity(quantity + 1)
}

const addToCart = () => {
addItem(product, { count: quantity })
setQuantity(1)
}

return (
<article className="flex flex-col gap-3 bg-white p-8 rounded-xl shadow-md text-center mb-6">
<div className="text-8xl cursor-default">{emoji}</div>
<div className="text-lg">{name}</div>
<div className="text-2xl font-semibold mt-auto">
{formatCurrencyString({ value: price, currency: 'GBP' })}
</div>
<div className="flex justify-around items-center mt-4 mb-2 ">
<button
onClick={decreaseQuantity}
className="hover:text-emerald-500 hover:bg-emerald-50 w-8 h-8 rounded-full transition-colors duration-500"
>
-
</button>
<span className="w-10 text-center rounded-md mx-3">{quantity}</span>
<button
onClick={increaseQuantity}
className="hover:text-emerald-500 hover:bg-emerald-50 w-8 h-8 rounded-full transition-colors duration-500"
>
+
</button>
</div>
<button
onClick={() => addToCart()}
className="bg-emerald-50 hover:bg-emerald-500 hover:text-white transition-colors duration-500 text-emerald-500 rounded-md px-5 py-2"
>
Add to cart
</button>
</article>
)
}
25 changes: 25 additions & 0 deletions examples/nextjs-root-layout/src/app/components/ShoppingCart.js
@@ -0,0 +1,25 @@
import { useShoppingCart } from "use-shopping-cart";
import CartItem from "./CartItem";
import CheckoutButton from "./CheckoutButton";

export default function ShoppingCart() {
const { shouldDisplayCart, cartCount, cartDetails } = useShoppingCart();
return (
<div
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 ${
shouldDisplayCart ? "opacity-100" : "opacity-0"
}`}
>
{cartCount && cartCount > 0 ? (
<>
{Object.values(cartDetails ?? {}).map((entry) => (
<CartItem key={entry.id} item={entry} />
))}
<CheckoutButton />
</>
) : (
<div className="p-5">You have no items in your cart</div>
)}
</div>
);
}
23 changes: 23 additions & 0 deletions examples/nextjs-root-layout/src/app/components/providers.js
@@ -0,0 +1,23 @@
'use client'

import React from 'react'
import { CartProvider as USCProvider } from 'use-shopping-cart'

function CartProvider({ children }) {
return (
<USCProvider
mode="checkout-session"
stripe={'test'}
currency={'USD'}
successUrl={'https://example.com/success'}
cancelUrl={'https://example.com/cancel'}
allowedCountries={['US', 'GB', 'CA']}
billingAddressCollection={true}
>
{children}
</USCProvider>
)
}

export default CartProvider

0 comments on commit 275cb65

Please sign in to comment.