├── .env ├── .eslintrc.json ├── .gitignore ├── .vercelignore ├── README.md ├── app ├── [category] │ └── page.tsx ├── components │ ├── AddToBag.tsx │ ├── CheckoutNow.tsx │ ├── Hero.tsx │ ├── ImageGallery.tsx │ ├── Navbar.tsx │ ├── Newest.tsx │ ├── Providers.tsx │ └── ShoppingCartModal.tsx ├── favicon.ico ├── globals.css ├── interface.ts ├── layout.tsx ├── lib │ └── sanity.ts ├── page.tsx ├── product │ └── [slug] │ │ └── page.tsx └── stripe │ ├── error │ └── page.tsx │ └── success │ └── page.tsx ├── components.json ├── components └── ui │ ├── button.tsx │ └── sheet.tsx ├── lib └── utils.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── HeroImages │ ├── 686dcf10-6030-4b31-967d-356f8b747732.webp │ └── ba06b5a3-3cc0-4730-957a-a484b2fcbaa9.webp ├── ProductFour │ ├── 2214184d-d5ab-4ac6-aac8-5670a08bb373.webp │ ├── 939da2cf-40ac-4963-ae88-a9098fd32929.jpg │ └── windrunner-herren-laufjacke-k4q9TV (1).jpeg ├── ProductOne │ ├── 3ee3420f-0395-47e9-b852-65c9e04a877d.webp │ ├── 600e6348-ab0e-4de4-9936-60a98594254f.webp │ └── air-vapormax-2023-flyknit-herrenschuh-xgCQsr.jpeg ├── ProductThree │ ├── 0453f3b6-c33b-44a6-bd68-3732ac2b8bf2.jpg │ ├── 3811255b-206e-4fd2-9477-1739c97ecc80.webp │ └── 72a3b44a-3e4c-44b7-b614-835b9d28bcc9.webp ├── ProductTwo │ ├── 795c66fd-1efc-4fb3-95c8-455ae2d390e9.png │ └── af318846-4fcb-4b8f-b13c-f7ddfe17d519.webp ├── next.svg └── vercel.svg ├── sanity ├── .eslintrc ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── sanity.cli.ts ├── sanity.config.ts ├── schemas │ ├── category.ts │ ├── heroImages.ts │ ├── index.ts │ └── product.ts ├── static │ └── .gitkeep └── tsconfig.json ├── tailwind.config.ts └── tsconfig.json /.env: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_STRIPE_KEY=pk_test_51O4Rq2HaexiOGySSjx149ZWEGTYULFYOe4uEadG1ivy2ugkXTtKCZoAqA1oboAQB65r6mmP4zcu1xCBq2vW8xwWW002NpKJvTI -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 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 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.vercelignore: -------------------------------------------------------------------------------- 1 | sanity -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Installation Command: 2 | 3 | ```bash 4 | npm i stripe use-shopping-cart next-sanity @stripe/stripe-js @sanity/image-url --force 5 | ``` 6 | 7 | ## Hero Images: 8 | https://github.com/ski043/nextjs-commerce-tutorial/tree/main/public/HeroImages 9 | 10 | ## Products: 11 | 12 | #Product One: 13 | Nike Air VaporMax 2023 Flyknit 14 | 15 | Price: 200 16 | 17 | Category: Men 18 | 19 | description: 20 | Elevate your sneaker game to new heights with the latest evolution of the iconic Air VaporMax series. The 2023 Flyknit combines cutting-edge technology, exceptional comfort, and bold style. Its innovative Flyknit upper offers a second-skin fit, ensuring a snug yet breathable feel with every step. The renowned VaporMax sole unit delivers unparalleled cushioning and responsiveness, providing a smooth ride that's perfect for both athletic performance and street-style fashion. 21 | 22 | images: https://github.com/ski043/nextjs-commerce-tutorial/tree/main/public/ProductOne 23 | 24 | 25 | #Product Two: 26 | Nike Sportswear Phoenix Fleece 27 | 28 | Price: 35 29 | 30 | Category: Women 31 | 32 | Description: 33 | Crafted with a blend of warmth and style, the Phoenix Fleece is a versatile addition to your wardrobe. Its soft and cozy fleece fabric offers a perfect balance of comfort and durability, making it ideal for cool days and relaxed outings. With a modern, sporty design and the iconic Nike Swoosh, this fleece adds a touch of urban flair to your look. Whether you're hitting the gym or hanging out with friends, the Nike Sportswear Phoenix Fleece keeps you both cozy and stylish. Elevate your everyday wear with this classic piece of Nike Sportswear. 34 | 35 | images: https://github.com/ski043/nextjs-commerce-tutorial/tree/main/public/ProductTwo 36 | 37 | #Product Three: 38 | Nike Air Force 1 '07 39 | 40 | Price: 85 41 | 42 | Category: Teens 43 | 44 | Description: 45 | The Nike Air Force 1 '07 represents a legend in the world of sneakers. With a design that transcends generations, this classic silhouette has remained a symbol of street-style culture for over three decades. Its white leather upper and clean lines are a canvas for self-expression, allowing you to pair it with any outfit, from casual to chic. 46 | 47 | Images: https://github.com/ski043/nextjs-commerce-tutorial/tree/main/public/ProductThree 48 | 49 | #Product Four 50 | Nike Windrunner 51 | 52 | Price: 200 53 | 54 | Category: Men 55 | 56 | Description: 57 | The Nike Windrunner is more than just a jacket; it's a symbol of enduring style and performance. With a design that has stood the test of time, this lightweight and versatile outerwear piece is your go-to choice for brisk mornings, breezy afternoons, and everything in between. Its distinctive chevron design on the chest pays homage to its heritage, while the modern materials and construction ensure it's ready for the demands of today. 58 | 59 | Images: https://github.com/ski043/nextjs-commerce-tutorial/tree/main/public/ProductFour 60 | 61 | ## Deploy on Vercel 62 | 63 | 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. 64 | 65 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 66 | -------------------------------------------------------------------------------- /app/[category]/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { simplifiedProduct } from "../interface"; 3 | import { client } from "../lib/sanity"; 4 | import Image from "next/image"; 5 | 6 | async function getData(cateogry: string) { 7 | const query = `*[_type == "product" && category->name == "${cateogry}"] { 8 | _id, 9 | "imageUrl": images[0].asset->url, 10 | price, 11 | name, 12 | "slug": slug.current, 13 | "categoryName": category->name 14 | }`; 15 | 16 | const data = await client.fetch(query); 17 | 18 | return data; 19 | } 20 | 21 | export const dynamic = "force-dynamic"; 22 | 23 | export default async function CategoryPage({ 24 | params, 25 | }: { 26 | params: { category: string }; 27 | }) { 28 | const data: simplifiedProduct[] = await getData(params.category); 29 | 30 | return ( 31 |
32 |
33 |
34 |

