Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules
.next
*.log
.git
.gitignore
.env
.env.local
.env.*.local
Dockerfile
docker-compose.yml
27 changes: 27 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

FROM node:20-alpine AS base
WORKDIR /app
COPY package.json pnpm-lock.yaml* ./
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN pnpm install --frozen-lockfile


FROM base AS dev
COPY . .
RUN pnpm install
CMD ["pnpm", "dev"]


FROM base AS build
COPY . .
RUN pnpm build


FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=build /app/.next ./.next
COPY --from=build /app/public ./public
COPY --from=base /app/node_modules ./node_modules
COPY --from=build /app/package.json ./package.json
EXPOSE 3000
CMD ["pnpm", "start"]
2 changes: 1 addition & 1 deletion app/[page]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Footer from 'components/layout/footer';
import Footer from "components/layout/footer";

export default function Layout({ children }: { children: React.ReactNode }) {
return (
Expand Down
4 changes: 2 additions & 2 deletions app/[page]/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import OpengraphImage from 'components/opengraph-image';
import { getPage } from 'lib/shopify';
import OpengraphImage from "components/opengraph-image";
import { getPage } from "lib/shopify";

export default async function Image({ params }: { params: { page: string } }) {
const page = await getPage(params.page);
Expand Down
29 changes: 17 additions & 12 deletions app/[page]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Metadata } from 'next';
import type { Metadata } from "next";

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

export async function generateMetadata(props: {
params: Promise<{ page: string }>;
Expand All @@ -18,12 +18,14 @@ export async function generateMetadata(props: {
openGraph: {
publishedTime: page.createdAt,
modifiedTime: page.updatedAt,
type: 'article'
}
type: "article",
},
};
}

export default async function Page(props: { params: Promise<{ page: string }> }) {
export default async function Page(props: {
params: Promise<{ page: string }>;
}) {
const params = await props.params;
const page = await getPage(params.page);

Expand All @@ -34,11 +36,14 @@ export default async function Page(props: { params: Promise<{ page: string }> })
<h1 className="mb-8 text-5xl font-bold">{page.title}</h1>
<Prose className="mb-8" html={page.body} />
<p className="text-sm italic">
{`This document was last updated on ${new Intl.DateTimeFormat(undefined, {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(new Date(page.updatedAt))}.`}
{`This document was last updated on ${new Intl.DateTimeFormat(
undefined,
{
year: "numeric",
month: "long",
day: "numeric",
},
).format(new Date(page.updatedAt))}.`}
</p>
</>
);
Expand Down
4 changes: 2 additions & 2 deletions app/api/revalidate/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { revalidate } from 'lib/shopify';
import { NextRequest, NextResponse } from 'next/server';
import { revalidate } from "lib/shopify";
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest): Promise<NextResponse> {
return revalidate(req);
Expand Down
6 changes: 3 additions & 3 deletions app/error.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use client';
"use client";

export default function Error({ reset }: { reset: () => void }) {
return (
<div className="mx-auto my-4 flex max-w-xl flex-col rounded-lg border border-neutral-200 bg-white p-8 md:p-12 dark:border-neutral-800 dark:bg-black">
<h2 className="text-xl font-bold">Oh no!</h2>
<p className="my-2">
There was an issue with our storefront. This could be a temporary issue, please try your
action again.
There was an issue with our storefront. This could be a temporary issue,
please try your action again.
</p>
<button
className="mx-auto mt-4 flex w-full items-center justify-center rounded-full bg-blue-600 p-4 tracking-wide text-white hover:opacity-90"
Expand Down
4 changes: 2 additions & 2 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import 'tailwindcss';
@import "tailwindcss";

@plugin "@tailwindcss/container-queries";
@plugin "@tailwindcss/typography";
Expand All @@ -20,7 +20,7 @@
}

@supports (font: -apple-system-body) and (-webkit-appearance: none) {
img[loading='lazy'] {
img[loading="lazy"] {
clip-path: inset(0.6px);
}
}
Expand Down
36 changes: 22 additions & 14 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
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 { ReactNode } from 'react';
import { Toaster } from 'sonner';
import './globals.css';
import { baseUrl } from 'lib/utils';
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 { ReactNode } from "react";
import { Toaster } from "sonner";
import "./globals.css";
import { baseUrl } from "lib/utils";

const { SITE_NAME } = process.env;

export const metadata = {
metadataBase: new URL(baseUrl),
title: {
default: SITE_NAME!,
template: `%s | ${SITE_NAME}`
template: `%s | ${SITE_NAME}`,
},
robots: {
follow: true,
index: true
}
index: true,
},
};

export default async function RootLayout({
children
children,
}: {
children: ReactNode;
}) {
// Don't await the fetch, pass the Promise to the context provider
const cart = getCart();
let cart: ReturnType<typeof getCart> | null;

//by doing this we are ensuring to pass Promise<Cart | undefined> in provider
//this is preventing an error on first load if the getcart throw some errors
try {
cart = getCart();
} catch (err) {
cart = Promise.resolve(undefined);
}

return (
<html lang="en" className={GeistSans.variable}>
Expand Down
2 changes: 1 addition & 1 deletion app/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import OpengraphImage from 'components/opengraph-image';
import OpengraphImage from "components/opengraph-image";

export default async function Image() {
return await OpengraphImage();
Expand Down
12 changes: 6 additions & 6 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Carousel } from 'components/carousel';
import { ThreeItemGrid } from 'components/grid/three-items';
import Footer from 'components/layout/footer';
import { Carousel } from "components/carousel";
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.',
"High-performance ecommerce store built with Next.js, Vercel, and Shopify.",
openGraph: {
type: 'website'
}
type: "website",
},
};

export default function HomePage() {
Expand Down
60 changes: 31 additions & 29 deletions app/product/[handle]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import type { Metadata } from "next";
import { notFound } from "next/navigation";

import { GridTileImage } from 'components/grid/tile';
import Footer from 'components/layout/footer';
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 Link from 'next/link';
import { Suspense } from 'react';
import { GridTileImage } from "components/grid/tile";
import Footer from "components/layout/footer";
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 Link from "next/link";
import { Suspense } from "react";

export async function generateMetadata(props: {
params: Promise<{ handle: string }>;
Expand All @@ -31,8 +31,8 @@ export async function generateMetadata(props: {
follow: indexable,
googleBot: {
index: indexable,
follow: indexable
}
follow: indexable,
},
},
openGraph: url
? {
Expand All @@ -41,43 +41,45 @@ export async function generateMetadata(props: {
url,
width,
height,
alt
}
]
alt,
},
],
}
: null
: null,
};
}

export default async function ProductPage(props: { params: Promise<{ handle: string }> }) {
export default async function ProductPage(props: {
params: Promise<{ handle: string }>;
}) {
const params = await props.params;
const product = await getProduct(params.handle);

if (!product) return notFound();

const productJsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
"@context": "https://schema.org",
"@type": "Product",
name: product.title,
description: product.description,
image: product.featuredImage.url,
offers: {
'@type': 'AggregateOffer',
"@type": "AggregateOffer",
availability: product.availableForSale
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
? "https://schema.org/InStock"
: "https://schema.org/OutOfStock",
priceCurrency: product.priceRange.minVariantPrice.currencyCode,
highPrice: product.priceRange.maxVariantPrice.amount,
lowPrice: product.priceRange.minVariantPrice.amount
}
lowPrice: product.priceRange.minVariantPrice.amount,
},
};

return (
<ProductProvider>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(productJsonLd)
__html: JSON.stringify(productJsonLd),
}}
/>
<div className="mx-auto max-w-(--breakpoint-2xl) px-4">
Expand All @@ -91,7 +93,7 @@ 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
altText: image.altText,
}))}
/>
</Suspense>
Expand Down Expand Up @@ -134,7 +136,7 @@ async function RelatedProducts({ id }: { id: string }) {
label={{
title: product.title,
amount: product.priceRange.maxVariantPrice.amount,
currencyCode: product.priceRange.maxVariantPrice.currencyCode
currencyCode: product.priceRange.maxVariantPrice.currencyCode,
}}
src={product.featuredImage?.url}
fill
Expand Down
8 changes: 4 additions & 4 deletions app/robots.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { baseUrl } from 'lib/utils';
import { baseUrl } from "lib/utils";

export default function robots() {
return {
rules: [
{
userAgent: '*'
}
userAgent: "*",
},
],
sitemap: `${baseUrl}/sitemap.xml`,
host: baseUrl
host: baseUrl,
};
}
6 changes: 3 additions & 3 deletions app/search/[collection]/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import OpengraphImage from 'components/opengraph-image';
import { getCollection } from 'lib/shopify';
import OpengraphImage from "components/opengraph-image";
import { getCollection } from "lib/shopify";

export default async function Image({
params
params,
}: {
params: { collection: string };
}) {
Expand Down
Loading