>
);
}
diff --git a/components/product/product-details.tsx b/components/product/product-details.tsx
new file mode 100644
index 000000000..e56ca27ea
--- /dev/null
+++ b/components/product/product-details.tsx
@@ -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 (
+
+ );
+};
+
+export default ProductDetails;
diff --git a/components/product/shipping-policy.tsx b/components/product/shipping-policy.tsx
new file mode 100644
index 000000000..be98d8e70
--- /dev/null
+++ b/components/product/shipping-policy.tsx
@@ -0,0 +1,44 @@
+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/special-offer.tsx b/components/product/special-offer.tsx
index 1cdb9a2e7..5bc8bc933 100644
--- a/components/product/special-offer.tsx
+++ b/components/product/special-offer.tsx
@@ -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 (
- <>
-
-
- Flat Rate Shipping
- (Commercial Address)
-
-
- Up to 5 Years
- Unlimited Miles Warranty
-
-
- Excellent Customer Support
-
-
- No Core Charge for
- 30 days
-
+
+
+
+
+ Flat Rate Shipping
+
+ We offer a flat $299 shipping fee to commercial addresses
+
+
- >
+
+
+
+ Best Price Guarantee
+
+ We will match or beat any competitor's pricing
+
+
+
+
+
+
+ Unbeatable Warranty
+ Up to 5 years with unlimited miles
+
+
+
+
+
+
+ Excellent Support
+
+ End-to-end, expert care from our customer service team
+
+
+
+
+
+
+
+ Core Charge Waiver
+
+ Avoid the core charge by returning within 30 days
+
+
+
+
+
+
+
+ Free Core Return
+
+ Unlike competitors, we pay for the return of your core
+
+
+
+
);
};
diff --git a/components/product/vairant-details.tsx b/components/product/vairant-details.tsx
index 6687250b7..6afc8bfed 100644
--- a/components/product/vairant-details.tsx
+++ b/components/product/vairant-details.tsx
@@ -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 (
- <>
-
-
SKU: {variant?.sku || 'N/A'}
-
Condition: {variant?.condition || 'N/A'}
-
+
- >
+
+ {variant?.availableForSale ? (
+
+ In Stock
+
+ ) : (
+
Out of Stock
+ )}
+
Condition: {variant?.condition || 'N/A'}
+
+
);
};
diff --git a/components/product/warranty-policy.tsx b/components/product/warranty-policy.tsx
new file mode 100644
index 000000000..0457e52b5
--- /dev/null
+++ b/components/product/warranty-policy.tsx
@@ -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 (
+
+ 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/lib/constants.ts b/lib/constants.ts
index 33fd75c8c..eeaf84cfb 100644
--- a/lib/constants.ts
+++ b/lib/constants.ts
@@ -46,3 +46,15 @@ export const MODEL_FILTER_ID = 'filter.p.m.custom.make_model_composite';
export const YEAR_FILTER_ID = 'filter.p.m.custom.make_model_year_composite';
export const PRODUCT_METAFIELD_PREFIX = 'filter.p.m';
export const VARIANT_METAFIELD_PREFIX = 'filter.v.m';
+
+export const CONDITIONS = {
+ Used: 'Used',
+ Remanufactured: 'Remanufactured'
+};
+
+export const DELIVERY_OPTION_KEY = 'delivery';
+
+export const ADD_ON_PRODUCT_TYPES = {
+ addOn: 'Add On',
+ coreCharge: 'Core Charge'
+};
diff --git a/lib/shopify/fragments/cart.ts b/lib/shopify/fragments/cart.ts
index 40d1beb77..66df6eaa8 100644
--- a/lib/shopify/fragments/cart.ts
+++ b/lib/shopify/fragments/cart.ts
@@ -1,9 +1,13 @@
-import productFragment from './product';
+import imageFragment from './image';
const cartFragment = /* GraphQL */ `
fragment cart on Cart {
id
checkoutUrl
+ attributes {
+ key
+ value
+ }
cost {
subtotalAmount {
amount
@@ -38,11 +42,22 @@ const cartFragment = /* GraphQL */ `
value
}
product {
- ...product
+ featuredImage {
+ ...image
+ }
+ handle
+ title
+ productType
}
coreVariantId: metafield(key: "coreVariant", namespace: "custom") {
value
}
+ addOnQuantity: metafield(namespace: "custom", key: "add_on_quantity") {
+ value
+ }
+ addOnProductId: metafield(namespace: "custom", key: "add_on") {
+ value
+ }
}
}
}
@@ -50,7 +65,7 @@ const cartFragment = /* GraphQL */ `
}
totalQuantity
}
- ${productFragment}
+ ${imageFragment}
`;
export default cartFragment;
diff --git a/lib/shopify/fragments/product.ts b/lib/shopify/fragments/product.ts
index 5f146e2f5..f5fd32e0a 100644
--- a/lib/shopify/fragments/product.ts
+++ b/lib/shopify/fragments/product.ts
@@ -64,6 +64,12 @@ const productFragment = /* GraphQL */ `
condition: metafield(namespace: "custom", key: "condition") {
value
}
+ addOnQuantity: metafield(namespace: "custom", key: "add_on_quantity") {
+ value
+ }
+ addOnProductId: metafield(namespace: "custom", key: "add_on") {
+ value
+ }
}
}
}
@@ -76,6 +82,21 @@ const productFragment = /* GraphQL */ `
fuelType: metafield(namespace: "custom", key: "fuel") {
value
}
+ transmissionType: metafield(namespace: "custom", key: "transmission_type") {
+ value
+ }
+ transmissionSpeeds: metafield(namespace: "custom", key: "transmission_speeds") {
+ value
+ }
+ driveType: metafield(namespace: "custom", key: "drive_type") {
+ value
+ }
+ transmissionCode: metafield(namespace: "custom", key: "transmission_code") {
+ value
+ }
+ transmissionTag: metafield(namespace: "custom", key: "transmission_tag") {
+ value
+ }
images(first: 20) {
edges {
node {
diff --git a/lib/shopify/index.ts b/lib/shopify/index.ts
index 6d5a46e33..652d6b4c0 100644
--- a/lib/shopify/index.ts
+++ b/lib/shopify/index.ts
@@ -1,4 +1,5 @@
import {
+ ADD_ON_PRODUCT_TYPES,
AVAILABILITY_FILTER_ID,
HIDDEN_PRODUCT_TAG,
MAKE_FILTER_ID,
@@ -41,7 +42,6 @@ import {
Address,
Cart,
CartAttributeInput,
- CartItem,
Collection,
Connection,
Customer,
@@ -84,6 +84,7 @@ import {
ShopifyRemoveFromCartOperation,
ShopifySetCartAttributesOperation,
ShopifyUpdateCartOperation,
+ TransmissionType,
ShopifyCustomer,
ShopifyOrder,
ShopifyAddress,
@@ -260,7 +261,7 @@ const reshapeCart = (cart: ShopifyCart): Cart => {
...lineItem,
merchandise: {
...lineItem.merchandise,
- product: reshapeProduct(lineItem.merchandise.product)
+ product: lineItem.merchandise.product
}
}))
};
@@ -273,6 +274,7 @@ const reshapeCollection = (collection: ShopifyCollection): Collection | undefine
return {
...collection,
+ helpfulLinks: parseMetaFieldValue
(collection.helpfulLinks),
path: `/search/${collection.handle}`
};
};
@@ -374,18 +376,29 @@ const reshapeImages = (images: Connection, productTitle: string) => {
};
const reshapeVariants = (variants: ShopifyProductVariant[]): ProductVariant[] => {
- return variants.map((variant) => ({
+ return variants.map(({ addOnProductId, addOnQuantity, ...variant }) => ({
...variant,
waiverAvailable: parseMetaFieldValue(variant.waiverAvailable),
coreVariantId: variant.coreVariantId?.value || null,
coreCharge: parseMetaFieldValue(variant.coreCharge),
mileage: variant.mileage?.value ?? null,
estimatedDelivery: variant.estimatedDelivery?.value || null,
- condition: variant.condition?.value || null
+ condition: variant.condition?.value || null,
+ ...(addOnProductId
+ ? {
+ addOnProduct: {
+ id: addOnProductId.value,
+ quantity: addOnQuantity?.value ? Number(addOnQuantity.value) : 1
+ }
+ }
+ : {})
}));
};
-const reshapeProduct = (product: ShopifyProduct, filterHiddenProducts: boolean = true) => {
+const reshapeProduct = (
+ product: ShopifyProduct,
+ filterHiddenProducts: boolean = true
+): Product | undefined => {
if (!product || (filterHiddenProducts && product.tags.includes(HIDDEN_PRODUCT_TAG))) {
return undefined;
}
@@ -393,6 +406,13 @@ const reshapeProduct = (product: ShopifyProduct, filterHiddenProducts: boolean =
const { images, variants, ...rest } = product;
return {
...rest,
+ transmissionCode: parseMetaFieldValue(product.transmissionCode),
+ transmissionSpeeds: parseMetaFieldValue(product.transmissionSpeeds),
+ transmissionTag: parseMetaFieldValue(product.transmissionTag),
+ driveType: parseMetaFieldValue(product.driveType),
+ transmissionType: product.transmissionType
+ ? (product.transmissionType.value as TransmissionType)
+ : null,
engineCylinders: parseMetaFieldValue(product.engineCylinders),
fuelType: product.fuelType?.value || null,
images: reshapeImages(images, product.title),
@@ -633,20 +653,34 @@ export async function getCart(cartId: string): Promise {
const cart = reshapeCart(res.body.data.cart);
// attach core charge as an additional attribute of a cart line, and remove the core charge line from cart
- const extendedCartLines = cart?.lines.reduce((lines, item) => {
- const coreVariantId = item.merchandise.coreVariantId?.value;
- if (coreVariantId) {
- const relatedCoreCharge = cart.lines.find((line) => line.merchandise.id === coreVariantId);
- return lines.concat([
- {
- ...item,
- coreCharge: relatedCoreCharge
- }
- ]);
- }
+ const extendedCartLines = cart?.lines
+ .map((item) => {
+ const coreVariantId = item.merchandise.coreVariantId?.value;
+ const addOnProductId = item.merchandise.addOnProductId;
+ const _item = { ...item };
- return lines;
- }, [] as CartItem[]);
+ if (coreVariantId) {
+ const relatedCoreCharge = cart.lines.find((line) => line.merchandise.id === coreVariantId);
+ _item.coreCharge = relatedCoreCharge;
+ }
+
+ if (addOnProductId) {
+ const relatedAddOnProduct = cart.lines.find(
+ (line) => line.merchandise.id === addOnProductId.value
+ );
+ _item.addOnProduct = relatedAddOnProduct
+ ? {
+ ...relatedAddOnProduct,
+ quantity: item.merchandise.addOnQuantity
+ ? Number(item.merchandise.addOnQuantity.value)
+ : 1
+ }
+ : undefined;
+ }
+ return _item;
+ })
+ // core charge shouldn't present as a dedicated product as it's tightly coupled with the product
+ .filter((item) => item.merchandise.product.productType !== ADD_ON_PRODUCT_TYPES.coreCharge);
const totalQuantity = extendedCartLines.reduce((sum, line) => sum + line.quantity, 0);
@@ -730,7 +764,8 @@ export async function getCollections(): Promise {
description: 'All products'
},
path: '/search',
- updatedAt: new Date().toISOString()
+ updatedAt: new Date().toISOString(),
+ helpfulLinks: null
},
// Filter out the `hidden` collections.
// Collections that start with `hidden-*` need to be hidden on the search page.
diff --git a/lib/shopify/queries/collection.ts b/lib/shopify/queries/collection.ts
index 57596d005..f59aec8f1 100644
--- a/lib/shopify/queries/collection.ts
+++ b/lib/shopify/queries/collection.ts
@@ -9,6 +9,9 @@ const collectionFragment = /* GraphQL */ `
seo {
...seo
}
+ helpfulLinks: metafield(namespace: "custom", key: "helpful_links") {
+ value
+ }
updatedAt
}
${seoFragment}
diff --git a/lib/shopify/types.ts b/lib/shopify/types.ts
index 4416d34af..95a07c76f 100644
--- a/lib/shopify/types.ts
+++ b/lib/shopify/types.ts
@@ -25,14 +25,24 @@ export type CartItem = {
name: string;
value: string;
}[];
- product: Product;
+ product: {
+ id: string;
+ handle: string;
+ title: string;
+ featuredImage: Image;
+ productType: string;
+ };
coreVariantId: { value: string } | null;
+ addOnQuantity: { value: string } | null;
+ addOnProductId: { value: string } | null;
};
coreCharge?: CartItem;
+ addOnProduct?: CartItem & { quantity: number };
};
-export type Collection = ShopifyCollection & {
+export type Collection = Omit & {
path: string;
+ helpfulLinks: string[] | null;
};
export type Customer = {
@@ -379,14 +389,29 @@ export type Metaobject = {
[key: string]: string;
};
+export type TransmissionType = 'Automatic' | 'Manual';
+
export type Product = Omit<
ShopifyProduct,
- 'variants' | 'images' | 'fuelType' | 'engineCylinders'
+ | 'variants'
+ | 'images'
+ | 'fuelType'
+ | 'engineCylinders'
+ | 'driveType'
+ | 'transmissionType'
+ | 'transmissionSpeeds'
+ | 'transmissionCode'
+ | 'transmissionTag'
> & {
variants: ProductVariant[];
images: Image[];
fuelType: string | null;
engineCylinders: number[] | null;
+ driveType: string[] | null;
+ transmissionType: TransmissionType | null;
+ transmissionSpeeds: number[] | null;
+ transmissionCode: string[] | null;
+ transmissionTag: string[] | null;
};
export type ProductOption = {
@@ -414,6 +439,10 @@ export type ProductVariant = {
condition: string | null;
engineCylinders: string | null;
fuelType: string | null;
+ addOnProduct?: {
+ quantity: number;
+ id: string;
+ };
};
export type ShopifyCartProductVariant = {
@@ -432,7 +461,13 @@ export type CartProductVariant = Omit & {
waiverAvailable: { value: string };
coreVariantId: { value: string } | null;
@@ -440,6 +475,8 @@ export type ShopifyProductVariant = Omit<
mileage: { value: number } | null;
estimatedDelivery: { value: string } | null;
condition: { value: string } | null;
+ addOnProductId: { value: string } | null;
+ addOnQuantity: { value: string } | null;
};
export type SEO = {
@@ -450,6 +487,7 @@ export type SEO = {
export type ShopifyCart = {
id: string;
checkoutUrl: string;
+ attributes: { key: string; value: string }[];
cost: {
subtotalAmount: Money;
totalAmount: Money;
@@ -465,6 +503,7 @@ export type ShopifyCollection = {
description: string;
seo: SEO;
updatedAt: string;
+ helpfulLinks: { value: string } | null;
};
export type ShopifyProduct = {
@@ -493,6 +532,11 @@ export type ShopifyProduct = {
};
engineCylinders: { value: string } | null;
fuelType: { value: string } | null;
+ transmissionType: { value: string } | null;
+ transmissionTag: { value: string } | null;
+ transmissionCode: { value: string } | null;
+ driveType: { value: string } | null;
+ transmissionSpeeds: { value: string } | null;
};
export type ShopifyCartOperation = {