35 | Our Products for {params.category} 36 |

37 |
38 | 39 |
40 | {data.map((product) => ( 41 |
42 |
43 | Product image 50 |
51 | 52 |
53 |
54 |

55 | 56 | {product.name} 57 | 58 |

59 |

60 | {product.categoryName} 61 |

62 |
63 |

64 | ${product.price} 65 |

66 |
67 |
68 | ))} 69 |
70 |
71 |
72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /app/components/AddToBag.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import { useShoppingCart } from "use-shopping-cart"; 5 | import { urlFor } from "../lib/sanity"; 6 | 7 | export interface ProductCart { 8 | name: string; 9 | description: string; 10 | price: number; 11 | currency: string; 12 | image: any; 13 | price_id: string; 14 | } 15 | 16 | export default function AddToBag({ 17 | currency, 18 | description, 19 | image, 20 | name, 21 | price, 22 | price_id, 23 | }: ProductCart) { 24 | const { addItem, handleCartClick } = useShoppingCart(); 25 | 26 | const product = { 27 | name: name, 28 | description: description, 29 | price: price, 30 | currency: currency, 31 | image: urlFor(image).url(), 32 | price_id: price_id, 33 | }; 34 | return ( 35 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /app/components/CheckoutNow.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import { useShoppingCart } from "use-shopping-cart"; 5 | import { urlFor } from "../lib/sanity"; 6 | import { ProductCart } from "./AddToBag"; 7 | 8 | export default function CheckoutNow({ 9 | currency, 10 | description, 11 | image, 12 | name, 13 | price, 14 | price_id, 15 | }: ProductCart) { 16 | const { checkoutSingleItem } = useShoppingCart(); 17 | 18 | function buyNow(priceId: string) { 19 | checkoutSingleItem(priceId); 20 | } 21 | 22 | const product = { 23 | name: name, 24 | description: description, 25 | price: price, 26 | currency: currency, 27 | image: urlFor(image).url(), 28 | price_id: price_id, 29 | }; 30 | return ( 31 | 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /app/components/Hero.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import { client, urlFor } from "../lib/sanity"; 3 | import Link from "next/link"; 4 | 5 | async function getData() { 6 | const query = "*[_type == 'heroImage'][0]"; 7 | 8 | const data = await client.fetch(query); 9 | 10 | return data; 11 | } 12 | 13 | export default async function Hero() { 14 | const data = await getData(); 15 | return ( 16 |
17 |
18 |
19 |

20 | Top Fashion for a top price! 21 |

22 |

23 | We sell only the most exclusive and high quality products for you. 24 | We are the best so come and shop with us. 25 |

26 |
27 | 28 |
29 |
30 | Great Photo 38 |
39 | 40 |
41 | Great Photo 49 |
50 |
51 |
52 | 53 |
54 |
55 | 59 | Men 60 | 61 | 65 | Women 66 | 67 | 71 | Teens 72 | 73 |
74 |
75 |
76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /app/components/ImageGallery.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image from "next/image"; 4 | import { urlFor } from "../lib/sanity"; 5 | import { useState } from "react"; 6 | 7 | interface iAppProps { 8 | images: any; 9 | } 10 | 11 | export default function ImageGallery({ images }: iAppProps) { 12 | const [bigImage, setBigImage] = useState(images[0]); 13 | 14 | const handleSmallImageClick = (image: any) => { 15 | setBigImage(image); 16 | }; 17 | return ( 18 |
19 |
20 | {images.map((image: any, idx: any) => ( 21 |
22 | photo handleSmallImageClick(image)} 29 | /> 30 |
31 | ))} 32 |
33 | 34 |
35 | Photo 42 | 43 | 44 | Sale 45 | 46 |
47 |
48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /app/components/Navbar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | import Link from "next/link"; 5 | import { usePathname } from "next/navigation"; 6 | import { ShoppingBag } from "lucide-react"; 7 | import { useShoppingCart } from "use-shopping-cart"; 8 | 9 | const links = [ 10 | { name: "Home", href: "/" }, 11 | { name: "Men", href: "/Men" }, 12 | { name: "Women", href: "/Women" }, 13 | { name: "Teens", href: "/Teens" }, 14 | ]; 15 | 16 | export default function Navbar() { 17 | const pathname = usePathname(); 18 | const { handleCartClick } = useShoppingCart(); 19 | return ( 20 |
21 |
22 | 23 |

24 | NextCommerce 25 |

26 | 27 | 28 | 49 | 50 |
51 | 61 |
62 |
63 |
64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /app/components/Newest.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { simplifiedProduct } from "../interface"; 3 | import { client } from "../lib/sanity"; 4 | import { ArrowRight } from "lucide-react"; 5 | import Image from "next/image"; 6 | 7 | async function getData() { 8 | const query = `*[_type == "product"][0...4] | order(_createdAt desc) { 9 | _id, 10 | price, 11 | name, 12 | "slug": slug.current, 13 | "categoryName": category->name, 14 | "imageUrl": images[0].asset->url 15 | }`; 16 | 17 | const data = await client.fetch(query); 18 | 19 | return data; 20 | } 21 | 22 | export default async function Newest() { 23 | const data: simplifiedProduct[] = await getData(); 24 | 25 | return ( 26 |
27 |
28 |
29 |

30 | Our Newest products 31 |

32 | 33 | 34 | See All{" "} 35 | 36 | 37 | 38 | 39 |
40 | 41 |
42 | {data.map((product) => ( 43 |
44 |
45 | Product image 52 |
53 | 54 |
55 |
56 |

57 | 58 | {product.name} 59 | 60 |

61 |

62 | {product.categoryName} 63 |

64 |
65 |

66 | ${product.price} 67 |

68 |
69 |
70 | ))} 71 |
72 |
73 |
74 | ); 75 | } 76 | -------------------------------------------------------------------------------- /app/components/Providers.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ReactNode } from "react"; 4 | import { CartProvider as USCProvider } from "use-shopping-cart"; 5 | 6 | export default function CartProvider({ children }: { children: ReactNode }) { 7 | return ( 8 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /app/components/ShoppingCartModal.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "@/components/ui/button"; 3 | import { 4 | Sheet, 5 | SheetContent, 6 | SheetHeader, 7 | SheetTitle, 8 | } from "@/components/ui/sheet"; 9 | 10 | import Image from "next/image"; 11 | import { useShoppingCart } from "use-shopping-cart"; 12 | 13 | export default function ShoppingCartModal() { 14 | const { 15 | cartCount, 16 | shouldDisplayCart, 17 | handleCartClick, 18 | cartDetails, 19 | removeItem, 20 | totalPrice, 21 | redirectToCheckout, 22 | } = useShoppingCart(); 23 | 24 | async function handleCheckoutClick(event: any) { 25 | event.preventDefault(); 26 | try { 27 | const result = await redirectToCheckout(); 28 | if (result?.error) { 29 | console.log("result"); 30 | } 31 | } catch (error) { 32 | console.log(error); 33 | } 34 | } 35 | return ( 36 | handleCartClick()}> 37 | 38 | 39 | Shopping Cart 40 | 41 | 42 |
43 |
44 |
    45 | {cartCount === 0 ? ( 46 |

    You dont have any items

    47 | ) : ( 48 | <> 49 | {Object.values(cartDetails ?? {}).map((entry) => ( 50 |
  • 51 |
    52 | Product image 58 |
    59 | 60 |
    61 |
    62 |
    63 |

    {entry.name}

    64 |

    ${entry.price}

    65 |
    66 |

    67 | {entry.description} 68 |

    69 |
    70 | 71 |
    72 |

    QTY: {entry.quantity}

    73 | 74 |
    75 | 82 |
    83 |
    84 |
    85 |
  • 86 | ))} 87 | 88 | )} 89 |
90 |
91 | 92 |
93 |
94 |

Subtotal:

95 |

${totalPrice}

96 |
97 |

98 | Shipping and taxes are calculated at checkout. 99 |

100 | 101 |
102 | 105 |
106 | 107 |
108 |

109 | OR{" "} 110 | 116 |

117 |
118 |
119 |
120 |
121 |
122 | ); 123 | } 124 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/app/favicon.ico -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 224 71.4% 4.1%; 9 | --card: 0 0% 100%; 10 | --card-foreground: 224 71.4% 4.1%; 11 | --popover: 0 0% 100%; 12 | --popover-foreground: 224 71.4% 4.1%; 13 | --primary: 262.1 83.3% 57.8%; 14 | --primary-foreground: 210 20% 98%; 15 | --secondary: 220 14.3% 95.9%; 16 | --secondary-foreground: 220.9 39.3% 11%; 17 | --muted: 220 14.3% 95.9%; 18 | --muted-foreground: 220 8.9% 46.1%; 19 | --accent: 220 14.3% 95.9%; 20 | --accent-foreground: 220.9 39.3% 11%; 21 | --destructive: 0 84.2% 60.2%; 22 | --destructive-foreground: 210 20% 98%; 23 | --border: 220 13% 91%; 24 | --input: 220 13% 91%; 25 | --ring: 262.1 83.3% 57.8%; 26 | --radius: 0.5rem; 27 | } 28 | 29 | .dark { 30 | --background: 224 71.4% 4.1%; 31 | --foreground: 210 20% 98%; 32 | --card: 224 71.4% 4.1%; 33 | --card-foreground: 210 20% 98%; 34 | --popover: 224 71.4% 4.1%; 35 | --popover-foreground: 210 20% 98%; 36 | --primary: 263.4 70% 50.4%; 37 | --primary-foreground: 210 20% 98%; 38 | --secondary: 215 27.9% 16.9%; 39 | --secondary-foreground: 210 20% 98%; 40 | --muted: 215 27.9% 16.9%; 41 | --muted-foreground: 217.9 10.6% 64.9%; 42 | --accent: 215 27.9% 16.9%; 43 | --accent-foreground: 210 20% 98%; 44 | --destructive: 0 62.8% 30.6%; 45 | --destructive-foreground: 210 20% 98%; 46 | --border: 215 27.9% 16.9%; 47 | --input: 215 27.9% 16.9%; 48 | --ring: 263.4 70% 50.4%; 49 | } 50 | } 51 | 52 | @layer base { 53 | * { 54 | @apply border-border; 55 | } 56 | body { 57 | @apply bg-background text-foreground; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/interface.ts: -------------------------------------------------------------------------------- 1 | export interface simplifiedProduct { 2 | _id: string; 3 | imageUrl: string; 4 | price: number; 5 | slug: string; 6 | categoryName: string; 7 | name: string; 8 | } 9 | 10 | export interface fullProduct { 11 | _id: string; 12 | images: any; 13 | price: number; 14 | slug: string; 15 | categoryName: string; 16 | name: string; 17 | description: string; 18 | price_id: string; 19 | } 20 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import CartProvider from "./components/Providers"; 5 | import Navbar from "./components/Navbar"; 6 | import ShoppingCartModal from "./components/ShoppingCartModal"; 7 | 8 | const inter = Inter({ subsets: ["latin"] }); 9 | 10 | export const metadata: Metadata = { 11 | title: "Create Next App", 12 | description: "Generated by create next app", 13 | }; 14 | 15 | export default function RootLayout({ 16 | children, 17 | }: { 18 | children: React.ReactNode; 19 | }) { 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | {children} 27 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /app/lib/sanity.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "next-sanity"; 2 | import imageUrlBuilder from "@sanity/image-url"; 3 | 4 | export const client = createClient({ 5 | projectId: "pz9ehg2k", 6 | dataset: "production", 7 | apiVersion: "2022-03-25", 8 | useCdn: true, 9 | }); 10 | 11 | const builder = imageUrlBuilder(client); 12 | 13 | export function urlFor(source: any) { 14 | return builder.image(source); 15 | } 16 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Hero from "./components/Hero"; 3 | import Newest from "./components/Newest"; 4 | 5 | export const dynamic = "force-dynamic"; 6 | 7 | export default function Home() { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /app/product/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import AddToBag from "@/app/components/AddToBag"; 2 | import CheckoutNow from "@/app/components/CheckoutNow"; 3 | import ImageGallery from "@/app/components/ImageGallery"; 4 | import { fullProduct } from "@/app/interface"; 5 | import { client } from "@/app/lib/sanity"; 6 | import { Button } from "@/components/ui/button"; 7 | import { Star, Truck } from "lucide-react"; 8 | 9 | async function getData(slug: string) { 10 | const query = `*[_type == "product" && slug.current == "${slug}"][0] { 11 | _id, 12 | images, 13 | price, 14 | name, 15 | description, 16 | "slug": slug.current, 17 | "categoryName": category->name, 18 | price_id 19 | }`; 20 | 21 | const data = await client.fetch(query); 22 | 23 | return data; 24 | } 25 | 26 | export const dynamic = "force-dynamic"; 27 | 28 | export default async function ProductPge({ 29 | params, 30 | }: { 31 | params: { slug: string }; 32 | }) { 33 | const data: fullProduct = await getData(params.slug); 34 | 35 | return ( 36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 44 | {data.categoryName} 45 | 46 |

47 | {data.name} 48 |

49 |
50 | 51 |
52 | 56 | 57 | 58 | 56 Ratings 59 | 60 |
61 | 62 |
63 |
64 | 65 | ${data.price} 66 | 67 | 68 | ${data.price + 30} 69 | 70 |
71 | 72 | 73 | Incl. Vat plus shipping 74 | 75 |
76 | 77 |
78 | 79 | 2-4 Day Shipping 80 |
81 | 82 |
83 | 92 | 101 |
102 | 103 |

104 | {data.description} 105 |

106 |
107 |
108 |
109 |
110 | ); 111 | } 112 | -------------------------------------------------------------------------------- /app/stripe/error/page.tsx: -------------------------------------------------------------------------------- 1 | export default function ErrorStripe() { 2 | return ( 3 |
4 |

Something went wrong....

5 |
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /app/stripe/success/page.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import { CheckCheck } from "lucide-react"; 3 | import Link from "next/link"; 4 | 5 | export default function stripeSuccess() { 6 | return ( 7 |
8 |
9 | 10 |
11 |

12 | Payment Done! 13 |

14 |

15 | Thank you for you pruchase We hope you enjoy it 16 |

17 |

Have a great day!

18 | 19 | 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } -------------------------------------------------------------------------------- /components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 | destructive: 14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 | outline: 16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 | secondary: 18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 | ghost: "hover:bg-accent hover:text-accent-foreground", 20 | link: "text-primary underline-offset-4 hover:underline", 21 | }, 22 | size: { 23 | default: "h-10 px-4 py-2", 24 | sm: "h-9 rounded-md px-3", 25 | lg: "h-11 rounded-md px-8", 26 | icon: "h-10 w-10", 27 | }, 28 | }, 29 | defaultVariants: { 30 | variant: "default", 31 | size: "default", 32 | }, 33 | } 34 | ) 35 | 36 | export interface ButtonProps 37 | extends React.ButtonHTMLAttributes, 38 | VariantProps { 39 | asChild?: boolean 40 | } 41 | 42 | const Button = React.forwardRef( 43 | ({ className, variant, size, asChild = false, ...props }, ref) => { 44 | const Comp = asChild ? Slot : "button" 45 | return ( 46 | 51 | ) 52 | } 53 | ) 54 | Button.displayName = "Button" 55 | 56 | export { Button, buttonVariants } 57 | -------------------------------------------------------------------------------- /components/ui/sheet.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SheetPrimitive from "@radix-ui/react-dialog" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | import { X } from "lucide-react" 7 | 8 | import { cn } from "@/lib/utils" 9 | 10 | const Sheet = SheetPrimitive.Root 11 | 12 | const SheetTrigger = SheetPrimitive.Trigger 13 | 14 | const SheetClose = SheetPrimitive.Close 15 | 16 | const SheetPortal = SheetPrimitive.Portal 17 | 18 | const SheetOverlay = React.forwardRef< 19 | React.ElementRef, 20 | React.ComponentPropsWithoutRef 21 | >(({ className, ...props }, ref) => ( 22 | 30 | )) 31 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName 32 | 33 | const sheetVariants = cva( 34 | "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", 35 | { 36 | variants: { 37 | side: { 38 | top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", 39 | bottom: 40 | "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", 41 | left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", 42 | right: 43 | "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", 44 | }, 45 | }, 46 | defaultVariants: { 47 | side: "right", 48 | }, 49 | } 50 | ) 51 | 52 | interface SheetContentProps 53 | extends React.ComponentPropsWithoutRef, 54 | VariantProps {} 55 | 56 | const SheetContent = React.forwardRef< 57 | React.ElementRef, 58 | SheetContentProps 59 | >(({ side = "right", className, children, ...props }, ref) => ( 60 | 61 | 62 | 67 | {children} 68 | 69 | 70 | Close 71 | 72 | 73 | 74 | )) 75 | SheetContent.displayName = SheetPrimitive.Content.displayName 76 | 77 | const SheetHeader = ({ 78 | className, 79 | ...props 80 | }: React.HTMLAttributes) => ( 81 |
88 | ) 89 | SheetHeader.displayName = "SheetHeader" 90 | 91 | const SheetFooter = ({ 92 | className, 93 | ...props 94 | }: React.HTMLAttributes) => ( 95 |
102 | ) 103 | SheetFooter.displayName = "SheetFooter" 104 | 105 | const SheetTitle = React.forwardRef< 106 | React.ElementRef, 107 | React.ComponentPropsWithoutRef 108 | >(({ className, ...props }, ref) => ( 109 | 114 | )) 115 | SheetTitle.displayName = SheetPrimitive.Title.displayName 116 | 117 | const SheetDescription = React.forwardRef< 118 | React.ElementRef, 119 | React.ComponentPropsWithoutRef 120 | >(({ className, ...props }, ref) => ( 121 | 126 | )) 127 | SheetDescription.displayName = SheetPrimitive.Description.displayName 128 | 129 | export { 130 | Sheet, 131 | SheetPortal, 132 | SheetOverlay, 133 | SheetTrigger, 134 | SheetClose, 135 | SheetContent, 136 | SheetHeader, 137 | SheetFooter, 138 | SheetTitle, 139 | SheetDescription, 140 | } 141 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | domains: ["cdn.sanity.io"], 5 | }, 6 | }; 7 | 8 | module.exports = nextConfig; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-commerce-tutorial", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbo", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-dialog": "^1.0.5", 13 | "@radix-ui/react-slot": "^1.0.2", 14 | "@sanity/image-url": "^1.0.2", 15 | "@stripe/stripe-js": "^2.1.10", 16 | "class-variance-authority": "^0.7.0", 17 | "clsx": "^2.0.0", 18 | "lucide-react": "^0.289.0", 19 | "next": "14.0.0", 20 | "next-sanity": "^5.5.8", 21 | "react": "^18", 22 | "react-dom": "^18", 23 | "stripe": "^14.2.0", 24 | "tailwind-merge": "^1.14.0", 25 | "tailwindcss-animate": "^1.0.7", 26 | "use-shopping-cart": "^3.1.8" 27 | }, 28 | "devDependencies": { 29 | "@types/node": "^20", 30 | "@types/react": "^18", 31 | "@types/react-dom": "^18", 32 | "autoprefixer": "^10", 33 | "eslint": "^8", 34 | "eslint-config-next": "14.0.0", 35 | "postcss": "^8", 36 | "tailwindcss": "^3", 37 | "typescript": "^5" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/HeroImages/686dcf10-6030-4b31-967d-356f8b747732.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/HeroImages/686dcf10-6030-4b31-967d-356f8b747732.webp -------------------------------------------------------------------------------- /public/HeroImages/ba06b5a3-3cc0-4730-957a-a484b2fcbaa9.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/HeroImages/ba06b5a3-3cc0-4730-957a-a484b2fcbaa9.webp -------------------------------------------------------------------------------- /public/ProductFour/2214184d-d5ab-4ac6-aac8-5670a08bb373.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductFour/2214184d-d5ab-4ac6-aac8-5670a08bb373.webp -------------------------------------------------------------------------------- /public/ProductFour/939da2cf-40ac-4963-ae88-a9098fd32929.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductFour/939da2cf-40ac-4963-ae88-a9098fd32929.jpg -------------------------------------------------------------------------------- /public/ProductFour/windrunner-herren-laufjacke-k4q9TV (1).jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductFour/windrunner-herren-laufjacke-k4q9TV (1).jpeg -------------------------------------------------------------------------------- /public/ProductOne/3ee3420f-0395-47e9-b852-65c9e04a877d.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductOne/3ee3420f-0395-47e9-b852-65c9e04a877d.webp -------------------------------------------------------------------------------- /public/ProductOne/600e6348-ab0e-4de4-9936-60a98594254f.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductOne/600e6348-ab0e-4de4-9936-60a98594254f.webp -------------------------------------------------------------------------------- /public/ProductOne/air-vapormax-2023-flyknit-herrenschuh-xgCQsr.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductOne/air-vapormax-2023-flyknit-herrenschuh-xgCQsr.jpeg -------------------------------------------------------------------------------- /public/ProductThree/0453f3b6-c33b-44a6-bd68-3732ac2b8bf2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductThree/0453f3b6-c33b-44a6-bd68-3732ac2b8bf2.jpg -------------------------------------------------------------------------------- /public/ProductThree/3811255b-206e-4fd2-9477-1739c97ecc80.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductThree/3811255b-206e-4fd2-9477-1739c97ecc80.webp -------------------------------------------------------------------------------- /public/ProductThree/72a3b44a-3e4c-44b7-b614-835b9d28bcc9.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductThree/72a3b44a-3e4c-44b7-b614-835b9d28bcc9.webp -------------------------------------------------------------------------------- /public/ProductTwo/795c66fd-1efc-4fb3-95c8-455ae2d390e9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductTwo/795c66fd-1efc-4fb3-95c8-455ae2d390e9.png -------------------------------------------------------------------------------- /public/ProductTwo/af318846-4fcb-4b8f-b13c-f7ddfe17d519.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ski043/nextjs-commerce-tutorial/9d3b46ca96dc459718152c88f37bf44eb627df91/public/ProductTwo/af318846-4fcb-4b8f-b13c-f7ddfe17d519.webp -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sanity/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sanity/eslint-config-studio" 3 | } 4 | -------------------------------------------------------------------------------- /sanity/.gitignore: -------------------------------------------------------------------------------- 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 | # Compiled Sanity Studio 9 | /dist 10 | 11 | # Temporary Sanity runtime, generated by the CLI on every dev server start 12 | /.sanity 13 | 14 | # Logs 15 | /logs 16 | *.log 17 | 18 | # Coverage directory used by testing tools 19 | /coverage 20 | 21 | # Misc 22 | .DS_Store 23 | *.pem 24 | 25 | # Typescript 26 | *.tsbuildinfo 27 | 28 | # Dotenv and similar local-only files 29 | *.local 30 | -------------------------------------------------------------------------------- /sanity/README.md: -------------------------------------------------------------------------------- 1 | # Sanity Clean Content Studio 2 | 3 | Congratulations, you have now installed the Sanity Content Studio, an open source real-time content editing environment connected to the Sanity backend. 4 | 5 | Now you can do the following things: 6 | 7 | - [Read “getting started” in the docs](https://www.sanity.io/docs/introduction/getting-started?utm_source=readme) 8 | - [Join the community Slack](https://slack.sanity.io/?utm_source=readme) 9 | - [Extend and build plugins](https://www.sanity.io/docs/content-studio/extending?utm_source=readme) 10 | -------------------------------------------------------------------------------- /sanity/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sanity-project", 3 | "private": true, 4 | "version": "1.0.0", 5 | "main": "package.json", 6 | "license": "UNLICENSED", 7 | "scripts": { 8 | "dev": "sanity dev", 9 | "start": "sanity start", 10 | "build": "sanity build", 11 | "deploy": "sanity deploy", 12 | "deploy-graphql": "sanity graphql deploy" 13 | }, 14 | "keywords": [ 15 | "sanity" 16 | ], 17 | "dependencies": { 18 | "@sanity/vision": "^3.18.1", 19 | "react": "^18.2.0", 20 | "react-dom": "^18.2.0", 21 | "react-is": "^18.2.0", 22 | "sanity": "^3.18.1", 23 | "styled-components": "^5.3.9" 24 | }, 25 | "devDependencies": { 26 | "@sanity/eslint-config-studio": "^3.0.1", 27 | "@types/react": "^18.0.25", 28 | "@types/styled-components": "^5.1.26", 29 | "eslint": "^8.6.0", 30 | "prettier": "^3.0.2", 31 | "typescript": "^5.1.6" 32 | }, 33 | "prettier": { 34 | "semi": false, 35 | "printWidth": 100, 36 | "bracketSpacing": false, 37 | "singleQuote": true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sanity/sanity.cli.ts: -------------------------------------------------------------------------------- 1 | import {defineCliConfig} from 'sanity/cli' 2 | 3 | export default defineCliConfig({ 4 | api: { 5 | projectId: 'pip196iz', 6 | dataset: 'production' 7 | } 8 | }) 9 | -------------------------------------------------------------------------------- /sanity/sanity.config.ts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'sanity' 2 | import {deskTool} from 'sanity/desk' 3 | import {visionTool} from '@sanity/vision' 4 | import {schemaTypes} from './schemas' 5 | 6 | export default defineConfig({ 7 | name: 'default', 8 | title: 'Sanity Project', 9 | 10 | projectId: 'pip196iz', 11 | dataset: 'production', 12 | 13 | plugins: [deskTool(), visionTool()], 14 | 15 | schema: { 16 | types: schemaTypes, 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /sanity/schemas/category.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'category', 3 | type: 'document', 4 | title: 'Categories', 5 | fields: [ 6 | { 7 | name: 'name', 8 | title: 'Name of Category', 9 | type: 'string', 10 | }, 11 | ], 12 | } 13 | -------------------------------------------------------------------------------- /sanity/schemas/heroImages.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'heroImage', 3 | type: 'document', 4 | title: 'Two Hero Images', 5 | fields: [ 6 | { 7 | name: 'image1', 8 | type: 'image', 9 | title: 'First Image', 10 | }, 11 | { 12 | name: 'image2', 13 | type: 'image', 14 | title: 'Second Image', 15 | }, 16 | ], 17 | } 18 | -------------------------------------------------------------------------------- /sanity/schemas/index.ts: -------------------------------------------------------------------------------- 1 | import category from "./category"; 2 | import heroImages from "./heroImages"; 3 | import product from "./product"; 4 | 5 | export const schemaTypes = [heroImages, product, category] 6 | -------------------------------------------------------------------------------- /sanity/schemas/product.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'product', 3 | type: 'document', 4 | title: 'Product', 5 | fields: [ 6 | { 7 | name: 'name', 8 | type: 'string', 9 | title: 'Name of Product', 10 | }, 11 | { 12 | name: 'images', 13 | type: 'array', 14 | title: 'Product Images', 15 | of: [{type: 'image'}], 16 | }, 17 | { 18 | name: 'description', 19 | type: 'text', 20 | title: 'Description of product', 21 | }, 22 | { 23 | name: 'slug', 24 | type: 'slug', 25 | title: 'Product Slug', 26 | options: { 27 | source: 'name', 28 | }, 29 | }, 30 | { 31 | name: 'price', 32 | title: 'Price', 33 | type: 'number', 34 | }, 35 | { 36 | name: 'price_id', 37 | title: 'Stripe Price ID', 38 | type: 'string', 39 | }, 40 | { 41 | name: 'category', 42 | title: 'Product Category', 43 | type: 'reference', 44 | to: [ 45 | { 46 | type: 'category', 47 | }, 48 | ], 49 | }, 50 | ], 51 | } 52 | -------------------------------------------------------------------------------- /sanity/static/.gitkeep: -------------------------------------------------------------------------------- 1 | Files placed here will be served by the Sanity server under the `/static`-prefix 2 | -------------------------------------------------------------------------------- /sanity/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | darkMode: ["class"], 4 | content: [ 5 | './pages/**/*.{ts,tsx}', 6 | './components/**/*.{ts,tsx}', 7 | './app/**/*.{ts,tsx}', 8 | './src/**/*.{ts,tsx}', 9 | ], 10 | theme: { 11 | container: { 12 | center: true, 13 | padding: "2rem", 14 | screens: { 15 | "2xl": "1400px", 16 | }, 17 | }, 18 | extend: { 19 | colors: { 20 | border: "hsl(var(--border))", 21 | input: "hsl(var(--input))", 22 | ring: "hsl(var(--ring))", 23 | background: "hsl(var(--background))", 24 | foreground: "hsl(var(--foreground))", 25 | primary: { 26 | DEFAULT: "hsl(var(--primary))", 27 | foreground: "hsl(var(--primary-foreground))", 28 | }, 29 | secondary: { 30 | DEFAULT: "hsl(var(--secondary))", 31 | foreground: "hsl(var(--secondary-foreground))", 32 | }, 33 | destructive: { 34 | DEFAULT: "hsl(var(--destructive))", 35 | foreground: "hsl(var(--destructive-foreground))", 36 | }, 37 | muted: { 38 | DEFAULT: "hsl(var(--muted))", 39 | foreground: "hsl(var(--muted-foreground))", 40 | }, 41 | accent: { 42 | DEFAULT: "hsl(var(--accent))", 43 | foreground: "hsl(var(--accent-foreground))", 44 | }, 45 | popover: { 46 | DEFAULT: "hsl(var(--popover))", 47 | foreground: "hsl(var(--popover-foreground))", 48 | }, 49 | card: { 50 | DEFAULT: "hsl(var(--card))", 51 | foreground: "hsl(var(--card-foreground))", 52 | }, 53 | }, 54 | borderRadius: { 55 | lg: "var(--radius)", 56 | md: "calc(var(--radius) - 2px)", 57 | sm: "calc(var(--radius) - 4px)", 58 | }, 59 | keyframes: { 60 | "accordion-down": { 61 | from: { height: 0 }, 62 | to: { height: "var(--radix-accordion-content-height)" }, 63 | }, 64 | "accordion-up": { 65 | from: { height: "var(--radix-accordion-content-height)" }, 66 | to: { height: 0 }, 67 | }, 68 | }, 69 | animation: { 70 | "accordion-down": "accordion-down 0.2s ease-out", 71 | "accordion-up": "accordion-up 0.2s ease-out", 72 | }, 73 | }, 74 | }, 75 | plugins: [require("tailwindcss-animate")], 76 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | --------------------------------------------------------------------------------