From b17652b26b97909da95b8bbce2926d015acdbb4d Mon Sep 17 00:00:00 2001 From: "alex.saiannyi" Date: Mon, 22 May 2023 15:22:52 +0200 Subject: [PATCH] feat: project initial setup --- .env.example | 13 +- app/[page]/page.tsx | 2 +- app/api/cart/route.ts | 18 +- app/page.tsx | 2 +- app/product/[handle]/page.tsx | 4 +- app/search/[collection]/page.tsx | 2 +- app/search/page.tsx | 2 +- app/sitemap.ts | 2 +- components/carousel.tsx | 2 +- components/cart/button.tsx | 2 +- components/cart/delete-item-button.tsx | 2 +- components/cart/edit-item-quantity-button.tsx | 2 +- components/cart/index.tsx | 2 +- components/cart/modal.tsx | 2 +- components/grid/three-items.tsx | 4 +- components/layout/footer.tsx | 4 +- components/layout/navbar/index.tsx | 11 +- components/layout/navbar/mobile-menu.tsx | 2 +- components/layout/product-grid-items.tsx | 2 +- components/layout/search/collections.tsx | 2 +- components/product/add-to-cart.tsx | 10 +- components/product/variant-selector.tsx | 5 +- lib/bigcommerce/constants.ts | 4 + lib/bigcommerce/fragments/cart.ts | 187 +++ lib/bigcommerce/fragments/page.ts | 12 + lib/bigcommerce/fragments/product.ts | 177 +++ lib/bigcommerce/index.ts | 580 +++++++++ lib/bigcommerce/mappers.ts | 336 +++++ lib/bigcommerce/mutations/cart.ts | 152 +++ lib/bigcommerce/queries/cart.ts | 94 ++ lib/bigcommerce/queries/category.ts | 28 + lib/bigcommerce/queries/checkout.ts | 20 + lib/bigcommerce/queries/menu.ts | 21 + lib/bigcommerce/queries/page.ts | 67 + lib/bigcommerce/queries/product.ts | 130 ++ lib/bigcommerce/queries/route.ts | 30 + lib/bigcommerce/storefront-config.ts | 29 + lib/bigcommerce/types.ts | 602 +++++++++ lib/constants.tsx | 30 +- lib/shopify/fragments/cart.ts | 53 - lib/shopify/fragments/image.ts | 10 - lib/shopify/fragments/product.ts | 64 - lib/shopify/fragments/seo.ts | 8 - lib/shopify/index.ts | 385 ------ lib/shopify/mutations/cart.ts | 45 - lib/shopify/queries/cart.ts | 10 - lib/shopify/queries/collection.ts | 56 - lib/shopify/queries/menu.ts | 10 - lib/shopify/queries/page.ts | 41 - lib/shopify/queries/product.ts | 32 - lib/shopify/types.ts | 265 ---- lib/type-guards.ts | 4 +- next.config.js | 5 +- pnpm-lock.yaml | 1146 +++++++++-------- 54 files changed, 3140 insertions(+), 1590 deletions(-) create mode 100644 lib/bigcommerce/constants.ts create mode 100644 lib/bigcommerce/fragments/cart.ts create mode 100644 lib/bigcommerce/fragments/page.ts create mode 100644 lib/bigcommerce/fragments/product.ts create mode 100644 lib/bigcommerce/index.ts create mode 100644 lib/bigcommerce/mappers.ts create mode 100644 lib/bigcommerce/mutations/cart.ts create mode 100644 lib/bigcommerce/queries/cart.ts create mode 100644 lib/bigcommerce/queries/category.ts create mode 100644 lib/bigcommerce/queries/checkout.ts create mode 100644 lib/bigcommerce/queries/menu.ts create mode 100644 lib/bigcommerce/queries/page.ts create mode 100644 lib/bigcommerce/queries/product.ts create mode 100644 lib/bigcommerce/queries/route.ts create mode 100644 lib/bigcommerce/storefront-config.ts create mode 100644 lib/bigcommerce/types.ts delete mode 100644 lib/shopify/fragments/cart.ts delete mode 100644 lib/shopify/fragments/image.ts delete mode 100644 lib/shopify/fragments/product.ts delete mode 100644 lib/shopify/fragments/seo.ts delete mode 100644 lib/shopify/index.ts delete mode 100644 lib/shopify/mutations/cart.ts delete mode 100644 lib/shopify/queries/cart.ts delete mode 100644 lib/shopify/queries/collection.ts delete mode 100644 lib/shopify/queries/menu.ts delete mode 100644 lib/shopify/queries/page.ts delete mode 100644 lib/shopify/queries/product.ts delete mode 100644 lib/shopify/types.ts diff --git a/.env.example b/.env.example index 088014635..f11b18968 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,10 @@ -TWITTER_CREATOR="@vercel" +TWITTER_CREATOR="@BigCommerce" TWITTER_SITE="https://nextjs.org/commerce" -SITE_NAME="Next.js Commerce" -SHOPIFY_STOREFRONT_ACCESS_TOKEN= -SHOPIFY_STORE_DOMAIN= +SITE_NAME="Next.js Commerce by BigCommerce" +BIGCOMMERCE_ACCESS_TOKEN= +BIGCOMMERCE_CHANNEL_ID= +BIGCOMMERCE_STORE_HASH= +# Optional +BIGCOMMERCE_CANONICAL_STORE_DOMAIN="mybigcommerce.com" +BIGCOMMERCE_API_URL="https://api.bigcommerce.com" +BIGCOMMERCE_CDN_HOSTNAME="*.bigcommerce.com" diff --git a/app/[page]/page.tsx b/app/[page]/page.tsx index 7e0defed3..6132c07e0 100644 --- a/app/[page]/page.tsx +++ b/app/[page]/page.tsx @@ -1,7 +1,7 @@ import type { Metadata } from 'next'; import Prose from 'components/prose'; -import { getPage } from 'lib/shopify'; +import { getPage } from 'lib/bigcommerce'; import { notFound } from 'next/navigation'; export const runtime = 'edge'; diff --git a/app/api/cart/route.ts b/app/api/cart/route.ts index 15f5fb09f..ba5ef9049 100644 --- a/app/api/cart/route.ts +++ b/app/api/cart/route.ts @@ -1,8 +1,8 @@ import { cookies } from 'next/headers'; import { NextRequest, NextResponse } from 'next/server'; -import { addToCart, removeFromCart, updateCart } from 'lib/shopify'; -import { isShopifyError } from 'lib/type-guards'; +import { addToCart, removeFromCart, updateCart } from 'lib/bigcommerce'; +import { isVercelCommerceError } from 'lib/type-guards'; function formatErrorMessage(err: Error): string { return JSON.stringify(err, Object.getOwnPropertyNames(err)); @@ -10,16 +10,18 @@ function formatErrorMessage(err: Error): string { export async function POST(req: NextRequest): Promise { const cartId = cookies().get('cartId')?.value; - const { merchandiseId } = await req.json(); + const { merchandiseId, isBigCommerceAPI } = await req.json(); - if (!cartId?.length || !merchandiseId?.length) { + if ((!isBigCommerceAPI && !cartId?.length) || !merchandiseId?.length) { return NextResponse.json({ error: 'Missing cartId or variantId' }, { status: 400 }); + } else if (isBigCommerceAPI && !merchandiseId?.length) { + return NextResponse.json({ error: 'Missing variantId' }, { status: 400 }); } try { - await addToCart(cartId, [{ merchandiseId, quantity: 1 }]); + await addToCart(cartId || '', [{ merchandiseId, quantity: 1 }]); return NextResponse.json({ status: 204 }); } catch (e) { - if (isShopifyError(e)) { + if (isVercelCommerceError(e)) { return NextResponse.json({ message: formatErrorMessage(e.message) }, { status: e.status }); } @@ -47,7 +49,7 @@ export async function PUT(req: NextRequest): Promise { ]); return NextResponse.json({ status: 204 }); } catch (e) { - if (isShopifyError(e)) { + if (isVercelCommerceError(e)) { return NextResponse.json({ message: formatErrorMessage(e.message) }, { status: e.status }); } @@ -66,7 +68,7 @@ export async function DELETE(req: NextRequest): Promise { await removeFromCart(cartId, [lineId]); return NextResponse.json({ status: 204 }); } catch (e) { - if (isShopifyError(e)) { + if (isVercelCommerceError(e)) { return NextResponse.json({ message: formatErrorMessage(e.message) }, { status: e.status }); } diff --git a/app/page.tsx b/app/page.tsx index a928be52e..a56591cd3 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -6,7 +6,7 @@ import { Suspense } from 'react'; export const runtime = 'edge'; export const metadata = { - description: 'High-performance ecommerce store built with Next.js, Vercel, and Shopify.', + description: 'High-performance ecommerce store built with Next.js, Vercel, and BigCommerce.', openGraph: { images: [ { diff --git a/app/product/[handle]/page.tsx b/app/product/[handle]/page.tsx index 612cd0236..a90c96ab3 100644 --- a/app/product/[handle]/page.tsx +++ b/app/product/[handle]/page.tsx @@ -10,8 +10,8 @@ import { Gallery } from 'components/product/gallery'; import { VariantSelector } from 'components/product/variant-selector'; import Prose from 'components/prose'; import { HIDDEN_PRODUCT_TAG } from 'lib/constants'; -import { getProduct, getProductRecommendations } from 'lib/shopify'; -import { Image } from 'lib/shopify/types'; +import { getProduct, getProductRecommendations } from 'lib/bigcommerce'; +import { Image } from 'lib/bigcommerce/types'; export const runtime = 'edge'; diff --git a/app/search/[collection]/page.tsx b/app/search/[collection]/page.tsx index 873701bf7..5374e5755 100644 --- a/app/search/[collection]/page.tsx +++ b/app/search/[collection]/page.tsx @@ -1,4 +1,4 @@ -import { getCollection, getCollectionProducts } from 'lib/shopify'; +import { getCollection, getCollectionProducts } from 'lib/bigcommerce'; import { Metadata } from 'next'; import { notFound } from 'next/navigation'; diff --git a/app/search/page.tsx b/app/search/page.tsx index 354a2022f..96afda4d7 100644 --- a/app/search/page.tsx +++ b/app/search/page.tsx @@ -1,7 +1,7 @@ import Grid from 'components/grid'; import ProductGridItems from 'components/layout/product-grid-items'; +import { getProducts } from 'lib/bigcommerce'; import { defaultSort, sorting } from 'lib/constants'; -import { getProducts } from 'lib/shopify'; export const runtime = 'edge'; diff --git a/app/sitemap.ts b/app/sitemap.ts index d8cdfd2ea..340cba3ea 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -1,4 +1,4 @@ -import { getCollections, getPages, getProducts } from 'lib/shopify'; +import { getCollections, getPages, getProducts } from 'lib/bigcommerce'; import { MetadataRoute } from 'next'; const baseUrl = process.env.NEXT_PUBLIC_VERCEL_URL diff --git a/components/carousel.tsx b/components/carousel.tsx index d86d17f45..5cec4d878 100644 --- a/components/carousel.tsx +++ b/components/carousel.tsx @@ -1,4 +1,4 @@ -import { getCollectionProducts } from 'lib/shopify'; +import { getCollectionProducts } from 'lib/bigcommerce'; import Image from 'next/image'; import Link from 'next/link'; diff --git a/components/cart/button.tsx b/components/cart/button.tsx index aed87ee7a..0899abb27 100644 --- a/components/cart/button.tsx +++ b/components/cart/button.tsx @@ -6,7 +6,7 @@ import { useCookies } from 'react-cookie'; import CartIcon from 'components/icons/cart'; import CartModal from './modal'; -import type { Cart } from 'lib/shopify/types'; +import type { VercelCart as Cart } from 'lib/bigcommerce/types'; export default function CartButton({ cart, diff --git a/components/cart/delete-item-button.tsx b/components/cart/delete-item-button.tsx index 789a87754..4fc5197f8 100644 --- a/components/cart/delete-item-button.tsx +++ b/components/cart/delete-item-button.tsx @@ -4,7 +4,7 @@ import { useRouter } from 'next/navigation'; import { startTransition, useState } from 'react'; import clsx from 'clsx'; -import type { CartItem } from 'lib/shopify/types'; +import type { VercelCartItem as CartItem } from 'lib/bigcommerce/types'; export default function DeleteItemButton({ item }: { item: CartItem }) { const router = useRouter(); diff --git a/components/cart/edit-item-quantity-button.tsx b/components/cart/edit-item-quantity-button.tsx index 2249bd1aa..fd1828536 100644 --- a/components/cart/edit-item-quantity-button.tsx +++ b/components/cart/edit-item-quantity-button.tsx @@ -4,7 +4,7 @@ import { startTransition, useState } from 'react'; import clsx from 'clsx'; import MinusIcon from 'components/icons/minus'; import PlusIcon from 'components/icons/plus'; -import type { CartItem } from 'lib/shopify/types'; +import type { VercelCartItem as CartItem } from 'lib/bigcommerce/types'; import LoadingDots from '../loading-dots'; export default function EditItemQuantityButton({ diff --git a/components/cart/index.tsx b/components/cart/index.tsx index 0e3ffcdc1..8d90c4e94 100644 --- a/components/cart/index.tsx +++ b/components/cart/index.tsx @@ -1,4 +1,4 @@ -import { createCart, getCart } from 'lib/shopify'; +import { createCart, getCart } from 'lib/bigcommerce'; import { cookies } from 'next/headers'; import CartButton from './button'; diff --git a/components/cart/modal.tsx b/components/cart/modal.tsx index 4220cfe94..6c4191525 100644 --- a/components/cart/modal.tsx +++ b/components/cart/modal.tsx @@ -7,7 +7,7 @@ import CloseIcon from 'components/icons/close'; import ShoppingBagIcon from 'components/icons/shopping-bag'; import Price from 'components/price'; import { DEFAULT_OPTION } from 'lib/constants'; -import type { Cart } from 'lib/shopify/types'; +import type { VercelCart as Cart } from 'lib/bigcommerce/types'; import { createUrl } from 'lib/utils'; import DeleteItemButton from './delete-item-button'; import EditItemQuantityButton from './edit-item-quantity-button'; diff --git a/components/grid/three-items.tsx b/components/grid/three-items.tsx index 2280de26e..511ea98d7 100644 --- a/components/grid/three-items.tsx +++ b/components/grid/three-items.tsx @@ -1,6 +1,6 @@ import { GridTileImage } from 'components/grid/tile'; -import { getCollectionProducts } from 'lib/shopify'; -import type { Product } from 'lib/shopify/types'; +import { getCollectionProducts } from 'lib/bigcommerce'; +import type { VercelProduct as Product } from 'lib/bigcommerce/types'; import Link from 'next/link'; function ThreeItemGridItem({ diff --git a/components/layout/footer.tsx b/components/layout/footer.tsx index 72e9e03fc..c48eb47c8 100644 --- a/components/layout/footer.tsx +++ b/components/layout/footer.tsx @@ -3,8 +3,8 @@ import Link from 'next/link'; import GitHubIcon from 'components/icons/github'; import LogoIcon from 'components/icons/logo'; import VercelIcon from 'components/icons/vercel'; -import { getMenu } from 'lib/shopify'; -import { Menu } from 'lib/shopify/types'; +import { getMenu } from 'lib/bigcommerce'; +import { VercelMenu as Menu } from 'lib/bigcommerce/types'; const { SITE_NAME } = process.env; diff --git a/components/layout/navbar/index.tsx b/components/layout/navbar/index.tsx index 537c450a1..0757d8bc0 100644 --- a/components/layout/navbar/index.tsx +++ b/components/layout/navbar/index.tsx @@ -4,18 +4,19 @@ import { Suspense } from 'react'; import Cart from 'components/cart'; import CartIcon from 'components/icons/cart'; import LogoIcon from 'components/icons/logo'; -import { getMenu } from 'lib/shopify'; -import { Menu } from 'lib/shopify/types'; +import { getMenu } from 'lib/bigcommerce'; +import { VercelMenu as Menu } from 'lib/bigcommerce/types'; import MobileMenu from './mobile-menu'; import Search from './search'; export default async function Navbar() { const menu = await getMenu('next-js-frontend-header-menu'); + const demoMenu = menu.slice(0, 4); return (