mirror of
https://github.com/vercel/commerce.git
synced 2025-05-18 23:46:58 +00:00
feat: add manufacturer section
Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
parent
c5af5b3069
commit
3ac4b140c9
@ -4,8 +4,10 @@ import { notFound } from 'next/navigation';
|
|||||||
|
|
||||||
import Breadcrumb from 'components/breadcrumb';
|
import Breadcrumb from 'components/breadcrumb';
|
||||||
import BreadcrumbHome from 'components/breadcrumb/breadcrumb-home';
|
import BreadcrumbHome from 'components/breadcrumb/breadcrumb-home';
|
||||||
|
import FAQ from 'components/faq';
|
||||||
import YMMFilters, { YMMFiltersPlaceholder } from 'components/filters';
|
import YMMFilters, { YMMFiltersPlaceholder } from 'components/filters';
|
||||||
import Grid from 'components/grid';
|
import Grid from 'components/grid';
|
||||||
|
import Manufacturers from 'components/home-page/manufacturers';
|
||||||
import ProductsList from 'components/layout/products-list';
|
import ProductsList from 'components/layout/products-list';
|
||||||
import { getProductsInCollection } from 'components/layout/products-list/actions';
|
import { getProductsInCollection } from 'components/layout/products-list/actions';
|
||||||
import FiltersContainer, {
|
import FiltersContainer, {
|
||||||
@ -82,42 +84,52 @@ export default async function CategorySearchPage(props: {
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid lg:grid-cols-3 lg:gap-x-10 xl:grid-cols-4">
|
<div className="mx-auto mt-6 max-w-screen-2xl px-8 pb-10">
|
||||||
<aside className="hidden lg:block">
|
<div className="grid lg:grid-cols-3 lg:gap-x-10 xl:grid-cols-4">
|
||||||
<div className="mb-5">
|
<aside className="hidden lg:block">
|
||||||
<Suspense fallback={<YMMFiltersPlaceholder />}>
|
<div className="mb-5">
|
||||||
<YMMFilters />
|
<Suspense fallback={<YMMFiltersPlaceholder />}>
|
||||||
|
<YMMFilters />
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SubMenu collection={props.params.collection} />
|
||||||
|
<h3 className="sr-only">Filters</h3>
|
||||||
|
<Suspense
|
||||||
|
fallback={<FiltersListPlaceholder />}
|
||||||
|
key={`filters-${props.params.collection}`}
|
||||||
|
>
|
||||||
|
<FiltersContainer searchParams={props.searchParams} />
|
||||||
|
<HelpfulLinks collection={props.params.collection} />
|
||||||
|
</Suspense>
|
||||||
|
</aside>
|
||||||
|
<div className="lg:col-span-2 xl:col-span-3">
|
||||||
|
<div className="mb-2">
|
||||||
|
<Suspense fallback={<BreadcrumbHome />} key={`breadcrumb-${props.params.collection}`}>
|
||||||
|
<Breadcrumb type="collection" handle={props.params.collection} />
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
<Suspense fallback={<HeaderPlaceholder />} key={`header-${props.params.collection}`}>
|
||||||
|
<Header collection={props.params.collection} />
|
||||||
|
</Suspense>
|
||||||
|
|
||||||
|
<Suspense
|
||||||
|
fallback={<ProductsGridPlaceholder />}
|
||||||
|
key={`products-${props.params.collection}`}
|
||||||
|
>
|
||||||
|
<CategoryPage {...props} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SubMenu collection={props.params.collection} />
|
|
||||||
<h3 className="sr-only">Filters</h3>
|
|
||||||
<Suspense
|
|
||||||
fallback={<FiltersListPlaceholder />}
|
|
||||||
key={`filters-${props.params.collection}`}
|
|
||||||
>
|
|
||||||
<FiltersContainer searchParams={props.searchParams} />
|
|
||||||
<HelpfulLinks collection={props.params.collection} />
|
|
||||||
</Suspense>
|
|
||||||
</aside>
|
|
||||||
<div className="lg:col-span-2 xl:col-span-3">
|
|
||||||
<div className="mb-2">
|
|
||||||
<Suspense fallback={<BreadcrumbHome />} key={`breadcrumb-${props.params.collection}`}>
|
|
||||||
<Breadcrumb type="collection" handle={props.params.collection} />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
|
||||||
<Suspense fallback={<HeaderPlaceholder />} key={`header-${props.params.collection}`}>
|
|
||||||
<Header collection={props.params.collection} />
|
|
||||||
</Suspense>
|
|
||||||
|
|
||||||
<Suspense
|
|
||||||
fallback={<ProductsGridPlaceholder />}
|
|
||||||
key={`products-${props.params.collection}`}
|
|
||||||
>
|
|
||||||
<CategoryPage {...props} />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<FAQ handle="plp-faqs" />
|
||||||
|
<Suspense>
|
||||||
|
<Manufacturers
|
||||||
|
variant={
|
||||||
|
(props.params.collection as string).includes('engines') ? 'engines' : 'transmissions'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
import FAQ from 'components/faq';
|
|
||||||
import Footer from 'components/layout/footer';
|
import Footer from 'components/layout/footer';
|
||||||
import { Suspense } from 'react';
|
|
||||||
|
|
||||||
export default function SearchLayout({ children }: { children: React.ReactNode }) {
|
export default function SearchLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mx-auto mt-6 min-h-[500px] max-w-screen-2xl px-8 pb-10 lg:min-h-[800px]">
|
<div className="min-h-[500px] lg:min-h-[800px]">{children}</div>
|
||||||
<Suspense>{children}</Suspense>
|
|
||||||
</div>
|
|
||||||
<FAQ handle="plp-faqs" />
|
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
|
@ -6,7 +6,7 @@ import { Menu, Metaobject } from 'lib/shopify/types';
|
|||||||
import { createUrl, findParentCollection } from 'lib/utils';
|
import { createUrl, findParentCollection } from 'lib/utils';
|
||||||
import get from 'lodash.get';
|
import get from 'lodash.get';
|
||||||
import { useParams, useRouter, useSearchParams } from 'next/navigation';
|
import { useParams, useRouter, useSearchParams } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import FilterField from './field';
|
import FilterField from './field';
|
||||||
|
|
||||||
type FiltersListProps = {
|
type FiltersListProps = {
|
||||||
@ -31,11 +31,13 @@ const FiltersList = ({ years, makes, models, menu, autoFocusField }: FiltersList
|
|||||||
PART_TYPES.find((type) => type.value === partTypeCollection) || null
|
PART_TYPES.find((type) => type.value === partTypeCollection) || null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const makeIdFromSearchParams = searchParams.get(MAKE_FILTER_ID);
|
||||||
|
|
||||||
const [make, setMake] = useState<Metaobject | null>(
|
const [make, setMake] = useState<Metaobject | null>(
|
||||||
(partType &&
|
(partType &&
|
||||||
makes.find((make) =>
|
makes.find((make) =>
|
||||||
searchParams.get(MAKE_FILTER_ID)
|
makeIdFromSearchParams
|
||||||
? make.id === searchParams.get(MAKE_FILTER_ID)
|
? make.id === makeIdFromSearchParams
|
||||||
: params.collection?.includes(make.name!.toLowerCase())
|
: params.collection?.includes(make.name!.toLowerCase())
|
||||||
)) ||
|
)) ||
|
||||||
null
|
null
|
||||||
@ -52,6 +54,22 @@ const FiltersList = ({ years, makes, models, menu, autoFocusField }: FiltersList
|
|||||||
|
|
||||||
const disabled = !partType || !make || !model || !year;
|
const disabled = !partType || !make || !model || !year;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (partType) {
|
||||||
|
const _make = makes.find((make) =>
|
||||||
|
makeIdFromSearchParams
|
||||||
|
? make.id === makeIdFromSearchParams
|
||||||
|
: params.collection?.includes(make.name!.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_make) {
|
||||||
|
setMake(_make);
|
||||||
|
setModel(null);
|
||||||
|
setYear(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [makeIdFromSearchParams, makes, params.collection, partType]);
|
||||||
|
|
||||||
const onChangeMake = (value: Metaobject | null) => {
|
const onChangeMake = (value: Metaobject | null) => {
|
||||||
setMake(value);
|
setMake(value);
|
||||||
setModel(null);
|
setModel(null);
|
||||||
|
@ -2,14 +2,24 @@ import ManufacturersGrid from 'components/manufacturers-grid';
|
|||||||
import Tag from 'components/tag';
|
import Tag from 'components/tag';
|
||||||
import { getMetaobjects } from 'lib/shopify';
|
import { getMetaobjects } from 'lib/shopify';
|
||||||
|
|
||||||
const Manufacturers = async () => {
|
const Manufacturers = async ({
|
||||||
|
variant = 'home'
|
||||||
|
}: {
|
||||||
|
variant?: 'engines' | 'transmissions' | 'home';
|
||||||
|
}) => {
|
||||||
const manufacturers = await getMetaobjects('make');
|
const manufacturers = await getMetaobjects('make');
|
||||||
|
const title: Record<typeof variant, string> = {
|
||||||
|
engines: 'Engines',
|
||||||
|
home: 'Parts',
|
||||||
|
transmissions: 'Transmissions'
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-6 py-20">
|
<div className="px-6 py-20">
|
||||||
<div className="mx-auto flex max-w-7xl flex-col gap-3">
|
<div className="mx-auto flex max-w-7xl flex-col gap-3">
|
||||||
<Tag text="Get Started" />
|
<Tag text="Get Started" />
|
||||||
<h3 className="mb-3 text-3xl font-semibold lg:text-4xl">Browse Parts By Manufacturer</h3>
|
<h3 className="mb-3 text-3xl font-semibold lg:text-4xl">{`Browse ${title[variant]} By Manufacturer`}</h3>
|
||||||
<ManufacturersGrid manufacturers={manufacturers} />
|
<ManufacturersGrid manufacturers={manufacturers} variant={variant} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { GlobeAltIcon, StarIcon } from '@heroicons/react/24/outline';
|
import { GlobeAltIcon, StarIcon } from '@heroicons/react/24/outline';
|
||||||
|
import { MAKE_FILTER_ID } from 'lib/constants';
|
||||||
import { Metaobject } from 'lib/shopify/types';
|
import { Metaobject } from 'lib/shopify/types';
|
||||||
import ButtonGroup from './button-group';
|
import ButtonGroup from './button-group';
|
||||||
import ManufacturerItem from './item';
|
import ManufacturerItem from './item';
|
||||||
|
|
||||||
type ManufacturersGridProps = {
|
type ManufacturersGridProps = {
|
||||||
manufacturers: Metaobject[];
|
manufacturers: Metaobject[];
|
||||||
variant?: 'engine' | 'transmission' | 'home';
|
variant?: 'engines' | 'transmissions' | 'home';
|
||||||
};
|
};
|
||||||
|
|
||||||
const ManufacturersGrid = ({ manufacturers, variant = 'home' }: ManufacturersGridProps) => {
|
const ManufacturersGrid = ({ manufacturers, variant = 'home' }: ManufacturersGridProps) => {
|
||||||
@ -22,7 +23,15 @@ const ManufacturersGrid = ({ manufacturers, variant = 'home' }: ManufacturersGri
|
|||||||
<div className="mt-6 grid grid-cols-2 gap-x-12 gap-y-5 md:grid-cols-3 md:gap-y-8 lg:grid-cols-4 xl:grid-cols-5">
|
<div className="mt-6 grid grid-cols-2 gap-x-12 gap-y-5 md:grid-cols-3 md:gap-y-8 lg:grid-cols-4 xl:grid-cols-5">
|
||||||
{popularManufacturers.map((manufacturer) => (
|
{popularManufacturers.map((manufacturer) => (
|
||||||
<div key={manufacturer.id} className="flex flex-col gap-2">
|
<div key={manufacturer.id} className="flex flex-col gap-2">
|
||||||
<ManufacturerItem manufacturer={manufacturer} />
|
{variant === 'home' ? (
|
||||||
|
<ManufacturerItem manufacturer={manufacturer} />
|
||||||
|
) : (
|
||||||
|
<ManufacturerItem
|
||||||
|
manufacturer={manufacturer}
|
||||||
|
className={'rounded border border-primary px-2 py-1'}
|
||||||
|
href={`/search/${variant}?${MAKE_FILTER_ID}=${manufacturer.id}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{variant === 'home' && <ButtonGroup manufacturer={manufacturer} />}
|
{variant === 'home' && <ButtonGroup manufacturer={manufacturer} />}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -37,7 +46,15 @@ const ManufacturersGrid = ({ manufacturers, variant = 'home' }: ManufacturersGri
|
|||||||
.toSorted((a, b) => a.display_name!.localeCompare(b.display_name!))
|
.toSorted((a, b) => a.display_name!.localeCompare(b.display_name!))
|
||||||
.map((manufacturer) => (
|
.map((manufacturer) => (
|
||||||
<div key={manufacturer.id} className="flex flex-col gap-2">
|
<div key={manufacturer.id} className="flex flex-col gap-2">
|
||||||
<ManufacturerItem manufacturer={manufacturer} />
|
{variant === 'home' ? (
|
||||||
|
<ManufacturerItem manufacturer={manufacturer} />
|
||||||
|
) : (
|
||||||
|
<ManufacturerItem
|
||||||
|
manufacturer={manufacturer}
|
||||||
|
className={'rounded border border-primary px-2 py-1'}
|
||||||
|
href={`/search/${variant}?${MAKE_FILTER_ID}=${manufacturer.id}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{variant === 'home' && <ButtonGroup manufacturer={manufacturer} />}
|
{variant === 'home' && <ButtonGroup manufacturer={manufacturer} />}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import ImageDisplay from 'components/page/image-display';
|
import ImageDisplay from 'components/page/image-display';
|
||||||
import { Metaobject } from 'lib/shopify/types';
|
import { Metaobject } from 'lib/shopify/types';
|
||||||
|
import Link from 'next/link';
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
const ManufacturerItem = ({
|
const ManufacturerItem = ({
|
||||||
manufacturer,
|
manufacturer,
|
||||||
className
|
className,
|
||||||
|
href
|
||||||
}: {
|
}: {
|
||||||
manufacturer: Metaobject;
|
manufacturer: Metaobject;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
href?: string;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
const children = (
|
||||||
<div className={twMerge('flex w-full flex-row items-center justify-between', className)}>
|
<div className={twMerge('flex w-full flex-row items-center justify-between', className)}>
|
||||||
<span className="text-sm leading-5">{manufacturer.display_name}</span>
|
<span className="text-sm leading-5">{manufacturer.display_name}</span>
|
||||||
<div className="hidden md:block">
|
<div className="hidden md:block">
|
||||||
@ -25,6 +28,11 @@ const ManufacturerItem = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (href) {
|
||||||
|
return <Link href={href}>{children}</Link>;
|
||||||
|
}
|
||||||
|
return children;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ManufacturerItem;
|
export default ManufacturerItem;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user