Added suspense and restructured layout AND pages

This commit is contained in:
Henrik Larsson 2023-08-09 14:19:27 +02:00
parent 5524d91da4
commit a16807c8d8
10 changed files with 30 additions and 149 deletions

View File

@ -3,6 +3,7 @@ import { pageQuery } from 'lib/sanity/queries';
import { clientFetch } from 'lib/sanity/sanity.client'; import { clientFetch } from 'lib/sanity/sanity.client';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { Suspense } from 'react';
export const runtime = 'edge'; export const runtime = 'edge';
@ -73,5 +74,12 @@ export default async function Page({ params }: PageParams) {
if (!page) return notFound(); if (!page) return notFound();
return <DynamicContentManager content={page?.content} />; return (
<>
<DynamicContentManager content={page?.content} />
<Suspense>
<Footer locale={params.locale} />
</Suspense>
</>
);
} }

View File

@ -1,8 +1,10 @@
import Footer from '@/components/layout/footer/footer';
import Text from 'components/ui/text/text'; import Text from 'components/ui/text/text';
import { categoryQuery } from 'lib/sanity/queries'; import { categoryQuery } from 'lib/sanity/queries';
import { clientFetch } from 'lib/sanity/sanity.client'; import { clientFetch } from 'lib/sanity/sanity.client';
import { Metadata } from 'next'; import { Metadata } from 'next';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { Suspense } from 'react';
export async function generateMetadata({ export async function generateMetadata({
params params
@ -36,6 +38,9 @@ export default async function ProductPage({ params }: CategoryPageParams) {
return ( return (
<div className="mb-8 flex w-full flex-col px-4 lg:my-16 lg:px-8 2xl:px-16"> <div className="mb-8 flex w-full flex-col px-4 lg:my-16 lg:px-8 2xl:px-16">
<Text variant={'pageHeading'}>{title}</Text> <Text variant={'pageHeading'}>{title}</Text>
<Suspense>
<Footer locale={params.locale} />
</Suspense>
</div> </div>
); );
} }

View File

@ -1,9 +1,8 @@
import Footer from 'components/layout/footer/footer';
import Header from 'components/layout/header/header'; import Header from 'components/layout/header/header';
import { NextIntlClientProvider } from 'next-intl'; import { NextIntlClientProvider } from 'next-intl';
import { Inter } from 'next/font/google'; import { Inter } from 'next/font/google';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { ReactNode } from 'react'; import { ReactNode, Suspense } from 'react';
import { supportedLanguages } from '../../i18n-config'; import { supportedLanguages } from '../../i18n-config';
import './globals.css'; import './globals.css';
@ -58,9 +57,9 @@ export default async function LocaleLayout({ children, params: { locale } }: Loc
<body className="flex min-h-screen flex-col"> <body className="flex min-h-screen flex-col">
<NextIntlClientProvider locale={locale} messages={messages}> <NextIntlClientProvider locale={locale} messages={messages}>
<Header locale={locale} /> <Header locale={locale} />
<Suspense>
<main className="flex-1">{children}</main> <main className="flex-1">{children}</main>
{/* @ts-expect-error Server Component (https://github.com/vercel/next.js/issues/42292) */} </Suspense>
<Footer locale={locale} />
</NextIntlClientProvider> </NextIntlClientProvider>
</body> </body>
</html> </html>

View File

@ -1,8 +1,10 @@
import Footer from '@/components/layout/footer/footer';
import DynamicContentManager from 'components/layout/dynamic-content-manager/dynamic-content-manager'; import DynamicContentManager from 'components/layout/dynamic-content-manager/dynamic-content-manager';
import { homePageQuery } from 'lib/sanity/queries'; import { homePageQuery } from 'lib/sanity/queries';
import { clientFetch } from 'lib/sanity/sanity.client'; import { clientFetch } from 'lib/sanity/sanity.client';
import { Metadata } from 'next'; import { Metadata } from 'next';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { Suspense } from 'react';
export const runtime = 'edge'; export const runtime = 'edge';
export async function generateMetadata({ export async function generateMetadata({
@ -32,6 +34,9 @@ export default async function HomePage({ params }: HomePageParams) {
return ( return (
<> <>
<DynamicContentManager content={data?.content} /> <DynamicContentManager content={data?.content} />
<Suspense>
<Footer locale={params.locale} />
</Suspense>
</> </>
); );
} }

View File

@ -1,8 +1,10 @@
import Footer from '@/components/layout/footer/footer';
import ProductView from 'components/product/product-view'; import ProductView from 'components/product/product-view';
import { productQuery } from 'lib/sanity/queries'; import { productQuery } from 'lib/sanity/queries';
import { clientFetch } from 'lib/sanity/sanity.client'; import { clientFetch } from 'lib/sanity/sanity.client';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { Suspense } from 'react';
interface ProductPageParams { interface ProductPageParams {
params: { params: {
@ -83,7 +85,10 @@ export default async function ProductPage({ params }: ProductPageParams) {
__html: JSON.stringify(productJsonLd) __html: JSON.stringify(productJsonLd)
}} }}
/> />
<ProductView product={product} relatedProducts={[]} />; <ProductView product={product} relatedProducts={[]} />
<Suspense>
<Footer locale={params.locale} />
</Suspense>
</> </>
); );
} }

View File

@ -1,75 +0,0 @@
import { cookies } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
import { addToCart, removeFromCart, updateCart } from 'lib/shopify';
import { isShopifyError } from 'lib/type-guards';
function formatErrorMessage(err: Error): string {
return JSON.stringify(err, Object.getOwnPropertyNames(err));
}
export async function POST(req: NextRequest): Promise<Response> {
const cartId = cookies().get('cartId')?.value;
const { merchandiseId } = await req.json();
if (!cartId?.length || !merchandiseId?.length) {
return NextResponse.json({ error: 'Missing cartId or variantId' }, { status: 400 });
}
try {
await addToCart(cartId, [{ merchandiseId, quantity: 1 }]);
return NextResponse.json({ status: 204 });
} catch (e) {
if (isShopifyError(e)) {
return NextResponse.json({ message: formatErrorMessage(e.message) }, { status: e.status });
}
return NextResponse.json({ status: 500 });
}
}
export async function PUT(req: NextRequest): Promise<Response> {
const cartId = cookies().get('cartId')?.value;
const { variantId, quantity, lineId } = await req.json();
if (!cartId || !variantId || !quantity || !lineId) {
return NextResponse.json(
{ error: 'Missing cartId, variantId, lineId, or quantity' },
{ status: 400 }
);
}
try {
await updateCart(cartId, [
{
id: lineId,
merchandiseId: variantId,
quantity
}
]);
return NextResponse.json({ status: 204 });
} catch (e) {
if (isShopifyError(e)) {
return NextResponse.json({ message: formatErrorMessage(e.message) }, { status: e.status });
}
return NextResponse.json({ status: 500 });
}
}
export async function DELETE(req: NextRequest): Promise<Response> {
const cartId = cookies().get('cartId')?.value;
const { lineId } = await req.json();
if (!cartId || !lineId) {
return NextResponse.json({ error: 'Missing cartId or lineId' }, { status: 400 });
}
try {
await removeFromCart(cartId, [lineId]);
return NextResponse.json({ status: 204 });
} catch (e) {
if (isShopifyError(e)) {
return NextResponse.json({ message: formatErrorMessage(e.message) }, { status: e.status });
}
return NextResponse.json({ status: 500 });
}
}

Binary file not shown.

Binary file not shown.

View File

@ -1,67 +0,0 @@
import { ImageResponse } from '@vercel/og';
import { NextRequest } from 'next/server';
export const runtime = 'edge';
const interRegular = fetch(new URL('./Inter-Regular.ttf', import.meta.url)).then((res) =>
res.arrayBuffer()
);
const interBold = fetch(new URL('./Inter-Bold.ttf', import.meta.url)).then((res) =>
res.arrayBuffer()
);
export async function GET(req: NextRequest): Promise<Response | ImageResponse> {
try {
const [regularFont, boldFont] = await Promise.all([interRegular, interBold]);
const { searchParams } = new URL(req.url);
const title = searchParams.has('title')
? searchParams.get('title')?.slice(0, 100)
: process.env.SITE_NAME;
return new ImageResponse(
(
<div tw="flex h-full w-full flex-col items-center justify-center bg-black">
<svg viewBox="0 0 32 32" width="140">
<rect width="100%" height="100%" rx="16" fill="white" />
<path
fillRule="evenodd"
clipRule="evenodd"
fill="black"
d="M17.6482 10.1305L15.8785 7.02583L7.02979 22.5499H10.5278L17.6482 10.1305ZM19.8798 14.0457L18.11 17.1983L19.394 19.4511H16.8453L15.1056 22.5499H24.7272L19.8798 14.0457Z"
/>
</svg>
<div tw="mt-12 text-6xl text-white font-bold">{title}</div>
</div>
),
{
width: 1200,
height: 630,
fonts: [
{
name: 'Inter',
data: regularFont,
style: 'normal',
weight: 400
},
{
name: 'Inter',
data: boldFont,
style: 'normal',
weight: 700
}
]
}
);
} catch (e) {
if (!(e instanceof Error)) throw e;
console.log(e.message);
return new Response(`Failed to generate the image`, {
status: 500
});
}
}

View File

@ -7,6 +7,7 @@ import ReusableSection from '@/components/modules/reusable-section/reusable-sect
import Slider from '@/components/modules/slider/slider'; import Slider from '@/components/modules/slider/slider';
import USPSection from '@/components/modules/usp-section/usp-section'; import USPSection from '@/components/modules/usp-section/usp-section';
import { InfoCircledIcon } from '@radix-ui/react-icons'; import { InfoCircledIcon } from '@radix-ui/react-icons';
interface getContentComponentProps { interface getContentComponentProps {
_type: string; _type: string;
_key: number; _key: number;