Browse Engines By Engine Sizes
- {engineSizes.values.map((engineSize) => (
-
(
+
-
- {engineSize.label}
-
-
+ />
))}
diff --git a/components/filters/actions.ts b/components/filters/actions.ts
index 55c4001dc..18a9aac79 100644
--- a/components/filters/actions.ts
+++ b/components/filters/actions.ts
@@ -1,12 +1,47 @@
'use server';
import { getAllMetaobjects } from 'lib/shopify';
+import get from 'lodash.get';
+import { cache } from 'react';
-export const fetMetaobjects = async (type: string) => {
+export const fetchModels = cache(async () => {
try {
- const data = await getAllMetaobjects(type);
- return data;
+ const data = await getAllMetaobjects('make_model_composite');
+
+ return data.toSorted((a, b) => {
+ const modelA = get(a, 'name').toLowerCase();
+ const modelB = get(b, 'name').toLowerCase();
+ return modelA.localeCompare(modelB);
+ });
} catch (error) {
- console.log('fetMetaobjects action', error);
+ console.log('fetchModels action', error);
}
-};
+});
+
+export const fetchYears = cache(async () => {
+ try {
+ const data = await getAllMetaobjects('make_model_year_composite');
+
+ return data.toSorted((a, b) => {
+ const yearA = parseInt(get(a, 'name'), 10);
+ const yearB = parseInt(get(b, 'name'), 10);
+ return yearB - yearA; // Descending order for years
+ });
+ } catch (error) {
+ console.log('fetchYears action', error);
+ }
+});
+
+export const fetchMakes = cache(async () => {
+ try {
+ const data = await getAllMetaobjects('make');
+
+ return data.toSorted((a, b) => {
+ const makeA = get(a, 'display_name').toLowerCase();
+ const makeB = get(b, 'display_name').toLowerCase();
+ return makeA.localeCompare(makeB);
+ });
+ } catch (error) {
+ console.log('fetchMakes action', error);
+ }
+});
diff --git a/components/filters/filters-list.tsx b/components/filters/filters-list.tsx
index b8b5125fb..4fa7716f5 100644
--- a/components/filters/filters-list.tsx
+++ b/components/filters/filters-list.tsx
@@ -6,17 +6,17 @@ import { Menu, Metaobject } from 'lib/shopify/types';
import { createUrl, findParentCollection } from 'lib/utils';
import get from 'lodash.get';
import { useParams, useRouter, useSearchParams } from 'next/navigation';
-import { useEffect, useMemo, useState } from 'react';
-import { fetMetaobjects } from './actions';
+import { useEffect, useState } from 'react';
+import { fetchModels, fetchYears } from './actions';
import FilterField from './field';
type FiltersListProps = {
- makes: Metaobject[];
+ makes?: Metaobject[];
menu: Menu[];
autoFocusField?: string;
};
-const FiltersList = ({ makes, menu, autoFocusField }: FiltersListProps) => {
+const FiltersList = ({ makes = [], menu, autoFocusField }: FiltersListProps) => {
const params = useParams<{ collection?: string }>();
const router = useRouter();
const searchParams = useSearchParams();
@@ -69,9 +69,9 @@ const FiltersList = ({ makes, menu, autoFocusField }: FiltersListProps) => {
}, [makeIdFromSearchParams, makes, params.collection, partType]);
useEffect(() => {
- const fetchModels = async () => {
+ const getModels = async () => {
setLoadingAttribute('models');
- const modelsResponse = await fetMetaobjects('make_model_composite');
+ const modelsResponse = await fetchModels();
if (modelIdFromSearchParams) {
setModel(
(currentModel) =>
@@ -83,14 +83,14 @@ const FiltersList = ({ makes, menu, autoFocusField }: FiltersListProps) => {
};
if (make?.id && models.length === 0) {
- fetchModels();
+ getModels();
}
}, [make?.id, modelIdFromSearchParams, models.length]);
useEffect(() => {
- const fetchYears = async () => {
+ const getYears = async () => {
setLoadingAttribute('years');
- const yearsResponse = await fetMetaobjects('make_model_year_composite');
+ const yearsResponse = await fetchYears();
if (yearIdFromSearchParams) {
setYear(
(currentYear) =>
@@ -102,7 +102,7 @@ const FiltersList = ({ makes, menu, autoFocusField }: FiltersListProps) => {
};
if (model?.id && years.length === 0) {
- fetchYears();
+ getYears();
}
}, [model?.id, yearIdFromSearchParams, years.length]);
@@ -136,31 +136,6 @@ const FiltersList = ({ makes, menu, autoFocusField }: FiltersListProps) => {
router.push(createUrl(`/search/${partType?.value}`, newSearchParams), { scroll: false });
};
- // Sorting logic
- const sortedMakes = useMemo(() => {
- return [...makes].sort((a, b) => {
- const makeA = get(a, 'display_name').toLowerCase();
- const makeB = get(b, 'display_name').toLowerCase();
- return makeA.localeCompare(makeB);
- });
- }, [makes]);
-
- const sortedModelOptions = useMemo(() => {
- return [...modelOptions].sort((a, b) => {
- const modelA = get(a, 'name').toLowerCase();
- const modelB = get(b, 'name').toLowerCase();
- return modelA.localeCompare(modelB);
- });
- }, [modelOptions]);
-
- const sortedYearOptions = useMemo(() => {
- return [...yearOptions].sort((a, b) => {
- const yearA = parseInt(get(a, 'name'), 10);
- const yearB = parseInt(get(b, 'name'), 10);
- return yearB - yearA; // Descending order for years
- });
- }, [yearOptions]);
-
return (
<>
{
label="Make"
onChange={onChangeMake}
selectedValue={make}
- options={sortedMakes}
+ options={makes}
getId={(option) => option.id}
disabled={!partType}
autoFocus={autoFocusField === 'make'}
@@ -186,7 +161,7 @@ const FiltersList = ({ makes, menu, autoFocusField }: FiltersListProps) => {
label="Model"
onChange={onChangeModel}
selectedValue={model}
- options={sortedModelOptions}
+ options={modelOptions}
getId={(option) => option.id}
disabled={!make}
autoFocus={autoFocusField === 'model'}
@@ -196,7 +171,7 @@ const FiltersList = ({ makes, menu, autoFocusField }: FiltersListProps) => {
label="Year"
onChange={onChangeYear}
selectedValue={year}
- options={sortedYearOptions}
+ options={yearOptions}
getId={(option) => option.id}
disabled={!model || !make}
autoFocus={autoFocusField === 'year'}
diff --git a/components/filters/hompage-filters.tsx b/components/filters/hompage-filters.tsx
index 9409c4d5d..47f4ff1a4 100644
--- a/components/filters/hompage-filters.tsx
+++ b/components/filters/hompage-filters.tsx
@@ -1,4 +1,5 @@
-import { getAllMetaobjects, getMenu } from 'lib/shopify';
+import { getMenu } from 'lib/shopify';
+import { fetchMakes, fetchModels, fetchYears } from './actions';
import FiltersList from './filters-list';
const title: Record = {
@@ -12,8 +13,11 @@ const title: Record = {
const { STORE_PREFIX } = process.env;
const HomePageFilters = async () => {
- const makes = await getAllMetaobjects('make');
+ const makes = await fetchMakes();
const menu = await getMenu('main-menu');
+ // preload models and years
+ fetchModels();
+ fetchYears();
return (
<>
diff --git a/components/filters/index.tsx b/components/filters/index.tsx
index 2caa5750f..c4239d87b 100644
--- a/components/filters/index.tsx
+++ b/components/filters/index.tsx
@@ -1,5 +1,6 @@
-import { getAllMetaobjects, getMenu } from 'lib/shopify';
+import { getMenu } from 'lib/shopify';
import { ReactNode } from 'react';
+import { fetchMakes, fetchModels, fetchYears } from './actions';
import FiltersList from './filters-list';
const YMMFiltersContainer = ({ children }: { children: ReactNode }) => {
@@ -14,8 +15,11 @@ const YMMFiltersContainer = ({ children }: { children: ReactNode }) => {
};
const YMMFilters = async () => {
- const makes = await getAllMetaobjects('make');
+ const makes = await fetchMakes();
const menu = await getMenu('main-menu');
+ // preload models and years
+ fetchModels();
+ fetchYears();
return (
diff --git a/components/layout/search/filters/make-model-filters.tsx b/components/layout/search/filters/make-model-filters.tsx
deleted file mode 100644
index 2e1636f52..000000000
--- a/components/layout/search/filters/make-model-filters.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { MODEL_FILTER_ID, YEAR_FILTER_ID } from 'lib/constants';
-import { getProductFilters } from 'lib/shopify';
-import { Filter } from 'lib/shopify/types';
-import { getCollectionUrl } from 'lib/utils';
-import kebabCase from 'lodash.kebabcase';
-import Link from 'next/link';
-
-type MakeModelFiltersProps = {
- collection: string;
-};
-
-const MakeModelFilters = async ({ collection }: MakeModelFiltersProps) => {
- if (!collection) return null;
-
- const [, make, model] = collection.split('_');
- if (!make && !model) return null;
-
- let data = null as Filter | null | undefined;
- let title = '';
-
- if (model) {
- data = await getProductFilters({ collection }, YEAR_FILTER_ID);
- title = 'Years';
- } else if (make) {
- data = await getProductFilters({ collection }, MODEL_FILTER_ID);
- title = 'Models';
- }
-
- if (!data?.values || !data?.values.length) return null;
-
- return (
-
-
{title}
-
- {data.values.map((item) => (
- -
-
- {item.label}
-
-
- ))}
-
-
- );
-};
-
-export default MakeModelFilters;
diff --git a/components/layout/search/filters/sub-menu.tsx b/components/layout/search/filters/sub-menu.tsx
index e88cbca8c..490d5368f 100644
--- a/components/layout/search/filters/sub-menu.tsx
+++ b/components/layout/search/filters/sub-menu.tsx
@@ -1,28 +1,79 @@
-import { getMenu } from 'lib/shopify';
-import { getCollectionUrl } from 'lib/utils';
+import { getCollection, getMetaobjectsByIds } from 'lib/shopify';
+import { Metaobject } from 'lib/shopify/types';
+import get from 'lodash.get';
import Link from 'next/link';
+const MenuItem = async ({ collectionId, title }: { collectionId?: string; title: string }) => {
+ if (!collectionId || !title) return null;
+
+ const collection = await getCollection({ id: collectionId });
+
+ if (!collection) return null;
+
+ return (
+
+
+ {title}
+
+
+ );
+};
const SubMenu = async ({ collection }: { collection: string }) => {
- const menu = await getMenu('main-menu');
- const subMenu = menu.find((item) => item.path === getCollectionUrl(collection))?.items || [];
+ const collectionData = await getCollection({ handle: collection });
+
+ if (!collectionData || !collectionData.plpType) return null;
+ let subMenu = [] as Metaobject[];
+ let displayField = '';
+ let title = '';
+
+ if (collectionData.plpType === 'Product Type' && collectionData.lhnLinks) {
+ displayField = 'make';
+ title = 'Make';
+ subMenu = await getMetaobjectsByIds(collectionData.lhnLinks);
+ }
+
+ if (collectionData.plpType === 'Make' && collectionData.lhnLinks) {
+ displayField = 'model';
+ title = 'Model';
+ subMenu = await getMetaobjectsByIds(collectionData.lhnLinks);
+ }
+
+ if (collectionData.plpType === 'Model' && collectionData.lhnLinks) {
+ displayField = 'year';
+ title = 'Year';
+ subMenu = await getMetaobjectsByIds(collectionData.lhnLinks);
+ }
return subMenu.length ? (
-
Manufacturers
+
{title}
{subMenu.map((subMenuItem) => (
- -
-
- {subMenuItem.title}
-
-
+
))}
) : null;
};
+export const SubMenuPlaceholder = () => {
+ return (
+
+ );
+};
export default SubMenu;
diff --git a/components/models/index.tsx b/components/models/index.tsx
deleted file mode 100644
index 937e2b3f1..000000000
--- a/components/models/index.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { GlobeAltIcon } from '@heroicons/react/24/outline';
-import { MODEL_FILTER_ID } from 'lib/constants';
-import { getProductFilters } from 'lib/shopify';
-import { getCollectionUrl } from 'lib/utils';
-import Link from 'next/link';
-
-const Models = async ({ collectionHandle }: { collectionHandle: string }) => {
- // eg: collectionHandle = transmission_bmw_x5
- const makeFromCollectionHandle = collectionHandle.split('_')[1];
-
- if (!makeFromCollectionHandle) {
- return null;
- }
- const transmissionModels = await getProductFilters(
- { collection: collectionHandle },
- MODEL_FILTER_ID
- );
-
- if (!transmissionModels || transmissionModels.values.length === 0) {
- return null;
- }
-
- const prefix = collectionHandle.startsWith('transmissions') ? 'Transmissions' : 'Engines';
-
- return (
-
-
-
{`Browse ${prefix} By Model`}
-
-
-
- Models
-
-
- {transmissionModels.values.map((model) => (
-
-
{model.label}
-
- ))}
-
-
-
-
- );
-};
-
-export default Models;
diff --git a/components/transmission-codes/index.tsx b/components/transmission-codes/index.tsx
index 2a5289bb8..62f70b6fe 100644
--- a/components/transmission-codes/index.tsx
+++ b/components/transmission-codes/index.tsx
@@ -1,22 +1,47 @@
import { StarIcon } from '@heroicons/react/24/outline';
import Tag from 'components/tag';
-import { TRANSMISSION_CODE_FILTER_ID } from 'lib/constants';
-import { getProductFilters } from 'lib/shopify';
-import { getCollectionUrl } from 'lib/utils';
+import { getCollection, getMetaobjectsByIds } from 'lib/shopify';
+import { Metaobject } from 'lib/shopify/types';
import Link from 'next/link';
-const TransmissionCode = async ({ collectionHandle }: { collectionHandle: string }) => {
- const transmissionCodes = await getProductFilters(
- { collection: collectionHandle },
- TRANSMISSION_CODE_FILTER_ID
- );
+const { STORE_PREFIX } = process.env;
- if (!transmissionCodes || transmissionCodes.values.length === 0) {
+const validStores = ['car-part-planet', 'reman-transmission', 'transmission-locator'];
+
+const LinkBlock = async ({ collectionId, title }: { collectionId?: string; title?: string }) => {
+ if (!collectionId || !title) return null;
+
+ const collection = await getCollection({ id: collectionId });
+
+ if (!collection) return null;
+
+ return (
+
+ {title}
+
+ );
+};
+const TransmissionCode = async ({ collectionHandle }: { collectionHandle: string }) => {
+ const collection = await getCollection({ handle: collectionHandle });
+ if (!collection || !collection.plpType || !validStores.includes(STORE_PREFIX!)) {
+ return null;
+ }
+
+ let transmissionCodes = [] as Metaobject[];
+
+ if (
+ (collection.plpType === 'Product Type' || collection.plpType === 'Make') &&
+ collection.transmissionCodeLinks
+ ) {
+ transmissionCodes = await getMetaobjectsByIds(collection.transmissionCodeLinks);
+ }
+
+ if (!transmissionCodes.length) {
return null;
}
return (
-
+
{`Browse By Transmission Code`}
@@ -26,15 +51,12 @@ const TransmissionCode = async ({ collectionHandle }: { collectionHandle: string
Popular Transmission Codes
- {transmissionCodes.values.map((transmissionCode) => (
-
(
+
-
- {transmissionCode.label}
-
-
+ />
))}
diff --git a/components/ui/index.ts b/components/ui/index.ts
index b550670bf..8a122e99a 100644
--- a/components/ui/index.ts
+++ b/components/ui/index.ts
@@ -1,18 +1,16 @@
-export { default as Badge } from './badge';
export * from './badge';
+export { default as Badge } from './badge';
export { default as Button } from './button';
-export * from './button';
-export { default as Card } from './card';
export * from './card';
-export { default as Heading } from './heading';
+export { default as Card } from './card';
export * from './heading';
+export { default as Heading } from './heading';
export { default as Input } from './input';
-export * from './input';
-export { default as Label } from './label';
export * from './label';
-export { default as Skeleton } from './skeleton';
+export { default as Label } from './label';
export * from './skeleton';
-export { default as Text } from './text';
+export { default as Skeleton } from './skeleton';
export * from './text';
+export { default as Text } from './text';
export { default as Chip } from './chip';
export * from './chip';
diff --git a/lib/shopify/index.ts b/lib/shopify/index.ts
index 8d98d4f14..174099af3 100644
--- a/lib/shopify/index.ts
+++ b/lib/shopify/index.ts
@@ -362,6 +362,10 @@ const reshapeCollection = (collection: ShopifyCollection): Collection | undefine
helpfulLinks: parseMetaFieldValue
(collection.helpfulLinks),
helpfulLinksTop: parseMetaFieldValue(collection.helpfulLinksTop),
dynamicContent: collection.dynamicContent?.value || null,
+ plpType: collection.plpType?.value || null,
+ lhnLinks: parseMetaFieldValue(collection.lhnLinks),
+ engineSizeLinks: parseMetaFieldValue(collection.engineSizeLinks),
+ transmissionCodeLinks: parseMetaFieldValue(collection.transmissionCodeLinks),
path: getCollectionUrl(collection.handle)
};
};
@@ -906,7 +910,6 @@ export async function getAllMetaobjects(type: string) {
while (hasNextPage) {
const res = await shopifyFetch({
query: getMetaobjectsQuery,
- tags: [TAGS.collections, TAGS.products],
variables: { type, after }
});
diff --git a/lib/shopify/queries/collection.ts b/lib/shopify/queries/collection.ts
index 4e50f8d03..eaf64c96e 100644
--- a/lib/shopify/queries/collection.ts
+++ b/lib/shopify/queries/collection.ts
@@ -18,6 +18,18 @@ const collectionFragment = /* GraphQL */ `
dynamicContent: metafield(namespace: "custom", key: "plp_content") {
value
}
+ plpType: metafield(namespace: "custom", key: "plp_type") {
+ value
+ }
+ lhnLinks: metafield(namespace: "custom", key: "lhn_links") {
+ value
+ }
+ engineSizeLinks: metafield(namespace: "custom", key: "engine_size_links") {
+ value
+ }
+ transmissionCodeLinks: metafield(namespace: "custom", key: "transmission_code_links") {
+ value
+ }
updatedAt
}
${seoFragment}
diff --git a/lib/shopify/types.ts b/lib/shopify/types.ts
index 9ef0da415..c46f4fd70 100644
--- a/lib/shopify/types.ts
+++ b/lib/shopify/types.ts
@@ -44,12 +44,22 @@ export type CartItem = {
export type Collection = Omit<
ShopifyCollection,
- 'helpfulLinks' | 'helpfulLinksTop' | 'dynamicContent'
+ | 'helpfulLinks'
+ | 'helpfulLinksTop'
+ | 'dynamicContent'
+ | 'plpType'
+ | 'lhnLinks'
+ | 'engineSizeLinks'
+ | 'transmissionCodeLinks'
> & {
path: string;
helpfulLinks: string[] | null;
helpfulLinksTop: string[] | null;
dynamicContent: string | null;
+ plpType: PLPType | null;
+ lhnLinks: string[] | null;
+ engineSizeLinks: string[] | null;
+ transmissionCodeLinks: string[] | null;
};
export type Customer = {
@@ -509,6 +519,14 @@ export type ShopifyCart = {
totalQuantity: number;
};
+export type PLPType =
+ | 'Product Type'
+ | 'Make'
+ | 'Model'
+ | 'Year'
+ | 'Transmission Code'
+ | 'Engine Size';
+
export type ShopifyCollection = {
handle: string;
title: string;
@@ -518,6 +536,10 @@ export type ShopifyCollection = {
helpfulLinks: { value: string } | null;
helpfulLinksTop: { value: string } | null;
dynamicContent: { value: string } | null;
+ plpType: { value: PLPType } | null;
+ lhnLinks: { value: string } | null;
+ engineSizeLinks: { value: string } | null;
+ transmissionCodeLinks: { value: string } | null;
};
export type ShopifyProduct = {