mirror of
https://github.com/vercel/commerce.git
synced 2025-07-25 11:11:24 +00:00
Merge remote-tracking branch 'origin' into CPP-153
This commit is contained in:
16
components/product/additional-information.tsx
Normal file
16
components/product/additional-information.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Product } from 'lib/shopify/types';
|
||||
import Details from './details';
|
||||
import ShippingPolicy from './shipping-policy';
|
||||
import WarrantyPolicy from './warranty-policy';
|
||||
|
||||
const AdditionalInformation = ({ product }: { product: Product }) => {
|
||||
return (
|
||||
<div className="my-5 w-full divide-y">
|
||||
<Details product={product} />
|
||||
<WarrantyPolicy />
|
||||
<ShippingPolicy />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdditionalInformation;
|
@@ -93,8 +93,8 @@ const CoreCharge = ({ variants }: CoreChargeProps) => {
|
||||
period, you will never need to pay the core charge.
|
||||
</p>
|
||||
<p className="text-sm">
|
||||
If you don't manage to return the old part within the 30-day period, we will then
|
||||
charge you the core charge. This keeps more money in your pocket upfront.
|
||||
If you don't manage to return the old part within the 30-day period, we will
|
||||
then charge you the core charge. This keeps more money in your pocket upfront.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
114
components/product/delivery.tsx
Normal file
114
components/product/delivery.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
'use client';
|
||||
|
||||
import { TruckIcon } from '@heroicons/react/24/outline';
|
||||
import Price from 'components/price';
|
||||
import SideDialog from 'components/side-dialog';
|
||||
import { DELIVERY_OPTION_KEY } from 'lib/constants';
|
||||
import { cn, createUrl } from 'lib/utils';
|
||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
||||
import { ReactNode, useState } from 'react';
|
||||
|
||||
const options = ['Commercial', 'Residential'] as const;
|
||||
type Option = (typeof options)[number];
|
||||
|
||||
export const deliveryOptions: Array<{
|
||||
key: Option;
|
||||
template: ReactNode;
|
||||
price: number;
|
||||
}> = [
|
||||
{
|
||||
template: <span className="font-bold">Commercial</span>,
|
||||
price: 299,
|
||||
key: 'Commercial'
|
||||
},
|
||||
{
|
||||
template: <span className="font-bold">Residential</span>,
|
||||
price: 398,
|
||||
key: 'Residential'
|
||||
}
|
||||
];
|
||||
|
||||
const Delivery = () => {
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
const [openingDialog, setOpeningDialog] = useState<'information' | 'terms-conditions' | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const newSearchParams = new URLSearchParams(searchParams.toString());
|
||||
const selectedDeliveryOption = newSearchParams.get(DELIVERY_OPTION_KEY);
|
||||
|
||||
const handleSelectDelivery = (option: Option) => {
|
||||
newSearchParams.set(DELIVERY_OPTION_KEY, option);
|
||||
|
||||
const newUrl = createUrl(pathname, newSearchParams);
|
||||
router.replace(newUrl, { scroll: false });
|
||||
};
|
||||
|
||||
if (!selectedDeliveryOption) {
|
||||
handleSelectDelivery(options[0]);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col text-xs lg:text-sm">
|
||||
<div className="mb-3 flex flex-row items-center space-x-1 divide-x divide-gray-400 leading-none lg:space-x-3">
|
||||
<div className="flex flex-row items-center space-x-2 text-base font-medium">
|
||||
<TruckIcon className="h-5 w-5" />
|
||||
<span>Delivery</span>
|
||||
</div>
|
||||
<div className="pl-2">
|
||||
<button
|
||||
onClick={() => setOpeningDialog('information')}
|
||||
className="text-xs text-blue-800 hover:underline lg:text-sm"
|
||||
>
|
||||
Information
|
||||
</button>
|
||||
<SideDialog
|
||||
title="Information"
|
||||
onClose={() => setOpeningDialog(null)}
|
||||
open={openingDialog === 'information'}
|
||||
>
|
||||
<p>Information</p>
|
||||
</SideDialog>
|
||||
</div>
|
||||
<div className="pl-2">
|
||||
<button
|
||||
onClick={() => setOpeningDialog('terms-conditions')}
|
||||
className="text-xs text-blue-800 hover:underline lg:text-sm"
|
||||
>
|
||||
Terms & Conditions
|
||||
</button>
|
||||
<SideDialog
|
||||
title="Terms & Conditions"
|
||||
onClose={() => setOpeningDialog(null)}
|
||||
open={openingDialog === 'terms-conditions'}
|
||||
>
|
||||
<p>Terms & Conditions</p>
|
||||
</SideDialog>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="flex min-h-16 flex-row space-x-4 pt-2">
|
||||
{deliveryOptions.map((option) => (
|
||||
<li className="flex w-32" key={option.key}>
|
||||
<button
|
||||
onClick={() => handleSelectDelivery(option.key)}
|
||||
className={cn(
|
||||
'font-base flex w-full flex-col flex-wrap items-center justify-center space-y-0.5 rounded border text-center text-xs',
|
||||
{
|
||||
'border-0 ring-2 ring-secondary': selectedDeliveryOption === option.key
|
||||
}
|
||||
)}
|
||||
>
|
||||
{option.template}
|
||||
<Price amount={String(option.price)} currencyCode="USD" />
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Delivery;
|
82
components/product/details.tsx
Normal file
82
components/product/details.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
'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;
|
25
components/product/disclosure-section.tsx
Normal file
25
components/product/disclosure-section.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
'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;
|
73
components/product/price-summary.tsx
Normal file
73
components/product/price-summary.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
'use client';
|
||||
|
||||
import Price from 'components/price';
|
||||
import { CORE_VARIANT_ID_KEY, CORE_WAIVER, DELIVERY_OPTION_KEY } from 'lib/constants';
|
||||
import { Money, ProductVariant } from 'lib/shopify/types';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { deliveryOptions } from './delivery';
|
||||
|
||||
type PriceSummaryProps = {
|
||||
variants: ProductVariant[];
|
||||
defaultPrice: Money;
|
||||
};
|
||||
|
||||
const PriceSummary = ({ variants, defaultPrice }: PriceSummaryProps) => {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const variant = variants.find((variant) =>
|
||||
variant.selectedOptions.every(
|
||||
(option) => option.value === searchParams.get(option.name.toLowerCase())
|
||||
)
|
||||
);
|
||||
|
||||
const price = variant?.price.amount || defaultPrice.amount;
|
||||
const selectedCoreChargeOption = searchParams.get(CORE_VARIANT_ID_KEY);
|
||||
const selectedDeliveryOption = searchParams.get(DELIVERY_OPTION_KEY);
|
||||
const deliveryPrice =
|
||||
deliveryOptions.find((option) => option.key === selectedDeliveryOption)?.price ?? 0;
|
||||
const currencyCode = variant?.price.currencyCode || defaultPrice.currencyCode;
|
||||
const corePrice = selectedCoreChargeOption === CORE_WAIVER ? 0 : variant?.coreCharge?.amount ?? 0;
|
||||
|
||||
const totalPrice = Number(price) + deliveryPrice + Number(corePrice);
|
||||
return (
|
||||
<div className="mb-3 flex flex-col gap-2">
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<span className="text-xl font-semibold">Our Price</span>
|
||||
<Price amount={price} currencyCode={currencyCode} className="text-2xl font-semibold" />
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<span className="text-sm text-gray-400">{`Core Charge ${selectedCoreChargeOption === CORE_WAIVER ? '(Waived for 30 days)' : ''}`}</span>
|
||||
{selectedCoreChargeOption === CORE_WAIVER ? (
|
||||
<span className="text-sm text-gray-400">{`+$0.00`}</span>
|
||||
) : (
|
||||
<Price
|
||||
amount={variant?.coreCharge?.amount ?? '0'}
|
||||
currencyCode={currencyCode}
|
||||
className="text-sm text-gray-400"
|
||||
prefix="+"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<span className="text-sm text-gray-400">{`Flat Rate Shipping (${selectedDeliveryOption} address)`}</span>
|
||||
<Price
|
||||
amount={String(deliveryPrice)}
|
||||
currencyCode={currencyCode}
|
||||
className="text-sm text-gray-400"
|
||||
prefix="+"
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<span className="text-sm text-gray-400">To Pay Today</span>
|
||||
<Price
|
||||
amount={String(totalPrice)}
|
||||
currencyCode={currencyCode}
|
||||
className="text-sm text-gray-400"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PriceSummary;
|
@@ -2,7 +2,11 @@ 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';
|
||||
import ProductDetails from './product-details';
|
||||
import SpecialOffer from './special-offer';
|
||||
import VariantDetails from './vairant-details';
|
||||
import { VariantSelector } from './variant-selector';
|
||||
@@ -11,7 +15,7 @@ import Warranty from './warranty';
|
||||
export function ProductDescription({ product }: { product: Product }) {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-5 flex flex-col dark:border-neutral-700">
|
||||
<div className="mb-4 flex flex-col">
|
||||
<h1 className="text-xl font-bold md:text-2xl">{product.title}</h1>
|
||||
|
||||
<VariantDetails
|
||||
@@ -34,6 +38,7 @@ export function ProductDescription({ product }: { product: Product }) {
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<ProductDetails product={product} />
|
||||
<div className="mb-2 border-t py-4 dark:border-neutral-700">
|
||||
<CoreCharge variants={product.variants} />
|
||||
</div>
|
||||
@@ -42,12 +47,16 @@ export function ProductDescription({ product }: { product: Product }) {
|
||||
<Warranty />
|
||||
</div>
|
||||
|
||||
<div className="mb-2 border-t py-4 dark:border-neutral-700">
|
||||
<Delivery />
|
||||
</div>
|
||||
|
||||
<PriceSummary variants={product.variants} defaultPrice={product.priceRange.minVariantPrice} />
|
||||
<Suspense fallback={null}>
|
||||
<AddToCart variants={product.variants} availableForSale={product.availableForSale} />
|
||||
</Suspense>
|
||||
<div className="mt-4 border-t pt-4">
|
||||
<SpecialOffer />
|
||||
</div>
|
||||
<SpecialOffer />
|
||||
<AdditionalInformation product={product} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
51
components/product/product-details.tsx
Normal file
51
components/product/product-details.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
BeakerIcon,
|
||||
BoltIcon,
|
||||
CogIcon,
|
||||
CpuChipIcon,
|
||||
CubeTransparentIcon
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { Product } from 'lib/shopify/types';
|
||||
|
||||
const ProductDetails = ({ product }: { product: Product }) => {
|
||||
return (
|
||||
<div className="mb-3 flex flex-col gap-3">
|
||||
<span className="font-medium">Details</span>
|
||||
<div className="grid grid-cols-4 gap-y-3 text-sm">
|
||||
{product.transmissionType && (
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<CubeTransparentIcon className="size-4 text-primary" />
|
||||
{product.transmissionType}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{product.transmissionSpeeds && product.transmissionSpeeds.length && (
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<BoltIcon className="size-4 text-primary" />
|
||||
{`${product.transmissionSpeeds[0]}-Speed`}
|
||||
</div>
|
||||
)}
|
||||
{product.driveType && (
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<CogIcon className="size-4 text-primary" />
|
||||
{product.driveType}
|
||||
</div>
|
||||
)}
|
||||
{product.engineCylinders?.length && (
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<BeakerIcon className="size-4 text-primary" />
|
||||
{`${product.engineCylinders[0]} Cylinders`}
|
||||
</div>
|
||||
)}
|
||||
{product.transmissionCode?.length && (
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<CpuChipIcon className="size-4 text-primary" />
|
||||
{product.transmissionCode[0]}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductDetails;
|
44
components/product/shipping-policy.tsx
Normal file
44
components/product/shipping-policy.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
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,28 +1,71 @@
|
||||
import { CurrencyDollarIcon, ShieldCheckIcon, UsersIcon } from '@heroicons/react/24/outline';
|
||||
import { TruckIcon } from '@heroicons/react/24/solid';
|
||||
import {
|
||||
ArrowPathIcon,
|
||||
CurrencyDollarIcon,
|
||||
ShieldCheckIcon,
|
||||
StarIcon,
|
||||
TruckIcon,
|
||||
UsersIcon
|
||||
} from '@heroicons/react/24/outline';
|
||||
|
||||
const SpecialOffer = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-3 text-base font-medium tracking-tight">Special Offers</div>
|
||||
<div className="flex flex-col space-y-2 pl-2 text-sm tracking-normal text-neutral-800 lg:text-base dark:text-white">
|
||||
<p className="flex items-center gap-3">
|
||||
<TruckIcon className="h-4 w-4 text-secondary lg:h-5 lg:w-5" /> Flat Rate Shipping
|
||||
(Commercial Address)
|
||||
</p>
|
||||
<p className="flex items-center gap-3">
|
||||
<ShieldCheckIcon className="h-4 w-4 text-secondary lg:h-5 lg:w-5" /> Up to 5 Years
|
||||
Unlimited Miles Warranty
|
||||
</p>
|
||||
<p className="flex items-center gap-3">
|
||||
<UsersIcon className="h-4 w-4 text-secondary lg:h-5 lg:w-5" /> Excellent Customer Support
|
||||
</p>
|
||||
<p className="flex items-center gap-3">
|
||||
<CurrencyDollarIcon className="h-4 w-4 text-secondary lg:h-5 lg:w-5" /> No Core Charge for
|
||||
30 days
|
||||
</p>
|
||||
<div className="mt-10 grid grid-cols-2 gap-y-5 xl:grid-cols-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<TruckIcon className="size-12 text-primary" />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium uppercase">Flat Rate Shipping</span>
|
||||
<span className="text-sm font-light">
|
||||
We offer a flat $299 shipping fee to commercial addresses
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<div className="flex items-start gap-3">
|
||||
<CurrencyDollarIcon className="size-10 text-primary" />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium uppercase">Best Price Guarantee</span>
|
||||
<span className="text-sm font-light">
|
||||
We will match or beat any competitor's pricing
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<ShieldCheckIcon className="size-8 text-primary" />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium uppercase">Unbeatable Warranty</span>
|
||||
<span className="text-sm font-light">Up to 5 years with unlimited miles</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<UsersIcon className="size-10 text-primary" />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium uppercase">Excellent Support</span>
|
||||
<span className="text-sm font-light">
|
||||
End-to-end, expert care from our customer service team
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<ArrowPathIcon className="size-10 text-primary" />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium uppercase">Core Charge Waiver</span>
|
||||
<span className="text-sm font-light">
|
||||
Avoid the core charge by returning within 30 days
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<StarIcon className="size-10 text-primary" />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium uppercase">Free Core Return</span>
|
||||
<span className="text-sm font-light">
|
||||
Unlike competitors, we pay for the return of your core
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { CheckCircleIcon } from '@heroicons/react/24/outline';
|
||||
import Price from 'components/price';
|
||||
import { Money, ProductVariant } from 'lib/shopify/types';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
@@ -20,17 +21,23 @@ const VariantDetails = ({ variants, defaultPrice }: VariantDetailsProps) => {
|
||||
const price = variant?.price.amount || defaultPrice.amount;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-5 flex items-center justify-start gap-x-2">
|
||||
<p className="text-sm">SKU: {variant?.sku || 'N/A'}</p>
|
||||
<p className="text-sm">Condition: {variant?.condition || 'N/A'}</p>
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
<Price
|
||||
amount={price}
|
||||
currencyCode={variant?.price.currencyCode || defaultPrice.currencyCode}
|
||||
className="text-2xl font-semibold"
|
||||
/>
|
||||
</>
|
||||
<div className="mt-2 flex items-center justify-start gap-x-2">
|
||||
{variant?.availableForSale ? (
|
||||
<div className="flex items-center gap-1 text-sm text-green-500">
|
||||
<CheckCircleIcon className="size-5" /> In Stock
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-red-600">Out of Stock</span>
|
||||
)}
|
||||
<p className="text-sm">Condition: {variant?.condition || 'N/A'}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
102
components/product/warranty-policy.tsx
Normal file
102
components/product/warranty-policy.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
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;
|
Reference in New Issue
Block a user