Skip to content

Commit

Permalink
Add example/nextjs-app-router (#334)
Browse files Browse the repository at this point in the history
* setting up new nextjs example for SSR testing

* Add example/nextjs-app-router

* Remove tsconfig.json

* fix husky, remove nextjs test, update to use alpha version of USC

* update .husky file

* Add date-fns in package.json

* Refactor code as per next13 app router

---------

Co-authored-by: Nick DeJesus <[email protected]>
  • Loading branch information
bhattrajat and dayhaysoos authored Oct 16, 2023
1 parent 0df8624 commit 46d1e87
Show file tree
Hide file tree
Showing 30 changed files with 1,017 additions and 493 deletions.
5 changes: 5 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm test
./node_modules/.bin/lint-staged
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "nextjs-root-layout",
"name": "nextjs-app-router",
"version": "0.1.0",
"private": true,
"scripts": {
Expand All @@ -9,15 +9,16 @@
"lint": "next lint"
},
"dependencies": {
"@stripe/stripe-js": "^1.14.0",
"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",
"stripe": "^13.10.0",
"tailwindcss": "3.3.2",
"use-shopping-cart": "workspace:^3.1.5"
"use-shopping-cart": "3.2.0-alpha.0"
}
}

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useShoppingCart } from "use-shopping-cart";
import { formatCurrencyString } from "use-shopping-cart";
import Image from "next/image";
'use client'
import { useShoppingCart, 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 { name, emoji, quantity, price } = item
const { removeItem } = useShoppingCart()

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

return (
<div className="flex items-center gap-4 mb-3">
Expand All @@ -17,7 +17,7 @@ export default function CartItem({ item }) {
{name} <span className="text-xs">({quantity})</span>
</div>
<div className="ml-auto">
{formatCurrencyString({ value: price, currency: "GBP" })}
{formatCurrencyString({ value: price, currency: 'GBP' })}
</div>
<button
onClick={() => removeItemFromCart()}
Expand All @@ -26,5 +26,5 @@ export default function CartItem({ item }) {
<Image alt="delete icon" src="./trash.svg" width={20} height={20} />
</button>
</div>
);
)
}
61 changes: 61 additions & 0 deletions examples/nextjs-app-router/src/app/components/CheckoutButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { useState } from 'react'
import { useShoppingCart } from 'use-shopping-cart'

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

async function handleClick(event) {
event.preventDefault()
if (cartCount > 0) {
setStatus('loading')
try {
const res = await fetch('/session', {
method: 'POST',
body: JSON.stringify(cartDetails)
})
const data = await res.json()
const result = await redirectToCheckout(data.sessionId)
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>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ 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>
<nav className="py-5 bg-white px-12 flex text-black justify-between">
<Link
href="/"
className="bg-white text-3xl font-bold underline underline-offset-4 decoration-wavy decoration-2 decoration-emerald-500"
>
fresh
</Link>
<button className="relative" onClick={() => handleCartClick()}>
<Image
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use client'

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

export default function Product({ product }) {
const { addItem } = useShoppingCart()
Expand All @@ -25,7 +24,7 @@ export default function Product({ product }) {
}

return (
<article className="flex flex-col gap-3 bg-white p-8 rounded-xl shadow-md text-center mb-6">
<article className="flex text-black 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">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useShoppingCart } from "use-shopping-cart";
import CartItem from "./CartItem";
import CheckoutButton from "./CheckoutButton";
import { useShoppingCart } from 'use-shopping-cart'
import CartItem from './CartItem'
import CheckoutButton from './CheckoutButton'

export default function ShoppingCart() {
const { shouldDisplayCart, cartCount, cartDetails } = useShoppingCart();
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"
className={`bg-white text-black 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 ? (
Expand All @@ -21,5 +21,5 @@ export default function ShoppingCart() {
<div className="p-5">You have no items in your cart</div>
)}
</div>
);
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ function CartProvider({ children }) {
return (
<USCProvider
mode="checkout-session"
stripe={'test'}
stripe={process.env.NEXT_PUBLIC_STRIPE_KEY}
currency={'USD'}
successUrl={'https://example.com/success'}
cancelUrl={'https://example.com/cancel'}
allowedCountries={['US', 'GB', 'CA']}
billingAddressCollection={true}
>
Expand All @@ -20,4 +18,3 @@ function CartProvider({ children }) {
}

export default CartProvider

Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import './globals.css'
import { Inter } from 'next/font/google'

// import { CartProvider } from 'use-shopping-cart'
import CartProvider from './components/providers'
import Layout from './components/Layout'
import NavBar from './components/NavBar'

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app'
title: 'Fresh',
description:
'Next.js 13 app router example to show how to use use-shopping-cart'
}

export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
<CartProvider>
<Layout>{children}</Layout>
<NavBar />
{children}
</CartProvider>
</body>
</html>
)
}

16 changes: 16 additions & 0 deletions examples/nextjs-app-router/src/app/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Product from './components/Product'
import { products } from './data/products'

export default function Home() {
return (
<main className="bg-[#f8f7f5] min-h-[calc(100vh-76px)] px-10 py-8">
<div className="container md:mx-auto md:max-w-[850px]">
<div className="grid sm:grid-cols-2 md:grid-cols-4 justify-center mx-auto gap-4 place-center flex-wrap w-100 md:max-w-[900px]">
{products.map((product) => (
<Product product={product} key={product.id} />
))}
</div>
</div>
</main>
)
}
19 changes: 19 additions & 0 deletions examples/nextjs-app-router/src/app/session/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { stripe } from '../../lib/stripe'
import { headers } from 'next/headers'
import { products } from '../data/products'
import { validateCartItems } from 'use-shopping-cart/utilities'

export async function POST(request) {
const inventory = products
const cartProducts = await request.json()
const line_items = validateCartItems(inventory, cartProducts)
console.log('line_items', line_items)
const checkoutSession = await stripe.checkout.sessions.create({
mode: 'payment',
submit_type: 'pay',
line_items,
success_url: `${headers().get('origin')}/success`,
cancel_url: `${headers().get('origin')}/`
})
return Response.json({ sessionId: checkoutSession.id })
}
7 changes: 7 additions & 0 deletions examples/nextjs-app-router/src/app/success/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function SuccessPage() {
return (
<p className="text-black text-center">
Hi, Your order has been successfully placed
</p>
)
}
9 changes: 9 additions & 0 deletions examples/nextjs-app-router/src/lib/stripe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Stripe from 'stripe'
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
// https://github.com/stripe/stripe-node#configuration
apiVersion: '2023-08-16',
appInfo: {
name: 'projectname',
url: 'http://localhost:3000/'
}
})
55 changes: 0 additions & 55 deletions examples/nextjs-root-layout/src/app/components/CheckoutButton.js

This file was deleted.

24 changes: 0 additions & 24 deletions examples/nextjs-root-layout/src/app/components/Layout.js

This file was deleted.

13 changes: 0 additions & 13 deletions examples/nextjs-root-layout/src/app/page.js

This file was deleted.

Loading

0 comments on commit 46d1e87

Please sign in to comment.