diff --git a/app/product/[handle]/page.tsx b/app/product/[handle]/page.tsx index 52a8a7b3d..4138a2c93 100644 --- a/app/product/[handle]/page.tsx +++ b/app/product/[handle]/page.tsx @@ -5,6 +5,7 @@ import { Suspense } from 'react'; import BreadcrumbComponent from 'components/breadcrumb'; import { GridTileImage } from 'components/grid/tile'; import Footer from 'components/layout/footer'; +import AdditionalInformation from 'components/product/additional-information'; import { Gallery } from 'components/product/gallery'; import { ProductDescription } from 'components/product/product-description'; 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); if (!product) return notFound(); @@ -86,6 +93,9 @@ export default async function ProductPage({ params }: { params: { handle: string
+ + +
@@ -95,7 +105,7 @@ export default async function ProductPage({ params }: { params: { handle: string } > ({ + images={product.images.slice(0, 5).map((image: Image) => ({ src: image.url, altText: image.altText }))} diff --git a/components/layout/search/filters/price-range.tsx b/components/layout/search/filters/price-range.tsx index 22048d195..370bdd280 100644 --- a/components/layout/search/filters/price-range.tsx +++ b/components/layout/search/filters/price-range.tsx @@ -1,7 +1,7 @@ 'use client'; import Price from 'components/price'; -import { useDebounce } from 'hooks'; +import { useDebounce } from 'hooks/use-debounce'; import { Filter } from 'lib/shopify/types'; import { createUrl } from 'lib/utils'; import get from 'lodash.get'; diff --git a/components/page/rich-text-display.tsx b/components/page/rich-text-display.tsx index 9b5927dc0..e7c2dcb5a 100644 --- a/components/page/rich-text-display.tsx +++ b/components/page/rich-text-display.tsx @@ -40,7 +40,7 @@ const RichTextBlock = ({ block }: { block: Content }) => { } return ( -

+

{block.children.map((child, index) => ( ))} @@ -50,7 +50,7 @@ const RichTextBlock = ({ block }: { block: Content }) => { const RichTextDisplay = ({ contentBlocks }: { contentBlocks: Content[] }) => { return ( -

+
{contentBlocks.map((block, index) => ( ))} diff --git a/components/page/text-block.tsx b/components/page/text-block.tsx index 545aacc84..4d511214b 100644 --- a/components/page/text-block.tsx +++ b/components/page/text-block.tsx @@ -8,7 +8,7 @@ const TextBlock = ({ block }: { block: Metaobject }) => {
{block.title && ( -

{block.title}

+

{block.title}

)} diff --git a/components/product/additional-information.tsx b/components/product/additional-information.tsx index 1ef09b598..d6d27ea63 100644 --- a/components/product/additional-information.tsx +++ b/components/product/additional-information.tsx @@ -1,14 +1,38 @@ +import PageContent from 'components/page/page-content'; +import { getMetaobject, getMetaobjectsByIds } from 'lib/shopify'; import { Product } from 'lib/shopify/types'; -import Details from './details'; -import ShippingPolicy from './shipping-policy'; -import WarrantyPolicy from './warranty-policy'; +import { getSelectedProductVariant } from 'lib/utils'; + +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 ( -
-
- - +
+ {pageContent.map((block) => ( +
+ +
+ ))}
); }; diff --git a/components/product/details.tsx b/components/product/details.tsx deleted file mode 100644 index 27ba66eb7..000000000 --- a/components/product/details.tsx +++ /dev/null @@ -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 ( - -
- Condition - {variant?.condition || 'N/A'} -
-
- Price - -
-
- Warranty - -
-
- Cylinders - {product.engineCylinders?.map((cylinder) => `${cylinder} Cylinders`).join()} -
- - {details.map(({ title, value }, index) => ( -
- {title} - {value} -
- ))} -
- ); -}; - -export default Details; diff --git a/components/product/disclosure-section.tsx b/components/product/disclosure-section.tsx deleted file mode 100644 index 9d5c28231..000000000 --- a/components/product/disclosure-section.tsx +++ /dev/null @@ -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 ( - - - {title} - - - {children} - - ); -}; - -export default DisclosureSection; diff --git a/components/product/product-description.tsx b/components/product/product-description.tsx index 76d4d55d4..cec16316b 100644 --- a/components/product/product-description.tsx +++ b/components/product/product-description.tsx @@ -2,7 +2,6 @@ import { AddToCart } from 'components/cart/add-to-cart'; import Prose from 'components/prose'; import { Product } from 'lib/shopify/types'; import { Suspense } from 'react'; -import AdditionalInformation from './additional-information'; import CoreCharge from './core-charge'; import Delivery from './delivery'; import PriceSummary from './price-summary'; @@ -56,7 +55,6 @@ export function ProductDescription({ product }: { product: Product }) { - ); } diff --git a/components/product/shipping-policy.tsx b/components/product/shipping-policy.tsx deleted file mode 100644 index be98d8e70..000000000 --- a/components/product/shipping-policy.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import DisclosureSection from './disclosure-section'; - -const { SITE_NAME } = process.env; -const ShippingPolicy = () => { - return ( - -

- 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. -

-

- 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. -

-

- 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. -

-
- ); -}; - -export default ShippingPolicy; diff --git a/components/product/warranty-policy.tsx b/components/product/warranty-policy.tsx deleted file mode 100644 index 0457e52b5..000000000 --- a/components/product/warranty-policy.tsx +++ /dev/null @@ -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 ( - -
Year 2001 and Newer
-
- Personal/Individual Transmission Warranty - 60 Months/ Unlimited Mileage -
-
- Commercial Transmissions Warranty - Prior to 03/01/2020 18 Months/ 100,000 Miles -
-
- Commercial Transmissions Warranty - Effective 03/01/2020 36 Months/ Unlimited Mileage -
-
- Continuously Variable Transmission (CVT) Warranty - 36 Months/ Unlimited Mileage -
-
- Manual Transmission Warranty - 36 Months/ Unlimited Miles -
-
Year 2000 and Older
-
- Personal/Individual Transmission Warranty - 36 Months/ Unlimited Mileage -
-
- Commercial Transmissions Warranty - 18 Months/ 100,000 Miles -
-
- Commercial Transmissions Warranty - 36 Months/ Unlimited Mileage -
-
- Continuously Variable Transmission (CVT) Warranty - 36 Months/ Unlimited Miles -
-
-
- - Easy, Hassle-Free, Transferable Warranty -
-

- 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. -

-
-
-
- - Nationwide Coverage -
-

- 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. -

-
-
-
- - Instant Replacement -
-

- 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. -

-
- -
-
- - Paid Parts & Labor -
-

- 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. -

-
-
- ); -}; - -export default WarrantyPolicy; diff --git a/hooks/index.tsx b/hooks/use-debounce.tsx similarity index 100% rename from hooks/index.tsx rename to hooks/use-debounce.tsx diff --git a/lib/shopify/fragments/product.ts b/lib/shopify/fragments/product.ts index f5fd32e0a..d3e8b4e9d 100644 --- a/lib/shopify/fragments/product.ts +++ b/lib/shopify/fragments/product.ts @@ -9,6 +9,7 @@ const productFragment = /* GraphQL */ ` title description descriptionHtml + productType options { id name diff --git a/lib/shopify/types.ts b/lib/shopify/types.ts index 5c63c5669..194952b1e 100644 --- a/lib/shopify/types.ts +++ b/lib/shopify/types.ts @@ -522,6 +522,7 @@ export type ShopifyProduct = { title: string; description: string; descriptionHtml: string; + productType: string; options: ProductOption[]; priceRange: { maxVariantPrice: Money; diff --git a/lib/styles.ts b/lib/styles.ts index d24035282..09ec76ab1 100644 --- a/lib/styles.ts +++ b/lib/styles.ts @@ -18,6 +18,9 @@ export const carPartPlanetColor = { 200: '#666C89', 500: '#2D3A7B', 600: '#111C55' + }, + black: { + 700: '#1A1A25' } }; @@ -41,6 +44,9 @@ export const remanTransmissionColor = { 200: '#666C89', 500: '#2D3A7B', 600: '#111C55' + }, + black: { + 700: '#1A1A25' } }; @@ -64,5 +70,8 @@ export const transmissionLocatorColor = { 200: '#666C89', 500: '#2D3A7B', 600: '#111C55' + }, + black: { + 700: '#1A1A25' } }; diff --git a/lib/utils.ts b/lib/utils.ts index 76cdcd732..5e2d7ffdb 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,7 +1,7 @@ import clsx, { ClassValue } from 'clsx'; import { ReadonlyURLSearchParams } from 'next/navigation'; import { twMerge } from 'tailwind-merge'; -import { Menu } from './shopify/types'; +import { Menu, Product, ProductVariant } from './shopify/types'; export function cx(...args: ClassValue[]) { return twMerge(clsx(...args)); @@ -149,3 +149,19 @@ export const getCollectionUrl = (handle: string, includeSlashPrefix = true) => { 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]; +};