Skip to content

Commit

Permalink
refactor(core): move components in PDP to own files (#560)
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgemoya authored Feb 15, 2024
1 parent 4eb528b commit afae7e3
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 191 deletions.
16 changes: 16 additions & 0 deletions apps/core/app/(default)/product/[slug]/_components/description.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getProduct } from '~/client/queries/get-product';

type Product = Awaited<ReturnType<typeof getProduct>>;

export const Description = ({ product }: { product: NonNullable<Product> }) => {
if (!product.description) {
return null;
}

return (
<>
<h2 className="mb-4 text-xl font-bold md:text-2xl">Description</h2>
<div dangerouslySetInnerHTML={{ __html: product.description }} />
</>
);
};
133 changes: 133 additions & 0 deletions apps/core/app/(default)/product/[slug]/_components/details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Suspense } from 'react';

import { getProduct } from '~/client/queries/get-product';
import { ProductForm } from '~/components/product-form';

import { ProductSchema } from './product-schema';
import { ReviewSummary } from './review-summary';

type Product = Awaited<ReturnType<typeof getProduct>>;

const currencyFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
});

export const Details = ({ product }: { product: NonNullable<Product> }) => {
const showPriceRange =
product.prices?.priceRange.min.value !== product.prices?.priceRange.max.value;

return (
<div>
{product.brand && (
<p className="mb-2 font-semibold uppercase text-gray-500">{product.brand.name}</p>
)}

<h1 className="mb-4 text-4xl font-black lg:text-5xl">{product.name}</h1>

<Suspense fallback="Loading...">
<ReviewSummary productId={product.entityId} />
</Suspense>

{product.prices && (
<div className="my-6 text-2xl font-bold lg:text-3xl">
{showPriceRange ? (
<span>
{currencyFormatter.format(product.prices.priceRange.min.value)} -{' '}
{currencyFormatter.format(product.prices.priceRange.max.value)}
</span>
) : (
<>
{product.prices.retailPrice?.value !== undefined && (
<span>
MSRP:{' '}
<span className="line-through">
{currencyFormatter.format(product.prices.retailPrice.value)}
</span>
<br />
</span>
)}
{product.prices.salePrice?.value !== undefined &&
product.prices.basePrice?.value !== undefined ? (
<>
<span>
Was:{' '}
<span className="line-through">
{currencyFormatter.format(product.prices.basePrice.value)}
</span>
</span>
<br />
<span>Now: {currencyFormatter.format(product.prices.salePrice.value)}</span>
</>
) : (
product.prices.price.value && (
<span>{currencyFormatter.format(product.prices.price.value)}</span>
)
)}
</>
)}
</div>
)}

<ProductForm product={product} />

<div className="my-12">
<h2 className="mb-4 text-xl font-bold md:text-2xl">Additional details</h2>
<div className="grid gap-3 sm:grid-cols-2">
{Boolean(product.sku) && (
<div>
<h3 className="font-semibold">SKU</h3>
<p>{product.sku}</p>
</div>
)}
{Boolean(product.upc) && (
<div>
<h3 className="font-semibold">UPC</h3>
<p>{product.upc}</p>
</div>
)}
{Boolean(product.minPurchaseQuantity) && (
<div>
<h3 className="font-semibold">Minimum purchase</h3>
<p>{product.minPurchaseQuantity}</p>
</div>
)}
{Boolean(product.maxPurchaseQuantity) && (
<div>
<h3 className="font-semibold">Maxiumum purchase</h3>
<p>{product.maxPurchaseQuantity}</p>
</div>
)}
{Boolean(product.availabilityV2.description) && (
<div>
<h3 className="font-semibold">Availability</h3>
<p>{product.availabilityV2.description}</p>
</div>
)}
{Boolean(product.condition) && (
<div>
<h3 className="font-semibold">Condition</h3>
<p>{product.condition}</p>
</div>
)}
{Boolean(product.weight) && (
<div>
<h3 className="font-semibold">Weight</h3>
<p>
{product.weight?.value} {product.weight?.unit}
</p>
</div>
)}
{Boolean(product.customFields) &&
product.customFields.map((customField) => (
<div key={customField.entityId}>
<h3 className="font-semibold">{customField.name}</h3>
<p>{customField.value}</p>
</div>
))}
</div>
</div>
<ProductSchema product={product} />
</div>
);
};
27 changes: 22 additions & 5 deletions apps/core/app/(default)/product/[slug]/_components/gallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,30 @@ import {
import Image from 'next/image';

import { getProduct } from '~/client/queries/get-product';
import { ExistingResultType } from '~/client/util';

interface Props {
images: ExistingResultType<typeof getProduct>['images'];
}
type Product = Awaited<ReturnType<typeof getProduct>>;

export const Gallery = ({ product }: { product: NonNullable<Product> }) => {
// Make a copy of product.images
const images = product.images;

// Pick the top-level default image out of the `Image` response
const topLevelDefaultImg = product.images.find((image) => image.isDefault);

// If product.defaultImage exists, and product.defaultImage.url is not equal to the url of the isDefault image in the Image response,
// mark the existing isDefault image to "isDefault = false" and append the correct default image to images
if (product.defaultImage && topLevelDefaultImg?.url !== product.defaultImage.url) {
images.forEach((image) => {
image.isDefault = false;
});

images.push({
url: product.defaultImage.url,
altText: product.defaultImage.altText,
isDefault: true,
});
}

export const Gallery = ({ images }: Props) => {
const defaultImageIndex = images.findIndex((image) => image.isDefault);

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { OptionValueId } from '~/client/generated/graphql';
import { getRelatedProducts } from '~/client/queries/get-related-products';
import { ProductCardCarousel } from '~/components/product-card-carousel';

export const RelatedProducts = async ({
productId,
optionValueIds,
}: {
productId: number;
optionValueIds: OptionValueId[];
}) => {
export const RelatedProducts = async ({ productId }: { productId: number }) => {
const relatedProducts = await getRelatedProducts({
productId,
optionValueIds,
imageWidth: 500,
imageHeight: 500,
});
Expand Down
16 changes: 16 additions & 0 deletions apps/core/app/(default)/product/[slug]/_components/warranty.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getProduct } from '~/client/queries/get-product';

type Product = Awaited<ReturnType<typeof getProduct>>;

export const Warranty = ({ product }: { product: NonNullable<Product> }) => {
if (!product.warranty) {
return null;
}

return (
<>
<h2 className="mb-4 mt-8 text-xl font-bold md:text-2xl">Warranty</h2>
<p>{product.warranty}</p>
</>
);
};
Loading

0 comments on commit afae7e3

Please sign in to comment.