mirror of
https://github.com/vercel/commerce.git
synced 2025-05-15 05:56:59 +00:00
feat(poc): seoUrls yes or no env
This commit is contained in:
parent
744243c3d8
commit
46e003196f
@ -4,3 +4,4 @@ SITE_NAME="Next.js Commerce with Shopware Composable Frontends"
|
|||||||
SHOPWARE_STORE_DOMAIN=""
|
SHOPWARE_STORE_DOMAIN=""
|
||||||
SHOPWARE_API_TYPE="store-api"
|
SHOPWARE_API_TYPE="store-api"
|
||||||
SHOPWARE_ACCESS_TOKEN=""
|
SHOPWARE_ACCESS_TOKEN=""
|
||||||
|
SHOPWARE_USE_SEO_URLS="false"
|
||||||
|
@ -3,8 +3,12 @@ import Image from 'next/image';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export async function Carousel() {
|
export async function Carousel() {
|
||||||
|
const collectionName =
|
||||||
|
`${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'
|
||||||
|
? 'Summer-BBQ/Hidden-Carousel-Category'
|
||||||
|
: 'ff7bf3c59f1342a685844fbf8fdf9dc8';
|
||||||
const { products } = await getCollectionProducts({
|
const { products } = await getCollectionProducts({
|
||||||
collection: 'Summer-BBQ/Hidden-Carousel-Category'
|
collection: collectionName
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!products?.length) return null;
|
if (!products?.length) return null;
|
||||||
|
@ -37,8 +37,12 @@ function ThreeItemGridItem({
|
|||||||
|
|
||||||
export async function ThreeItemGrid() {
|
export async function ThreeItemGrid() {
|
||||||
// Collections that start with `hidden-*` are hidden from the search page.
|
// Collections that start with `hidden-*` are hidden from the search page.
|
||||||
|
const collectionName =
|
||||||
|
`${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'
|
||||||
|
? 'Summer-BBQ/Hidden-Category'
|
||||||
|
: '4ab73c06d90d4a5cb312209a64480d87';
|
||||||
const { products: homepageItems } = await getCollectionProducts({
|
const { products: homepageItems } = await getCollectionProducts({
|
||||||
collection: 'Summer-BBQ/Hidden-Category'
|
collection: collectionName
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!homepageItems[0] || !homepageItems[1] || !homepageItems[2]) return null;
|
if (!homepageItems[0] || !homepageItems[1] || !homepageItems[2]) return null;
|
||||||
|
@ -168,15 +168,13 @@ export async function requestSeoUrl(
|
|||||||
page: number = 1,
|
page: number = 1,
|
||||||
limit: number = 1
|
limit: number = 1
|
||||||
): Promise<SeoURLResultSW | undefined> {
|
): Promise<SeoURLResultSW | undefined> {
|
||||||
console.log(handle);
|
|
||||||
try {
|
try {
|
||||||
const FirstCriteria = {
|
const criteriaSeoUrls = {
|
||||||
page: page,
|
page: page,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
filter: [
|
filter: [
|
||||||
{
|
{
|
||||||
type: 'multi',
|
type: 'multi',
|
||||||
// @ts-ignore
|
|
||||||
operator: 'or',
|
operator: 'or',
|
||||||
queries: [
|
queries: [
|
||||||
{
|
{
|
||||||
@ -194,27 +192,7 @@ export async function requestSeoUrl(
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const firstResult = await apiInstance.invoke('readSeoUrl post /seo-url', FirstCriteria);
|
return await apiInstance.invoke('readSeoUrl post /seo-url', criteriaSeoUrls);
|
||||||
if (firstResult.total && firstResult.total > 0) {
|
|
||||||
return firstResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastPart = handle.split('/').pop() + '';
|
|
||||||
console.log(lastPart);
|
|
||||||
|
|
||||||
const SecondCriteria = {
|
|
||||||
page: page,
|
|
||||||
limit: limit,
|
|
||||||
filter: [
|
|
||||||
{
|
|
||||||
type: 'contains',
|
|
||||||
field: 'seoPathInfo',
|
|
||||||
value: lastPart
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
return await apiInstance.invoke('readSeoUrl post /seo-url', SecondCriteria);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof ApiClientError) {
|
if (error instanceof ApiClientError) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -40,6 +40,7 @@ import {
|
|||||||
ProductListingCriteria,
|
ProductListingCriteria,
|
||||||
StoreNavigationTypeSW
|
StoreNavigationTypeSW
|
||||||
} from './types';
|
} from './types';
|
||||||
|
const useSeoUrls = `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true';
|
||||||
|
|
||||||
export async function getMenu(params?: {
|
export async function getMenu(params?: {
|
||||||
type?: StoreNavigationTypeSW;
|
type?: StoreNavigationTypeSW;
|
||||||
@ -53,21 +54,26 @@ export async function getMenu(params?: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getPage(handle: string | []): Promise<Page | undefined> {
|
export async function getPage(handle: string | []): Promise<Page | undefined> {
|
||||||
const pageHandle = decodeURIComponent(transformHandle(handle));
|
let seoUrlElement;
|
||||||
const seoUrlElement = await getFirstSeoUrlElement(pageHandle);
|
let pageIdOrHandle = decodeURIComponent(transformHandle(handle)).replace('cms/', '');
|
||||||
if (seoUrlElement) {
|
|
||||||
const category = await getCategory(seoUrlElement);
|
|
||||||
|
|
||||||
if (!category) {
|
if (useSeoUrls) {
|
||||||
console.log('[getPage] Did not found any category with page handle:', pageHandle);
|
seoUrlElement = await getFirstSeoUrlElement(pageIdOrHandle);
|
||||||
|
if (seoUrlElement) {
|
||||||
|
pageIdOrHandle = seoUrlElement.foreignKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
return category ? transformPage(seoUrlElement, category) : undefined;
|
if (!seoUrlElement) {
|
||||||
|
console.log('[getPage] Did not found any seoUrl element with page handle:', pageIdOrHandle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!seoUrlElement) {
|
const category = await getCategory(pageIdOrHandle);
|
||||||
console.log('[getPage] Did not found any seoUrl element with page handle:', pageHandle);
|
if (!category) {
|
||||||
|
console.log('[getPage] Did not found any category with handle:', pageIdOrHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return category ? transformPage(category, seoUrlElement) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFirstSeoUrlElement(
|
export async function getFirstSeoUrlElement(
|
||||||
@ -91,17 +97,22 @@ 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 getSubCollections(collection: string) {
|
export async function getSubCollections(collection: string) {
|
||||||
|
const collectionName = decodeURIComponent(transformHandle(collection ?? ''));
|
||||||
|
let criteria = getDefaultSubCategoriesCriteria(collectionName);
|
||||||
let res: CategoryListingResultSW | undefined = undefined;
|
let res: CategoryListingResultSW | undefined = undefined;
|
||||||
const parentCollectionName =
|
const parentCollectionName =
|
||||||
Array.isArray(collection) && collection[0] ? collection[0] : undefined;
|
Array.isArray(collection) && collection[0] ? collection[0] : undefined;
|
||||||
const collectionName = transformHandle(collection ?? '');
|
|
||||||
const seoUrlElement = await getFirstSeoUrlElement(collectionName);
|
if (useSeoUrls) {
|
||||||
if (seoUrlElement) {
|
const seoUrlElement = await getFirstSeoUrlElement(collectionName);
|
||||||
const criteria = getDefaultSubCategoriesCriteria(seoUrlElement.foreignKey);
|
if (seoUrlElement) {
|
||||||
// @ts-ignore
|
criteria = getDefaultSubCategoriesCriteria(seoUrlElement.foreignKey);
|
||||||
res = await requestCategoryList(criteria);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
res = await requestCategoryList(criteria);
|
||||||
|
|
||||||
return res ? transformSubCollection(res, parentCollectionName) : [];
|
return res ? transformSubCollection(res, parentCollectionName) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +171,7 @@ export async function getCollectionProducts(params?: {
|
|||||||
const collectionName = decodeURIComponent(transformHandle(params?.collection ?? ''));
|
const collectionName = decodeURIComponent(transformHandle(params?.collection ?? ''));
|
||||||
const sorting = getSortingCriteria(params?.sortKey, params?.reverse);
|
const sorting = getSortingCriteria(params?.sortKey, params?.reverse);
|
||||||
|
|
||||||
if (!category && collectionName !== '') {
|
if (useSeoUrls && !category && collectionName !== '') {
|
||||||
const seoUrlElement = await getFirstSeoUrlElement(collectionName);
|
const seoUrlElement = await getFirstSeoUrlElement(collectionName);
|
||||||
if (seoUrlElement) {
|
if (seoUrlElement) {
|
||||||
category = seoUrlElement.foreignKey;
|
category = seoUrlElement.foreignKey;
|
||||||
@ -173,6 +184,10 @@ export async function getCollectionProducts(params?: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!useSeoUrls) {
|
||||||
|
category = params?.collection ?? undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (category) {
|
if (category) {
|
||||||
const criteria = !params?.defaultSearchCriteria
|
const criteria = !params?.defaultSearchCriteria
|
||||||
? getDefaultProductsCriteria(params?.page)
|
? getDefaultProductsCriteria(params?.page)
|
||||||
@ -194,29 +209,37 @@ export async function getCollectionProducts(params?: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getCategory(
|
export async function getCategory(
|
||||||
seoUrl: ApiSchemas['SeoUrl'],
|
categoryId: string,
|
||||||
cms: boolean = false
|
cms: boolean = false
|
||||||
): Promise<ExtendedCategory | undefined> {
|
): Promise<ExtendedCategory | undefined> {
|
||||||
const criteria = cms ? getDefaultCategoryWithCmsCriteria() : getDefaultCategoryCriteria();
|
const criteria = cms ? getDefaultCategoryWithCmsCriteria() : getDefaultCategoryCriteria();
|
||||||
return await requestCategory(seoUrl.foreignKey, criteria);
|
return await requestCategory(categoryId, criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is only used for generateMetadata at app/search/(collection)/[...collection]/page.tsx
|
// This function is only used for generateMetadata at app/search/(collection)/[...collection]/page.tsx
|
||||||
export async function getCollection(handle: string | []) {
|
export async function getCollection(handle: string | []) {
|
||||||
const collectionName = decodeURIComponent(transformHandle(handle));
|
let path;
|
||||||
const seoUrlElement = await getFirstSeoUrlElement(collectionName);
|
let seoUrlElement;
|
||||||
if (seoUrlElement) {
|
let categoryIdOrHandle = decodeURIComponent(transformHandle(handle));
|
||||||
const category = await getCategory(seoUrlElement);
|
|
||||||
const path = seoUrlElement.seoPathInfo ?? '';
|
|
||||||
if (category) {
|
|
||||||
const collection = transformCollection(seoUrlElement, category);
|
|
||||||
|
|
||||||
return {
|
if (useSeoUrls) {
|
||||||
...collection,
|
seoUrlElement = await getFirstSeoUrlElement(categoryIdOrHandle);
|
||||||
path: `/search/${path}`
|
if (seoUrlElement) {
|
||||||
};
|
categoryIdOrHandle = seoUrlElement.foreignKey;
|
||||||
|
path = seoUrlElement.seoPathInfo ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const category = await getCategory(categoryIdOrHandle);
|
||||||
|
if (category) {
|
||||||
|
const collection = transformCollection(category, seoUrlElement);
|
||||||
|
path = path ?? category.id ?? '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
...collection,
|
||||||
|
path: `/search/${path}`
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProductSeoUrls() {
|
export async function getProductSeoUrls() {
|
||||||
@ -236,15 +259,19 @@ export async function getProduct(handle: string | []): Promise<Product | undefin
|
|||||||
let productSW: ExtendedProduct | undefined;
|
let productSW: ExtendedProduct | undefined;
|
||||||
let productId: string | undefined;
|
let productId: string | undefined;
|
||||||
const productHandle = decodeURIComponent(transformHandle(handle));
|
const productHandle = decodeURIComponent(transformHandle(handle));
|
||||||
|
productId = productHandle; // if we do not use seoUrls the handle should be the product id
|
||||||
|
|
||||||
const seoUrlElement = await getFirstSeoUrlElement(productHandle);
|
if (useSeoUrls) {
|
||||||
if (seoUrlElement) {
|
const seoUrlElement = await getFirstSeoUrlElement(productHandle);
|
||||||
productId = seoUrlElement.foreignKey;
|
if (seoUrlElement) {
|
||||||
|
productId = seoUrlElement.foreignKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!productId) {
|
if (!productId) {
|
||||||
console.log('[getProduct][search] Did not found any product with handle:', productHandle);
|
console.log('[getProduct][search] Did not found any product with handle:', handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (productId) {
|
if (productId) {
|
||||||
const firstProduct = await getFirstProduct(productId);
|
const firstProduct = await getFirstProduct(productId);
|
||||||
if (firstProduct) {
|
if (firstProduct) {
|
||||||
|
@ -25,24 +25,30 @@ export function transformMenu(res: ExtendedCategory[], type: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function transformMenuItem(item: ExtendedCategory, type: string): Menu {
|
function transformMenuItem(item: ExtendedCategory, type: string): Menu {
|
||||||
|
const path =
|
||||||
|
`${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'
|
||||||
|
? item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo
|
||||||
|
? type === 'footer-navigation'
|
||||||
|
? '/cms/' + item.seoUrls[0].seoPathInfo
|
||||||
|
: '/search/' + item.seoUrls[0].seoPathInfo
|
||||||
|
: ''
|
||||||
|
: type === 'footer-navigation'
|
||||||
|
? '/cms/' + item.id ?? ''
|
||||||
|
: '/search/' + item.id ?? '';
|
||||||
|
|
||||||
// @ToDo: currently only footer-navigation is used for cms pages, this need to be more dynamic (shoud depending on the item)
|
// @ToDo: currently only footer-navigation is used for cms pages, this need to be more dynamic (shoud depending on the item)
|
||||||
return {
|
return {
|
||||||
id: item.id ?? '',
|
id: item.id ?? '',
|
||||||
title: item.name,
|
title: item.name,
|
||||||
children: item.children?.map((item) => transformMenuItem(item, type)) ?? [],
|
children: item.children?.map((item) => transformMenuItem(item, type)) ?? [],
|
||||||
path:
|
path: path,
|
||||||
item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo
|
|
||||||
? type === 'footer-navigation'
|
|
||||||
? '/cms/' + item.seoUrls[0].seoPathInfo
|
|
||||||
: '/search/' + item.seoUrls[0].seoPathInfo
|
|
||||||
: '',
|
|
||||||
type: item.children && item.children.length > 0 ? 'headline' : 'link'
|
type: item.children && item.children.length > 0 ? 'headline' : 'link'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformPage(
|
export function transformPage(
|
||||||
seoUrlElement: ApiSchemas['SeoUrl'],
|
category: ExtendedCategory,
|
||||||
category: ExtendedCategory
|
seoUrlElement?: ApiSchemas['SeoUrl']
|
||||||
): Page {
|
): Page {
|
||||||
let plainHtmlContent;
|
let plainHtmlContent;
|
||||||
if (category.cmsPage) {
|
if (category.cmsPage) {
|
||||||
@ -51,20 +57,20 @@ export function transformPage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: seoUrlElement.id ?? '',
|
id: seoUrlElement?.id ?? category.id ?? '',
|
||||||
title: category.translated?.metaTitle ?? category.name ?? '',
|
title: category.translated?.metaTitle ?? category.name ?? '',
|
||||||
handle: seoUrlElement.seoPathInfo,
|
handle: seoUrlElement?.seoPathInfo ?? category.id ?? '',
|
||||||
body: plainHtmlContent ?? category.description ?? '',
|
body: plainHtmlContent ?? category.description ?? '',
|
||||||
bodySummary: category.translated?.metaDescription ?? category.description ?? '',
|
bodySummary: category.translated?.metaDescription ?? category.description ?? '',
|
||||||
seo: {
|
seo: {
|
||||||
title: category.translated?.metaTitle ?? category.name ?? '',
|
title: category.translated?.metaTitle ?? category.name ?? '',
|
||||||
description: category.translated?.metaDescription ?? category.description ?? ''
|
description: category.translated?.metaDescription ?? category.description ?? ''
|
||||||
},
|
},
|
||||||
createdAt: seoUrlElement.createdAt ?? '',
|
createdAt: seoUrlElement?.createdAt ?? category.createdAt ?? '',
|
||||||
updatedAt: seoUrlElement.updatedAt ?? '',
|
updatedAt: seoUrlElement?.updatedAt ?? category.updatedAt ?? '',
|
||||||
routeName: seoUrlElement.routeName,
|
routeName: seoUrlElement?.routeName,
|
||||||
originalCmsPage: category.cmsPage,
|
originalCmsPage: category.cmsPage,
|
||||||
foreignKey: seoUrlElement.foreignKey
|
foreignKey: seoUrlElement?.foreignKey ?? category.id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,18 +95,22 @@ export function transformToPlainHtmlContent(cmsPage: ExtendedCmsPage): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function transformCollection(
|
export function transformCollection(
|
||||||
seoUrlElement: ApiSchemas['SeoUrl'],
|
resCategory: ExtendedCategory,
|
||||||
resCategory: ExtendedCategory
|
seoUrlElement?: ApiSchemas['SeoUrl']
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
handle: seoUrlElement.seoPathInfo,
|
handle: seoUrlElement?.seoPathInfo ?? resCategory.id ?? '',
|
||||||
title: resCategory.translated?.metaTitle ?? resCategory.name ?? '',
|
title: resCategory.translated?.metaTitle ?? resCategory.name ?? '',
|
||||||
description: resCategory.description ?? '',
|
description: resCategory.description ?? '',
|
||||||
seo: {
|
seo: {
|
||||||
title: resCategory.translated?.metaTitle ?? resCategory.name ?? '',
|
title: resCategory.translated?.metaTitle ?? resCategory.name ?? '',
|
||||||
description: resCategory.translated?.metaDescription ?? resCategory.description ?? ''
|
description: resCategory.translated?.metaDescription ?? resCategory.description ?? ''
|
||||||
},
|
},
|
||||||
updatedAt: seoUrlElement.updatedAt ?? seoUrlElement.createdAt ?? ''
|
updatedAt:
|
||||||
|
seoUrlElement?.updatedAt ??
|
||||||
|
seoUrlElement?.createdAt ??
|
||||||
|
resCategory.updatedAt ??
|
||||||
|
resCategory.createdAt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +126,10 @@ export function transformSubCollection(
|
|||||||
.filter((item) => item.visible)
|
.filter((item) => item.visible)
|
||||||
.filter((item) => item.type !== 'link')
|
.filter((item) => item.type !== 'link')
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
const handle = item.seoUrls ? findHandle(item.seoUrls, parentCollectionName) : undefined;
|
const handle =
|
||||||
|
item.seoUrls && `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true'
|
||||||
|
? findHandle(item.seoUrls, parentCollectionName)
|
||||||
|
: item.id;
|
||||||
if (handle) {
|
if (handle) {
|
||||||
collection.push({
|
collection.push({
|
||||||
handle: handle,
|
handle: handle,
|
||||||
@ -183,15 +196,21 @@ export function transformProducts(res: ExtendedProductListingResult): Product[]
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function transformProduct(item: ExtendedProduct): Product {
|
export function transformProduct(item: ExtendedProduct): Product {
|
||||||
|
const useSeoUrls = `${process.env.SHOPWARE_USE_SEO_URLS}` === 'true';
|
||||||
const productOptions = transformOptions(item);
|
const productOptions = transformOptions(item);
|
||||||
const productVariants = transformVariants(item);
|
const productVariants = transformVariants(item);
|
||||||
|
|
||||||
return {
|
let path = item.id ? item.id : '';
|
||||||
id: item.id ?? '',
|
if (useSeoUrls) {
|
||||||
path:
|
path =
|
||||||
item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo
|
item.seoUrls && item.seoUrls.length > 0 && item.seoUrls[0] && item.seoUrls[0].seoPathInfo
|
||||||
? item.seoUrls[0].seoPathInfo
|
? item.seoUrls[0].seoPathInfo
|
||||||
: '',
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id ?? '',
|
||||||
|
path: path,
|
||||||
availableForSale: item.available ?? false,
|
availableForSale: item.available ?? false,
|
||||||
title: item.translated ? item.translated.name ?? '' : item.name,
|
title: item.translated ? item.translated.name ?? '' : item.name,
|
||||||
description: item.translated?.metaDescription
|
description: item.translated?.metaDescription
|
||||||
|
Loading…
x
Reference in New Issue
Block a user