diff --git a/.env.example b/.env.example
deleted file mode 100644
index 9ff0463db..000000000
--- a/.env.example
+++ /dev/null
@@ -1,7 +0,0 @@
-COMPANY_NAME="Vercel Inc."
-TWITTER_CREATOR="@vercel"
-TWITTER_SITE="https://nextjs.org/commerce"
-SITE_NAME="Next.js Commerce"
-SHOPIFY_REVALIDATION_SECRET=""
-SHOPIFY_STOREFRONT_ACCESS_TOKEN=""
-SHOPIFY_STORE_DOMAIN="[your-shopify-store-subdomain].myshopify.com"
diff --git a/app/error.tsx b/app/error.tsx
index 80b32a42a..7a95af8a7 100644
--- a/app/error.tsx
+++ b/app/error.tsx
@@ -1,6 +1,14 @@
'use client';
-export default function Error({ reset }: { reset: () => void }) {
+import { useRouter } from 'next/navigation';
+
+export default function Error({ resetAction }: { resetAction?: () => void }) {
+ const router = useRouter();
+
+ const defaultReset = () => {
+ router.push('/');
+ };
+
return (
Oh no!
@@ -10,7 +18,7 @@ export default function Error({ reset }: { reset: () => void }) {
diff --git a/app/globals.css b/app/globals.css
index 0a6d36768..1077f8084 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,3 +1,4 @@
+@import url('https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
@@ -14,8 +15,12 @@
}
}
+body {
+ @apply font-baskerville;
+}
+
a,
input,
button {
- @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-400 focus-visible:ring-offset-2 focus-visible:ring-offset-neutral-50 dark:focus-visible:ring-neutral-600 dark:focus-visible:ring-offset-neutral-900;
+ outline: none;
}
diff --git a/app/layout.tsx b/app/layout.tsx
index 3b09ab7e4..447685b88 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,12 +1,9 @@
-import { CartProvider } from 'components/cart/cart-context';
import { Navbar } from 'components/layout/navbar';
-import { WelcomeToast } from 'components/welcome-toast';
import { GeistSans } from 'geist/font/sans';
import { getCart } from 'lib/shopify';
import { ensureStartsWith } from 'lib/utils';
import { cookies } from 'next/headers';
import { ReactNode } from 'react';
-import { Toaster } from 'sonner';
import './globals.css';
const { TWITTER_CREATOR, TWITTER_SITE, SITE_NAME } = process.env;
@@ -44,14 +41,8 @@ export default async function RootLayout({ children }: { children: ReactNode })
return (
-
-
-
- {children}
-
-
-
-
+
+
{children}
);
diff --git a/app/page.tsx b/app/page.tsx
index 7d407ede8..f28b9b51f 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,7 +1,12 @@
-import { Carousel } from 'components/carousel';
-import { ThreeItemGrid } from 'components/grid/three-items';
+import Error from 'app/error';
import Footer from 'components/layout/footer';
+import { Search } from 'components/layout/search';
+import { PriceBox } from 'components/price-box';
+import { getCollectionProducts } from 'lib/shopify';
+import type { Product } from 'lib/shopify/types';
+import Image from 'next/image';
+//Todo: change to proper metadata
export const metadata = {
description: 'High-performance ecommerce store built with Next.js, Vercel, and Shopify.',
openGraph: {
@@ -9,12 +14,173 @@ export const metadata = {
}
};
-export default function HomePage() {
+export default async function HomePage() {
+ const products = await getCollectionProducts({ collection: 'landing' });
+
+ //Todo: change to proper error handling
+ if (!products[0]) return
;
+
return (
<>
-
-
+
+
+
+
+
+
{products[0].title}
+
Read more
+
+
+
+
+
>
);
}
+
+//temp: for ProductGridItems test
+const mockProducts: Product[] = [
+ {
+ id: 'prod_001',
+ handle: 'product-1',
+ availableForSale: true,
+ title: 'Product 1',
+ description: 'This is the description for Product 1',
+ descriptionHtml: '
This is the HTML description for Product 1
',
+ options: [
+ {
+ id: 'option_001',
+ name: 'Size',
+ values: ['S', 'M', 'L']
+ }
+ ],
+ priceRange: {
+ maxVariantPrice: {
+ amount: '100.00',
+ currencyCode: 'USD'
+ },
+ minVariantPrice: {
+ amount: '80.00',
+ currencyCode: 'USD'
+ }
+ },
+ featuredImage: {
+ url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
+ altText: 'Product 1 Featured Image',
+ width: 500,
+ height: 500
+ },
+ seo: {
+ title: 'Product 1 SEO Title',
+ description: 'This is the SEO description for Product 1'
+ },
+ tags: ['tag1', 'tag2'],
+ updatedAt: new Date().toISOString(),
+ variants: [
+ {
+ id: 'variant_001',
+ title: 'Variant 1',
+ availableForSale: true,
+ selectedOptions: [
+ {
+ name: 'Size',
+ value: 'M'
+ }
+ ],
+ price: {
+ amount: '90.00',
+ currencyCode: 'USD'
+ }
+ }
+ ],
+ images: [
+ {
+ url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
+ altText: 'Product 1 Image 1',
+ width: 500,
+ height: 500
+ },
+ {
+ url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
+ altText: 'Product 1 Image 2',
+ width: 400,
+ height: 400
+ }
+ ]
+ },
+ {
+ id: 'prod_002',
+ handle: 'product-2',
+ availableForSale: false,
+ title: 'Product 2',
+ description: 'This is the description for Product 2',
+ descriptionHtml: '
This is the HTML description for Product 2
',
+ options: [
+ {
+ id: 'option_002',
+ name: 'Color',
+ values: ['Red', 'Blue', 'Green']
+ }
+ ],
+ priceRange: {
+ maxVariantPrice: {
+ amount: '120.00',
+ currencyCode: 'USD'
+ },
+ minVariantPrice: {
+ amount: '100.00',
+ currencyCode: 'USD'
+ }
+ },
+ featuredImage: {
+ url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
+ altText: 'Product 2 Featured Image',
+ width: 500,
+ height: 500
+ },
+ seo: {
+ title: 'Product 2 SEO Title',
+ description: 'This is the SEO description for Product 2'
+ },
+ tags: ['tag3', 'tag4'],
+ updatedAt: new Date().toISOString(),
+ variants: [
+ {
+ id: 'variant_002',
+ title: 'Variant 2',
+ availableForSale: false,
+ selectedOptions: [
+ {
+ name: 'Color',
+ value: 'Red'
+ }
+ ],
+ price: {
+ amount: '110.00',
+ currencyCode: 'USD'
+ }
+ }
+ ],
+ images: [
+ {
+ url: 'https://cdn.shopify.com/static/sample-images/garnished.jpeg',
+ altText: 'Product 2 Image 1',
+ width: 500,
+ height: 500
+ }
+ ]
+ }
+];
diff --git a/components/icons/logo.tsx b/components/icons/logo.tsx
index 46fa02464..74be9d5f9 100644
--- a/components/icons/logo.tsx
+++ b/components/icons/logo.tsx
@@ -3,14 +3,111 @@ import clsx from 'clsx';
export default function LogoIcon(props: React.ComponentProps<'svg'>) {
return (
);
}
diff --git a/components/layout/navbar/index.tsx b/components/layout/navbar/index.tsx
index 6c7f3dead..34ff5ba00 100644
--- a/components/layout/navbar/index.tsx
+++ b/components/layout/navbar/index.tsx
@@ -1,61 +1,109 @@
-import CartModal from 'components/cart/modal';
-import LogoSquare from 'components/logo-square';
-import { getMenu } from 'lib/shopify';
-import { Menu } from 'lib/shopify/types';
-import Link from 'next/link';
-import { Suspense } from 'react';
-import MobileMenu from './mobile-menu';
-import Search, { SearchSkeleton } from './search';
+'use client';
-const { SITE_NAME } = process.env;
+import {
+ Bars3Icon,
+ MagnifyingGlassIcon,
+ ShoppingBagIcon,
+ UserIcon
+} from '@heroicons/react/24/outline';
+import Logo from 'components/icons/logo';
+import { LiHTMLAttributes, useState } from 'react';
-export async function Navbar() {
- const menu = await getMenu('next-js-frontend-header-menu');
+interface MenuListItemProps extends LiHTMLAttributes
{
+ type: 'main' | 'sub';
+}
+
+function MenuListItem({ type, children }: MenuListItemProps) {
+ return (
+ <>
+ {type === 'main' ? (
+ {children}
+ ) : (
+ {children}
+ )}
+ >
+ );
+}
+
+export function Navbar() {
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
-