Skip to content

Commit

Permalink
feat: basic starter functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
dlhck committed Jan 14, 2025
1 parent 88762ba commit 6da1039
Show file tree
Hide file tree
Showing 73 changed files with 7,991 additions and 1,622 deletions.
8 changes: 3 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
COMPANY_NAME="Vercel Inc."
TWITTER_CREATOR="@vercel"
COMPANY_NAME="Vendure GmbH"
TWITTER_CREATOR="@vendure_io"
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"
VENDURE_API_ENDPOINT="http://localhost:3000/shop-api"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# misc
.DS_Store
*.pem
.idea

# debug
npm-debug.log*
Expand Down
7 changes: 5 additions & 2 deletions app/[page]/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import OpengraphImage from 'components/opengraph-image';
import { getPage } from 'lib/shopify';
import { getPage } from 'lib/vendure';

export const runtime = 'edge';

export default async function Image({ params }: { params: { page: string } }) {
const page = await getPage(params.page);
const page = {
title: 'Test',
seo: { title: 'Test' }
};
const title = page.seo?.title || page.title;

return await OpengraphImage({ title });
Expand Down
2 changes: 1 addition & 1 deletion app/[page]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Metadata } from 'next';

import Prose from 'components/prose';
import { getPage } from 'lib/shopify';
import { getPage } from 'lib/vendure';
import { notFound } from 'next/navigation';

