chore: transfer repo

This commit is contained in:
Danijel
2026-01-19 20:21:14 +01:00
commit 7d2fb0c737
213 changed files with 18085 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { BackButton } from '@/components/product/BackButton';
import { ProductDescription } from '@/components/product/ProductDescription';
import { container } from '@/lib/utils';
import { ProductProvider } from 'components/product/product-context';
import { ProductDetailsSection } from 'components/product/ProductDetailsSection';
import { ProductGallery } from 'components/product/ProductGallery';
import { HIDDEN_PRODUCT_TAG } from 'lib/constants';
import { getProduct } from 'lib/shopify';
import { Suspense } from 'react';
export async function generateMetadata(props: {
params: Promise<{ handle: string }>;
}): Promise<Metadata> {
const params = await props.params;
const product = await getProduct(params.handle);
if (!product) return notFound();
const { url, width, height, altText: alt } = product.featuredImage || {};
const indexable = !product.tags.includes(HIDDEN_PRODUCT_TAG);
return {
title: product.seo.title || product.title,
description: product.seo.description || product.description,
robots: {
index: indexable,
follow: indexable,
googleBot: {
index: indexable,
follow: indexable
}
},
openGraph: url
? {
images: [
{
url,
width,
height,
alt
}
]
}
: null
};
}
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',
name: product.title,
description: product.description,
image: product.featuredImage.url,
offers: {
'@type': 'AggregateOffer',
availability: product.availableForSale
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
priceCurrency: product.priceRange.minVariantPrice.currencyCode,
highPrice: product.priceRange.maxVariantPrice.amount,
lowPrice: product.priceRange.minVariantPrice.amount
}
};
// If product has only one image, duplicate it to create the gallery effect
const galleryImages = product.images.length === 1
? Array(5).fill(product.images[0]).map(image => ({
url: image.url,
altText: image.altText,
width: image.width,
height: image.height
}))
: product.images;
return (
<ProductProvider>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(productJsonLd)
}}
/>
<div className="py-4">
<div className={container}>
<BackButton />
</div>
</div>
<div className={container}>
<div className="flex flex-col md:flex-row lg:gap-8">
<div className="w-full lg:w-3/5">
<Suspense
fallback={
<div className="relative aspect-square h-full max-h-[600px] w-full overflow-hidden" />
}
>
<ProductGallery
images={galleryImages.slice(0, 5)}
/>
</Suspense>
</div>
<div className="w-full lg:w-2/5 mt-8 lg:mt-0">
<Suspense fallback={null}>
<ProductDescription product={product} />
<ProductDetailsSection />
</Suspense>
</div>
</div>
</div>
</ProductProvider>
);
}