mirror of
https://github.com/vercel/commerce.git
synced 2025-05-15 05:56:59 +00:00
feat(poc): carousel and improved sub-collections
This commit is contained in:
parent
8dcf6db08f
commit
8550185eae
@ -3,6 +3,8 @@ import { Metadata } from 'next';
|
|||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
import Grid from 'components/grid';
|
import Grid from 'components/grid';
|
||||||
|
import Collections from 'components/layout/search/collections';
|
||||||
|
import FilterList from 'components/layout/search/filter';
|
||||||
import ProductGridItems from 'components/layout/product-grid-items';
|
import ProductGridItems from 'components/layout/product-grid-items';
|
||||||
import Pagination from 'components/collection/pagination';
|
import Pagination from 'components/collection/pagination';
|
||||||
import { defaultSort, sorting } from 'lib/constants';
|
import { defaultSort, sorting } from 'lib/constants';
|
||||||
@ -47,13 +49,21 @@ export default async function CategoryPage({
|
|||||||
{products.length === 0 ? (
|
{products.length === 0 ? (
|
||||||
<p className="py-3 text-lg">{`No products found in this collection`}</p>
|
<p className="py-3 text-lg">{`No products found in this collection`}</p>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div className='mx-auto flex max-w-7xl flex-col bg-white py-6 text-black dark:bg-black dark:text-white md:flex-row'>
|
||||||
<Grid className="grid-cols-2 lg:grid-cols-3">
|
<div className="order-first flex-none md:w-1/6">
|
||||||
<ProductGridItems products={products} />
|
<Collections collection={params.collection} />
|
||||||
</Grid>
|
</div>
|
||||||
<nav aria-label="Collection pagination" className='block sm:flex items-center'>
|
<div className="order-last min-h-screen w-full md:order-none">
|
||||||
<Pagination itemsPerPage={limit} itemsTotal={total} currentPage={page ? parseInt(page) - 1 : 0} />
|
<Grid className="grid-cols-2 lg:grid-cols-3">
|
||||||
</nav>
|
<ProductGridItems products={products} />
|
||||||
|
</Grid>
|
||||||
|
<nav aria-label="Collection pagination" className='block sm:flex items-center'>
|
||||||
|
<Pagination itemsPerPage={limit} itemsTotal={total} currentPage={page ? parseInt(page) - 1 : 0} />
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div className="order-none md:order-last md:w-1/6 md:flex-none">
|
||||||
|
<FilterList list={sorting} title="Sort by" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,21 +1,11 @@
|
|||||||
import Footer from 'components/layout/footer';
|
import Footer from 'components/layout/footer';
|
||||||
import Collections from 'components/layout/search/collections';
|
|
||||||
import FilterList from 'components/layout/search/filter';
|
|
||||||
import { sorting } from 'lib/constants';
|
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
|
|
||||||
|
// @ToDo: We could use dynamic Layout per page, see https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts#with-typescript
|
||||||
export default function SearchLayout({ children }: { children: React.ReactNode }) {
|
export default function SearchLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<div className="mx-auto flex max-w-7xl flex-col bg-white py-6 text-black dark:bg-black dark:text-white md:flex-row">
|
{children}
|
||||||
<div className="order-first flex-none md:w-1/6">
|
|
||||||
<Collections />
|
|
||||||
</div>
|
|
||||||
<div className="order-last min-h-screen w-full md:order-none">{children}</div>
|
|
||||||
<div className="order-none md:order-last md:w-1/6 md:flex-none">
|
|
||||||
<FilterList list={sorting} title="Sort by" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import Grid from 'components/grid';
|
import Grid from 'components/grid';
|
||||||
import ProductGridItems from 'components/layout/product-grid-items';
|
import ProductGridItems from 'components/layout/product-grid-items';
|
||||||
|
import FilterList from 'components/layout/search/filter';
|
||||||
import { defaultSort, sorting } from 'lib/constants';
|
import { defaultSort, sorting } from 'lib/constants';
|
||||||
import { getSearchCollectionProducts } from 'lib/shopware';
|
import { getSearchCollectionProducts } from 'lib/shopware';
|
||||||
|
|
||||||
@ -22,19 +23,32 @@ export default async function SearchPage({
|
|||||||
const resultsText = products.length > 1 ? 'results' : 'result';
|
const resultsText = products.length > 1 ? 'results' : 'result';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>{searchValue && products.length === 0 ? (
|
||||||
{searchValue ? (
|
<div className='mx-auto flex max-w-7xl flex-col bg-white py-6 text-black dark:bg-black dark:text-white md:flex-row'>
|
||||||
<p>
|
<p>
|
||||||
{products.length === 0
|
{'There are no products that match '}
|
||||||
? 'There are no products that match '
|
|
||||||
: `Showing ${products.length} ${resultsText} for `}
|
|
||||||
<span className="font-bold">"{searchValue}"</span>
|
<span className="font-bold">"{searchValue}"</span>
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
</div>
|
||||||
|
) : null}
|
||||||
{products.length > 0 ? (
|
{products.length > 0 ? (
|
||||||
<Grid className="grid-cols-2 lg:grid-cols-3">
|
<div className='mx-auto flex max-w-7xl flex-col bg-white py-6 text-black dark:bg-black dark:text-white md:flex-row'>
|
||||||
<ProductGridItems products={products} />
|
<div className="order-first flex-none md:w-1/6">
|
||||||
</Grid>
|
{searchValue ? (
|
||||||
|
<p>
|
||||||
|
{`Showing ${products.length} ${resultsText} for `}
|
||||||
|
<span className="font-bold">"{searchValue}"</span>
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
<p className='pt-4'>Good place to add other suggest search terms ;)</p>
|
||||||
|
</div>
|
||||||
|
<Grid className="grid-cols-2 lg:grid-cols-3">
|
||||||
|
<ProductGridItems products={products} />
|
||||||
|
</Grid>
|
||||||
|
<div className="order-none md:order-last md:w-1/6 md:flex-none">
|
||||||
|
<FilterList list={sorting} title="Sort by" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,39 +3,36 @@ import Image from 'next/image';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export async function Carousel() {
|
export async function Carousel() {
|
||||||
// Collections that start with `hidden-*` are hidden from the search page.
|
const { products } = await getCollectionProducts({ collection: 'Summer-BBQ/Hidden-Carousel-Category' });
|
||||||
// const products = await getCollectionProducts({ collection: 'hidden-homepage-carousel' });
|
|
||||||
|
|
||||||
// if (!products?.length) return null;
|
if (!products?.length) return null;
|
||||||
|
|
||||||
return null;
|
return (
|
||||||
|
<div className="relative w-full overflow-hidden bg-white dark:bg-black">
|
||||||
// return (
|
<div className="flex animate-carousel">
|
||||||
// <div className="relative w-full overflow-hidden bg-black dark:bg-white">
|
{[...products, ...products].map((product, i) => (
|
||||||
// <div className="flex animate-carousel">
|
<Link
|
||||||
// {[...products, ...products].map((product, i) => (
|
key={`${product.path}${i}`}
|
||||||
// <Link
|
href={`/product/${product.path}`}
|
||||||
// key={`${product.handle}${i}`}
|
className="relative h-[30vh] w-1/2 flex-none md:w-1/3"
|
||||||
// href={`/product/${product.handle}`}
|
>
|
||||||
// className="relative h-[30vh] w-1/2 flex-none md:w-1/3"
|
{product.featuredImage ? (
|
||||||
// >
|
<Image
|
||||||
// {product.featuredImage ? (
|
alt={product.title}
|
||||||
// <Image
|
className="h-full object-contain"
|
||||||
// alt={product.title}
|
fill
|
||||||
// className="h-full object-contain"
|
sizes="33vw"
|
||||||
// fill
|
src={product.featuredImage.url}
|
||||||
// sizes="33vw"
|
/>
|
||||||
// src={product.featuredImage.url}
|
) : null}
|
||||||
// />
|
<div className="absolute inset-y-0 right-0 flex items-center justify-center">
|
||||||
// ) : null}
|
<div className="inline-flex bg-white p-4 text-xl font-semibold text-black dark:bg-black dark:text-white">
|
||||||
// <div className="absolute inset-y-0 right-0 flex items-center justify-center">
|
{product.title}
|
||||||
// <div className="inline-flex bg-white p-4 text-xl font-semibold text-black dark:bg-black dark:text-white">
|
</div>
|
||||||
// {product.title}
|
</div>
|
||||||
// </div>
|
</Link>
|
||||||
// </div>
|
))}
|
||||||
// </Link>
|
</div>
|
||||||
// ))}
|
</div>
|
||||||
// </div>
|
);
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
'use client'
|
'use client';
|
||||||
|
|
||||||
import ReactPaginate from 'react-paginate';
|
import ReactPaginate from 'react-paginate';
|
||||||
import { createUrl } from 'lib/utils';
|
import { createUrl } from 'lib/utils';
|
||||||
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
|
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
|
||||||
|
|
||||||
export default function Pagination({ itemsPerPage, itemsTotal, currentPage }: { itemsPerPage: number, itemsTotal: number, currentPage: number }) {
|
export default function Pagination({
|
||||||
|
itemsPerPage,
|
||||||
|
itemsTotal,
|
||||||
|
currentPage
|
||||||
|
}: {
|
||||||
|
itemsPerPage: number;
|
||||||
|
itemsTotal: number;
|
||||||
|
currentPage: number;
|
||||||
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const currentParams = useSearchParams();
|
const currentParams = useSearchParams();
|
||||||
@ -12,20 +20,26 @@ export default function Pagination({ itemsPerPage, itemsTotal, currentPage }: {
|
|||||||
const sort = currentParams.get('sort');
|
const sort = currentParams.get('sort');
|
||||||
const pageCount = Math.ceil(itemsTotal / itemsPerPage);
|
const pageCount = Math.ceil(itemsTotal / itemsPerPage);
|
||||||
|
|
||||||
// Invoke when user click to request another page. test
|
// Invoke when user click to request another page.
|
||||||
const handlePageClick = (event: clickEvent) => {
|
const handlePageClick = (event: clickEvent) => {
|
||||||
const page = event.selected;
|
const page = event.selected;
|
||||||
const newPage = page + 1;
|
const newPage = page + 1;
|
||||||
let newUrl = createUrl(pathname, new URLSearchParams({
|
let newUrl = createUrl(
|
||||||
...(q && { q }),
|
pathname,
|
||||||
...(sort && { sort }),
|
new URLSearchParams({
|
||||||
}));
|
|
||||||
if (page !== 0) {
|
|
||||||
newUrl = createUrl(pathname, new URLSearchParams({
|
|
||||||
...(q && { q }),
|
...(q && { q }),
|
||||||
...(sort && { sort }),
|
...(sort && { sort })
|
||||||
page: newPage.toString(),
|
})
|
||||||
}));
|
);
|
||||||
|
if (page !== 0) {
|
||||||
|
newUrl = createUrl(
|
||||||
|
pathname,
|
||||||
|
new URLSearchParams({
|
||||||
|
...(q && { q }),
|
||||||
|
...(sort && { sort }),
|
||||||
|
page: newPage.toString()
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
router.replace(newUrl);
|
router.replace(newUrl);
|
||||||
};
|
};
|
||||||
@ -65,4 +79,4 @@ type clickEvent = {
|
|||||||
isNext: boolean;
|
isNext: boolean;
|
||||||
isBreak: boolean;
|
isBreak: boolean;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
}
|
};
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
|
|
||||||
import { getStaticCollections } from 'lib/shopware';
|
import { getSubCollections } from 'lib/shopware';
|
||||||
import FilterList from './filter';
|
import FilterList from './filter';
|
||||||
import { transformStaticCollectionToList } from 'lib/shopware/transform';
|
import { transformCollectionToList } from 'lib/shopware/transform';
|
||||||
|
|
||||||
async function CollectionList() {
|
async function CollectionList({ collection }: { collection: string }) {
|
||||||
const collections = await getStaticCollections();
|
const collections = await getSubCollections(collection);
|
||||||
if (collections) {
|
if (collections) {
|
||||||
const list = transformStaticCollectionToList(collections);
|
const list = transformCollectionToList(collections);
|
||||||
return <FilterList list={list} title="Collections" />;
|
if (list.length > 0) return <FilterList list={list} title="Sub-Collections" />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ const skeleton = 'mb-3 h-4 w-5/6 animate-pulse rounded';
|
|||||||
const activeAndTitles = 'bg-gray-800 dark:bg-gray-300';
|
const activeAndTitles = 'bg-gray-800 dark:bg-gray-300';
|
||||||
const items = 'bg-gray-400 dark:bg-gray-700';
|
const items = 'bg-gray-400 dark:bg-gray-700';
|
||||||
|
|
||||||
export default function Collections() {
|
export default function Collections({ collection }: { collection: string }) {
|
||||||
return (
|
return (
|
||||||
<Suspense
|
<Suspense
|
||||||
fallback={
|
fallback={
|
||||||
@ -35,7 +35,7 @@ export default function Collections() {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CollectionList />
|
<CollectionList collection={collection} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ export function getStaticCollectionCriteria(page: number = 1, limit: number = 20
|
|||||||
export function getDefaultSubCategoriesCriteria(
|
export function getDefaultSubCategoriesCriteria(
|
||||||
categoryId: string,
|
categoryId: string,
|
||||||
page: number = 1,
|
page: number = 1,
|
||||||
limit: number = 10
|
limit: number = 1
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
page: page,
|
page: page,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { Cart } from 'lib/shopify/types';
|
|
||||||
import {
|
import {
|
||||||
requestCart,
|
requestCart,
|
||||||
requestCategory,
|
requestCategory,
|
||||||
@ -19,8 +18,8 @@ import {
|
|||||||
getDefaultProductCriteria,
|
getDefaultProductCriteria,
|
||||||
getDefaultProductsCriteria,
|
getDefaultProductsCriteria,
|
||||||
getDefaultSearchProductsCriteria,
|
getDefaultSearchProductsCriteria,
|
||||||
getSortingCriteria,
|
getDefaultSubCategoriesCriteria,
|
||||||
getStaticCollectionCriteria
|
getSortingCriteria
|
||||||
} from './criteria';
|
} from './criteria';
|
||||||
import {
|
import {
|
||||||
transformCollection,
|
transformCollection,
|
||||||
@ -29,10 +28,12 @@ import {
|
|||||||
transformPage,
|
transformPage,
|
||||||
transformProduct,
|
transformProduct,
|
||||||
transformProducts,
|
transformProducts,
|
||||||
transformStaticCollection
|
transformSubCollection
|
||||||
} from './transform';
|
} from './transform';
|
||||||
import {
|
import {
|
||||||
ApiSchemas,
|
ApiSchemas,
|
||||||
|
Cart,
|
||||||
|
CategoryListingResultSW,
|
||||||
Menu,
|
Menu,
|
||||||
Page,
|
Page,
|
||||||
Product,
|
Product,
|
||||||
@ -55,9 +56,17 @@ export async function getPage(handle: string | []): Promise<Page | undefined> {
|
|||||||
const pageHandle = transformHandle(handle).replace('cms/', '');
|
const pageHandle = transformHandle(handle).replace('cms/', '');
|
||||||
const seoUrlElement = await getFirstSeoUrlElement(pageHandle);
|
const seoUrlElement = await getFirstSeoUrlElement(pageHandle);
|
||||||
if (seoUrlElement) {
|
if (seoUrlElement) {
|
||||||
const resCategory = await getCategory(seoUrlElement);
|
const category = await getCategory(seoUrlElement);
|
||||||
|
|
||||||
return resCategory ? transformPage(seoUrlElement, resCategory) : undefined;
|
if (!category) {
|
||||||
|
console.log('[getPage] Did not found any category with page handle:', pageHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return category ? transformPage(seoUrlElement, category) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!seoUrlElement) {
|
||||||
|
console.log('[getPage] Did not found any seoUrl element with page handle:', pageHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,12 +88,19 @@ export async function getFirstProduct(productId: string): Promise<ExtendedProduc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToDo: should be more dynamic (depending on handle), should work with server and not client see generateStaticParams from next.js
|
// ToDo: should be more dynamic (depending on handle), should work with server and not client see generateStaticParams from next.js
|
||||||
export async function getStaticCollections() {
|
export async function getSubCollections(collection: string) {
|
||||||
// @ToDo: This is an example about multi-filter with new store API client
|
let res: CategoryListingResultSW | undefined = undefined;
|
||||||
// @ts-ignore
|
const parentCollectionName =
|
||||||
const resCategory = await requestCategoryList(getStaticCollectionCriteria());
|
Array.isArray(collection) && collection[0] ? collection[0] : undefined;
|
||||||
|
const collectionName = transformHandle(collection ?? '');
|
||||||
|
const seoUrlElement = await getFirstSeoUrlElement(collectionName);
|
||||||
|
if (seoUrlElement) {
|
||||||
|
const criteria = getDefaultSubCategoriesCriteria(seoUrlElement.foreignKey);
|
||||||
|
// @ts-ignore
|
||||||
|
res = await requestCategoryList(criteria);
|
||||||
|
}
|
||||||
|
|
||||||
return resCategory ? transformStaticCollection(resCategory) : [];
|
return res ? transformSubCollection(res, parentCollectionName) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSearchCollectionProducts(params?: {
|
export async function getSearchCollectionProducts(params?: {
|
||||||
@ -220,6 +236,7 @@ export async function getProductRecommendations(productId: string): Promise<Prod
|
|||||||
export async function getCart(): Promise<Cart> {
|
export async function getCart(): Promise<Cart> {
|
||||||
const cartData = await requestCart();
|
const cartData = await requestCart();
|
||||||
|
|
||||||
|
// @ToDo: should be moved to transformCart function
|
||||||
let cart: Cart = {
|
let cart: Cart = {
|
||||||
checkoutUrl: 'https://frontends-demo.vercel.app',
|
checkoutUrl: 'https://frontends-demo.vercel.app',
|
||||||
cost: {
|
cost: {
|
||||||
@ -240,33 +257,44 @@ export async function getCart(): Promise<Cart> {
|
|||||||
lines:
|
lines:
|
||||||
cartData.lineItems?.map((lineItem) => ({
|
cartData.lineItems?.map((lineItem) => ({
|
||||||
id: lineItem.id || '',
|
id: lineItem.id || '',
|
||||||
quantity: lineItem.quantity,
|
quantity: lineItem.quantity ?? 0,
|
||||||
cost: {
|
cost: {
|
||||||
totalAmount: {
|
totalAmount: {
|
||||||
amount: (lineItem as any)?.price?.totalPrice || ''
|
amount: (lineItem as any)?.price?.totalPrice || '',
|
||||||
|
currencyCode: 'EUR'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
merchandise: {
|
merchandise: {
|
||||||
id: lineItem.referencedId,
|
id: lineItem.referencedId ?? '',
|
||||||
title: lineItem.label,
|
title: lineItem.label ?? '',
|
||||||
selectedOptions: [],
|
selectedOptions: [],
|
||||||
product: {
|
product: {
|
||||||
description: lineItem.description,
|
description: lineItem.description ?? '',
|
||||||
descriptionHtml: lineItem.description,
|
descriptionHtml: lineItem.description ?? '',
|
||||||
id: lineItem.referencedId,
|
id: lineItem.referencedId ?? '',
|
||||||
images: [],
|
images: [],
|
||||||
|
path: '',
|
||||||
seo: {
|
seo: {
|
||||||
description: lineItem.description,
|
description: lineItem.description ?? '',
|
||||||
title: lineItem.label
|
title: lineItem.label ?? ''
|
||||||
},
|
},
|
||||||
availableForSale: true,
|
availableForSale: true,
|
||||||
featuredImage: (lineItem as any).cover?.url,
|
featuredImage: (lineItem as any).cover?.url,
|
||||||
handle: '',
|
handle: '',
|
||||||
options: [],
|
options: [],
|
||||||
variants: [],
|
variants: [],
|
||||||
priceRange: {},
|
priceRange: {
|
||||||
|
minVariantPrice: {
|
||||||
|
amount: '', // @ToDo: should be correct value
|
||||||
|
currencyCode: 'EUR'
|
||||||
|
},
|
||||||
|
maxVariantPrice: {
|
||||||
|
amount: '', // @ToDo: should be correct value
|
||||||
|
currencyCode: 'EUR'
|
||||||
|
}
|
||||||
|
},
|
||||||
tags: [],
|
tags: [],
|
||||||
title: lineItem.label,
|
title: lineItem.label ?? '',
|
||||||
updatedAt: (lineItem as any)?.payload?.updatedAt
|
updatedAt: (lineItem as any)?.payload?.updatedAt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,40 +104,69 @@ export function transformCollection(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformStaticCollection(resCategory: CategoryListingResultSW): Collection[] {
|
export function transformSubCollection(
|
||||||
|
category: CategoryListingResultSW,
|
||||||
|
parentCollectionName?: string
|
||||||
|
): Collection[] {
|
||||||
const collection: Collection[] = [];
|
const collection: Collection[] = [];
|
||||||
|
|
||||||
if (resCategory.elements && resCategory.elements.length > 0) {
|
if (category.elements && category.elements[0] && category.elements[0].children) {
|
||||||
resCategory.elements.map((item) =>
|
// we do not support type links at the moment and show only visible categories
|
||||||
collection.push({
|
category.elements[0].children
|
||||||
handle:
|
.filter((item) => item.visible)
|
||||||
item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo
|
.filter((item) => item.type !== 'link')
|
||||||
? item.seoUrls[0].seoPathInfo
|
.map((item) => {
|
||||||
: '',
|
const handle = item.seoUrls ? findHandle(item.seoUrls, parentCollectionName) : undefined;
|
||||||
title: item.translated?.metaTitle ?? item.name ?? '',
|
if (handle) {
|
||||||
description: item.description ?? '',
|
collection.push({
|
||||||
seo: {
|
handle: handle,
|
||||||
title: item.translated?.metaTitle ?? item.name ?? '',
|
title: item.translated?.metaTitle ?? item.name ?? '',
|
||||||
description: item.translated?.metaDescription ?? item.description ?? ''
|
description: item.description ?? '',
|
||||||
},
|
seo: {
|
||||||
updatedAt: item.updatedAt ?? item.createdAt ?? ''
|
title: item.translated?.metaTitle ?? item.name ?? '',
|
||||||
})
|
description: item.translated?.metaDescription ?? item.description ?? ''
|
||||||
);
|
},
|
||||||
|
childCount: item.childCount ?? 0,
|
||||||
|
updatedAt: item.updatedAt ?? item.createdAt ?? ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return collection;
|
return collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformStaticCollectionToList(collection: Collection[]): ListItem[] {
|
// small function to find longest handle and to make sure parent collection name is in the path
|
||||||
|
function findHandle(seoUrls: ApiSchemas['SeoUrl'][], parentCollectionName?: string): string {
|
||||||
|
let handle: string = '';
|
||||||
|
seoUrls.map((item) => {
|
||||||
|
if (
|
||||||
|
!item.isDeleted &&
|
||||||
|
item.isCanonical &&
|
||||||
|
item.seoPathInfo &&
|
||||||
|
item.seoPathInfo.length > handle.length &&
|
||||||
|
item.seoPathInfo.includes(parentCollectionName ?? '')
|
||||||
|
) {
|
||||||
|
handle = item.seoPathInfo;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformCollectionToList(collection: Collection[]): ListItem[] {
|
||||||
const listItem: ListItem[] = [];
|
const listItem: ListItem[] = [];
|
||||||
|
|
||||||
if (collection && collection.length > 0) {
|
if (collection && collection.length > 0) {
|
||||||
collection.map((item) =>
|
collection.map((item) => {
|
||||||
|
// we asume that when there is not product child count it must be a cms page
|
||||||
|
const pagePrefix = item.childCount === 0 ? '/cms' : '/search';
|
||||||
|
const newHandle = item.handle.replace('Main-navigation/', '');
|
||||||
listItem.push({
|
listItem.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
path: `/search/${item.handle}`
|
path: `${pagePrefix}/${newHandle}`
|
||||||
})
|
});
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return listItem;
|
return listItem;
|
||||||
|
@ -96,5 +96,35 @@ export type Collection = {
|
|||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
seo: SEO;
|
seo: SEO;
|
||||||
|
childCount: number;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Cart = {
|
||||||
|
id: string;
|
||||||
|
checkoutUrl: string;
|
||||||
|
cost: {
|
||||||
|
subtotalAmount: Money;
|
||||||
|
totalAmount: Money;
|
||||||
|
totalTaxAmount: Money;
|
||||||
|
};
|
||||||
|
lines: CartItem[];
|
||||||
|
totalQuantity: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CartItem = {
|
||||||
|
id: string;
|
||||||
|
quantity: number;
|
||||||
|
cost: {
|
||||||
|
totalAmount: Money;
|
||||||
|
};
|
||||||
|
merchandise: {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
selectedOptions: {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
|
product: Product;
|
||||||
|
};
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user