export async function generateMetadata(props: {
Expand Down
2 changes: 1 addition & 1 deletion app/api/revalidate/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { revalidate } from 'lib/shopify';
import { revalidate } from 'lib/vendure';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(req: NextRequest): Promise<NextResponse> {
Expand Down
25 changes: 15 additions & 10 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ 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 { getActiveChannel, getActiveOrder, getCart } from 'lib/vendure';
import { ensureStartsWith } from 'lib/utils';
import { cookies } from 'next/headers';
import { ReactNode } from 'react';
import { Toaster } from 'sonner';
import './globals.css';
import { getActiveChannelQuery } from '../lib/vendure/queries/active-channel';
import { ChannelProvider } from '../components/cart/channel-context';

const { TWITTER_CREATOR, TWITTER_SITE, SITE_NAME } = process.env;
const baseUrl = process.env.NEXT_PUBLIC_VERCEL_URL
Expand Down Expand Up @@ -39,19 +41,22 @@ export const metadata = {
export default async function RootLayout({ children }: { children: ReactNode }) {
const cartId = (await cookies()).get('cartId')?.value;
// Don't await the fetch, pass the Promise to the context provider
const cart = getCart(cartId);
const activeOrder = getActiveOrder();
const activeChannel = getActiveChannel();

return (
<html lang="en" className={GeistSans.variable}>
<body className="bg-neutral-50 text-black selection:bg-teal-300 dark:bg-neutral-900 dark:text-white dark:selection:bg-pink-500 dark:selection:text-white">
<CartProvider cartPromise={cart}>
<Navbar />
<main>
{children}
<Toaster closeButton />
<WelcomeToast />
</main>
</CartProvider>
<ChannelProvider channelPromise={activeChannel}>
<CartProvider activeOrderPromise={activeOrder}>
<Navbar />
<main>
{children}
<Toaster closeButton />
<WelcomeToast />
</main>
</CartProvider>
</ChannelProvider>
</body>
</html>
);
Expand Down
2 changes: 1 addition & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ThreeItemGrid } from 'components/grid/three-items';
import Footer from 'components/layout/footer';

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 Vendure.',
openGraph: {
type: 'website'
}
Expand Down
55 changes: 28 additions & 27 deletions app/product/[handle]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { Gallery } from 'components/product/gallery';
import { ProductProvider } from 'components/product/product-context';
import { ProductDescription } from 'components/product/product-description';
import { HIDDEN_PRODUCT_TAG } from 'lib/constants';
import { getProduct, getProductRecommendations } from 'lib/shopify';
import { Image } from 'lib/shopify/types';
import { getActiveChannel, getProduct, getProductRecommendations } from 'lib/vendure';
import Link from 'next/link';
import { Suspense } from 'react';
import {Product} from "../../../lib/vendure/types";

export async function generateMetadata(props: {
params: Promise<{ handle: string }>;
Expand All @@ -20,12 +20,12 @@ export async function generateMetadata(props: {

if (!product) return notFound();

const { url, width, height, altText: alt } = product.featuredImage || {};
const indexable = !product.tags.includes(HIDDEN_PRODUCT_TAG);
const { source, width, height } = product.featuredAsset || {};
const indexable = product.enabled;

return {
title: product.seo.title || product.title,
description: product.seo.description || product.description,
title: product.name,
description: product.description,
robots: {
index: indexable,
follow: indexable,
Expand All @@ -34,14 +34,13 @@ export async function generateMetadata(props: {
follow: indexable
}
},
openGraph: url
openGraph: source
? {
images: [
{
url,
url: source,
width,
height,
alt
height
}
]
}
Expand All @@ -52,23 +51,25 @@ export async function generateMetadata(props: {
export default async function ProductPage(props: { params: Promise<{ handle: string }> }) {
const params = await props.params;
const product = await getProduct(params.handle);
const activeChannel = await getActiveChannel();

if (!product) return notFound();

const productJsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.title,
name: product.name,
description: product.description,
image: product.featuredImage.url,
image: product.featuredAsset?.source,
offers: {
'@type': 'AggregateOffer',
availability: product.availableForSale
availability: product.enabled
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
priceCurrency: product.priceRange.minVariantPrice.currencyCode,
highPrice: product.priceRange.maxVariantPrice.amount,
lowPrice: product.priceRange.minVariantPrice.amount
priceCurrency: activeChannel.defaultCurrencyCode
// TODO: needs client helper or schema extension in Vendure
// highPrice: product.priceRange.maxVariantPrice.amount,
// lowPrice: product.priceRange.minVariantPrice.amount
}
};

Expand All @@ -89,9 +90,9 @@ export default async function ProductPage(props: { params: Promise<{ handle: str
}
>
<Gallery
images={product.images.slice(0, 5).map((image: Image) => ({
src: image.url,
altText: image.altText
images={product.assets.slice(0, 5).map((asset) => ({
src: asset.preview,
altText: product.name
}))}
/>
</Suspense>
Expand All @@ -103,15 +104,15 @@ export default async function ProductPage(props: { params: Promise<{ handle: str
</Suspense>
</div>
</div>
<RelatedProducts id={product.id} />
{/*<RelatedProducts id={product.id} />*/}
</div>
<Footer />
</ProductProvider>
);
}

async function RelatedProducts({ id }: { id: string }) {
const relatedProducts = await getProductRecommendations(id);
const relatedProducts: Product[] = []

if (!relatedProducts.length) return null;

Expand All @@ -121,22 +122,22 @@ async function RelatedProducts({ id }: { id: string }) {
<ul className="flex w-full gap-4 overflow-x-auto pt-1">
{relatedProducts.map((product) => (
<li
key={product.handle}
key={product.slug}
className="aspect-square w-full flex-none min-[475px]:w-1/2 sm:w-1/3 md:w-1/4 lg:w-1/5"
>
<Link
className="relative h-full w-full"
href={`/product/${product.handle}`}
href={`/product/${product.slug}`}
prefetch={true}
>
<GridTileImage
alt={product.title}
alt={product.name}
label={{
title: product.title,
amount: product.priceRange.maxVariantPrice.amount,
currencyCode: product.priceRange.maxVariantPrice.currencyCode
amount: '0',
currencyCode: 'USD'
}}
src={product.featuredImage?.url}
src={product.featuredAsset?.preview ?? ''}
fill
sizes="(min-width: 1024px) 20vw, (min-width: 768px) 25vw, (min-width: 640px) 33vw, (min-width: 475px) 50vw, 100vw"
/>
Expand Down
2 changes: 1 addition & 1 deletion app/search/[collection]/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import OpengraphImage from 'components/opengraph-image';
import { getCollection } from 'lib/shopify';
import { getCollection } from 'lib/vendure';

export const runtime = 'edge';

Expand Down
14 changes: 7 additions & 7 deletions app/search/[collection]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getCollection, getCollectionProducts } from 'lib/shopify';
import {getActiveChannel, getCollection, getCollectionProducts} from 'lib/vendure';
import { Metadata } from 'next';
import { notFound } from 'next/navigation';

Expand All @@ -15,9 +15,8 @@ export async function generateMetadata(props: {
if (!collection) return notFound();

return {
title: collection.seo?.title || collection.title,
description:
collection.seo?.description || collection.description || `${collection.title} products`
title: collection.customFields?.seoTitle || collection.name,
description: collection.customFields?.seoDescription || `${collection.name} products`
};
}

Expand All @@ -28,16 +27,17 @@ export default async function CategoryPage(props: {
const searchParams = await props.searchParams;
const params = await props.params;
const { sort } = searchParams as { [key: string]: string };
const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort;
const products = await getCollectionProducts({ collection: params.collection, sortKey, reverse });
const { sortKey, direction } = sorting.find((item) => item.slug === sort) || defaultSort;
const products = await getCollectionProducts({ collection: params.collection, sortKey,direction });
const activeChannel = await getActiveChannel()

return (
<section>
{products.length === 0 ? (
<p className="py-3 text-lg">{`No products found in this collection`}</p>
) : (
<Grid className="grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
<ProductGridItems products={products} />
<ProductGridItems currencyCode={activeChannel.defaultCurrencyCode} products={products} />
</Grid>
)}
</section>
Expand Down
2 changes: 1 addition & 1 deletion app/search/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import FilterList from 'components/layout/search/filter';
import { sorting } from 'lib/constants';
import ChildrenWrapper from './children-wrapper';

export default function SearchLayout({ children }: { children: React.ReactNode }) {
export default async function SearchLayout({ children, params }: { children: React.ReactNode }) {
return (
<>
<div className="mx-auto flex max-w-screen-2xl flex-col gap-8 px-4 pb-4 text-black md:flex-row dark:text-white">
Expand Down
9 changes: 5 additions & 4 deletions app/search/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Grid from 'components/grid';
import ProductGridItems from 'components/layout/product-grid-items';
import { defaultSort, sorting } from 'lib/constants';
import { getProducts } from 'lib/shopify';
import { getActiveChannel, getProducts } from 'lib/vendure';

export const metadata = {
title: 'Search',
Expand All @@ -13,10 +13,11 @@ export default async function SearchPage(props: {
}) {
const searchParams = await props.searchParams;
const { sort, q: searchValue } = searchParams as { [key: string]: string };
const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort;
const { sortKey, direction } = sorting.find((item) => item.slug === sort) || defaultSort;

const products = await getProducts({ sortKey, reverse, query: searchValue });
const products = await getProducts({ sortKey, direction, query: searchValue });
const resultsText = products.length > 1 ? 'results' : 'result';
const activeChannel = await getActiveChannel();

return (
<>
Expand All @@ -30,7 +31,7 @@ export default async function SearchPage(props: {
) : null}
{products.length > 0 ? (
<Grid className="grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
<ProductGridItems products={products} />
<ProductGridItems currencyCode={activeChannel.defaultCurrencyCode} products={products} />
</Grid>
) : null}
</>
Expand Down
2 changes: 1 addition & 1 deletion app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getCollections, getPages, getProducts } from 'lib/shopify';
import { getCollections, getPages, getProducts } from 'lib/vendure';
import { validateEnvironmentVariables } from 'lib/utils';
import { MetadataRoute } from 'next';

Expand Down
18 changes: 18 additions & 0 deletions codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
overwrite: true,
schema: process.env.VENDURE_API_ENDPOINT,
documents: ['lib/vendure/**/*.ts'],
generates: {
'./lib/vendure/types.ts': {
config: {
scalars: { Money: 'number' },
namingConvention: { enumValues: 'keep' }
},
plugins: ['typescript', 'typescript-operations', 'typed-document-node']
}
}
};

export default config;
2 changes: 1 addition & 1 deletion components/carousel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getCollectionProducts } from 'lib/shopify';
import { getCollectionProducts } from 'lib/vendure';
import Link from 'next/link';
import { GridTileImage } from './grid/tile';

Expand Down
Loading

0 comments on commit 6da1039

Please sign in to comment.