mirror of
https://github.com/vercel/commerce.git
synced 2025-05-11 20:27:51 +00:00
add PDP content
Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
parent
fab2a5e967
commit
cc2c79764d
@ -5,6 +5,7 @@ import { Suspense } from 'react';
|
|||||||
import BreadcrumbComponent from 'components/breadcrumb';
|
import BreadcrumbComponent from 'components/breadcrumb';
|
||||||
import { GridTileImage } from 'components/grid/tile';
|
import { GridTileImage } from 'components/grid/tile';
|
||||||
import Footer from 'components/layout/footer';
|
import Footer from 'components/layout/footer';
|
||||||
|
import AdditionalInformation from 'components/product/additional-information';
|
||||||
import { Gallery } from 'components/product/gallery';
|
import { Gallery } from 'components/product/gallery';
|
||||||
import { ProductDescription } from 'components/product/product-description';
|
import { ProductDescription } from 'components/product/product-description';
|
||||||
import { HIDDEN_PRODUCT_TAG } from 'lib/constants';
|
import { HIDDEN_PRODUCT_TAG } from 'lib/constants';
|
||||||
@ -49,7 +50,13 @@ export async function generateMetadata({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function ProductPage({ params }: { params: { handle: string } }) {
|
export default async function ProductPage({
|
||||||
|
params,
|
||||||
|
searchParams
|
||||||
|
}: {
|
||||||
|
params: { handle: string };
|
||||||
|
searchParams?: { [key: string]: string | string[] | undefined };
|
||||||
|
}) {
|
||||||
const product = await getProduct(params.handle);
|
const product = await getProduct(params.handle);
|
||||||
|
|
||||||
if (!product) return notFound();
|
if (!product) return notFound();
|
||||||
@ -86,6 +93,9 @@ export default async function ProductPage({ params }: { params: { handle: string
|
|||||||
<div className="my-3 flex flex-col space-x-0 lg:flex-row lg:gap-8 lg:space-x-3">
|
<div className="my-3 flex flex-col space-x-0 lg:flex-row lg:gap-8 lg:space-x-3">
|
||||||
<div className="h-full w-full basis-full lg:basis-7/12">
|
<div className="h-full w-full basis-full lg:basis-7/12">
|
||||||
<ProductDescription product={product} />
|
<ProductDescription product={product} />
|
||||||
|
<Suspense>
|
||||||
|
<AdditionalInformation product={product} searchParams={searchParams} />
|
||||||
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="hidden lg:block lg:basis-5/12">
|
<div className="hidden lg:block lg:basis-5/12">
|
||||||
@ -95,7 +105,7 @@ export default async function ProductPage({ params }: { params: { handle: string
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Gallery
|
<Gallery
|
||||||
images={product.images.map((image: Image) => ({
|
images={product.images.slice(0, 5).map((image: Image) => ({
|
||||||
src: image.url,
|
src: image.url,
|
||||||
altText: image.altText
|
altText: image.altText
|
||||||
}))}
|
}))}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Price from 'components/price';
|
import Price from 'components/price';
|
||||||
import { useDebounce } from 'hooks';
|
import { useDebounce } from 'hooks/use-debounce';
|
||||||
import { Filter } from 'lib/shopify/types';
|
import { Filter } from 'lib/shopify/types';
|
||||||
import { createUrl } from 'lib/utils';
|
import { createUrl } from 'lib/utils';
|
||||||
import get from 'lodash.get';
|
import get from 'lodash.get';
|
||||||
|
@ -40,7 +40,7 @@ const RichTextBlock = ({ block }: { block: Content }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p className="text-blue-200">
|
<p className="text-black-700">
|
||||||
{block.children.map((child, index) => (
|
{block.children.map((child, index) => (
|
||||||
<RichTextBlock key={index} block={child} />
|
<RichTextBlock key={index} block={child} />
|
||||||
))}
|
))}
|
||||||
@ -50,7 +50,7 @@ const RichTextBlock = ({ block }: { block: Content }) => {
|
|||||||
|
|
||||||
const RichTextDisplay = ({ contentBlocks }: { contentBlocks: Content[] }) => {
|
const RichTextDisplay = ({ contentBlocks }: { contentBlocks: Content[] }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col gap-2">
|
<div className="flex w-full flex-col gap-4">
|
||||||
{contentBlocks.map((block, index) => (
|
{contentBlocks.map((block, index) => (
|
||||||
<RichTextBlock key={index} block={block} />
|
<RichTextBlock key={index} block={block} />
|
||||||
))}
|
))}
|
||||||
|
@ -8,7 +8,7 @@ const TextBlock = ({ block }: { block: Metaobject }) => {
|
|||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
<div className="flex flex-col gap-5 px-4 md:px-0">
|
<div className="flex flex-col gap-5 px-4 md:px-0">
|
||||||
{block.title && (
|
{block.title && (
|
||||||
<h3 className="text-xl font-semibold leading-6 text-gray-900">{block.title}</h3>
|
<h3 className="text-xl font-bold leading-6 text-black-700">{block.title}</h3>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<RichTextDisplay contentBlocks={content.children} />
|
<RichTextDisplay contentBlocks={content.children} />
|
||||||
|
@ -1,14 +1,38 @@
|
|||||||
|
import PageContent from 'components/page/page-content';
|
||||||
|
import { getMetaobject, getMetaobjectsByIds } from 'lib/shopify';
|
||||||
import { Product } from 'lib/shopify/types';
|
import { Product } from 'lib/shopify/types';
|
||||||
import Details from './details';
|
import { getSelectedProductVariant } from 'lib/utils';
|
||||||
import ShippingPolicy from './shipping-policy';
|
|
||||||
import WarrantyPolicy from './warranty-policy';
|
const AdditionalInformation = async ({
|
||||||
|
product,
|
||||||
|
searchParams
|
||||||
|
}: {
|
||||||
|
product: Product;
|
||||||
|
searchParams?: { [key: string]: string | string[] | undefined };
|
||||||
|
}) => {
|
||||||
|
const selectedVariant = getSelectedProductVariant({ product, searchParams });
|
||||||
|
|
||||||
|
if (!selectedVariant) return null;
|
||||||
|
|
||||||
|
const pdpContent = await getMetaobject({
|
||||||
|
handle: {
|
||||||
|
handle: `${selectedVariant.condition}-${product.productType}`.toLowerCase(),
|
||||||
|
type: 'pdp_content'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!pdpContent) return null;
|
||||||
|
|
||||||
|
const contentIds = pdpContent.content ? JSON.parse(pdpContent.content) : [];
|
||||||
|
const pageContent = await getMetaobjectsByIds(contentIds);
|
||||||
|
|
||||||
const AdditionalInformation = ({ product }: { product: Product }) => {
|
|
||||||
return (
|
return (
|
||||||
<div className="my-5 w-full divide-y">
|
<div className="my-6 w-full divide-y">
|
||||||
<Details product={product} />
|
{pageContent.map((block) => (
|
||||||
<WarrantyPolicy />
|
<div key={block.id} className="py-5">
|
||||||
<ShippingPolicy />
|
<PageContent block={block} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import clsx from 'clsx';
|
|
||||||
import Price from 'components/price';
|
|
||||||
import { Product } from 'lib/shopify/types';
|
|
||||||
import { useSearchParams } from 'next/navigation';
|
|
||||||
import DisclosureSection from './disclosure-section';
|
|
||||||
|
|
||||||
const Details = ({ product }: { product: Product }) => {
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const variants = product.variants;
|
|
||||||
|
|
||||||
const variant = variants.find((variant) =>
|
|
||||||
variant.selectedOptions.every(
|
|
||||||
(option) => option.value === searchParams.get(option.name.toLowerCase())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const details = [
|
|
||||||
...(product.transmissionTag
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
title: 'Transmission Tag',
|
|
||||||
value: product.transmissionTag.join()
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(product.transmissionCode
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
title: 'Transmission Code',
|
|
||||||
value: product.transmissionCode.join()
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(product.transmissionSpeeds
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
title: 'Transmission Speeds',
|
|
||||||
value: product.transmissionSpeeds.map((speed) => `${speed}-Speed`).join()
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: [])
|
|
||||||
];
|
|
||||||
return (
|
|
||||||
<DisclosureSection title="Product Details" defaultOpen>
|
|
||||||
<div className="flex w-full items-center p-1">
|
|
||||||
<span className="basis-2/5">Condition</span>
|
|
||||||
<span>{variant?.condition || 'N/A'}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex w-full items-center bg-gray-100 p-1">
|
|
||||||
<span className="basis-2/5">Price</span>
|
|
||||||
<Price
|
|
||||||
amount={variant?.price.amount || product.priceRange.minVariantPrice.amount}
|
|
||||||
currencyCode={
|
|
||||||
variant?.price.currencyCode || product.priceRange.minVariantPrice.currencyCode
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex w-full items-center p-1">
|
|
||||||
<span className="basis-2/5">Warranty</span>
|
|
||||||
<span />
|
|
||||||
</div>
|
|
||||||
<div className="flex w-full items-center bg-gray-100 p-1">
|
|
||||||
<span className="basis-2/5">Cylinders</span>
|
|
||||||
<span>{product.engineCylinders?.map((cylinder) => `${cylinder} Cylinders`).join()}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{details.map(({ title, value }, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className={clsx('flex w-full items-center p-1', { 'bg-gray-100': index % 2 !== 0 })}
|
|
||||||
>
|
|
||||||
<span className="basis-2/5">{title}</span>
|
|
||||||
<span>{value}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</DisclosureSection>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Details;
|
|
@ -1,25 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react';
|
|
||||||
import { ChevronDownIcon } from '@heroicons/react/24/outline';
|
|
||||||
import { ReactNode } from 'react';
|
|
||||||
|
|
||||||
type DisclosureProps = {
|
|
||||||
children: ReactNode;
|
|
||||||
defaultOpen?: boolean;
|
|
||||||
title: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const DisclosureSection = ({ children, title, defaultOpen }: DisclosureProps) => {
|
|
||||||
return (
|
|
||||||
<Disclosure as="div" className="p-3" defaultOpen={defaultOpen}>
|
|
||||||
<DisclosureButton className="group flex w-full items-center justify-between">
|
|
||||||
<span className="font-medium">{title}</span>
|
|
||||||
<ChevronDownIcon className="size-4 group-data-[open]:rotate-180" />
|
|
||||||
</DisclosureButton>
|
|
||||||
<DisclosurePanel className="mt-2 py-2 text-sm">{children}</DisclosurePanel>
|
|
||||||
</Disclosure>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DisclosureSection;
|
|
@ -2,7 +2,6 @@ import { AddToCart } from 'components/cart/add-to-cart';
|
|||||||
import Prose from 'components/prose';
|
import Prose from 'components/prose';
|
||||||
import { Product } from 'lib/shopify/types';
|
import { Product } from 'lib/shopify/types';
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import AdditionalInformation from './additional-information';
|
|
||||||
import CoreCharge from './core-charge';
|
import CoreCharge from './core-charge';
|
||||||
import Delivery from './delivery';
|
import Delivery from './delivery';
|
||||||
import PriceSummary from './price-summary';
|
import PriceSummary from './price-summary';
|
||||||
@ -56,7 +55,6 @@ export function ProductDescription({ product }: { product: Product }) {
|
|||||||
<AddToCart variants={product.variants} availableForSale={product.availableForSale} />
|
<AddToCart variants={product.variants} availableForSale={product.availableForSale} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<SpecialOffer />
|
<SpecialOffer />
|
||||||
<AdditionalInformation product={product} />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
import DisclosureSection from './disclosure-section';
|
|
||||||
|
|
||||||
const { SITE_NAME } = process.env;
|
|
||||||
const ShippingPolicy = () => {
|
|
||||||
return (
|
|
||||||
<DisclosureSection title="Shipping & returns">
|
|
||||||
<p>
|
|
||||||
At {SITE_NAME}, we offer a Flat Rate Shipping (Commercial address) service as long as the
|
|
||||||
delivery address is in a commercially zoned location. Unfortunately, residential and home
|
|
||||||
businesses are not considered commercial addresses. A business or commercial address
|
|
||||||
location must be able to receive freight without the requirement of prior appointment setup
|
|
||||||
or notification. This location should also have the capability of unloading the
|
|
||||||
remanufactured transmission with a forklift from the delivery truck. If you don't have
|
|
||||||
a commercial or business address that meets these specifications, you should ship it
|
|
||||||
directly to the dealership or repair shop that is performing the repairs to ensure you enjoy
|
|
||||||
Flat Rate Shipping (Commercial address). Residential delivery or Liftgate service will
|
|
||||||
result in additional $99 fee.
|
|
||||||
</p>
|
|
||||||
<p className="my-3">
|
|
||||||
After placing the order for a remanufactured transmission, most customers will receive it
|
|
||||||
within 7-14 business days — not including holidays or weekends. Please keep in mind that
|
|
||||||
certain locations (remote areas) and locations in Colorado, Utah, New York, Oregon, and
|
|
||||||
California may require an additional delivery fee. In either case, we will always ship your
|
|
||||||
remanufactured transmission out as soon as possible. Because of weather conditions,
|
|
||||||
increasing order volumes, and conditions outside of our control, all shipping times are
|
|
||||||
estimates, not guarantees. It's important to note that {SITE_NAME} will not be liable
|
|
||||||
for any extra fees the carrier may levy due to storage or redelivery. While every
|
|
||||||
transmission from {SITE_NAME} has been rigorously inspected and tested prior to being
|
|
||||||
shipped, damage may occur during transportation.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
As such, we strongly suggest you carefully inspect your transmission upon receipt. If you
|
|
||||||
notice any missing parts, wrong parts, or damage, you should report it prior to signing any
|
|
||||||
delivery documentation. It"s imperative to report missing parts, damage, or wrong parts
|
|
||||||
at the time of delivery. If you fail to do so prior to signing your shipping documents,
|
|
||||||
responsibility will be placed on the purchaser or receiver. For clarity,
|
|
||||||
"purchaser" refers to any representative of the company designated to sign for the
|
|
||||||
delivery of the remanufactured transmission.
|
|
||||||
</p>
|
|
||||||
</DisclosureSection>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ShippingPolicy;
|
|
@ -1,102 +0,0 @@
|
|||||||
import {
|
|
||||||
ArrowPathIcon,
|
|
||||||
ArrowsRightLeftIcon,
|
|
||||||
CurrencyDollarIcon,
|
|
||||||
FlagIcon
|
|
||||||
} from '@heroicons/react/24/outline';
|
|
||||||
import DisclosureSection from './disclosure-section';
|
|
||||||
|
|
||||||
const { SITE_NAME } = process.env;
|
|
||||||
|
|
||||||
const WarrantyPolicy = () => {
|
|
||||||
return (
|
|
||||||
<DisclosureSection title="Warranty">
|
|
||||||
<div className="mb-3 font-medium">Year 2001 and Newer</div>
|
|
||||||
<div className="flex items-center p-1">
|
|
||||||
<span className="basis-1/2">Personal/Individual Transmission Warranty</span>
|
|
||||||
<span>60 Months/ Unlimited Mileage</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center bg-gray-100 p-1">
|
|
||||||
<span className="basis-1/2">Commercial Transmissions Warranty</span>
|
|
||||||
<span>Prior to 03/01/2020 18 Months/ 100,000 Miles</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center p-1">
|
|
||||||
<span className="basis-1/2">Commercial Transmissions Warranty</span>
|
|
||||||
<span>Effective 03/01/2020 36 Months/ Unlimited Mileage</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center bg-gray-100 p-1">
|
|
||||||
<span className="basis-1/2">Continuously Variable Transmission (CVT) Warranty</span>
|
|
||||||
<span>36 Months/ Unlimited Mileage</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center p-1">
|
|
||||||
<span className="basis-1/2">Manual Transmission Warranty</span>
|
|
||||||
<span>36 Months/ Unlimited Miles</span>
|
|
||||||
</div>
|
|
||||||
<div className="my-3 font-medium">Year 2000 and Older</div>
|
|
||||||
<div className="flex items-center p-1">
|
|
||||||
<span className="basis-1/2">Personal/Individual Transmission Warranty</span>
|
|
||||||
<span>36 Months/ Unlimited Mileage</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center bg-gray-100 p-1">
|
|
||||||
<span className="basis-1/2">Commercial Transmissions Warranty</span>
|
|
||||||
<span>18 Months/ 100,000 Miles</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center p-1">
|
|
||||||
<span className="basis-1/2">Commercial Transmissions Warranty</span>
|
|
||||||
<span>36 Months/ Unlimited Mileage</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center bg-gray-100 p-1">
|
|
||||||
<span className="basis-1/2">Continuously Variable Transmission (CVT) Warranty</span>
|
|
||||||
<span>36 Months/ Unlimited Miles</span>
|
|
||||||
</div>
|
|
||||||
<div className="my-5">
|
|
||||||
<div className="mb-1 flex items-center gap-2 font-medium">
|
|
||||||
<ArrowsRightLeftIcon className="size-4 text-primary" />
|
|
||||||
Easy, Hassle-Free, Transferable Warranty
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
At {SITE_NAME}, we offer an easy, transferable, hassle-free warranty. Instead of being
|
|
||||||
associated only with you, the warranty is attached to your Vehicle Identification Number.
|
|
||||||
As such, the warranty is transferable with vehicle ownership, which means you never have
|
|
||||||
to worry about any paperwork or fees involved. Please note, that the used parts warranty
|
|
||||||
is not transferable.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="my-5">
|
|
||||||
<div className="mb-1 flex items-center gap-2 font-medium">
|
|
||||||
<FlagIcon className="size-4 text-primary" />
|
|
||||||
Nationwide Coverage
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Whether you're in California, Chicago, New York, Florida, or anywhere in between, you
|
|
||||||
are covered with a nationwide warranty. This warranty covers you anywhere in the
|
|
||||||
continental U.S.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="my-5">
|
|
||||||
<div className="mb-1 flex items-center gap-2 font-medium">
|
|
||||||
<ArrowPathIcon className="size-4 text-primary" />
|
|
||||||
Instant Replacement
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
With instant replacement, your replacement transmission will be sent out as soon as you
|
|
||||||
submit your claim. This way you can spend less time waiting and more time doing whatever
|
|
||||||
needs to be done.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="my-5">
|
|
||||||
<div className="mb-1 flex items-center gap-2 font-medium">
|
|
||||||
<CurrencyDollarIcon className="size-4 text-primary" />
|
|
||||||
Paid Parts & Labor
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
When you have your work performed in a certified shop, your {SITE_NAME} warranty will pay
|
|
||||||
for parts and labor at $50 an hour, which is the Mitchell labor reimbursement rate.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</DisclosureSection>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WarrantyPolicy;
|
|
@ -9,6 +9,7 @@ const productFragment = /* GraphQL */ `
|
|||||||
title
|
title
|
||||||
description
|
description
|
||||||
descriptionHtml
|
descriptionHtml
|
||||||
|
productType
|
||||||
options {
|
options {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
@ -522,6 +522,7 @@ export type ShopifyProduct = {
|
|||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
descriptionHtml: string;
|
descriptionHtml: string;
|
||||||
|
productType: string;
|
||||||
options: ProductOption[];
|
options: ProductOption[];
|
||||||
priceRange: {
|
priceRange: {
|
||||||
maxVariantPrice: Money;
|
maxVariantPrice: Money;
|
||||||
|
@ -18,6 +18,9 @@ export const carPartPlanetColor = {
|
|||||||
200: '#666C89',
|
200: '#666C89',
|
||||||
500: '#2D3A7B',
|
500: '#2D3A7B',
|
||||||
600: '#111C55'
|
600: '#111C55'
|
||||||
|
},
|
||||||
|
black: {
|
||||||
|
700: '#1A1A25'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,6 +44,9 @@ export const remanTransmissionColor = {
|
|||||||
200: '#666C89',
|
200: '#666C89',
|
||||||
500: '#2D3A7B',
|
500: '#2D3A7B',
|
||||||
600: '#111C55'
|
600: '#111C55'
|
||||||
|
},
|
||||||
|
black: {
|
||||||
|
700: '#1A1A25'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,5 +70,8 @@ export const transmissionLocatorColor = {
|
|||||||
200: '#666C89',
|
200: '#666C89',
|
||||||
500: '#2D3A7B',
|
500: '#2D3A7B',
|
||||||
600: '#111C55'
|
600: '#111C55'
|
||||||
|
},
|
||||||
|
black: {
|
||||||
|
700: '#1A1A25'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
18
lib/utils.ts
18
lib/utils.ts
@ -1,7 +1,7 @@
|
|||||||
import clsx, { ClassValue } from 'clsx';
|
import clsx, { ClassValue } from 'clsx';
|
||||||
import { ReadonlyURLSearchParams } from 'next/navigation';
|
import { ReadonlyURLSearchParams } from 'next/navigation';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
import { Menu } from './shopify/types';
|
import { Menu, Product, ProductVariant } from './shopify/types';
|
||||||
|
|
||||||
export function cx(...args: ClassValue[]) {
|
export function cx(...args: ClassValue[]) {
|
||||||
return twMerge(clsx(...args));
|
return twMerge(clsx(...args));
|
||||||
@ -149,3 +149,19 @@ export const getCollectionUrl = (handle: string, includeSlashPrefix = true) => {
|
|||||||
|
|
||||||
return includeSlashPrefix ? `/${rewriteUrl}` : rewriteUrl;
|
return includeSlashPrefix ? `/${rewriteUrl}` : rewriteUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getSelectedProductVariant = ({
|
||||||
|
product,
|
||||||
|
searchParams
|
||||||
|
}: {
|
||||||
|
product: Product;
|
||||||
|
searchParams?: { [key: string]: string | string[] | undefined };
|
||||||
|
}) => {
|
||||||
|
const variant = product.variants.find((variant: ProductVariant) =>
|
||||||
|
variant.selectedOptions.every(
|
||||||
|
(option) => option.value === searchParams?.[option.name.toLowerCase()]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return variant || product.variants[0];
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user