diff --git a/app/search/(collection)/[...collection]/page.tsx b/app/search/(collection)/[...collection]/page.tsx
index 67e3c326d..0bce5ab50 100644
--- a/app/search/(collection)/[...collection]/page.tsx
+++ b/app/search/(collection)/[...collection]/page.tsx
@@ -3,6 +3,8 @@ import { Metadata } from 'next';
import { notFound } from 'next/navigation';
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 Pagination from 'components/collection/pagination';
import { defaultSort, sorting } from 'lib/constants';
@@ -47,13 +49,21 @@ export default async function CategoryPage({
{products.length === 0 ? (
-
-
-
-
-
-
+
)}
diff --git a/app/search/layout.tsx b/app/search/layout.tsx
index 872276d7e..7bd3f1593 100644
--- a/app/search/layout.tsx
+++ b/app/search/layout.tsx
@@ -1,21 +1,11 @@
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';
+// @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 }) {
return (
-
-
-
-
-
{children}
-
-
-
-
+ {children}
);
diff --git a/app/search/page.tsx b/app/search/page.tsx
index 3eeac0c6a..a8940ac44 100644
--- a/app/search/page.tsx
+++ b/app/search/page.tsx
@@ -1,5 +1,6 @@
import Grid from 'components/grid';
import ProductGridItems from 'components/layout/product-grid-items';
+import FilterList from 'components/layout/search/filter';
import { defaultSort, sorting } from 'lib/constants';
import { getSearchCollectionProducts } from 'lib/shopware';
@@ -22,19 +23,32 @@ export default async function SearchPage({
const resultsText = products.length > 1 ? 'results' : 'result';
return (
- <>
- {searchValue ? (
+ <>{searchValue && products.length === 0 ? (
+
- {products.length === 0
- ? 'There are no products that match '
- : `Showing ${products.length} ${resultsText} for `}
+ {'There are no products that match '}
"{searchValue}"
- ) : null}
+
+ ) : null}
{products.length > 0 ? (
-
-
-
+
+
+ {searchValue ? (
+
+ {`Showing ${products.length} ${resultsText} for `}
+ "{searchValue}"
+
+ ) : null}
+
Good place to add other suggest search terms ;)
+
+
+
+
+
+
+
+
) : null}
>
);
diff --git a/components/carousel.tsx b/components/carousel.tsx
index 86f05c54c..3d1986aaf 100644
--- a/components/carousel.tsx
+++ b/components/carousel.tsx
@@ -3,39 +3,36 @@ import Image from 'next/image';
import Link from 'next/link';
export async function Carousel() {
- // Collections that start with `hidden-*` are hidden from the search page.
- // const products = await getCollectionProducts({ collection: 'hidden-homepage-carousel' });
+ const { products } = await getCollectionProducts({ collection: 'Summer-BBQ/Hidden-Carousel-Category' });
- // if (!products?.length) return null;
+ if (!products?.length) return null;
- return null;
-
- // return (
- //
- //
- // {[...products, ...products].map((product, i) => (
- //
- // {product.featuredImage ? (
- //
- // ) : null}
- //
- //
- // {product.title}
- //
- //
- //
- // ))}
- //
- //
- // );
+ return (
+
+
+ {[...products, ...products].map((product, i) => (
+
+ {product.featuredImage ? (
+
+ ) : null}
+
+
+ ))}
+
+
+ );
}
diff --git a/components/collection/pagination.tsx b/components/collection/pagination.tsx
index 2d560cf52..40ad782f3 100644
--- a/components/collection/pagination.tsx
+++ b/components/collection/pagination.tsx
@@ -1,10 +1,18 @@
-'use client'
+'use client';
import ReactPaginate from 'react-paginate';
import { createUrl } from 'lib/utils';
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 pathname = usePathname();
const currentParams = useSearchParams();
@@ -12,20 +20,26 @@ export default function Pagination({ itemsPerPage, itemsTotal, currentPage }: {
const sort = currentParams.get('sort');
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 page = event.selected;
const newPage = page + 1;
- let newUrl = createUrl(pathname, new URLSearchParams({
- ...(q && { q }),
- ...(sort && { sort }),
- }));
- if (page !== 0) {
- newUrl = createUrl(pathname, new URLSearchParams({
+ let newUrl = createUrl(
+ pathname,
+ new URLSearchParams({
...(q && { q }),
- ...(sort && { sort }),
- page: newPage.toString(),
- }));
+ ...(sort && { sort })
+ })
+ );
+ if (page !== 0) {
+ newUrl = createUrl(
+ pathname,
+ new URLSearchParams({
+ ...(q && { q }),
+ ...(sort && { sort }),
+ page: newPage.toString()
+ })
+ );
}
router.replace(newUrl);
};
@@ -65,4 +79,4 @@ type clickEvent = {
isNext: boolean;
isBreak: boolean;
isActive: boolean;
-}
\ No newline at end of file
+};
diff --git a/components/layout/search/collections.tsx b/components/layout/search/collections.tsx
index 0d2289e1a..2009790ee 100644
--- a/components/layout/search/collections.tsx
+++ b/components/layout/search/collections.tsx
@@ -1,15 +1,15 @@
import clsx from 'clsx';
import { Suspense } from 'react';
-import { getStaticCollections } from 'lib/shopware';
+import { getSubCollections } from 'lib/shopware';
import FilterList from './filter';
-import { transformStaticCollectionToList } from 'lib/shopware/transform';
+import { transformCollectionToList } from 'lib/shopware/transform';
-async function CollectionList() {
- const collections = await getStaticCollections();
+async function CollectionList({ collection }: { collection: string }) {
+ const collections = await getSubCollections(collection);
if (collections) {
- const list = transformStaticCollectionToList(collections);
- return
;
+ const list = transformCollectionToList(collections);
+ if (list.length > 0) return
;
}
}
@@ -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 items = 'bg-gray-400 dark:bg-gray-700';
-export default function Collections() {
+export default function Collections({ collection }: { collection: string }) {
return (
}
>
-
+
);
}
diff --git a/lib/shopware/criteria.ts b/lib/shopware/criteria.ts
index 9abaa0899..0de5b1007 100644
--- a/lib/shopware/criteria.ts
+++ b/lib/shopware/criteria.ts
@@ -165,7 +165,7 @@ export function getStaticCollectionCriteria(page: number = 1, limit: number = 20
export function getDefaultSubCategoriesCriteria(
categoryId: string,
page: number = 1,
- limit: number = 10
+ limit: number = 1
) {
return {
page: page,
diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts
index bb974a196..febe5e013 100644
--- a/lib/shopware/index.ts
+++ b/lib/shopware/index.ts
@@ -1,4 +1,3 @@
-import { Cart } from 'lib/shopify/types';
import {
requestCart,
requestCategory,
@@ -19,8 +18,8 @@ import {
getDefaultProductCriteria,
getDefaultProductsCriteria,
getDefaultSearchProductsCriteria,
- getSortingCriteria,
- getStaticCollectionCriteria
+ getDefaultSubCategoriesCriteria,
+ getSortingCriteria
} from './criteria';
import {
transformCollection,
@@ -29,10 +28,12 @@ import {
transformPage,
transformProduct,
transformProducts,
- transformStaticCollection
+ transformSubCollection
} from './transform';
import {
ApiSchemas,
+ Cart,
+ CategoryListingResultSW,
Menu,
Page,
Product,
@@ -55,9 +56,17 @@ export async function getPage(handle: string | []): Promise
{
const pageHandle = transformHandle(handle).replace('cms/', '');
const seoUrlElement = await getFirstSeoUrlElement(pageHandle);
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 {
const cartData = await requestCart();
+ // @ToDo: should be moved to transformCart function
let cart: Cart = {
checkoutUrl: 'https://frontends-demo.vercel.app',
cost: {
@@ -240,33 +257,44 @@ export async function getCart(): Promise {
lines:
cartData.lineItems?.map((lineItem) => ({
id: lineItem.id || '',
- quantity: lineItem.quantity,
+ quantity: lineItem.quantity ?? 0,
cost: {
totalAmount: {
- amount: (lineItem as any)?.price?.totalPrice || ''
+ amount: (lineItem as any)?.price?.totalPrice || '',
+ currencyCode: 'EUR'
}
},
merchandise: {
- id: lineItem.referencedId,
- title: lineItem.label,
+ id: lineItem.referencedId ?? '',
+ title: lineItem.label ?? '',
selectedOptions: [],
product: {
- description: lineItem.description,
- descriptionHtml: lineItem.description,
- id: lineItem.referencedId,
+ description: lineItem.description ?? '',
+ descriptionHtml: lineItem.description ?? '',
+ id: lineItem.referencedId ?? '',
images: [],
+ path: '',
seo: {
- description: lineItem.description,
- title: lineItem.label
+ description: lineItem.description ?? '',
+ title: lineItem.label ?? ''
},
availableForSale: true,
featuredImage: (lineItem as any).cover?.url,
handle: '',
options: [],
variants: [],
- priceRange: {},
+ priceRange: {
+ minVariantPrice: {
+ amount: '', // @ToDo: should be correct value
+ currencyCode: 'EUR'
+ },
+ maxVariantPrice: {
+ amount: '', // @ToDo: should be correct value
+ currencyCode: 'EUR'
+ }
+ },
tags: [],
- title: lineItem.label,
+ title: lineItem.label ?? '',
updatedAt: (lineItem as any)?.payload?.updatedAt
}
}
diff --git a/lib/shopware/transform.ts b/lib/shopware/transform.ts
index 8b0c23d83..3afef3a06 100644
--- a/lib/shopware/transform.ts
+++ b/lib/shopware/transform.ts
@@ -104,40 +104,69 @@ export function transformCollection(
};
}
-export function transformStaticCollection(resCategory: CategoryListingResultSW): Collection[] {
+export function transformSubCollection(
+ category: CategoryListingResultSW,
+ parentCollectionName?: string
+): Collection[] {
const collection: Collection[] = [];
- if (resCategory.elements && resCategory.elements.length > 0) {
- resCategory.elements.map((item) =>
- collection.push({
- handle:
- item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo
- ? item.seoUrls[0].seoPathInfo
- : '',
- title: item.translated?.metaTitle ?? item.name ?? '',
- description: item.description ?? '',
- seo: {
- title: item.translated?.metaTitle ?? item.name ?? '',
- description: item.translated?.metaDescription ?? item.description ?? ''
- },
- updatedAt: item.updatedAt ?? item.createdAt ?? ''
- })
- );
+ if (category.elements && category.elements[0] && category.elements[0].children) {
+ // we do not support type links at the moment and show only visible categories
+ category.elements[0].children
+ .filter((item) => item.visible)
+ .filter((item) => item.type !== 'link')
+ .map((item) => {
+ const handle = item.seoUrls ? findHandle(item.seoUrls, parentCollectionName) : undefined;
+ if (handle) {
+ collection.push({
+ handle: handle,
+ title: item.translated?.metaTitle ?? item.name ?? '',
+ description: item.description ?? '',
+ seo: {
+ title: item.translated?.metaTitle ?? item.name ?? '',
+ description: item.translated?.metaDescription ?? item.description ?? ''
+ },
+ childCount: item.childCount ?? 0,
+ updatedAt: item.updatedAt ?? item.createdAt ?? ''
+ });
+ }
+ });
}
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[] = [];
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({
title: item.title,
- path: `/search/${item.handle}`
- })
- );
+ path: `${pagePrefix}/${newHandle}`
+ });
+ });
}
return listItem;
diff --git a/lib/shopware/types.ts b/lib/shopware/types.ts
index 405ed0b42..4e4331d80 100644
--- a/lib/shopware/types.ts
+++ b/lib/shopware/types.ts
@@ -96,5 +96,35 @@ export type Collection = {
title: string;
description: string;
seo: SEO;
+ childCount: number;
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;
+ };
+};
\ No newline at end of file